From eaa848af7cbdacbbfea1802849d7fc0318f606a4 Mon Sep 17 00:00:00 2001 From: Michel Rottleuthner Date: Thu, 9 Nov 2023 19:14:38 +0100 Subject: [PATCH] gcoap: clear observe state on RST response to notification In order to properly handle an observe cancellation of a client, the server has to keep track of the notification MIDs (to be able to match an RST to a notification), see [RFC7641, 3.6 Cancellation](https://www.rfc-editor.org/rfc/rfc7641.html#section-3.6) for mor details. An alternative to this would be to make either the client send an explicit observe deregister request, or make the server send the next notification via CON (which hten allows matching of the RST due to the CON state). --- sys/include/net/gcoap.h | 1 + sys/net/application_layer/gcoap/gcoap.c | 57 +++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/sys/include/net/gcoap.h b/sys/include/net/gcoap.h index 2547bcd82457..33af5fefce04 100644 --- a/sys/include/net/gcoap.h +++ b/sys/include/net/gcoap.h @@ -837,6 +837,7 @@ typedef struct { sock_udp_ep_t *observer; /**< Client endpoint; unused if null */ const coap_resource_t *resource; /**< Entity being observed */ uint8_t token[GCOAP_TOKENLEN_MAX]; /**< Client token for notifications */ + uint16_t last_msgid; /**< Message ID of last notification */ unsigned token_len; /**< Actual length of token attribute */ gcoap_socket_t socket; /**< Transport type to observer */ } gcoap_observe_memo_t; diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index 76615d02c152..6c9a8a4e4a8c 100644 --- a/sys/net/application_layer/gcoap/gcoap.c +++ b/sys/net/application_layer/gcoap/gcoap.c @@ -83,6 +83,10 @@ static int _find_obs_memo(gcoap_observe_memo_t **memo, sock_udp_ep_t *remote, coap_pkt_t *pdu); static void _find_obs_memo_resource(gcoap_observe_memo_t **memo, const coap_resource_t *resource); + +static void _check_and_expire_obs_memo_last_mid(sock_udp_ep_t *remote, + uint16_t last_notify_mid); + static nanocoap_cache_entry_t *_cache_lookup_memo(gcoap_request_memo_t *cache_key); static void _cache_process(gcoap_request_memo_t *memo, coap_pkt_t *pdu); @@ -410,6 +414,10 @@ static void _process_coap_pdu(gcoap_socket_t *sock, sock_udp_ep_t *remote, sock_ event_timeout_clear(&memo->resp_evt_tmout); _expire_request(memo); } + + /* check if this RST is due to the client not being interested + * in receiving observe notifications anymore. */ + _check_and_expire_obs_memo_last_mid(remote, coap_get_id(&pdu)); } /* validate class and type for incoming */ @@ -1062,6 +1070,51 @@ static int _find_obs_memo(gcoap_observe_memo_t **memo, sock_udp_ep_t *remote, return empty_slot; } +/* + * Checks if an observe memo exists for which a notification with the given + * msg ID was sent out. If so, it expires the memo and frees up the + * observer entry if needed. + * + * remote[in] The remote to check for a stale observe memo. + * last_notify_mid[in] The message ID of the last notification send to the + * given remote. + */ +static void _check_and_expire_obs_memo_last_mid(sock_udp_ep_t *remote, + uint16_t last_notify_mid) +{ + /* find observer entry from remote */ + sock_udp_ep_t *observer; + _find_observer(&observer, remote); + + if (observer) { + gcoap_observe_memo_t *stale_obs_memo; + /* get the observe memo corresponding to the notification with the + * given msg ID. */ + for (unsigned i = 0; i < CONFIG_GCOAP_OBS_REGISTRATIONS_MAX; i++) { + if (_coap_state.observe_memos[i].observer == NULL) { + continue; + } + if ((_coap_state.observe_memos[i].observer == observer) && + (last_notify_mid == _coap_state.observe_memos[i].last_msgid)) { + stale_obs_memo = &_coap_state.observe_memos[i]; + return; + } + } + + if (stale_obs_memo) { + stale_obs_memo->observer = NULL; /* clear memo */ + + /* check if the observer has more observe memos registered... */ + stale_obs_memo = NULL; + _find_obs_memo(&stale_obs_memo, observer, NULL); + if (stale_obs_memo == NULL) { + /* ... if not -> also free the observer entry */ + observer->family = AF_UNSPEC; + } + } + } +} + /* * Find registered observe memo for a resource. * @@ -1754,6 +1807,10 @@ int gcoap_obs_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, coap_pkt_init(pdu, buf, len, hdrlen); _add_generated_observe_option(pdu); + /* Store message ID of the last notification sent. This is needed + * to match a potential RST returned by a client in order to signal + * it does not recognize this notification. */ + memo->last_msgid = msgid; return GCOAP_OBS_INIT_OK; }