libcoap 4.3.5-develop-aef27f2
Loading...
Searching...
No Matches
coap_dgrm_lwip.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2012,2014 Olaf Bergmann <bergmann@tzi.org>
3 * 2014 chrysn <chrysn@fsfe.org>
4 * 2022-2025 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
18
19#if defined(WITH_LWIP)
20
21#include <lwip/udp.h>
22#include <lwip/timeouts.h>
23#include <lwip/tcpip.h>
24
25#if ! COAP_DISABLE_TCP
26#include <lwip/tcp.h>
27#endif /* !COAP_DISABLE_TCP */
28
29#if NO_SYS == 0
30extern sys_sem_t coap_io_timeout_sem;
31extern uint32_t coap_lwip_in_call_back_ref;
32#endif /* NO_SYS == 0 */
33
34typedef struct {
35 coap_session_t *session;
36 struct udp_pcb *udp_pcb;
37 struct pbuf *pbuf;
38 coap_address_t addr;
39} coap_lwip_udp_t;
40
41/*
42 * Not used for LwIP (done with coap_recvc()), but need dummy function.
43 */
44ssize_t
46 (void)sock;
47 (void)packet;
48 assert(0);
49 return -1;
50}
51
52#if COAP_CLIENT_SUPPORT
61static void
62coap_recvc(void *arg, struct udp_pcb *upcb, struct pbuf *p,
63 const ip_addr_t *addr, u16_t port) {
64 coap_pdu_t *pdu = NULL;
65 coap_session_t *session = (coap_session_t *)arg;
66 int result = -1;
67 (void)upcb;
68
69 assert(session);
70 LWIP_ASSERT("Proto not supported for LWIP", COAP_PROTO_NOT_RELIABLE(session->proto));
71
72 if (p->len < 4) {
73 /* Minimum size of CoAP header - ignore runt */
74 return;
75 }
76 coap_lock_lock(return);
77 memcpy(&session->addr_info.remote.addr, addr, sizeof(session->addr_info.remote.addr));
78 coap_address_set_port(&session->addr_info.remote, port);
79
80 coap_log_debug("* %s: lwip: recv %4d bytes\n",
81 coap_session_str(session), p->len);
82 if (session->proto == COAP_PROTO_DTLS) {
83 if (session->tls) {
84 result = coap_dtls_receive(session, p->payload, p->len);
85 if (result < 0)
86 goto error;
87 }
88 pbuf_free(p);
89 } else {
90 coap_opt_filter_t error_opts;
91
92 pdu = coap_pdu_from_pbuf(p);
93 if (!pdu)
94 goto error;
95
96 coap_option_filter_clear(&error_opts);
97 if (!coap_pdu_parse2(session->proto, p->payload, p->len, pdu, &error_opts)) {
99 coap_log_warn("discard malformed PDU\n");
100 if (error_opts.mask && COAP_PDU_IS_REQUEST(pdu)) {
101 coap_pdu_t *response =
103 COAP_RESPONSE_CODE(402), &error_opts);
104 if (!response) {
105 coap_log_warn("coap_handle_dgram: cannot create error response\n");
106 } else {
107 if (coap_send_internal(session, response, NULL) == COAP_INVALID_MID)
108 coap_log_warn("coap_handle_dgram: error sending response\n");
109 }
112#if NO_SYS == 0
113 sys_sem_signal(&coap_io_timeout_sem);
114#endif /* NO_SYS == 0 */
115 return;
116 } else {
117 goto error;
118 }
119 }
120#if NO_SYS == 0
121 coap_lwip_in_call_back_ref++;
122#endif /* NO_SYS == 0 */
123 coap_dispatch(session->context, session, pdu);
124#if NO_SYS == 0
125 coap_lwip_in_call_back_ref--;
126#endif /* NO_SYS == 0 */
127 }
128#if NO_SYS == 0
129 sys_sem_signal(&coap_io_timeout_sem);
130#endif /* NO_SYS == 0 */
133 return;
134
135error:
136 /*
137 * https://rfc-editor.org/rfc/rfc7252#section-4.2 MUST send RST
138 * https://rfc-editor.org/rfc/rfc7252#section-4.3 MAY send RST
139 */
140 if (session)
141 coap_send_rst_lkd(session, pdu);
144#if NO_SYS == 0
145 sys_sem_signal(&coap_io_timeout_sem);
146#endif /* NO_SYS == 0 */
147 return;
148}
149#endif /* ! COAP_CLIENT_SUPPORT */
150
151#if COAP_SERVER_SUPPORT
152
153static void
154coap_free_packet(coap_packet_t *packet) {
156}
157
166static void
167coap_udp_recvs(void *arg, struct udp_pcb *upcb, struct pbuf *p,
168 const ip_addr_t *addr, u16_t port) {
169 coap_endpoint_t *ep = (coap_endpoint_t *)arg;
170 coap_pdu_t *pdu = NULL;
171 coap_session_t *session = NULL;
172 coap_tick_t now;
173 coap_packet_t *packet = NULL;
174 int result = -1;
175
176 if (p->len < 4) {
177 /* Minimum size of CoAP header - ignore runt */
178 goto error_free_pbuf;
179 }
180
181 packet = coap_malloc_type(COAP_PACKET, sizeof(coap_packet_t));
182
183 /* this is fatal because due to the short life of the packet, never should
184 there be more than one coap_packet_t required */
185 LWIP_ASSERT("Insufficient coap_packet_t resources.", packet != NULL);
186 /* Need to do this as there may be holes in addr_info */
187 memset(&packet->addr_info, 0, sizeof(packet->addr_info));
188 packet->length = p->len;
189 packet->payload = p->payload;
190 packet->addr_info.remote.port = port;
191 packet->addr_info.remote.addr = *addr;
192 packet->addr_info.local.port = upcb->local_port;
193 packet->addr_info.local.addr = *ip_current_dest_addr();
194 packet->ifindex = netif_get_index(ip_current_netif());
195
196 coap_ticks(&now);
197
198 coap_lock_lock(goto error_free_pbuf);
199 session = coap_endpoint_get_session(ep, packet, now);
200 if (!session)
201 goto error_free_pbuf;
202 LWIP_ASSERT("Proto not supported for LWIP", COAP_PROTO_NOT_RELIABLE(session->proto));
203
204 coap_log_debug("* %s: lwip: recv %4d bytes\n",
205 coap_session_str(session), p->len);
206
207 if (session->proto == COAP_PROTO_DTLS) {
208 if (session->type == COAP_SESSION_TYPE_HELLO)
209 result = coap_dtls_hello(session, p->payload, p->len);
210 else if (session->tls)
211 result = coap_dtls_receive(session, p->payload, p->len);
212 if (session->type == COAP_SESSION_TYPE_HELLO && result == 1)
213 coap_session_new_dtls_session(session, now);
214 pbuf_free(p);
215 } else {
216 coap_opt_filter_t error_opts;
217
218 pdu = coap_pdu_from_pbuf(p);
219 if (!pdu)
220 goto error;
221
222 coap_option_filter_clear(&error_opts);
223 if (!coap_pdu_parse2(ep->proto, p->payload, p->len, pdu, &error_opts)) {
225 coap_log_warn("discard malformed PDU\n");
226 if (error_opts.mask && COAP_PDU_IS_REQUEST(pdu)) {
227 coap_pdu_t *response =
229 COAP_RESPONSE_CODE(402), &error_opts);
230 if (!response) {
231 coap_log_warn("coap_handle_dgram: cannot create error response\n");
232 } else {
233 if (coap_send_internal(session, response, NULL) == COAP_INVALID_MID)
234 coap_log_warn("coap_handle_dgram: error sending response\n");
235 }
236 goto cleanup;
237 } else {
238 goto error;
239 }
240 }
241#if NO_SYS == 0
242 coap_lwip_in_call_back_ref++;
243#endif /* NO_SYS == 0 */
244 coap_dispatch(ep->context, session, pdu);
245#if NO_SYS == 0
246 coap_lwip_in_call_back_ref--;
247#endif /* NO_SYS == 0 */
248 }
249
251 coap_free_packet(packet);
253#if NO_SYS == 0
254 sys_sem_signal(&coap_io_timeout_sem);
255#endif /* NO_SYS == 0 */
256 return;
257
258error_free_pbuf:
259 pbuf_free(p);
260
261error:
262 /*
263 * https://rfc-editor.org/rfc/rfc7252#section-4.2 MUST send RST
264 * https://rfc-editor.org/rfc/rfc7252#section-4.3 MAY send RST
265 */
266 if (session && pdu)
267 coap_send_rst_lkd(session, pdu);
268cleanup:
270 coap_free_packet(packet);
272#if NO_SYS == 0
273 sys_sem_signal(&coap_io_timeout_sem);
274#endif /* NO_SYS == 0 */
275 return;
276}
277
278#endif /* ! COAP_SERVER_SUPPORT */
279
280#if NO_SYS == 0
281static void
282coap_lwip_udp_sendto(void *ctx) {
283 coap_lwip_udp_t *cb_ctx = (coap_lwip_udp_t *)ctx;
284 int err;
285
286 if (cb_ctx) {
287 err = udp_sendto(cb_ctx->udp_pcb, cb_ctx->pbuf, &cb_ctx->addr.addr,
288 cb_ctx->addr.port);
289 if (err < 0) {
290 if (err == ERR_RTE) {
291 coap_log_warn("** %s: udp_sendto: Packet not routable\n",
292 coap_session_str(cb_ctx->session));
293 } else {
294 coap_log_warn("** %s: udp_sendto: error %d\n",
295 coap_session_str(cb_ctx->session), err);
296 }
297 }
298 pbuf_free(cb_ctx->pbuf);
300 }
301}
302#endif /* NO_SYS == 0 */
303
304/*
305 * dgram
306 * return +ve Number of bytes written.
307 * -1 Error error in errno).
308 */
309ssize_t
311 const uint8_t *data, size_t data_len) {
312 struct pbuf *pbuf;
313 int err;
314
316 pbuf = pbuf_alloc(PBUF_TRANSPORT, data_len, PBUF_RAM);
317 if (pbuf == NULL) {
318 errno = ENOMEM;
319 return -1;
320 }
321 memcpy(pbuf->payload, data, data_len);
322
323#if NO_SYS == 0
324 if (coap_lwip_in_call_back_ref == 0) {
325 coap_lwip_udp_t *cb_ctx = coap_malloc_type(COAP_STRING, sizeof(coap_lwip_udp_t));
326
327 if (!cb_ctx) {
328 coap_log_warn("** %s: coap_socket_send: error\n",
329 coap_session_str(session));
330 pbuf_free(pbuf);
331 errno = ENOMEM;
332 return -1;
333 }
334 cb_ctx->session = session;
335 cb_ctx->pbuf = pbuf;
336 cb_ctx->udp_pcb = sock->udp_pcb;
337 cb_ctx->addr = session->addr_info.remote;
338 err = tcpip_try_callback(coap_lwip_udp_sendto, cb_ctx);
339
340 if (err < 0) {
341 coap_log_warn("** %s: tcpip_try_callback: error %d\n",
342 coap_session_str(session), err);
343 errno = EAGAIN;
344 return -1;
345 }
346 } else {
347#endif /* NO_SYS == 0 */
348 err = udp_sendto(sock->udp_pcb, pbuf, &session->addr_info.remote.addr,
349 session->addr_info.remote.port);
350 pbuf_free(pbuf);
351 if (err < 0) {
352 if (err == ERR_RTE) {
353 coap_log_warn("** %s: udp_sendto: Packet not routable\n",
354 coap_session_str(session));
355 } else {
356 coap_log_warn("** %s: udp_sendto: error %d\n",
357 coap_session_str(session), err);
358 }
359 return -1;
360 }
361#if NO_SYS == 0
362 }
363#endif /* NO_SYS == 0 */
364 }
365 return data_len;
366}
367
368#if COAP_SERVER_SUPPORT
369int
371 const coap_address_t *listen_addr,
372 coap_address_t *bound_addr) {
373 int err;
374 coap_address_t l_listen = *listen_addr;
375
376 sock->udp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
377 if (sock->udp_pcb == NULL)
378 return 0;
379
380#if LWIP_IPV6 && LWIP_IPV4
381 if (l_listen.addr.type == IPADDR_TYPE_V6)
382 l_listen.addr.type = IPADDR_TYPE_ANY;
383#endif /* LWIP_IPV6 && LWIP_IPV4 */
384 udp_recv(sock->udp_pcb, coap_udp_recvs, (void *)sock->endpoint);
385 err = udp_bind(sock->udp_pcb, &l_listen.addr, l_listen.port);
386 if (err) {
387 udp_remove(sock->udp_pcb);
388 sock->udp_pcb = NULL;
389 }
390 *bound_addr = l_listen;
391 return err ? 0 : 1;
392}
393#endif /* COAP_SERVER_SUPPORT */
394
395#if COAP_CLIENT_SUPPORT
396int
398 const coap_address_t *local_if,
399 const coap_address_t *server,
400 int default_port,
401 coap_address_t *local_addr,
402 coap_address_t *remote_addr) {
403 err_t err;
404 struct udp_pcb *pcb;
405 int is_mcast = coap_is_mcast(server);
406 coap_address_t connect_addr;
407
408 coap_address_copy(&connect_addr, server);
409 if (connect_addr.port == 0)
410 connect_addr.port = default_port;
411
412 coap_lock_invert(LOCK_TCPIP_CORE(),
413 goto err_unlock);
414
415 pcb = udp_new();
416
417 if (!pcb) {
418 goto err_unlock;
419 }
420
421 if (local_if) {
422 pcb->local_ip = local_if->addr;
423 pcb->local_port = local_if->port;
424 }
425 err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
426 if (err) {
427 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
428 ("coap_socket_connect_udp: port bind failed\n"));
429 goto err_udp_remove;
430 }
431
432 if (local_addr) {
433 local_addr->addr = pcb->local_ip;
434 local_addr->port = pcb->local_port;
435 }
436 sock->session->addr_info.local.port = pcb->local_port;
437
438 if (remote_addr) {
439 coap_address_copy(remote_addr, &connect_addr);
440 }
441
442 if (is_mcast) {
443 coap_address_copy(&sock->mcast_addr, &connect_addr);
445 } else {
446 err = udp_connect(pcb, &connect_addr.addr, connect_addr.port);
447 if (err) {
448 goto err_udp_unbind;
449 }
450 }
451
452#if LWIP_IPV6 && LWIP_IPV4
453 pcb->local_ip.type = pcb->remote_ip.type;
454#endif /* LWIP_IPV6 && LWIP_IPV4 */
455
456 sock->udp_pcb = pcb;
457
458 udp_recv(sock->udp_pcb, coap_recvc, (void *)sock->session);
459
460 UNLOCK_TCPIP_CORE();
461
462 return 1;
463
464err_udp_unbind:
465err_udp_remove:
466 udp_remove(pcb);
467err_unlock:
468 UNLOCK_TCPIP_CORE();
469 return 0;
470}
471#endif /* ! COAP_CLIENT_SUPPORT */
472
473#if NO_SYS == 0
474static void
475coap_lwip_udp_remove(void *ctx) {
476 struct udp_pcb *udp_pcb = (struct udp_pcb *)ctx;
477
478 if (udp_pcb) {
479 udp_remove(udp_pcb);
480 }
481}
482#endif /* NO_SYS == 0 */
483
484void
486 struct udp_pcb *udp_pcb = sock->udp_pcb;
487 err_t err;
488
489 sock->udp_pcb = NULL;
490 if (udp_pcb) {
491#if NO_SYS == 0
492 if (coap_lwip_in_call_back_ref == 0) {
493 err = tcpip_try_callback(coap_lwip_udp_remove, udp_pcb);
494
495 if (err < 0) {
496 coap_log_warn("** %s: tcpip_try_callback: error %d\n",
497 sock->session ? coap_session_str(sock->session) : "", err);
498 errno = EAGAIN;
499 return;
500 }
501 } else {
502#endif /* NO_SYS == 0 */
503 udp_remove(udp_pcb);
504#if NO_SYS == 0
505 }
506#endif /* NO_SYS == 0 */
507 }
508 return;
509}
510
511#else /* ! WITH_LWIP */
512
513#ifdef __clang__
514/* Make compilers happy that do not like empty modules. As this function is
515 * never used, we ignore -Wunused-function at the end of compiling this file
516 */
517#pragma GCC diagnostic ignored "-Wunused-function"
518#endif
519static inline void
520dummy(void) {
521}
522
523#endif /* ! WITH_LWIP */
void coap_address_set_port(coap_address_t *addr, uint16_t port)
Set the port field of addr to port (in host byte order).
int coap_is_mcast(const coap_address_t *a)
Checks if given address a denotes a multicast address.
void coap_address_copy(coap_address_t *dst, const coap_address_t *src)
int coap_debug_send_packet(void)
Check to see whether a packet should be sent or not.
static void dummy(void)
#define COAP_SOCKET_MULTICAST
socket is used for multicast communication
Library specific build wrapper for coap_internal.h.
@ COAP_PACKET
Definition coap_mem.h:37
@ COAP_STRING
Definition coap_mem.h:34
void * coap_malloc_type(coap_memory_tag_t type, size_t size)
Allocates a chunk of size bytes and returns a pointer to the newly allocated memory.
void coap_free_type(coap_memory_tag_t type, void *p)
Releases the memory that was allocated by coap_malloc_type().
int coap_dtls_receive(coap_session_t *session COAP_UNUSED, const uint8_t *data COAP_UNUSED, size_t data_len COAP_UNUSED)
Definition coap_notls.c:243
coap_mid_t coap_send_rst_lkd(coap_session_t *session, const coap_pdu_t *request)
Sends an RST message with code 0 for the specified request to dst.
Definition coap_net.c:1067
uint64_t coap_tick_t
This data type represents internal timer ticks with COAP_TICKS_PER_SECOND resolution.
Definition coap_time.h:151
int coap_handle_event_lkd(coap_context_t *context, coap_event_t event, coap_session_t *session)
Invokes the event handler of context for the given event and data.
Definition coap_net.c:4915
void coap_dispatch(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu)
Dispatches the PDUs from the receive queue in given context.
Definition coap_net.c:4378
coap_mid_t coap_send_internal(coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *request_pdu)
Sends a CoAP message to given peer.
Definition coap_net.c:1836
coap_pdu_t * coap_new_error_response(const coap_pdu_t *request, coap_pdu_code_t code, coap_opt_filter_t *opts)
Creates a new ACK PDU with specified error code.
Definition coap_net.c:3112
void coap_ticks(coap_tick_t *t)
Returns the current value of an internal tick counter.
Definition coap_time.c:90
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)
Create a new UDP socket and 'connect' it to the address tuple.
ssize_t coap_socket_recv(coap_socket_t *sock, coap_packet_t *packet)
Function interface for reading data.
ssize_t coap_socket_send(coap_socket_t *sock, coap_session_t *session, const uint8_t *data, size_t datalen)
Function interface for data transmission.
int coap_socket_bind_udp(coap_socket_t *sock, const coap_address_t *listen_addr, coap_address_t *bound_addr)
Create a new UDP socket and then listen for new incoming UDP sessions to the specified IP address and...
void coap_socket_dgrm_close(coap_socket_t *sock)
Function interface to close off a datagram socket.
coap_session_t * coap_session_new_dtls_session(coap_session_t *session, coap_tick_t now)
Create a new DTLS session for the session.
int coap_dtls_hello(coap_session_t *coap_session, const uint8_t *data, size_t data_len)
Handling client HELLO messages from a new candiate peer.
@ COAP_EVENT_BAD_PACKET
Triggered when badly formatted packet received.
Definition coap_event.h:110
#define coap_lock_invert(alt_lock, failed)
Dummy for no thread-safe code.
#define coap_lock_unlock()
Dummy for no thread-safe code.
#define coap_lock_lock(failed)
Dummy for no thread-safe code.
#define coap_log_debug(...)
Definition coap_debug.h:126
const char * coap_session_str(const coap_session_t *session)
Get session description.
#define coap_log_warn(...)
Definition coap_debug.h:108
void coap_option_filter_clear(coap_opt_filter_t *filter)
Clears filter filter.
void coap_delete_pdu_lkd(coap_pdu_t *pdu)
Dispose of an CoAP PDU and free off associated storage.
Definition coap_pdu.c:194
int coap_pdu_parse2(coap_proto_t proto, const uint8_t *data, size_t length, coap_pdu_t *pdu, coap_opt_filter_t *error_opts)
Parses data into the CoAP PDU structure given in result.
Definition coap_pdu.c:1493
#define COAP_PDU_IS_REQUEST(pdu)
#define COAP_RESPONSE_CODE(N)
Definition coap_pdu.h:165
#define COAP_INVALID_MID
Indicates an invalid message id.
Definition coap_pdu.h:271
@ COAP_PROTO_DTLS
Definition coap_pdu.h:321
coap_session_t * coap_endpoint_get_session(coap_endpoint_t *endpoint, const coap_packet_t *packet, coap_tick_t now)
Lookup the server session for the packet received on an endpoint, or create a new one.
#define COAP_PROTO_NOT_RELIABLE(p)
@ COAP_SESSION_TYPE_HELLO
server-side ephemeral session for responding to a client hello
coap_address_t remote
remote address and port
Definition coap_io.h:60
coap_address_t local
local address and port
Definition coap_io.h:61
Multi-purpose address abstraction.
union coap_address_t::@0 addr
Abstraction of virtual endpoint that can be attached to coap_context_t.
coap_context_t * context
endpoint's context
coap_proto_t proto
protocol used on this interface
size_t length
length of payload
coap_addr_tuple_t addr_info
local and remote addresses
unsigned char * payload
payload
int ifindex
the interface index
structure for CoAP PDUs
Abstraction of virtual session that can be attached to coap_context_t (client) or coap_endpoint_t (se...
coap_addr_tuple_t addr_info
remote/local address info
coap_proto_t proto
protocol used
void * tls
security parameters
coap_session_type_t type
client or server side socket
coap_context_t * context
session's context
coap_session_t * session
Used to determine session owner.
coap_endpoint_t * endpoint
Used by the epoll logic for a listening endpoint.
coap_address_t mcast_addr
remote address and port (multicast track)
coap_socket_flags_t flags
1 or more of COAP_SOCKET* flag values