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