xref: /third_party/libcoap/src/coap_io_lwip.c (revision c87c5fba)
1/*
2 * Copyright (C) 2012,2014 Olaf Bergmann <bergmann@tzi.org>
3 *               2014 chrysn <chrysn@fsfe.org>
4 *               2022-2023 Jon Shallow <supjps-libcoap@jpshallow.com>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 *
8 * This file is part of the CoAP library libcoap. Please see
9 * README for terms of use.
10 */
11
12/**
13 * @file coap_io_lwip.c
14 * @brief LwIP specific functions
15 */
16
17#include "coap3/coap_internal.h"
18#include <lwip/udp.h>
19#include <lwip/timeouts.h>
20#include <lwip/tcpip.h>
21
22void
23coap_lwip_dump_memory_pools(coap_log_t log_level) {
24#if MEMP_STATS && LWIP_STATS_DISPLAY
25  int i;
26
27  /* Save time if not needed */
28  if (log_level > coap_get_log_level())
29    return;
30
31  coap_log(log_level, "*   LwIP custom memory pools information\n");
32  /*
33   * Make sure LwIP and libcoap have been built with the same
34   * -DCOAP_CLIENT_ONLY or -DCOAP_SERVER_ONLY options for
35   * MEMP_MAX to be correct.
36   */
37  for (i = 0; i < MEMP_MAX; i++) {
38    coap_log(log_level, "*    %-17s avail %3d  in-use %3d  peak %3d failed %3d\n",
39             memp_pools[i]->stats->name, memp_pools[i]->stats->avail,
40             memp_pools[i]->stats->used, memp_pools[i]->stats->max,
41             memp_pools[i]->stats->err);
42  }
43#endif /* MEMP_STATS && LWIP_STATS_DISPLAY */
44}
45
46void
47coap_lwip_set_input_wait_handler(coap_context_t *context,
48                                 coap_lwip_input_wait_handler_t handler,
49                                 void *input_arg) {
50  context->input_wait = handler;
51  context->input_arg = input_arg;
52}
53
54void
55coap_io_process_timeout(void *arg) {
56  coap_context_t *context = (coap_context_t *)arg;
57  coap_tick_t before;
58  unsigned int num_sockets;
59  unsigned int timeout;
60
61  coap_ticks(&before);
62  timeout = coap_io_prepare_io(context, NULL, 0, &num_sockets, before);
63  if (context->timer_configured) {
64    sys_untimeout(coap_io_process_timeout, (void *)context);
65    context->timer_configured = 0;
66  }
67  if (timeout == 0) {
68    /* Garbage collect 1 sec hence */
69    timeout = 1000;
70  }
71#ifdef COAP_DEBUG_WAKEUP_TIMES
72  coap_log_info("****** Next wakeup msecs %u (1)\n",
73                timeout);
74#endif /* COAP_DEBUG_WAKEUP_TIMES */
75  sys_timeout(timeout, coap_io_process_timeout, context);
76  context->timer_configured = 1;
77}
78
79int
80coap_io_process(coap_context_t *context, uint32_t timeout_ms) {
81  coap_tick_t before;
82  coap_tick_t now;
83  unsigned int num_sockets;
84  unsigned int timeout;
85
86  coap_ticks(&before);
87  timeout = coap_io_prepare_io(context, NULL, 0, &num_sockets, before);
88  if (timeout_ms != 0 && timeout_ms != COAP_IO_NO_WAIT &&
89      timeout > timeout_ms) {
90    timeout = timeout_ms;
91  }
92
93  LOCK_TCPIP_CORE();
94
95  if (context->timer_configured) {
96    sys_untimeout(coap_io_process_timeout, (void *)context);
97    context->timer_configured = 0;
98  }
99  if (timeout == 0) {
100    /* Garbage collect 1 sec hence */
101    timeout = 1000;
102  }
103#ifdef COAP_DEBUG_WAKEUP_TIMES
104  coap_log_info("****** Next wakeup msecs %u (2)\n",
105                timeout);
106#endif /* COAP_DEBUG_WAKEUP_TIMES */
107  sys_timeout(timeout, coap_io_process_timeout, context);
108  context->timer_configured = 1;
109
110  UNLOCK_TCPIP_CORE();
111
112  if (context->input_wait) {
113    context->input_wait(context->input_arg, timeout);
114  }
115
116  LOCK_TCPIP_CORE();
117
118  sys_check_timeouts();
119
120  UNLOCK_TCPIP_CORE();
121
122  coap_ticks(&now);
123  return (int)(((now - before) * 1000) / COAP_TICKS_PER_SECOND);
124}
125
126/*
127 * Not used for LwIP (done with coap_recvc()), but need dummy function.
128 */
129ssize_t
130coap_socket_recv(coap_socket_t *sock, coap_packet_t *packet) {
131  (void)sock;
132  (void)packet;
133  assert(0);
134  return -1;
135}
136
137#if COAP_CLIENT_SUPPORT
138/** Callback from lwIP when a package was received for a client.
139 *
140 * The current implementation deals this to coap_dispatch immediately, but
141 * other mechanisms (as storing the package in a queue and later fetching it
142 * when coap_io_do_io is called) can be envisioned.
143 *
144 * It handles everything coap_io_do_io does on other implementations.
145 */
146static void
147coap_recvc(void *arg, struct udp_pcb *upcb, struct pbuf *p,
148           const ip_addr_t *addr, u16_t port) {
149  coap_pdu_t *pdu = NULL;
150  coap_session_t *session = (coap_session_t *)arg;
151  int result = -1;
152  (void)upcb;
153  (void)addr;
154  (void)port;
155
156  assert(session);
157  LWIP_ASSERT("Proto not supported for LWIP", COAP_PROTO_NOT_RELIABLE(session->proto));
158
159  if (p->len < 4) {
160    /* Minimum size of CoAP header - ignore runt */
161    return;
162  }
163
164  coap_log_debug("*  %s: lwip:  recv %4d bytes\n",
165                 coap_session_str(session), p->len);
166  if (session->proto == COAP_PROTO_DTLS) {
167    if (session->tls) {
168      result = coap_dtls_receive(session, p->payload, p->len);
169      if (result < 0)
170        goto error;
171    }
172    pbuf_free(p);
173  } else {
174    pdu = coap_pdu_from_pbuf(p);
175    if (!pdu)
176      goto error;
177
178    if (!coap_pdu_parse(session->proto, p->payload, p->len, pdu)) {
179      goto error;
180    }
181    coap_dispatch(session->context, session, pdu);
182  }
183  coap_delete_pdu(pdu);
184  return;
185
186error:
187  /*
188   * https://rfc-editor.org/rfc/rfc7252#section-4.2 MUST send RST
189   * https://rfc-editor.org/rfc/rfc7252#section-4.3 MAY send RST
190   */
191  if (session)
192    coap_send_rst(session, pdu);
193  coap_delete_pdu(pdu);
194  return;
195}
196#endif /* ! COAP_CLIENT_SUPPORT */
197
198#if COAP_SERVER_SUPPORT
199
200static void
201coap_free_packet(coap_packet_t *packet) {
202  coap_free_type(COAP_PACKET, packet);
203}
204
205/** Callback from lwIP when a package was received for a server.
206 *
207 * The current implementation deals this to coap_dispatch immediately, but
208 * other mechanisms (as storing the package in a queue and later fetching it
209 * when coap_io_do_io is called) can be envisioned.
210 *
211 * It handles everything coap_io_do_io does on other implementations.
212 */
213static void
214coap_recvs(void *arg, struct udp_pcb *upcb, struct pbuf *p,
215           const ip_addr_t *addr, u16_t port) {
216  coap_endpoint_t *ep = (coap_endpoint_t *)arg;
217  coap_pdu_t *pdu = NULL;
218  coap_session_t *session = NULL;
219  coap_tick_t now;
220  coap_packet_t *packet;
221  int result = -1;
222
223  if (p->len < 4) {
224    /* Minimum size of CoAP header - ignore runt */
225    return;
226  }
227
228  packet = coap_malloc_type(COAP_PACKET, sizeof(coap_packet_t));
229
230  /* this is fatal because due to the short life of the packet, never should
231     there be more than one coap_packet_t required */
232  LWIP_ASSERT("Insufficient coap_packet_t resources.", packet != NULL);
233  /* Need to do this as there may be holes in addr_info */
234  memset(&packet->addr_info, 0, sizeof(packet->addr_info));
235  packet->length = p->len;
236  packet->payload = p->payload;
237  packet->addr_info.remote.port = port;
238  packet->addr_info.remote.addr = *addr;
239  packet->addr_info.local.port = upcb->local_port;
240  packet->addr_info.local.addr = *ip_current_dest_addr();
241  packet->ifindex = netif_get_index(ip_current_netif());
242
243  coap_ticks(&now);
244
245  session = coap_endpoint_get_session(ep, packet, now);
246  if (!session)
247    goto error;
248  LWIP_ASSERT("Proto not supported for LWIP", COAP_PROTO_NOT_RELIABLE(session->proto));
249
250  coap_log_debug("*  %s: lwip:  recv %4d bytes\n",
251                 coap_session_str(session), p->len);
252
253  if (session->proto == COAP_PROTO_DTLS) {
254    if (session->type == COAP_SESSION_TYPE_HELLO)
255      result = coap_dtls_hello(session, p->payload, p->len);
256    else if (session->tls)
257      result = coap_dtls_receive(session, p->payload, p->len);
258    if (session->type == COAP_SESSION_TYPE_HELLO && result == 1)
259      coap_session_new_dtls_session(session, now);
260    pbuf_free(p);
261  } else {
262    pdu = coap_pdu_from_pbuf(p);
263    if (!pdu)
264      goto error;
265
266    if (!coap_pdu_parse(ep->proto, p->payload, p->len, pdu)) {
267      goto error;
268    }
269    coap_dispatch(ep->context, session, pdu);
270  }
271
272  coap_delete_pdu(pdu);
273  coap_free_packet(packet);
274  return;
275
276error:
277  /*
278   * https://rfc-editor.org/rfc/rfc7252#section-4.2 MUST send RST
279   * https://rfc-editor.org/rfc/rfc7252#section-4.3 MAY send RST
280   */
281  if (session)
282    coap_send_rst(session, pdu);
283  coap_delete_pdu(pdu);
284  coap_free_packet(packet);
285  return;
286}
287
288#endif /* ! COAP_SERVER_SUPPORT */
289
290ssize_t
291coap_socket_send_pdu(coap_socket_t *sock, coap_session_t *session,
292                     coap_pdu_t *pdu) {
293  /* FIXME: we can't check this here with the existing infrastructure, but we
294  * should actually check that the pdu is not held by anyone but us. the
295  * respective pbuf is already exclusively owned by the pdu. */
296  struct pbuf *pbuf;
297  int err;
298
299  pbuf_realloc(pdu->pbuf, pdu->used_size + coap_pdu_parse_header_size(session->proto,
300               pdu->pbuf->payload));
301
302  if (coap_debug_send_packet()) {
303    /* Need to take a copy as we may be re-using the origin in a retransmit */
304    pbuf = pbuf_clone(PBUF_TRANSPORT, PBUF_RAM, pdu->pbuf);
305    if (pbuf == NULL)
306      return -1;
307    err = udp_sendto(sock->pcb, pbuf, &session->addr_info.remote.addr,
308                     session->addr_info.remote.port);
309    pbuf_free(pbuf);
310    if (err < 0)
311      return -1;
312  }
313  return pdu->used_size;
314}
315
316/*
317 * dgram
318 * return +ve Number of bytes written.
319 *         -1 Error error in errno).
320 */
321ssize_t
322coap_socket_send(coap_socket_t *sock, const coap_session_t *session,
323                 const uint8_t *data, size_t data_len) {
324  struct pbuf *pbuf;
325  int err;
326
327  if (coap_debug_send_packet()) {
328    pbuf = pbuf_alloc(PBUF_TRANSPORT, data_len, PBUF_RAM);
329    if (pbuf == NULL)
330      return -1;
331    memcpy(pbuf->payload, data, data_len);
332
333    LOCK_TCPIP_CORE();
334
335    err = udp_sendto(sock->pcb, pbuf, &session->addr_info.remote.addr,
336                     session->addr_info.remote.port);
337
338    UNLOCK_TCPIP_CORE();
339
340    pbuf_free(pbuf);
341    if (err < 0)
342      return -1;
343  }
344  return data_len;
345}
346
347#if COAP_SERVER_SUPPORT
348int
349coap_socket_bind_udp(coap_socket_t *sock,
350                     const coap_address_t *listen_addr,
351                     coap_address_t *bound_addr) {
352  int err;
353  coap_address_t l_listen = *listen_addr;
354
355  sock->pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
356  if (sock->pcb == NULL)
357    return 0;
358
359#if LWIP_IPV6 && LWIP_IPV4
360  if (l_listen.addr.type == IPADDR_TYPE_V6)
361    l_listen.addr.type = IPADDR_TYPE_ANY;
362#endif /* LWIP_IPV6 && LWIP_IPV4 */
363  udp_recv(sock->pcb, coap_recvs, (void *)sock->endpoint);
364  err = udp_bind(sock->pcb, &l_listen.addr, l_listen.port);
365  if (err) {
366    udp_remove(sock->pcb);
367    sock->pcb = NULL;
368  }
369  *bound_addr = l_listen;
370  return err ? 0 : 1;
371}
372#endif /* COAP_SERVER_SUPPORT */
373
374#if COAP_CLIENT_SUPPORT
375int
376coap_socket_connect_udp(coap_socket_t *sock,
377                        const coap_address_t *local_if,
378                        const coap_address_t *server,
379                        int default_port,
380                        coap_address_t *local_addr,
381                        coap_address_t *remote_addr) {
382  err_t err;
383  struct udp_pcb *pcb;
384
385  (void)local_if;
386  (void)default_port;
387  (void)local_addr;
388  (void)remote_addr;
389
390  LOCK_TCPIP_CORE();
391
392  pcb = udp_new();
393
394  if (!pcb) {
395    goto err_unlock;
396  }
397
398  err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
399  if (err) {
400    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
401                ("coap_socket_connect_udp: port bind failed\n"));
402    goto err_udp_remove;
403  }
404
405  sock->session->addr_info.local.port = pcb->local_port;
406
407  err = udp_connect(pcb, &server->addr, server->port);
408  if (err) {
409    goto err_udp_unbind;
410  }
411
412#if LWIP_IPV6 && LWIP_IPV4
413  pcb->local_ip.type = pcb->remote_ip.type;
414#endif /* LWIP_IPV6 && LWIP_IPV4 */
415
416  sock->pcb = pcb;
417
418  udp_recv(sock->pcb, coap_recvc, (void *)sock->session);
419
420  UNLOCK_TCPIP_CORE();
421
422  return 1;
423
424err_udp_unbind:
425err_udp_remove:
426  udp_remove(pcb);
427err_unlock:
428  UNLOCK_TCPIP_CORE();
429  return 0;
430}
431#endif /* ! COAP_CLIENT_SUPPORT */
432
433#if ! COAP_DISABLE_TCP
434int
435coap_socket_connect_tcp1(coap_socket_t *sock,
436                         const coap_address_t *local_if,
437                         const coap_address_t *server,
438                         int default_port,
439                         coap_address_t *local_addr,
440                         coap_address_t *remote_addr) {
441  (void)sock;
442  (void)local_if;
443  (void)server;
444  (void)default_port;
445  (void)local_addr;
446  (void)remote_addr;
447  return 0;
448}
449
450int
451coap_socket_connect_tcp2(coap_socket_t *sock,
452                         coap_address_t *local_addr,
453                         coap_address_t *remote_addr) {
454  (void)sock;
455  (void)local_addr;
456  (void)remote_addr;
457  return 0;
458}
459
460int
461coap_socket_bind_tcp(coap_socket_t *sock,
462                     const coap_address_t *listen_addr,
463                     coap_address_t *bound_addr) {
464  (void)sock;
465  (void)listen_addr;
466  (void)bound_addr;
467  return 0;
468}
469
470int
471coap_socket_accept_tcp(coap_socket_t *server,
472                       coap_socket_t *new_client,
473                       coap_address_t *local_addr,
474                       coap_address_t *remote_addr) {
475  (void)server;
476  (void)new_client;
477  (void)local_addr;
478  (void)remote_addr;
479  return 0;
480}
481#endif /* !COAP_DISABLE_TCP */
482
483ssize_t
484coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) {
485  (void)sock;
486  (void)data;
487  (void)data_len;
488  return -1;
489}
490
491ssize_t
492coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) {
493  (void)sock;
494  (void)data;
495  (void)data_len;
496  return -1;
497}
498
499void
500coap_socket_close(coap_socket_t *sock) {
501  if (sock->pcb) {
502    LOCK_TCPIP_CORE();
503    udp_remove(sock->pcb);
504    UNLOCK_TCPIP_CORE();
505  }
506  sock->pcb = NULL;
507  return;
508}
509