• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 void
coap_lwip_dump_memory_pools(coap_log_t log_level)23 coap_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 
46 void
coap_lwip_set_input_wait_handler(coap_context_t * context,coap_lwip_input_wait_handler_t handler,void * input_arg)47 coap_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 
54 void
coap_io_process_timeout(void * arg)55 coap_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 
79 int
coap_io_process(coap_context_t * context,uint32_t timeout_ms)80 coap_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  */
129 ssize_t
coap_socket_recv(coap_socket_t * sock,coap_packet_t * packet)130 coap_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  */
146 static void
coap_recvc(void * arg,struct udp_pcb * upcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)147 coap_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 
186 error:
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 
200 static void
coap_free_packet(coap_packet_t * packet)201 coap_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  */
213 static void
coap_recvs(void * arg,struct udp_pcb * upcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)214 coap_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 
276 error:
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 
290 ssize_t
coap_socket_send_pdu(coap_socket_t * sock,coap_session_t * session,coap_pdu_t * pdu)291 coap_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 #ifdef SUPPORT_OPTIC_ONT
300   pbuf_realloc(pdu->pbuf, pdu->used_size + coap_pdu_get_fix_header_size());
301 #else
302   pbuf_realloc(pdu->pbuf, pdu->used_size + coap_pdu_parse_header_size(session->proto, pdu->pbuf->payload));
303 #endif
304 
305   if (coap_debug_send_packet()) {
306     /* Need to take a copy as we may be re-using the origin in a retransmit */
307     pbuf = pbuf_clone(PBUF_TRANSPORT, PBUF_RAM, pdu->pbuf);
308     if (pbuf == NULL)
309       return -1;
310     err = udp_sendto(sock->pcb, pbuf, &session->addr_info.remote.addr,
311                      session->addr_info.remote.port);
312     pbuf_free(pbuf);
313     if (err < 0)
314       return -1;
315   }
316   return pdu->used_size;
317 }
318 
319 /*
320  * dgram
321  * return +ve Number of bytes written.
322  *         -1 Error error in errno).
323  */
324 ssize_t
coap_socket_send(coap_socket_t * sock,const coap_session_t * session,const uint8_t * data,size_t data_len)325 coap_socket_send(coap_socket_t *sock, const coap_session_t *session,
326                  const uint8_t *data, size_t data_len) {
327   struct pbuf *pbuf;
328   int err;
329 
330   if (coap_debug_send_packet()) {
331     pbuf = pbuf_alloc(PBUF_TRANSPORT, data_len, PBUF_RAM);
332     if (pbuf == NULL)
333       return -1;
334     memcpy(pbuf->payload, data, data_len);
335 
336     LOCK_TCPIP_CORE();
337 
338     err = udp_sendto(sock->pcb, pbuf, &session->addr_info.remote.addr,
339                      session->addr_info.remote.port);
340 
341     UNLOCK_TCPIP_CORE();
342 
343     pbuf_free(pbuf);
344     if (err < 0)
345       return -1;
346   }
347   return data_len;
348 }
349 
350 #if COAP_SERVER_SUPPORT
351 int
coap_socket_bind_udp(coap_socket_t * sock,const coap_address_t * listen_addr,coap_address_t * bound_addr)352 coap_socket_bind_udp(coap_socket_t *sock,
353                      const coap_address_t *listen_addr,
354                      coap_address_t *bound_addr) {
355   int err;
356   coap_address_t l_listen = *listen_addr;
357 
358   sock->pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
359   if (sock->pcb == NULL)
360     return 0;
361 
362 #if LWIP_IPV6 && LWIP_IPV4
363   if (l_listen.addr.type == IPADDR_TYPE_V6)
364     l_listen.addr.type = IPADDR_TYPE_ANY;
365 #endif /* LWIP_IPV6 && LWIP_IPV4 */
366   udp_recv(sock->pcb, coap_recvs, (void *)sock->endpoint);
367   err = udp_bind(sock->pcb, &l_listen.addr, l_listen.port);
368   if (err) {
369     udp_remove(sock->pcb);
370     sock->pcb = NULL;
371   }
372   *bound_addr = l_listen;
373   return err ? 0 : 1;
374 }
375 #endif /* COAP_SERVER_SUPPORT */
376 
377 #if COAP_CLIENT_SUPPORT
378 int
coap_socket_connect_udp(coap_socket_t * sock,const coap_address_t * local_if,const coap_address_t * server,int default_port,coap_address_t * local_addr,coap_address_t * remote_addr)379 coap_socket_connect_udp(coap_socket_t *sock,
380                         const coap_address_t *local_if,
381                         const coap_address_t *server,
382                         int default_port,
383                         coap_address_t *local_addr,
384                         coap_address_t *remote_addr) {
385   err_t err;
386   struct udp_pcb *pcb;
387 
388   (void)local_if;
389   (void)default_port;
390   (void)local_addr;
391   (void)remote_addr;
392 
393   LOCK_TCPIP_CORE();
394 
395   pcb = udp_new();
396 
397   if (!pcb) {
398     goto err_unlock;
399   }
400 
401   err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
402   if (err) {
403     LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
404                 ("coap_socket_connect_udp: port bind failed\n"));
405     goto err_udp_remove;
406   }
407 
408   sock->session->addr_info.local.port = pcb->local_port;
409 
410   err = udp_connect(pcb, &server->addr, server->port);
411   if (err) {
412     goto err_udp_unbind;
413   }
414 
415 #if LWIP_IPV6 && LWIP_IPV4
416   pcb->local_ip.type = pcb->remote_ip.type;
417 #endif /* LWIP_IPV6 && LWIP_IPV4 */
418 
419   sock->pcb = pcb;
420 
421   udp_recv(sock->pcb, coap_recvc, (void *)sock->session);
422 
423   UNLOCK_TCPIP_CORE();
424 
425   return 1;
426 
427 err_udp_unbind:
428 err_udp_remove:
429   udp_remove(pcb);
430 err_unlock:
431   UNLOCK_TCPIP_CORE();
432   return 0;
433 }
434 #endif /* ! COAP_CLIENT_SUPPORT */
435 
436 #if ! COAP_DISABLE_TCP
437 int
coap_socket_connect_tcp1(coap_socket_t * sock,const coap_address_t * local_if,const coap_address_t * server,int default_port,coap_address_t * local_addr,coap_address_t * remote_addr)438 coap_socket_connect_tcp1(coap_socket_t *sock,
439                          const coap_address_t *local_if,
440                          const coap_address_t *server,
441                          int default_port,
442                          coap_address_t *local_addr,
443                          coap_address_t *remote_addr) {
444   (void)sock;
445   (void)local_if;
446   (void)server;
447   (void)default_port;
448   (void)local_addr;
449   (void)remote_addr;
450   return 0;
451 }
452 
453 int
coap_socket_connect_tcp2(coap_socket_t * sock,coap_address_t * local_addr,coap_address_t * remote_addr)454 coap_socket_connect_tcp2(coap_socket_t *sock,
455                          coap_address_t *local_addr,
456                          coap_address_t *remote_addr) {
457   (void)sock;
458   (void)local_addr;
459   (void)remote_addr;
460   return 0;
461 }
462 
463 int
coap_socket_bind_tcp(coap_socket_t * sock,const coap_address_t * listen_addr,coap_address_t * bound_addr)464 coap_socket_bind_tcp(coap_socket_t *sock,
465                      const coap_address_t *listen_addr,
466                      coap_address_t *bound_addr) {
467   (void)sock;
468   (void)listen_addr;
469   (void)bound_addr;
470   return 0;
471 }
472 
473 int
coap_socket_accept_tcp(coap_socket_t * server,coap_socket_t * new_client,coap_address_t * local_addr,coap_address_t * remote_addr)474 coap_socket_accept_tcp(coap_socket_t *server,
475                        coap_socket_t *new_client,
476                        coap_address_t *local_addr,
477                        coap_address_t *remote_addr) {
478   (void)server;
479   (void)new_client;
480   (void)local_addr;
481   (void)remote_addr;
482   return 0;
483 }
484 #endif /* !COAP_DISABLE_TCP */
485 
486 ssize_t
coap_socket_write(coap_socket_t * sock,const uint8_t * data,size_t data_len)487 coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) {
488   (void)sock;
489   (void)data;
490   (void)data_len;
491   return -1;
492 }
493 
494 ssize_t
coap_socket_read(coap_socket_t * sock,uint8_t * data,size_t data_len)495 coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) {
496   (void)sock;
497   (void)data;
498   (void)data_len;
499   return -1;
500 }
501 
502 void
coap_socket_close(coap_socket_t * sock)503 coap_socket_close(coap_socket_t *sock) {
504   if (sock->pcb) {
505     LOCK_TCPIP_CORE();
506     udp_remove(sock->pcb);
507     UNLOCK_TCPIP_CORE();
508   }
509   sock->pcb = NULL;
510   return;
511 }
512