libcoap 4.3.5-develop-bd47692
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-2026 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 uint32_t coap_lwip_in_call_back_ref;
31#endif /* NO_SYS == 0 */
32
33typedef struct {
34 coap_session_t *session;
35 struct udp_pcb *udp_pcb;
36 struct pbuf *pbuf;
37 coap_address_t addr;
38} coap_lwip_udp_t;
39
40/*
41 * Not used for LwIP (done with coap_recvc()), but need dummy function.
42 */
43ssize_t
45 (void)sock;
46 (void)packet;
47 assert(0);
48 return -1;
49}
50
51#if COAP_CLIENT_SUPPORT
60static void
61coap_recvc(void *arg, struct udp_pcb *upcb, struct pbuf *p,
62 const ip_addr_t *addr, u16_t port) {
63 coap_pdu_t *pdu = NULL;
64 coap_session_t *session = (coap_session_t *)arg;
65 int result = -1;
66 (void)upcb;
67
68 assert(session);
69 LWIP_ASSERT("Proto not supported for LWIP", COAP_PROTO_NOT_RELIABLE(session->proto));
70
71 if (p->len < 4) {
72 /* Minimum size of CoAP header - ignore runt */
73 return;
74 }
75 coap_lock_lock(return);
76 memcpy(&session->addr_info.remote.addr, addr, sizeof(session->addr_info.remote.addr));
77 coap_address_set_port(&session->addr_info.remote, port);
78
79 coap_log_debug("* %s: lwip: recv %4d bytes\n",
80 coap_session_str(session), p->len);
81 if (session->proto == COAP_PROTO_DTLS) {
82 if (session->tls) {
83 result = coap_dtls_receive(session, p->payload, p->len);
84 if (result < 0)
85 goto error;
86 }
87 pbuf_free(p);
88 } else {
89 coap_opt_filter_t error_opts;
90
91 pdu = coap_pdu_from_pbuf(p);
92 if (!pdu)
93 goto error;
94
95 coap_option_filter_clear(&error_opts);
96 if (!coap_pdu_parse2(session->proto, p->payload, p->len, pdu, &error_opts)) {
98 coap_log_warn("discard malformed PDU\n");
99 if (error_opts.mask && COAP_PDU_IS_REQUEST(pdu)) {
100 coap_pdu_t *response =
102 COAP_RESPONSE_CODE(402), &error_opts);
103 if (!response) {
104 coap_log_warn("coap_handle_dgram: cannot create error response\n");
105 } else {
106 if (coap_send_internal(session, response, NULL) == COAP_INVALID_MID)
107 coap_log_warn("coap_handle_dgram: error sending response\n");
108 }
111#if NO_SYS == 0
112 sys_sem_signal(&session->context->coap_io_timeout_sem);
113#endif /* NO_SYS == 0 */
114 return;
115 } else {
116 goto error;
117 }
118 }
119#if NO_SYS == 0
120 coap_lwip_in_call_back_ref++;
121#endif /* NO_SYS == 0 */
122 coap_dispatch(session->context, session, pdu);
123#if NO_SYS == 0
124 coap_lwip_in_call_back_ref--;
125#endif /* NO_SYS == 0 */
126 }
127#if NO_SYS == 0
128 sys_sem_signal(&session->context->coap_io_timeout_sem);
129#endif /* NO_SYS == 0 */
132 return;
133
134error:
135 /*
136 * https://rfc-editor.org/rfc/rfc7252#section-4.2 MUST send RST
137 * https://rfc-editor.org/rfc/rfc7252#section-4.3 MAY send RST
138 */
139 if (session)
140 coap_send_rst_lkd(session, pdu);
143#if NO_SYS == 0
144 sys_sem_signal(&session->context->coap_io_timeout_sem);
145#endif /* NO_SYS == 0 */
146 return;
147}
148#endif /* ! COAP_CLIENT_SUPPORT */
149
150#if COAP_SERVER_SUPPORT
151
152static void
153coap_free_packet(coap_packet_t *packet) {
155}
156
165static void
166coap_udp_recvs(void *arg, struct udp_pcb *upcb, struct pbuf *p,
167 const ip_addr_t *addr, u16_t port) {
168 coap_endpoint_t *ep = (coap_endpoint_t *)arg;
169 coap_pdu_t *pdu = NULL;
170 coap_session_t *session = NULL;
171 coap_tick_t now;
172 coap_packet_t *packet = NULL;
173 int result = -1;
174
175 if (p->len < 4) {
176 /* Minimum size of CoAP header - ignore runt */
177 goto error_free_pbuf;
178 }
179
180 packet = coap_malloc_type(COAP_PACKET, sizeof(coap_packet_t));
181
182 /* this is fatal because due to the short life of the packet, never should
183 there be more than one coap_packet_t required */
184 LWIP_ASSERT("Insufficient coap_packet_t resources.", packet != NULL);
185 /* Need to do this as there may be holes in addr_info */
186 memset(&packet->addr_info, 0, sizeof(packet->addr_info));
187 packet->length = p->len;
188 packet->payload = p->payload;
189 packet->addr_info.remote.port = port;
190 packet->addr_info.remote.addr = *addr;
191 packet->addr_info.local.port = upcb->local_port;
192 packet->addr_info.local.addr = *ip_current_dest_addr();
193 packet->ifindex = netif_get_index(ip_current_netif());
194
195 coap_ticks(&now);
196
197 coap_lock_lock(goto error_free_pbuf);
198 session = coap_endpoint_get_session(ep, packet, now);
199 if (!session)
200 goto error_free_pbuf;
201 LWIP_ASSERT("Proto not supported for LWIP", COAP_PROTO_NOT_RELIABLE(session->proto));
202
203 coap_log_debug("* %s: lwip: recv %4d bytes\n",
204 coap_session_str(session), p->len);
205
206 if (session->proto == COAP_PROTO_DTLS) {
207 if (session->type == COAP_SESSION_TYPE_HELLO)
208 result = coap_dtls_hello(session, p->payload, p->len);
209 else if (session->tls)
210 result = coap_dtls_receive(session, p->payload, p->len);
211 if (session->type == COAP_SESSION_TYPE_HELLO && result == 1)
212 coap_session_new_dtls_session(session, now);
213 pbuf_free(p);
214 } else {
215 coap_opt_filter_t error_opts;
216
217 pdu = coap_pdu_from_pbuf(p);
218 if (!pdu)
219 goto error;
220
221 coap_option_filter_clear(&error_opts);
222 if (!coap_pdu_parse2(ep->proto, p->payload, p->len, pdu, &error_opts)) {
223 coap_handle_event_lkd(ep->context, COAP_EVENT_BAD_PACKET, session);
224 coap_log_warn("discard malformed PDU\n");
225 if (error_opts.mask && COAP_PDU_IS_REQUEST(pdu)) {
226 coap_pdu_t *response =
228 COAP_RESPONSE_CODE(402), &error_opts);
229 if (!response) {
230 coap_log_warn("coap_handle_dgram: cannot create error response\n");
231 } else {
232 if (coap_send_internal(session, response, NULL) == COAP_INVALID_MID)
233 coap_log_warn("coap_handle_dgram: error sending response\n");
234 }
235 goto cleanup;
236 } else {
237 goto error;
238 }
239 }
240#if NO_SYS == 0
241 coap_lwip_in_call_back_ref++;
242#endif /* NO_SYS == 0 */
243 coap_dispatch(ep->context, session, pdu);
244#if NO_SYS == 0
245 coap_lwip_in_call_back_ref--;
246#endif /* NO_SYS == 0 */
247 }
248
250 coap_free_packet(packet);
252#if NO_SYS == 0
253 sys_sem_signal(&session->context->coap_io_timeout_sem);
254#endif /* NO_SYS == 0 */
255 return;
256
257error_free_pbuf:
258 pbuf_free(p);
259
260error:
261 /*
262 * https://rfc-editor.org/rfc/rfc7252#section-4.2 MUST send RST
263 * https://rfc-editor.org/rfc/rfc7252#section-4.3 MAY send RST
264 */
265 if (session && pdu)
266 coap_send_rst_lkd(session, pdu);
267cleanup:
269 coap_free_packet(packet);
271#if NO_SYS == 0
272 sys_sem_signal(&session->context->coap_io_timeout_sem);
273#endif /* NO_SYS == 0 */
274 return;
275}
276
277#endif /* ! COAP_SERVER_SUPPORT */
278
279#if NO_SYS == 0
280static void
281coap_lwip_udp_sendto(void *ctx) {
282 coap_lwip_udp_t *cb_ctx = (coap_lwip_udp_t *)ctx;
283 int err;
284
285 if (cb_ctx) {
286 err = udp_sendto(cb_ctx->udp_pcb, cb_ctx->pbuf, &cb_ctx->addr.addr,
287 cb_ctx->addr.port);
288 if (err < 0) {
289 if (err == ERR_RTE) {
290 coap_log_warn("** %s: udp_sendto: Packet not routable\n",
291 coap_session_str(cb_ctx->session));
292 } else {
293 coap_log_warn("** %s: udp_sendto: error %d\n",
294 coap_session_str(cb_ctx->session), err);
295 }
296 }
297 pbuf_free(cb_ctx->pbuf);
299 }
300}
301#endif /* NO_SYS == 0 */
302
303/*
304 * dgram
305 * return +ve Number of bytes written.
306 * -1 Error error in errno).
307 */
308ssize_t
310 const uint8_t *data, size_t data_len) {
311 struct pbuf *pbuf;
312 int err;
313 int r;
314
315 if ((r = coap_debug_send_packet()) != 1) {
316 if (r)
317 data_len = -1;
318 } else {
319 pbuf = pbuf_alloc(PBUF_TRANSPORT, data_len, PBUF_RAM);
320 if (pbuf == NULL) {
321 errno = ENOMEM;
322 return -1;
323 }
324 memcpy(pbuf->payload, data, data_len);
325
326#if NO_SYS == 0
327 if (coap_lwip_in_call_back_ref == 0) {
328 coap_lwip_udp_t *cb_ctx = coap_malloc_type(COAP_STRING, sizeof(coap_lwip_udp_t));
329
330 if (!cb_ctx) {
331 coap_log_warn("** %s: coap_socket_send: error\n",
332 coap_session_str(session));
333 pbuf_free(pbuf);
334 errno = ENOMEM;
335 return -1;
336 }
337 cb_ctx->session = session;
338 cb_ctx->pbuf = pbuf;
339 cb_ctx->udp_pcb = sock->udp_pcb;
340 cb_ctx->addr = session->addr_info.remote;
341 err = tcpip_try_callback(coap_lwip_udp_sendto, cb_ctx);
342
343 if (err < 0) {
344 coap_log_warn("** %s: tcpip_try_callback: error %d\n",
345 coap_session_str(session), err);
346 errno = EAGAIN;
347 return -1;
348 }
349 } else {
350#endif /* NO_SYS == 0 */
351 err = udp_sendto(sock->udp_pcb, pbuf, &session->addr_info.remote.addr,
352 session->addr_info.remote.port);
353 pbuf_free(pbuf);
354 if (err < 0) {
355 if (err == ERR_RTE) {
356 coap_log_warn("** %s: udp_sendto: Packet not routable\n",
357 coap_session_str(session));
358 } else {
359 coap_log_warn("** %s: udp_sendto: error %d\n",
360 coap_session_str(session), err);
361 }
362 return -1;
363 }
364#if NO_SYS == 0
365 }
366#endif /* NO_SYS == 0 */
367 }
368 return data_len;
369}
370
371#if COAP_SERVER_SUPPORT
372int
373coap_socket_bind_udp(coap_socket_t *sock,
374 const coap_address_t *listen_addr,
375 coap_address_t *bound_addr) {
376 int err;
377 coap_address_t l_listen = *listen_addr;
378
379 sock->udp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
380 if (sock->udp_pcb == NULL)
381 return 0;
382
383#if LWIP_IPV6 && LWIP_IPV4
384 if (l_listen.addr.type == IPADDR_TYPE_V6)
385 l_listen.addr.type = IPADDR_TYPE_ANY;
386#endif /* LWIP_IPV6 && LWIP_IPV4 */
387 udp_recv(sock->udp_pcb, coap_udp_recvs, (void *)sock->endpoint);
388 err = udp_bind(sock->udp_pcb, &l_listen.addr, l_listen.port);
389 if (err) {
390 udp_remove(sock->udp_pcb);
391 sock->udp_pcb = NULL;
392 }
393 *bound_addr = l_listen;
394 return err ? 0 : 1;
395}
396#endif /* COAP_SERVER_SUPPORT */
397
398#if COAP_CLIENT_SUPPORT
399int
400coap_socket_connect_udp(coap_socket_t *sock,
401 const coap_address_t *local_if,
402 const coap_address_t *server,
403 int default_port,
404 coap_address_t *local_addr,
405 coap_address_t *remote_addr) {
406 err_t err;
407 struct udp_pcb *pcb;
408 int is_mcast = coap_is_mcast(server);
409 coap_address_t connect_addr;
410
411 coap_address_copy(&connect_addr, server);
412 if (connect_addr.port == 0)
413 connect_addr.port = default_port;
414
415 coap_lock_invert(LOCK_TCPIP_CORE(),
416 goto err_unlock);
417
418 pcb = udp_new();
419
420 if (!pcb) {
421 goto err_unlock;
422 }
423
424 if (local_if) {
425 pcb->local_ip = local_if->addr;
426 pcb->local_port = local_if->port;
427 }
428 err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
429 if (err) {
430 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
431 ("coap_socket_connect_udp: port bind failed\n"));
432 goto err_udp_remove;
433 }
434
435 if (local_addr) {
436 local_addr->addr = pcb->local_ip;
437 local_addr->port = pcb->local_port;
438 }
439 sock->session->addr_info.local.port = pcb->local_port;
440
441 if (remote_addr) {
442 coap_address_copy(remote_addr, &connect_addr);
443 }
444
445 if (is_mcast) {
446 coap_address_copy(&sock->mcast_addr, &connect_addr);
448 } else {
449 err = udp_connect(pcb, &connect_addr.addr, connect_addr.port);
450 if (err) {
451 goto err_udp_unbind;
452 }
453 }
454
455#if LWIP_IPV6 && LWIP_IPV4
456 pcb->local_ip.type = pcb->remote_ip.type;
457#endif /* LWIP_IPV6 && LWIP_IPV4 */
458
459 sock->udp_pcb = pcb;
460
461 udp_recv(sock->udp_pcb, coap_recvc, (void *)sock->session);
462
463 UNLOCK_TCPIP_CORE();
464
465 return 1;
466
467err_udp_unbind:
468err_udp_remove:
469 udp_remove(pcb);
470err_unlock:
471 UNLOCK_TCPIP_CORE();
472 return 0;
473}
474#endif /* ! COAP_CLIENT_SUPPORT */
475
476#if NO_SYS == 0
477static void
478coap_lwip_udp_remove(void *ctx) {
479 struct udp_pcb *udp_pcb = (struct udp_pcb *)ctx;
480
481 if (udp_pcb) {
482 udp_remove(udp_pcb);
483 }
484}
485#endif /* NO_SYS == 0 */
486
487void
489 if (!(sock->flags & COAP_SOCKET_SLAVE)) {
490 struct udp_pcb *udp_pcb = sock->udp_pcb;
491
492 sock->udp_pcb = NULL;
493 sock->flags = COAP_SOCKET_EMPTY;
494 if (udp_pcb) {
495#if NO_SYS == 0
496 err_t err;
497 if (coap_lwip_in_call_back_ref == 0) {
498 err = tcpip_callback_wait(coap_lwip_udp_remove, udp_pcb);
499
500 if (err < 0) {
501 coap_log_warn("** %s: tcpip_callback_wait: error %d\n",
502 sock->session ? coap_session_str(sock->session) : "", err);
503 errno = EAGAIN;
504 return;
505 }
506 } else {
507#endif /* NO_SYS == 0 */
508 udp_remove(udp_pcb);
509#if NO_SYS == 0
510 }
511#endif /* NO_SYS == 0 */
512 }
513 }
514 return;
515}
516
517#else /* ! WITH_LWIP */
518
519#ifdef __clang__
520/* Make compilers happy that do not like empty modules. As this function is
521 * never used, we ignore -Wunused-function at the end of compiling this file
522 */
523#pragma GCC diagnostic ignored "-Wunused-function"
524#endif
525static inline void
526dummy(void) {
527}
528
529#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)
struct coap_endpoint_t coap_endpoint_t
#define COAP_SOCKET_MULTICAST
socket is used for multicast communication
#define COAP_SOCKET_SLAVE
socket is a slave socket - do not close
#define COAP_SOCKET_EMPTY
coap_socket_flags_t values
Library specific build wrapper for coap_internal.h.
@ COAP_PACKET
Definition coap_mem.h:36
@ COAP_STRING
Definition coap_mem.h:33
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:247
#define NULL
Definition coap_option.h:30
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:1117
uint64_t coap_tick_t
This data type represents internal timer ticks with COAP_TICKS_PER_SECOND resolution.
Definition coap_time.h:149
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:5123
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:4549
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:2041
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:3269
void coap_ticks(coap_tick_t *t)
Returns the current value of an internal tick counter.
Definition coap_time.c:90
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.
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.
@ 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:195
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:1568
#define COAP_PDU_IS_REQUEST(pdu)
#define COAP_RESPONSE_CODE(N)
Definition coap_pdu.h:164
#define COAP_INVALID_MID
Indicates an invalid message id.
Definition coap_pdu.h:270
@ COAP_PROTO_DTLS
Definition coap_pdu.h:320
#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:58
coap_address_t local
local address and port
Definition coap_io.h:59
Multi-purpose address abstraction.
union coap_address_t::@0 addr
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_socket_flags_t flags
1 or more of COAP_SOCKET* flag values