From 52350852cce5346e25603e1679255bbfda11d789 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 17 May 2024 14:38:17 +0200 Subject: [PATCH 1/9] sys/event/timeout: uninitialized event is not pending --- sys/include/event/timeout.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sys/include/event/timeout.h b/sys/include/event/timeout.h index 3f7b9b7f46..7885bb19b0 100644 --- a/sys/include/event/timeout.h +++ b/sys/include/event/timeout.h @@ -115,6 +115,11 @@ void event_timeout_clear(event_timeout_t *event_timeout); */ static inline bool event_timeout_is_pending(const event_timeout_t *event_timeout) { + if (event_timeout->clock == NULL || event_timeout->queue == NULL || + event_timeout->event == NULL) { + return false; + } + return ztimer_is_set(event_timeout->clock, &event_timeout->timer) || event_is_queued(event_timeout->queue, event_timeout->event); } From 42754b58eace28972f8ab779756a8c2f64fc075d Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Mon, 15 Jan 2024 23:10:42 +0100 Subject: [PATCH 2/9] gnrc_sock_udp: set source port when aux local is present --- sys/net/gnrc/sock/udp/gnrc_sock_udp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c index 2d8f4a6abe..15dd113db8 100644 --- a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c +++ b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c @@ -423,6 +423,7 @@ ssize_t sock_udp_sendv_aux(sock_udp_t *sock, if ((aux != NULL) && (aux->flags & SOCK_AUX_SET_LOCAL)) { local.family = aux->local.family; local.netif = aux->local.netif; + src_port = aux->local.port; memcpy(&local.addr, &aux->local.addr, sizeof(local.addr)); aux->flags &= ~SOCK_AUX_SET_LOCAL; From 879d312960c26ce3e8f43f904532186a16ca2843 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 16 Jan 2024 19:21:38 +0100 Subject: [PATCH 3/9] nanocoap: make token const in coap_build_hdr() --- sys/include/net/nanocoap.h | 2 +- sys/net/application_layer/nanocoap/nanocoap.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 3655fd7bd2..bdd63469eb 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -1922,7 +1922,7 @@ ssize_t coap_block2_build_reply(coap_pkt_t *pkt, unsigned code, * * @returns length of resulting header */ -ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, uint8_t *token, +ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, const void *token, size_t token_len, unsigned code, uint16_t id); /** diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index 86ea74019f..ce758144a7 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -683,7 +683,7 @@ ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code, return len; } -ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, uint8_t *token, +ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, const void *token, size_t token_len, unsigned code, uint16_t id) { assert(!(type & ~0x3)); From 2c232dd0d269cc510586ad04f443f4018bfe093a Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 16 Jan 2024 19:22:23 +0100 Subject: [PATCH 4/9] nanocoap: don't set type & code twice in coap_build_reply() --- sys/net/application_layer/nanocoap/nanocoap.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index ce758144a7..653b93f9dd 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -675,9 +675,6 @@ ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code, coap_build_hdr((coap_hdr_t *)rbuf, type, coap_get_token(pkt), tkl, code, ntohs(pkt->hdr->id)); - coap_hdr_set_type((coap_hdr_t *)rbuf, type); - coap_hdr_set_code((coap_hdr_t *)rbuf, code); - len += payload_len; return len; From c280076594a56bf3450e5d89ddd665acf9022eb0 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 16 Jan 2024 19:38:54 +0100 Subject: [PATCH 5/9] nanocoap: add coap_build_empty_ack() --- sys/include/net/nanocoap.h | 16 ++++++++++++++++ sys/net/application_layer/nanocoap/nanocoap.c | 12 ++++++++++++ 2 files changed, 28 insertions(+) diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index bdd63469eb..4e4694eff0 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -1956,6 +1956,22 @@ ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, const void *token, ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code, uint8_t *rbuf, unsigned rlen, unsigned payload_len); +/** + * @brief Build empty reply to CoAP request + * + * This function can be used to create an empty ACK so that a later, separate + * response can be sent independently. + * + * If the request was non-confirmable, this will generate nothing. + * + * @param[in] pkt packet to reply to + * @param[out] ack buffer to write reply to + * + * @returns size of reply packet on success + * @returns -ENOSPC if @p rbuf too small + */ +ssize_t coap_build_empty_ack(coap_pkt_t *pkt, coap_hdr_t *ack); + /** * @brief Handle incoming CoAP request * diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index 653b93f9dd..85ac77bb51 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -626,6 +626,18 @@ ssize_t coap_reply_simple(coap_pkt_t *pkt, return header_len + payload_len; } +ssize_t coap_build_empty_ack(coap_pkt_t *pkt, coap_hdr_t *ack) +{ + if (coap_get_type(pkt) != COAP_TYPE_CON) { + return 0; + } + + coap_build_hdr(ack, COAP_TYPE_ACK, NULL, 0, + COAP_CODE_EMPTY, ntohs(pkt->hdr->id)); + + return sizeof(*ack); +} + ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code, uint8_t *rbuf, unsigned rlen, unsigned payload_len) { From 330a6cfb912479fe1503ed7a4b6ea3e4e9409f29 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Mon, 15 Jan 2024 17:18:33 +0100 Subject: [PATCH 6/9] nanocoap_sock: add local information to coap_request_ctx_t --- sys/include/net/nanocoap.h | 3 +++ sys/net/application_layer/nanocoap/sock.c | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 4e4694eff0..eb899dfd64 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -338,6 +338,9 @@ void coap_request_ctx_init(coap_request_ctx_t *ctx, sock_udp_ep_t *remote); struct _coap_request_ctx { const coap_resource_t *resource; /**< resource of the request */ sock_udp_ep_t *remote; /**< remote endpoint of the request */ +#if defined(MODULE_SOCK_AUX_LOCAL) || DOXYGEN + sock_udp_ep_t *local; /**< local endpoint of the request */ +#endif #if defined(MODULE_GCOAP) || DOXYGEN /** * @brief transport the packet was received over diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index 892c977498..cda2d66d20 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -825,10 +825,6 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize) DEBUG("nanocoap: error parsing packet\n"); continue; } - if ((res = coap_handle_req(&pkt, buf, bufsize, &ctx)) <= 0) { - DEBUG("nanocoap: error handling request %" PRIdSIZE "\n", res); - continue; - } sock_udp_aux_tx_t *aux_out_ptr = NULL; #ifdef MODULE_SOCK_AUX_LOCAL @@ -841,7 +837,13 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize) if (!sock_udp_ep_is_multicast(&aux_in.local)) { aux_out_ptr = &aux_out; } + ctx.local = &aux_in.local; #endif + if ((res = coap_handle_req(&pkt, buf, bufsize, &ctx)) <= 0) { + DEBUG("nanocoap: error handling request %" PRIdSIZE "\n", res); + continue; + } + sock_udp_send_aux(&sock.udp, buf, res, &remote, aux_out_ptr); } From 7e69c1363016a9b1666b1084bf153ea06354ac78 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 16 Jan 2024 19:41:58 +0100 Subject: [PATCH 7/9] nanocoap_sock: implement nanocoap_sock_send_separate() --- examples/nanocoap_server/coap_handler.c | 42 +++++++++++++++ sys/Makefile.dep | 5 ++ sys/include/net/nanocoap_sock.h | 49 ++++++++++++++++++ sys/net/application_layer/nanocoap/sock.c | 63 +++++++++++++++++++++++ 4 files changed, 159 insertions(+) diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index 2a04452b9f..065284bef0 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -182,6 +182,48 @@ NANOCOAP_RESOURCE(sha256) { .path = "/sha256", .methods = COAP_POST, .handler = _sha256_handler }; +/* separate response requires an event thread to execute it */ +#ifdef MODULE_EVENT_THREAD +static nanocoap_server_response_ctx_t _separate_ctx; +static bool _separate_in_progress; + +static void _send_response(void *ctx) +{ + const char response[] = "This is a delayed response."; + + puts("_separate_handler(): send delayed response"); + nanocoap_sock_send_separate(ctx, COAP_CODE_CONTENT, COAP_TYPE_NON, + response, sizeof(response)); + _separate_in_progress = false; +} + +static ssize_t _separate_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_request_ctx_t *context) +{ + static event_timeout_t event_timeout; + static event_callback_t event_timed = EVENT_CALLBACK_INIT(_send_response, &_separate_ctx); + + if (_separate_in_progress) { + puts("_separate_handler(): response already scheduled"); + return coap_build_reply(pkt, COAP_CODE_SERVICE_UNAVAILABLE, buf, len, 0); + } + + puts("_separate_handler(): send ACK, schedule response"); + + nanocoap_sock_prepare_separate(&_separate_ctx, pkt, context); + _separate_in_progress = true; + + event_timeout_ztimer_init(&event_timeout, ZTIMER_MSEC, EVENT_PRIO_MEDIUM, + &event_timed.super); + event_timeout_set(&event_timeout, 1 * MS_PER_SEC); + + return coap_build_empty_ack(pkt, buf, len); +} + +NANOCOAP_RESOURCE(separate) { + .path = "/separate", .methods = COAP_GET, .handler = _separate_handler, +}; +#endif /* MODULE_EVENT_THREAD */ + /* we can also include the fileserver module */ #ifdef MODULE_NANOCOAP_FILESERVER #include "net/nanocoap/fileserver.h" diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 06765ece83..f51e39a7c2 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -544,6 +544,11 @@ ifneq (,$(filter nanocoap_server_auto_init,$(USEMODULE))) USEMODULE += nanocoap_server endif +ifneq (,$(filter nanocoap_server_separate,$(USEMODULE))) + USEMODULE += nanocoap_server + USEMODULE += sock_aux_local +endif + ifneq (,$(filter nanocoap_server,$(USEMODULE))) USEMODULE += nanocoap_resources USEMODULE += nanocoap_sock diff --git a/sys/include/net/nanocoap_sock.h b/sys/include/net/nanocoap_sock.h index d1e668cd20..5995cc8538 100644 --- a/sys/include/net/nanocoap_sock.h +++ b/sys/include/net/nanocoap_sock.h @@ -209,6 +209,55 @@ typedef struct { uint8_t blksize; /**< CoAP blocksize exponent */ } coap_block_request_t; +/** + * @brief Context from CoAP request for separate response + */ +typedef struct { + sock_udp_ep_t remote; /**< remote to send response to */ +#if defined(MODULE_SOCK_AUX_LOCAL) || DOXYGEN + sock_udp_ep_t local; /**< local from which to send response */ +#endif + uint8_t token[COAP_TOKEN_LENGTH_MAX]; /**< request token */ + uint8_t tkl; /**< request token length */ + uint8_t no_response; /**< no-response bitmap */ +} nanocoap_server_response_ctx_t; + +/** + * @brief Prepare the context for a separate response + * + * This function serializes the CoAP request information so that + * a separate response can be generated outside the CoAP handler. + * + * The CoAP handler should then respond with an empty ACK by calling + * @ref coap_build_empty_ack + * + * @param[out] ctx Context information for separate response + * @param[in] pkt CoAP packet to which the response will be generated + * @param[in] req Context of the CoAP request + */ +void nanocoap_server_prepare_separate(nanocoap_server_response_ctx_t *ctx, + coap_pkt_t *pkt, const coap_request_ctx_t *req); + +/** + * @brief Send a separate response to a CoAP request + * + * This sends a response to a CoAP request outside the CoAP handler + * + * @pre @ref nanocoap_server_prepare_separate has been called on @p ctx + * inside the CoAP handler + * + * @param[in] ctx Context information for the CoAP response + * @param[in] code CoAP response code + * @param[in] type Response type, may be `COAP_TYPE_NON` + * @param[in] payload Response payload + * @param[in] len Payload length + * + * @returns 0 on success + * negative error (see @ref sock_udp_sendv_aux) + */ +int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx, + unsigned code, unsigned type, + const void *payload, size_t len); /** * @brief Get next consecutive message ID for use when building a new * CoAP request. diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index cda2d66d20..a095c5e80a 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -882,3 +882,66 @@ void auto_init_nanocoap_server(void) nanocoap_server_start(&local); } + +void nanocoap_server_prepare_separate(nanocoap_server_response_ctx_t *ctx, + coap_pkt_t *pkt, const coap_request_ctx_t *req) +{ + ctx->tkl = coap_get_token_len(pkt); + memcpy(ctx->token, coap_get_token(pkt), ctx->tkl); + memcpy(&ctx->remote, req->remote, sizeof(ctx->remote)); +#ifdef MODULE_SOCK_AUX_LOCAL + assert(req->local); + memcpy(&ctx->local, req->local, sizeof(ctx->local)); +#endif + uint32_t no_response = 0; + coap_opt_get_uint(pkt, COAP_OPT_NO_RESPONSE, &no_response); + ctx->no_response = no_response; +} + +int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx, + unsigned code, unsigned type, + const void *payload, size_t len) +{ + uint8_t rbuf[sizeof(coap_hdr_t) + COAP_TOKEN_LENGTH_MAX + 1]; + assert(type != COAP_TYPE_ACK); + assert(type != COAP_TYPE_CON); /* TODO: add support */ + + const uint8_t no_response_index = (code >> 5) - 1; + /* If the handler code misbehaved here, we'd face UB otherwise */ + assert(no_response_index < 7); + + const uint8_t mask = 1 << no_response_index; + if (ctx->no_response & mask) { + return 0; + } + + iolist_t data = { + .iol_base = (void *)payload, + .iol_len = len, + }; + + iolist_t head = { + .iol_next = &data, + .iol_base = rbuf, + }; + head.iol_len = coap_build_hdr((coap_hdr_t *)rbuf, type, + ctx->token, ctx->tkl, + code, random_uint32()); + if (len) { + rbuf[head.iol_len++] = 0xFF; + } + + sock_udp_aux_tx_t *aux_out_ptr = NULL; +#ifdef MODULE_SOCK_AUX_LOCAL + /* make sure we reply with the same address that the request was + * destined for -- except in the multicast case */ + sock_udp_aux_tx_t aux_out = { + .flags = SOCK_AUX_SET_LOCAL, + .local = ctx->local, + }; + if (!sock_udp_ep_is_multicast(&ctx->local)) { + aux_out_ptr = &aux_out; + } +#endif + return sock_udp_sendv_aux(NULL, &head, &ctx->remote, aux_out_ptr); +} From dd458cb993c9bfdf4d60014e4f3e9f4e65b3e786 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 17 May 2024 14:26:34 +0200 Subject: [PATCH 8/9] nanocoap_sock: use normal udp sock in nanocoap_server() --- sys/net/application_layer/nanocoap/sock.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index a095c5e80a..572839dd0f 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -789,7 +789,7 @@ ssize_t nanocoap_get_blockwise_url_to_buf(const char *url, int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize) { - nanocoap_sock_t sock; + sock_udp_t sock; sock_udp_ep_t remote; coap_request_ctx_t ctx = { .remote = &remote, @@ -799,7 +799,7 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize) local->port = COAP_PORT; } - ssize_t res = sock_udp_create(&sock.udp, local, NULL, 0); + ssize_t res = sock_udp_create(&sock, local, NULL, 0); if (res != 0) { return -1; } @@ -814,7 +814,7 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize) aux_in_ptr = &aux_in; #endif - res = sock_udp_recv_aux(&sock.udp, buf, bufsize, SOCK_NO_TIMEOUT, + res = sock_udp_recv_aux(&sock, buf, bufsize, SOCK_NO_TIMEOUT, &remote, aux_in_ptr); if (res <= 0) { DEBUG("nanocoap: error receiving UDP packet %" PRIdSIZE "\n", res); @@ -844,7 +844,7 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize) continue; } - sock_udp_send_aux(&sock.udp, buf, res, &remote, aux_out_ptr); + sock_udp_send_aux(&sock, buf, res, &remote, aux_out_ptr); } return 0; From 63d5a5aafa4231e13c57e188eee0218186897fed Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 16 Jan 2024 16:51:35 +0100 Subject: [PATCH 9/9] examples/nanocoap_server: add endpoint with separate response --- examples/nanocoap_server/Makefile | 7 +++++++ examples/nanocoap_server/coap_handler.c | 15 ++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/examples/nanocoap_server/Makefile b/examples/nanocoap_server/Makefile index 9f8b91f201..a4efa1e191 100644 --- a/examples/nanocoap_server/Makefile +++ b/examples/nanocoap_server/Makefile @@ -47,6 +47,13 @@ endif HIGH_MEMORY_BOARDS := native same54-xpro mcb2388 ifneq (,$(filter $(BOARD),$(HIGH_MEMORY_BOARDS))) + # enable separate response + USEMODULE += nanocoap_server_separate + USEMODULE += event_callback + USEMODULE += event_thread + USEMODULE += event_timeout_ztimer + + # enable fileserver USEMODULE += nanocoap_fileserver USEMODULE += vfs_default diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index 065284bef0..f4c2516c2a 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -10,8 +10,12 @@ #include #include +#include "event/callback.h" +#include "event/timeout.h" +#include "event/thread.h" #include "fmt.h" #include "net/nanocoap.h" +#include "net/nanocoap_sock.h" #include "hashes/sha256.h" #include "kernel_defines.h" @@ -185,16 +189,14 @@ NANOCOAP_RESOURCE(sha256) { /* separate response requires an event thread to execute it */ #ifdef MODULE_EVENT_THREAD static nanocoap_server_response_ctx_t _separate_ctx; -static bool _separate_in_progress; static void _send_response(void *ctx) { const char response[] = "This is a delayed response."; puts("_separate_handler(): send delayed response"); - nanocoap_sock_send_separate(ctx, COAP_CODE_CONTENT, COAP_TYPE_NON, + nanocoap_server_send_separate(ctx, COAP_CODE_CONTENT, COAP_TYPE_NON, response, sizeof(response)); - _separate_in_progress = false; } static ssize_t _separate_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_request_ctx_t *context) @@ -202,21 +204,20 @@ static ssize_t _separate_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap static event_timeout_t event_timeout; static event_callback_t event_timed = EVENT_CALLBACK_INIT(_send_response, &_separate_ctx); - if (_separate_in_progress) { + if (event_timeout_is_pending(&event_timeout)) { puts("_separate_handler(): response already scheduled"); return coap_build_reply(pkt, COAP_CODE_SERVICE_UNAVAILABLE, buf, len, 0); } puts("_separate_handler(): send ACK, schedule response"); - nanocoap_sock_prepare_separate(&_separate_ctx, pkt, context); - _separate_in_progress = true; + nanocoap_server_prepare_separate(&_separate_ctx, pkt, context); event_timeout_ztimer_init(&event_timeout, ZTIMER_MSEC, EVENT_PRIO_MEDIUM, &event_timed.super); event_timeout_set(&event_timeout, 1 * MS_PER_SEC); - return coap_build_empty_ack(pkt, buf, len); + return coap_build_empty_ack(pkt, (void *)buf); } NANOCOAP_RESOURCE(separate) {