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