From f8872c17e75deca868bd03dcda8c757a8c49102f Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 20 Jan 2025 11:49:59 +0100 Subject: [PATCH 01/10] sys/net/nanocoap: improve separate response handling This allows sending a separate response with CoAP Options and adds a helper to detect duplicate requests, so that resource handlers can repeat their empty ACK on duplicates. --- examples/nanocoap_server/coap_handler.c | 7 +- sys/include/net/nanocoap_sock.h | 74 ++++++++++++++++++- sys/net/application_layer/nanocoap/sock.c | 87 ++++++++++++++++------- 3 files changed, 137 insertions(+), 31 deletions(-) diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index f40a425362c2..31b506775bd3 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -204,7 +204,12 @@ 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 (event_timeout_is_pending(&event_timeout) && !sock_udp_ep_equal(context->remote, &_separate_ctx.remote)) { + if (event_timeout_is_pending(&event_timeout)) { + if (nanocoap_is_duplicate_in_separate_ctx(&_separate_ctx, pkt, context)) { + /* duplicate of the request a separate response is already scheduled + * for --> resending the ACK */ + return coap_build_empty_ack(pkt, (void *)buf); + } puts("_separate_handler(): response already scheduled"); return coap_build_reply(pkt, COAP_CODE_SERVICE_UNAVAILABLE, buf, len, 0); } diff --git a/sys/include/net/nanocoap_sock.h b/sys/include/net/nanocoap_sock.h index 9bdf41ce2f6c..10b7894f4174 100644 --- a/sys/include/net/nanocoap_sock.h +++ b/sys/include/net/nanocoap_sock.h @@ -261,7 +261,21 @@ int 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 + * @brief Check if the given packet is a duplicate of the one the + * separate response content contains was prepared for + * + * @param[in] ctx Context to check against + * @param[in] pkt Possible duplication + * @param[in] req Request context of the possible duplicate + * + * @retval true @p pkt is a duplicate + * @retval false @p pkt is *NOT* a duplicate + */ +bool nanocoap_is_duplicate_in_separate_ctx(const nanocoap_server_response_ctx_t *ctx, + coap_pkt_t *pkt, const coap_request_ctx_t *req); + +/** + * @brief Build and send a separate response to a CoAP request * * This sends a response to a CoAP request outside the CoAP handler * @@ -279,12 +293,66 @@ int nanocoap_server_prepare_separate(nanocoap_server_response_ctx_t *ctx, * @param[in] payload Response payload * @param[in] len Payload length * - * @returns 0 on success - * negative error (see @ref sock_udp_sendv_aux) + * @retval 0 Success + * @retval -ECANCELED Request contained no-response option that did match the given @p code + * @retval <0 Negative errno code indicating the error */ int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx, unsigned code, unsigned type, const void *payload, size_t len); + +/** + * @brief Build a separate response header to a CoAP request + * + * This builds the response packet header. You may add CoAP Options, a payload + * marker and a payload as needed after the header. + * + * @pre @ref nanocoap_server_prepare_separate has been called on @p ctx + * inside the CoAP handler + * @pre Synchronization between calls of this function and calls of + * @ref nanocoap_server_prepare_separate is ensured + * + * @warning This function is only available when using the module + * `nanocoap_server_separate` + * + * @param[in] ctx Context information for the CoAP response + * @param[out] buf Buffer to write the header to + * @param[in] buf_len Length of @p buf in bytes + * @param[in] code CoAP response code + * @param[in] type Response type, may be `COAP_TYPE_NON` + * @param[in] msg_id Message ID to send + * + * @return Length of the header build in bytes + * @retval -ECANCELED Request contained no-response option that did match the given @p code + * @retval <0 Negative errno code indicating the error + */ +ssize_t nanocoap_server_build_separate(const nanocoap_server_response_ctx_t *ctx, + uint8_t *buf, size_t buf_len, + unsigned code, unsigned type, + uint16_t msg_id); + +/** + * @brief Send an already build separate response + * + * @pre @ref nanocoap_server_prepare_separate has been called on @p ctx + * inside the CoAP handler + * @pre Synchronization between calls of this function and calls of + * @ref nanocoap_server_prepare_separate is ensured + * @pre @ref nanocoap_server_build_separate has been used to build the + * header in @p msg + * + * @warning This function is only available when using the module + * `nanocoap_server_separate` + * + * @param[in] ctx Context information for the CoAP response + * @param[in] reply I/O list containing the reply to send + * + * @retval 0 Success + * @retval <0 negative errno code indicating the error + */ +int nanocoap_server_sendv_separate(const nanocoap_server_response_ctx_t *ctx, + const iolist_t *reply); + /** * @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 0b3db123b777..8580ee2104f4 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -25,16 +25,13 @@ #include #include -#include "atomic_utils.h" #include "net/credman.h" #include "net/nanocoap.h" #include "net/nanocoap_sock.h" -#include "net/sock/util.h" #include "net/sock/udp.h" -#include "net/iana/portrange.h" +#include "net/sock/util.h" #include "random.h" -#include "sys/uio.h" -#include "timex.h" +#include "sys/uio.h" /* IWYU pragma: keep (exports struct iovec) */ #include "ztimer.h" #define ENABLE_DEBUG 0 @@ -48,7 +45,7 @@ * if mode or key-size change especially if certificates instead of PSK are used. */ #ifndef CONFIG_NANOCOAP_DTLS_HANDSHAKE_BUF_SIZE -#define CONFIG_NANOCOAP_DTLS_HANDSHAKE_BUF_SIZE (160) +# define CONFIG_NANOCOAP_DTLS_HANDSHAKE_BUF_SIZE (160) #endif enum { @@ -1111,13 +1108,27 @@ int nanocoap_server_prepare_separate(nanocoap_server_response_ctx_t *ctx, return 0; } -int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx, - unsigned code, unsigned type, - const void *payload, size_t len) +bool nanocoap_is_duplicate_in_separate_ctx(const nanocoap_server_response_ctx_t *ctx, + coap_pkt_t *pkt, const coap_request_ctx_t *req) +{ + /* We assume NSTART == 1 and do not actually look into the packet here. + * But having the API prepared to allow also comparing token and message ID + * is probably a good thing. */ + (void)pkt; + + return sock_udp_ep_equal(&ctx->remote, req->remote); +} + +ssize_t nanocoap_server_build_separate(const nanocoap_server_response_ctx_t *ctx, + uint8_t *buf, size_t buf_len, + unsigned code, unsigned type, + uint16_t msg_id) { - 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 */ + if ((sizeof(coap_hdr_t) + COAP_TOKEN_LENGTH_MAX + 1) > buf_len) { + return -EOVERFLOW; + } const uint8_t no_response_index = (code >> 5) - 1; /* If the handler code misbehaved here, we'd face UB otherwise */ @@ -1125,25 +1136,16 @@ int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx, const uint8_t mask = 1 << no_response_index; if (ctx->no_response & mask) { - return 0; + return -ECANCELED; } - 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; - } + return coap_build_hdr((coap_hdr_t *)buf, type, ctx->token, ctx->tkl, + code, msg_id); +} +int nanocoap_server_sendv_separate(const nanocoap_server_response_ctx_t *ctx, + const iolist_t *reply) +{ sock_udp_aux_tx_t *aux_out_ptr = NULL; /* make sure we reply with the same address that the request was * destined for -- except in the multicast case */ @@ -1154,6 +1156,37 @@ int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx, if (!sock_udp_ep_is_multicast(&ctx->local)) { aux_out_ptr = &aux_out; } - return sock_udp_sendv_aux(NULL, &head, &ctx->remote, aux_out_ptr); + return sock_udp_sendv_aux(NULL, reply, &ctx->remote, aux_out_ptr); +} + +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]; + + ssize_t hdr_len = nanocoap_server_build_separate(ctx, rbuf, sizeof(rbuf), + code, type, random_uint32()); + if (hdr_len < 0) { + return hdr_len; + } + + /* add payload marker if needed */ + if (len) { + rbuf[hdr_len++] = 0xFF; + } + + iolist_t data = { + .iol_base = (void *)payload, + .iol_len = len, + }; + + iolist_t head = { + .iol_next = &data, + .iol_base = rbuf, + .iol_len = hdr_len, + }; + + return nanocoap_server_sendv_separate(ctx, &head); } #endif From 26f4f06199b0b61093f665ba5d10148b749554d4 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 20 Jan 2025 23:50:03 +0100 Subject: [PATCH 02/10] sys/net/nanocoap: implement observe This adds the new `nanocoap_server_observe` module that implements the server side of the CoAP Observe option. It does require cooperation from the resource handler to work, though. Co-Authored-By: mguetschow --- examples/nanocoap_server/Makefile | 6 +- examples/nanocoap_server/coap_handler.c | 99 ++++++++++- examples/nanocoap_server/main.c | 10 +- sys/Makefile.dep | 4 + sys/include/net/nanocoap_sock.h | 95 ++++++++++- sys/net/application_layer/nanocoap/nanocoap.c | 6 + sys/net/application_layer/nanocoap/sock.c | 154 +++++++++++++++++- 7 files changed, 364 insertions(+), 10 deletions(-) diff --git a/examples/nanocoap_server/Makefile b/examples/nanocoap_server/Makefile index 2b418732cbdf..b944748b8464 100644 --- a/examples/nanocoap_server/Makefile +++ b/examples/nanocoap_server/Makefile @@ -20,7 +20,7 @@ USEMODULE += gnrc_icmpv6_echo USEMODULE += nanocoap_sock USEMODULE += nanocoap_resources -USEMODULE += xtimer +USEMODULE += ztimer_msec # include this for nicely formatting the returned internal value USEMODULE += fmt @@ -48,10 +48,12 @@ HIGH_MEMORY_BOARDS := native native64 same54-xpro mcb2388 ifneq (,$(filter $(BOARD),$(HIGH_MEMORY_BOARDS))) # enable separate response - USEMODULE += nanocoap_server_separate USEMODULE += event_callback + USEMODULE += event_periodic USEMODULE += event_thread USEMODULE += event_timeout_ztimer + USEMODULE += nanocoap_server_observe + USEMODULE += nanocoap_server_separate # enable fileserver USEMODULE += nanocoap_fileserver diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index 31b506775bd3..f4ade8ddf066 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -11,13 +11,13 @@ #include #include "event/callback.h" -#include "event/timeout.h" +#include "event/periodic.h" #include "event/thread.h" +#include "event/timeout.h" #include "fmt.h" #include "net/nanocoap.h" #include "net/nanocoap_sock.h" #include "hashes/sha256.h" -#include "kernel_defines.h" /* internal value that can be read/written via CoAP */ static uint8_t internal_value = 0; @@ -196,7 +196,7 @@ static void _send_response(void *ctx) puts("_separate_handler(): send delayed response"); nanocoap_server_send_separate(ctx, COAP_CODE_CONTENT, COAP_TYPE_NON, - response, sizeof(response)); + response, sizeof(response)); } static ssize_t _separate_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_request_ctx_t *context) @@ -234,6 +234,99 @@ NANOCOAP_RESOURCE(separate) { }; #endif /* MODULE_EVENT_THREAD */ +#ifdef MODULE_NANOCOAP_SERVER_OBSERVE +static ssize_t _time_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_request_ctx_t *context) +{ + uint32_t obs; + bool registered = false; + if (coap_opt_get_uint(pkt, COAP_OPT_OBSERVE, &obs)) { + /* No (valid) observe option present */ + obs = UINT32_MAX; + } + + uint32_t now = ztimer_now(ZTIMER_MSEC); + + switch (obs) { + case 0: + /* register */ + if (nanocoap_register_observer(context, pkt) == 0) { + registered = true; + } + break; + case 1: + /* unregister */ + nanocoap_unregister_observer(context); + break; + default: + /* No (valid) observe option present --> ignore observe and handle + * as regular GET */ + break; + } + + const size_t estimated_data_len = + 4 /* Max Observe Option size */ + + 1 /* payload marker */ + + 10 /* strlen("4294967295"), 4294967295 == UINT32_MAX */ + + 1 /* '\n' */; + ssize_t hdr_len = coap_build_reply(pkt, COAP_CODE_CONTENT, buf, len, estimated_data_len); + + if (hdr_len < 0) { + /* we undo any potential registration if we cannot reply */ + nanocoap_unregister_observer(context); + return len; + } + + if (hdr_len == 0) { + return 0; + } + + uint8_t *pos = buf + hdr_len - estimated_data_len; + + if (registered) { + uint16_t last_opt = 0; + pos += coap_opt_put_uint(pos, last_opt, COAP_OPT_OBSERVE, now & 0xffffff); + } + *pos++ = 0xff; /* payload marker */ + pos += fmt_u32_dec((void *)pos, now); + *pos++ = '\n'; + + return (uintptr_t)pos - (uintptr_t)buf; +} + +NANOCOAP_RESOURCE(time) { + .path = "/time", .methods = COAP_GET, .handler = _time_handler, +}; + +static void _notify_observer_handler(event_t *ev) +{ + (void)ev; + uint32_t now = ztimer_now(ZTIMER_MSEC); + uint8_t buf[32]; + uint8_t *pos = buf; + uint16_t last_opt = 0; + pos += coap_opt_put_uint(pos, last_opt, COAP_OPT_OBSERVE, now & 0xffffff); + *pos++ = 0xff; /* payload marker */ + pos += fmt_u32_dec((void *)pos, now); + *pos++ = '\n'; + iolist_t data = { + .iol_base = buf, + .iol_len = (uintptr_t)pos - (uintptr_t)buf, + }; + nanocoap_notify_observers(&coap_resource_time, &data); +} + +void setup_observe_event(void) +{ + static event_t ev = { + .handler = _notify_observer_handler + }; + static event_periodic_t pev; + + event_periodic_init(&pev, ZTIMER_MSEC, EVENT_PRIO_MEDIUM, &ev); + event_periodic_start(&pev, MS_PER_SEC); +} +#endif /* MODULE_NANOCOAP_SERVER_OBSERVE */ + /* we can also include the fileserver module */ #ifdef MODULE_NANOCOAP_FILESERVER #include "net/nanocoap/fileserver.h" diff --git a/examples/nanocoap_server/main.c b/examples/nanocoap_server/main.c index 739a63e3d96f..72dba3209f49 100644 --- a/examples/nanocoap_server/main.c +++ b/examples/nanocoap_server/main.c @@ -20,13 +20,15 @@ #include #include "net/nanocoap_sock.h" -#include "xtimer.h" +#include "ztimer.h" #define COAP_INBUF_SIZE (256U) #define MAIN_QUEUE_SIZE (8) static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; +extern void setup_observe_event(void); + int main(void) { puts("RIOT nanocoap example application"); @@ -35,7 +37,11 @@ int main(void) msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); puts("Waiting for address autoconfiguration..."); - xtimer_sleep(3); + ztimer_sleep(ZTIMER_MSEC, 3 * MS_PER_SEC); + + if (IS_USED(MODULE_NANOCOAP_SERVER_OBSERVE)) { + setup_observe_event(); + } /* print network addresses */ printf("{\"IPv6 addresses\": [\""); diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 00afb08a25ee..db8cef9c2a27 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -529,6 +529,10 @@ ifneq (,$(filter nanocoap_server_auto_init,$(USEMODULE))) USEMODULE += nanocoap_server endif +ifneq (,$(filter nanocoap_server_observe,$(USEMODULE))) + USEMODULE += nanocoap_server_separate +endif + ifneq (,$(filter nanocoap_server_separate,$(USEMODULE))) USEMODULE += nanocoap_server USEMODULE += sock_aux_local diff --git a/sys/include/net/nanocoap_sock.h b/sys/include/net/nanocoap_sock.h index 10b7894f4174..839dc5f07b81 100644 --- a/sys/include/net/nanocoap_sock.h +++ b/sys/include/net/nanocoap_sock.h @@ -261,8 +261,8 @@ int nanocoap_server_prepare_separate(nanocoap_server_response_ctx_t *ctx, coap_pkt_t *pkt, const coap_request_ctx_t *req); /** - * @brief Check if the given packet is a duplicate of the one the - * separate response content contains was prepared for + * @brief Check if given request is a duplicate of the one a separate response + * was prepared for * * @param[in] ctx Context to check against * @param[in] pkt Possible duplication @@ -353,6 +353,97 @@ ssize_t nanocoap_server_build_separate(const nanocoap_server_response_ctx_t *ctx int nanocoap_server_sendv_separate(const nanocoap_server_response_ctx_t *ctx, const iolist_t *reply); +/** + * @brief Register an observer + * @param[in] req_ctx Request context belonging to @p req_pkt + * @param[in,out] req_pkt Request that contained the observe registration request + * + * @warning This depends on module `nanocoap_server_observe` + * + * @note If the same endpoint already was registered on the same resource, + * it will just update the token and keep the existing entry. This + * way duplicate detection is not needed and we eagerly can reclaim + * resources when a client lost state. + * + * @warning Preventing the same endpoint to registers more than once (using + * different tokens) to the same resource deviates from RFC 7641. + * + * The deviation here is intentional. A server can receive a second registration + * from the same endpoint for the same resource for one of the following + * reasons: + * + * 1. Reaffirming the registration by using the same token again. + * 2. Loosing state on the client side. + * 3. A malicious client trying to exhaust resources. + * 4. The same resource has different representations depending on the + * request. (E.g. `/.well-known/core` can yield a wildly different response + * depending on filters provided via URI-Query Options.) + * + * For case 1 updating the registration is matching what the spec mandates. + * For two the old registration will not be of value for the client, and + * overwriting it makes more efficient use of network bandwidth and RAM. + * For 3 the deviation forces the adversary to send observe requests from + * different ports to exhaust resources, which is a very minor improvement. + * For 4 the deviation is a problem. However, the observe API does not allow to + * send out different notification messages for the same resource anyway, so + * case 4 cannot occur here. + * + * @retval 0 Success + * @retval -ENOMEM Not enough resources to register another observer + * @retval <0 Negative errno code indicating error + */ +int nanocoap_register_observer(const coap_request_ctx_t *req_ctx, coap_pkt_t *req_pkt); + +/** + * @brief Unregister an observer + * @param req_ctx Request context belonging to @p req_pkt + * + * @warning This depends on module `nanocoap_server_observe` + * + * @note It is safe to call this multiple times, e.g. duplicate detection + * is not needed for this. + */ +void nanocoap_unregister_observer(const coap_request_ctx_t *req_ctx); + +/** + * @brief Unregister a stale observation due to a reset message received + * @param[in] ep Endpoint to wipe from the observer list + * @param[in] msg_id Message ID of the notification send. + */ +void nanocoap_unregister_observer_due_to_reset(const sock_udp_ep_t *ep, + uint16_t msg_id); + +/** + * @brief Notify all currently registered observers of the given resource + * + * @param[in] res Resource to send updates for + * @param[in] iol I/O list containing the CoAP Options, payload marker, + * and payload of the update to send up + * + * @pre @p iol contains everything but the CoAP header needed to send out. + * This will at least be a CoAP observe option, a payload marker, + * and a payload + * + * @post For each registered observer a CoAP packet header is generated and + * the concatenation of that header and the provided list is sent + */ +void nanocoap_notify_observers(const coap_resource_t *res, const iolist_t *iol); + +/** + * @brief Build and send notification to observers registered to a specific + * resource. + * + * @note Use @ref nanocoap_notify_observers for more control (such + * as adding custom options) over the notification(s) to send. + * + * @param[in] res Resource to send updates for + * @param[in] obs 24-bit number to add as observe option + * @param[in] payload Payload to send out + * @param[in] payload_len Length of @p payload in bytes + */ +void nanocoap_notify_observers_simple(const coap_resource_t *res, uint32_t obs, + const void *payload, size_t payload_len); + /** * @brief Get next consecutive message ID for use when building a new * CoAP request. diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index 5f6a6e8f0d29..1d218bbb945a 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -28,6 +28,7 @@ #include "bitarithm.h" #include "net/nanocoap.h" +#include "net/nanocoap_sock.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -494,6 +495,11 @@ ssize_t coap_handle_req(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_le { assert(ctx); + if (IS_USED(MODULE_NANOCOAP_SERVER_OBSERVE) && (coap_get_type(pkt) == COAP_TYPE_RST)) { + nanocoap_unregister_observer_due_to_reset(coap_request_ctx_get_remote_udp(ctx), + coap_get_id(pkt)); + } + if (coap_get_code_class(pkt) != COAP_REQ) { DEBUG("coap_handle_req(): not a request.\n"); return -EBADMSG; diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index 8580ee2104f4..0930f24e00c0 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -25,6 +25,7 @@ #include #include +#include "container.h" #include "net/credman.h" #include "net/nanocoap.h" #include "net/nanocoap_sock.h" @@ -48,6 +49,10 @@ # define CONFIG_NANOCOAP_DTLS_HANDSHAKE_BUF_SIZE (160) #endif +#ifndef CONFIG_NANOCOAP_MAX_OBSERVERS +# define CONFIG_NANOCOAP_MAX_OBSERVERS 4 +#endif + enum { STATE_REQUEST_SEND, /**< request was just sent or will be sent again */ STATE_STOP_RETRANSMIT, /**< stop retransmissions due to a matching empty ACK */ @@ -64,6 +69,35 @@ typedef struct { #endif } _block_ctx_t; +/** + * @brief Structure to track the state of an observation + */ +typedef struct { + /** + * @brief Context needed to build notifications (e.g. Token, endpoint + * to send to) + * + * @details To safe ROM, we reuse the separate response code to also + * send notifications, as the functionality is almost identical. + */ + nanocoap_server_response_ctx_t response; + /** + * @brief The resource the client has subscribed to + * + * @details This is `NULL` when the slot is free + */ + const coap_resource_t *resource; + /** + * @brief Message ID used in the last notification + */ + uint16_t msg_id; +} _observer_t; + +#if MODULE_NANOCOAP_SERVER_OBSERVE +static _observer_t _observer_pool[CONFIG_NANOCOAP_MAX_OBSERVERS]; +static mutex_t _observer_pool_lock; +#endif + int nanocoap_sock_dtls_connect(nanocoap_sock_t *sock, sock_udp_ep_t *local, const sock_udp_ep_t *remote, credman_tag_t tag) { @@ -1156,7 +1190,13 @@ int nanocoap_server_sendv_separate(const nanocoap_server_response_ctx_t *ctx, if (!sock_udp_ep_is_multicast(&ctx->local)) { aux_out_ptr = &aux_out; } - return sock_udp_sendv_aux(NULL, reply, &ctx->remote, aux_out_ptr); + ssize_t retval = sock_udp_sendv_aux(NULL, reply, &ctx->remote, aux_out_ptr); + + if (retval < 0) { + return retval; + } + + return 0; } int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx, @@ -1190,3 +1230,115 @@ int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx, return nanocoap_server_sendv_separate(ctx, &head); } #endif + +#if MODULE_NANOCOAP_SERVER_OBSERVE +int nanocoap_register_observer(const coap_request_ctx_t *req_ctx, coap_pkt_t *req_pkt) +{ + mutex_lock(&_observer_pool_lock); + + _observer_t *free = NULL; + const coap_resource_t *resource = req_ctx->resource; + + for (size_t i = 0; i < CONFIG_NANOCOAP_MAX_OBSERVERS; i++) { + if (_observer_pool[i].resource == NULL) { + free = &_observer_pool[i]; + } + if ((_observer_pool[i].resource == resource) + && sock_udp_ep_equal(&_observer_pool[i].response.remote, + coap_request_ctx_get_remote_udp(req_ctx))) + { + /* Deviation from the standard: Subscribing twice makes no + * sense with our CoAP implementation, so either this is a + * reaffirmation of an existing subscription (same token) or the + * client lost state (different token). We just update the + * subscription in either case */ + DEBUG("nanocoap: observe slot %" PRIuSIZE " reused\n", i); + uint8_t tkl = coap_get_token_len(req_pkt); + _observer_pool[i].response.tkl = tkl; + memcpy(_observer_pool[i].response.token, coap_get_token(req_pkt), tkl); + mutex_unlock(&_observer_pool_lock); + return 0; + } + } + + if (!free) { + DEBUG_PUTS("nanocoap: observe registration failed, no free slot"); + mutex_unlock(&_observer_pool_lock); + return -ENOMEM; + } + + int retval = nanocoap_server_prepare_separate(&free->response, req_pkt, req_ctx); + if (retval) { + DEBUG("nanocoap: observe registration failed: %d\n", retval); + mutex_unlock(&_observer_pool_lock); + return retval; + } + free->resource = req_ctx->resource; + free->msg_id = random_uint32(); + mutex_unlock(&_observer_pool_lock); + DEBUG("nanocoap: new observe registration at slot %" PRIuSIZE "\n", + index_of(_observer_pool, free)); + return 0; +} + +void nanocoap_unregister_observer(const coap_request_ctx_t *req_ctx) +{ + mutex_lock(&_observer_pool_lock); + for (size_t i = 0; i < CONFIG_NANOCOAP_MAX_OBSERVERS; i++) { + if ((_observer_pool[i].resource == req_ctx->resource) + && sock_udp_ep_equal(&_observer_pool[i].response.remote, coap_request_ctx_get_remote_udp(req_ctx))) { + DEBUG("nanocoap: observer at index %" PRIuSIZE " unregistred\n", i); + _observer_pool[i].resource = NULL; + } + } + mutex_unlock(&_observer_pool_lock); +} + +void nanocoap_unregister_observer_due_to_reset(const sock_udp_ep_t *ep, + uint16_t msg_id) +{ + mutex_lock(&_observer_pool_lock); + for (size_t i = 0; i < CONFIG_NANOCOAP_MAX_OBSERVERS; i++) { + if ((_observer_pool[i].resource != NULL) + && (_observer_pool[i].msg_id == msg_id) + && sock_udp_ep_equal(&_observer_pool[i].response.remote, ep)) { + DEBUG("nanocoap: observer at index %" PRIuSIZE " unregistred due to RST\n", i); + _observer_pool[i].resource = NULL; + return; + } + } + mutex_unlock(&_observer_pool_lock); +} + +void nanocoap_notify_observers(const coap_resource_t *res, const iolist_t *iol) +{ + mutex_lock(&_observer_pool_lock); + for (size_t i = 0; i < CONFIG_NANOCOAP_MAX_OBSERVERS; i++) { + if (_observer_pool[i].resource == res) { + uint8_t rbuf[sizeof(coap_hdr_t) + COAP_TOKEN_LENGTH_MAX + 1]; + + ssize_t hdr_len = nanocoap_server_build_separate(&_observer_pool[i].response, rbuf, sizeof(rbuf), + COAP_CODE_CONTENT, COAP_TYPE_NON, + ++_observer_pool[i].msg_id); + if (hdr_len < 0) { + /* no need to keep the observer in the pool, if we cannot + * send anyway */ + _observer_pool[i].resource = NULL; + } + + const iolist_t msg = { + .iol_base = rbuf, + .iol_len = hdr_len, + .iol_next = (iolist_t *)iol + }; + + if (nanocoap_server_sendv_separate(&_observer_pool[i].response, &msg)) { + /* no need to keep the observer in the pool, if we cannot + * send anyway */ + _observer_pool[i].resource = NULL; + } + } + } + mutex_unlock(&_observer_pool_lock); +} +#endif From a42b6726a2431d48724e305860441d6a875909cf Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 21 Jan 2025 21:59:31 +0100 Subject: [PATCH 03/10] fixup! sys/net/nanocoap: implement observe --- examples/nanocoap_server/coap_handler.c | 4 ++-- sys/include/net/nanocoap_sock.h | 4 +++- sys/net/application_layer/nanocoap/sock.c | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index f4ade8ddf066..2065798e230b 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -255,7 +255,7 @@ static ssize_t _time_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_req break; case 1: /* unregister */ - nanocoap_unregister_observer(context); + nanocoap_unregister_observer(context, pkt); break; default: /* No (valid) observe option present --> ignore observe and handle @@ -272,7 +272,7 @@ static ssize_t _time_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_req if (hdr_len < 0) { /* we undo any potential registration if we cannot reply */ - nanocoap_unregister_observer(context); + nanocoap_unregister_observer(context, pkt); return len; } diff --git a/sys/include/net/nanocoap_sock.h b/sys/include/net/nanocoap_sock.h index 839dc5f07b81..0489b896fe74 100644 --- a/sys/include/net/nanocoap_sock.h +++ b/sys/include/net/nanocoap_sock.h @@ -397,13 +397,15 @@ int nanocoap_register_observer(const coap_request_ctx_t *req_ctx, coap_pkt_t *re /** * @brief Unregister an observer * @param req_ctx Request context belonging to @p req_pkt + * @param req_pkt Received request for unregistration * * @warning This depends on module `nanocoap_server_observe` * * @note It is safe to call this multiple times, e.g. duplicate detection * is not needed for this. */ -void nanocoap_unregister_observer(const coap_request_ctx_t *req_ctx); +void nanocoap_unregister_observer(const coap_request_ctx_t *req_ctx, + const coap_pkt_t *req_pkt); /** * @brief Unregister a stale observation due to a reset message received diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index 0930f24e00c0..86fad3420a79 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -1281,11 +1281,15 @@ int nanocoap_register_observer(const coap_request_ctx_t *req_ctx, coap_pkt_t *re return 0; } -void nanocoap_unregister_observer(const coap_request_ctx_t *req_ctx) +void nanocoap_unregister_observer(const coap_request_ctx_t *req_ctx, + const coap_pkt_t *req_pkt) { mutex_lock(&_observer_pool_lock); for (size_t i = 0; i < CONFIG_NANOCOAP_MAX_OBSERVERS; i++) { if ((_observer_pool[i].resource == req_ctx->resource) + && (_observer_pool[i].response.tkl == coap_get_token_len(req_pkt)) + && !memcmp(_observer_pool[i].response.token, coap_get_token(req_pkt), + _observer_pool[i].response.tkl) && sock_udp_ep_equal(&_observer_pool[i].response.remote, coap_request_ctx_get_remote_udp(req_ctx))) { DEBUG("nanocoap: observer at index %" PRIuSIZE " unregistred\n", i); _observer_pool[i].resource = NULL; From 91d3f3e3c685b88f3cf823f6eb63580600d6bf88 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 21 Jan 2025 22:00:42 +0100 Subject: [PATCH 04/10] fixup! fixup! sys/net/nanocoap: implement observe --- examples/nanocoap_server/coap_handler.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index 2065798e230b..966aad8582cb 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -264,10 +264,10 @@ static ssize_t _time_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_req } const size_t estimated_data_len = - 4 /* Max Observe Option size */ - + 1 /* payload marker */ - + 10 /* strlen("4294967295"), 4294967295 == UINT32_MAX */ - + 1 /* '\n' */; + 4 /* Max Observe Option size */ + + 1 /* payload marker */ + + 10 /* strlen("4294967295"), 4294967295 == UINT32_MAX */ + + 1; /* '\n' */ ssize_t hdr_len = coap_build_reply(pkt, COAP_CODE_CONTENT, buf, len, estimated_data_len); if (hdr_len < 0) { From b045d8ca00d9aac55d24abaffc51c92c23ba760f Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 21 Jan 2025 22:44:37 +0100 Subject: [PATCH 05/10] fixup! fixup! fixup! sys/net/nanocoap: implement observe --- sys/net/application_layer/nanocoap/sock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index 86fad3420a79..500da22d873c 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -1328,6 +1328,7 @@ void nanocoap_notify_observers(const coap_resource_t *res, const iolist_t *iol) /* no need to keep the observer in the pool, if we cannot * send anyway */ _observer_pool[i].resource = NULL; + continue; } const iolist_t msg = { From 13fe953a25239b4967c30bccd9cb8e57a67cfd84 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 22 Jan 2025 21:04:30 +0100 Subject: [PATCH 06/10] fixup! sys/net/nanocoap: improve separate response handling --- examples/nanocoap_server/coap_handler.c | 2 +- sys/include/net/nanocoap_sock.h | 17 ++++++++--------- sys/net/application_layer/nanocoap/sock.c | 9 ++------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index 966aad8582cb..05d347867650 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -205,7 +205,7 @@ static ssize_t _separate_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap static event_callback_t event_timed = EVENT_CALLBACK_INIT(_send_response, &_separate_ctx); if (event_timeout_is_pending(&event_timeout)) { - if (nanocoap_is_duplicate_in_separate_ctx(&_separate_ctx, pkt, context)) { + if (nanocoap_server_is_remote_in_response_ctx(&_separate_ctx, context)) { /* duplicate of the request a separate response is already scheduled * for --> resending the ACK */ return coap_build_empty_ack(pkt, (void *)buf); diff --git a/sys/include/net/nanocoap_sock.h b/sys/include/net/nanocoap_sock.h index 0489b896fe74..246c388cd314 100644 --- a/sys/include/net/nanocoap_sock.h +++ b/sys/include/net/nanocoap_sock.h @@ -261,18 +261,17 @@ int nanocoap_server_prepare_separate(nanocoap_server_response_ctx_t *ctx, coap_pkt_t *pkt, const coap_request_ctx_t *req); /** - * @brief Check if given request is a duplicate of the one a separate response - * was prepared for + * @brief Check if a given separate response context was prepared for the + * remote endpoint of a given request * - * @param[in] ctx Context to check against - * @param[in] pkt Possible duplication - * @param[in] req Request context of the possible duplicate + * @param[in] ctx Separate response context to check + * @param[in] req Request from the remote to check for * - * @retval true @p pkt is a duplicate - * @retval false @p pkt is *NOT* a duplicate + * @retval true The remote endpoint given by @p req is in @p ctx + * @retval false @p ctx was prepared for a different remote endpoint */ -bool nanocoap_is_duplicate_in_separate_ctx(const nanocoap_server_response_ctx_t *ctx, - coap_pkt_t *pkt, const coap_request_ctx_t *req); +bool nanocoap_server_is_remote_in_response_ctx(const nanocoap_server_response_ctx_t *ctx, + const coap_request_ctx_t *req); /** * @brief Build and send a separate response to a CoAP request diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index 500da22d873c..6320f95d859e 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -1142,14 +1142,9 @@ int nanocoap_server_prepare_separate(nanocoap_server_response_ctx_t *ctx, return 0; } -bool nanocoap_is_duplicate_in_separate_ctx(const nanocoap_server_response_ctx_t *ctx, - coap_pkt_t *pkt, const coap_request_ctx_t *req) +bool nanocoap_is_remote_already_registered(const nanocoap_server_response_ctx_t *ctx, + const coap_request_ctx_t *req) { - /* We assume NSTART == 1 and do not actually look into the packet here. - * But having the API prepared to allow also comparing token and message ID - * is probably a good thing. */ - (void)pkt; - return sock_udp_ep_equal(&ctx->remote, req->remote); } From 55a1aa507a333ca8610e2efa67482c15d64ad83a Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 22 Jan 2025 21:39:28 +0100 Subject: [PATCH 07/10] fixup! sys/net/nanocoap: implement observe --- examples/nanocoap_server/coap_handler.c | 10 +++++++++- sys/include/net/coap.h | 1 + sys/include/net/nanocoap.h | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index 05d347867650..103add7436ba 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -277,14 +277,19 @@ static ssize_t _time_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_req } if (hdr_len == 0) { + /* no response required, probably because of no-response option matching + * the response class */ return 0; } + /* coap_build_reply() is a bit goofy: It returns the size of the written + * header + `estiamted_data_len`, so we have to subtract it again to obtain + * the size of data written. */ uint8_t *pos = buf + hdr_len - estimated_data_len; if (registered) { uint16_t last_opt = 0; - pos += coap_opt_put_uint(pos, last_opt, COAP_OPT_OBSERVE, now & 0xffffff); + pos += coap_opt_observe(pos, last_opt, now); } *pos++ = 0xff; /* payload marker */ pos += fmt_u32_dec((void *)pos, now); @@ -312,6 +317,9 @@ static void _notify_observer_handler(event_t *ev) .iol_base = buf, .iol_len = (uintptr_t)pos - (uintptr_t)buf, }; + + /* `NANOCOAP_RESOURCE(time)` expends to XFA magic adding an entry named + * `coap_resource_time`. */ nanocoap_notify_observers(&coap_resource_time, &data); } diff --git a/sys/include/net/coap.h b/sys/include/net/coap.h index 275510463d51..797d4d7f8a73 100644 --- a/sys/include/net/coap.h +++ b/sys/include/net/coap.h @@ -519,6 +519,7 @@ typedef enum { */ #define COAP_OBS_REGISTER (0) #define COAP_OBS_DEREGISTER (1) +#define COAP_OBS_MAX_VALUE_MASK (0xffffff) /**< observe value is 24 bits */ /** @} */ /** diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 45fc28ed2660..81c9eef67606 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -1790,6 +1790,22 @@ static inline size_t coap_opt_put_block2_control(uint8_t *buf, uint16_t lastonum (block->blknum << 4) | block->szx); } +/** + * @brief Insert an CoAP Observe Option into the buffer + * + * @param[out] buf Buffer to write to + * @param[in] lastonum last option number (must be < 6) + * @param[in] obs observe number to write + * + * @param[in] lastonum last option number (must be < 27) + */ +static inline size_t coap_opt_put_observe(uint8_t *buf, uint16_t lastonum, + uint32_t obs) +{ + obs &= COAP_OBS_MAX_VALUE_MASK; /* trim obs down to 24 bit */ + return coap_opt_put_uint(buf, lastonum, COAP_OPT_OBSERVE, obs); +} + /** * @brief Encode the given string as multi-part option into buffer * From 032607496dd76ae0868539adc77e2eac0f47ee86 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 22 Jan 2025 21:41:01 +0100 Subject: [PATCH 08/10] Apply suggestions from code review Co-authored-by: benpicco --- sys/include/net/nanocoap_sock.h | 4 ++-- sys/net/application_layer/nanocoap/sock.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/include/net/nanocoap_sock.h b/sys/include/net/nanocoap_sock.h index 246c388cd314..d74c629e046e 100644 --- a/sys/include/net/nanocoap_sock.h +++ b/sys/include/net/nanocoap_sock.h @@ -326,7 +326,7 @@ int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx, * @retval <0 Negative errno code indicating the error */ ssize_t nanocoap_server_build_separate(const nanocoap_server_response_ctx_t *ctx, - uint8_t *buf, size_t buf_len, + void *buf, size_t buf_len, unsigned code, unsigned type, uint16_t msg_id); @@ -372,7 +372,7 @@ int nanocoap_server_sendv_separate(const nanocoap_server_response_ctx_t *ctx, * reasons: * * 1. Reaffirming the registration by using the same token again. - * 2. Loosing state on the client side. + * 2. Losing state on the client side. * 3. A malicious client trying to exhaust resources. * 4. The same resource has different representations depending on the * request. (E.g. `/.well-known/core` can yield a wildly different response diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index 6320f95d859e..e95a80a64011 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -1286,7 +1286,7 @@ void nanocoap_unregister_observer(const coap_request_ctx_t *req_ctx, && !memcmp(_observer_pool[i].response.token, coap_get_token(req_pkt), _observer_pool[i].response.tkl) && sock_udp_ep_equal(&_observer_pool[i].response.remote, coap_request_ctx_get_remote_udp(req_ctx))) { - DEBUG("nanocoap: observer at index %" PRIuSIZE " unregistred\n", i); + DEBUG("nanocoap: observer at index %" PRIuSIZE " unregistered\n", i); _observer_pool[i].resource = NULL; } } @@ -1301,7 +1301,7 @@ void nanocoap_unregister_observer_due_to_reset(const sock_udp_ep_t *ep, if ((_observer_pool[i].resource != NULL) && (_observer_pool[i].msg_id == msg_id) && sock_udp_ep_equal(&_observer_pool[i].response.remote, ep)) { - DEBUG("nanocoap: observer at index %" PRIuSIZE " unregistred due to RST\n", i); + DEBUG("nanocoap: observer at index %" PRIuSIZE " unregistered due to RST\n", i); _observer_pool[i].resource = NULL; return; } From 7a0a41b34db36e344a4d5efbee631ef14adb85d1 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 22 Jan 2025 22:03:42 +0100 Subject: [PATCH 09/10] fixup! sys/net/nanocoap: implement observe --- examples/nanocoap_server/coap_handler.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index 103add7436ba..86dd39993997 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -59,7 +59,7 @@ static ssize_t _riot_block2_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, c bufpos += coap_put_option_ct(bufpos, 0, COAP_FORMAT_TEXT); bufpos += coap_opt_put_block2(bufpos, COAP_OPT_CONTENT_FORMAT, &slicer, 1); - *bufpos++ = 0xff; + *bufpos++ = COAP_PAYLOAD_MARKER; /* Add actual content */ bufpos += coap_blockwise_put_bytes(&slicer, bufpos, block2_intro, sizeof(block2_intro)-1); @@ -289,9 +289,9 @@ static ssize_t _time_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_req if (registered) { uint16_t last_opt = 0; - pos += coap_opt_observe(pos, last_opt, now); + pos += coap_opt_put_observe(pos, last_opt, now); } - *pos++ = 0xff; /* payload marker */ + *pos++ = COAP_PAYLOAD_MARKER; pos += fmt_u32_dec((void *)pos, now); *pos++ = '\n'; @@ -309,8 +309,8 @@ static void _notify_observer_handler(event_t *ev) uint8_t buf[32]; uint8_t *pos = buf; uint16_t last_opt = 0; - pos += coap_opt_put_uint(pos, last_opt, COAP_OPT_OBSERVE, now & 0xffffff); - *pos++ = 0xff; /* payload marker */ + pos += coap_opt_put_observe(pos, last_opt, now); + *pos++ = COAP_PAYLOAD_MARKER; pos += fmt_u32_dec((void *)pos, now); *pos++ = '\n'; iolist_t data = { From 813077a2c756fec98fbb930a26f0059c4e3c2ad1 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 22 Jan 2025 22:05:02 +0100 Subject: [PATCH 10/10] fixup! sys/net/nanocoap: improve separate response handling --- sys/net/application_layer/nanocoap/sock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index e95a80a64011..e0f57bfbee76 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -1142,8 +1142,8 @@ int nanocoap_server_prepare_separate(nanocoap_server_response_ctx_t *ctx, return 0; } -bool nanocoap_is_remote_already_registered(const nanocoap_server_response_ctx_t *ctx, - const coap_request_ctx_t *req) +bool nanocoap_server_is_remote_in_response_ctx(const nanocoap_server_response_ctx_t *ctx, + const coap_request_ctx_t *req) { return sock_udp_ep_equal(&ctx->remote, req->remote); }