• 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   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  */
321 ssize_t
coap_socket_send(coap_socket_t * sock,const coap_session_t * session,const uint8_t * data,size_t data_len)322 coap_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
348 int
coap_socket_bind_udp(coap_socket_t * sock,const coap_address_t * listen_addr,coap_address_t * bound_addr)349 coap_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
375 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)376 coap_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 
424 err_udp_unbind:
425 err_udp_remove:
426   udp_remove(pcb);
427 err_unlock:
428   UNLOCK_TCPIP_CORE();
429   return 0;
430 }
431 #endif /* ! COAP_CLIENT_SUPPORT */
432 
433 #if ! COAP_DISABLE_TCP
434 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)435 coap_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 
450 int
coap_socket_connect_tcp2(coap_socket_t * sock,coap_address_t * local_addr,coap_address_t * remote_addr)451 coap_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 
460 int
coap_socket_bind_tcp(coap_socket_t * sock,const coap_address_t * listen_addr,coap_address_t * bound_addr)461 coap_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 
470 int
coap_socket_accept_tcp(coap_socket_t * server,coap_socket_t * new_client,coap_address_t * local_addr,coap_address_t * remote_addr)471 coap_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 
483 ssize_t
coap_socket_write(coap_socket_t * sock,const uint8_t * data,size_t data_len)484 coap_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 
491 ssize_t
coap_socket_read(coap_socket_t * sock,uint8_t * data,size_t data_len)492 coap_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 
499 void
coap_socket_close(coap_socket_t * sock)500 coap_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