Skip to content

Commit

Permalink
keepalive: Add in server keepalive support
Browse files Browse the repository at this point in the history
If coap_context_set_keepalive() is set in the server, CoAP keepalive packets
will get sent for any CoAP session that is curently observing a resource.

If the keepalive times out, then the observation(s) are canceled and the
server session is deleted.
  • Loading branch information
mrdeep1 committed Jan 10, 2025
1 parent 080da9a commit 4d84859
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 12 deletions.
1 change: 1 addition & 0 deletions examples/coap-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -2563,6 +2563,7 @@ main(int argc, char **argv) {
coap_mcast_per_resource(ctx);
coap_context_set_block_mode(ctx, block_mode);
coap_context_set_max_block_size(ctx, max_block_size);
coap_context_set_keepalive(ctx, 30);
if (csm_max_message_size)
coap_context_set_csm_max_message_size(ctx, csm_max_message_size);
if (doing_tls_engine) {
Expand Down
2 changes: 1 addition & 1 deletion include/coap3/coap_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ coap_context_t *coap_session_get_context(const coap_session_t *session);
*
* @return @c 1 if updated, @c 0 on failure.
*/
int coap_session_set_type_client(coap_session_t *session);
COAP_API int coap_session_set_type_client(coap_session_t *session);

/**
* Set the session MTU. This is the maximum message size that can be sent,
Expand Down
19 changes: 19 additions & 0 deletions include/coap3/coap_session_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ struct coap_session_t {
UT_hash_handle hh;
coap_addr_tuple_t addr_info; /**< remote/local address info */
int ifindex; /**< interface index */
unsigned ref_subscriptions; /**< reference count of current subscriptions */
coap_socket_t sock; /**< socket object for the session, if
any */
#if COAP_SERVER_SUPPORT
Expand Down Expand Up @@ -378,6 +379,24 @@ void coap_session_establish(coap_session_t *session);
*/
coap_session_t *coap_session_reference_lkd(coap_session_t *session);

/**
* Set the session type to client. Typically used in a call-home server.
* The session initially needs to be of type COAP_SESSION_TYPE_SERVER,
* which is then made COAP_SESSION_TYPE_CLIENT.
* Note: If this function is successful, the session reference count is
* incremented and a subsequent coap_session_release() taking the
* reference count to 0 will cause the (now client) session to be freed off.
* Note: This function will fail for a DTLS server type session if done before
* the ClientHello is seen.
*
* Note: This function must be called in the locked state.
*
* @param session The CoAP session.
*
* @return @c 1 if updated, @c 0 on failure.
*/
int coap_session_set_type_client_lkd(coap_session_t *session);

/**
* Decrement reference counter on a session.
* Note that the session may be deleted as a result and should not be used
Expand Down
7 changes: 7 additions & 0 deletions man/coap_keepalive.txt.in
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,18 @@ If the keepalive fails to solicit a response, then this can be tracked by
defining the handler to use by using *coap_register_event_handler*() which will
be called with a reason of COAP_EVENT_KEEPALIVE_FAILURE.

*NOTE:* A keepalive will only be transmitted when there has been no traffic on
the session for _seconds_. It does not regularily repeat every _seconds_.

*NOTE:* As this may be used to keep an interim NAT device "warm", the
exponentially increasing retransmit times for CON requests is limited to
_seconds_, but the retransmit counter is not affected. Unless needed,
_seconds_ shold not be set to less than 30.

*NOTE:* If this is used at the server end of a CoAP session (set up by using
*coap_new_endpoint*(3)), keepalives will only be transmitted for sessions
that have an active observe subscription.

SEE ALSO
--------
*coap_handler*(3)
Expand Down
38 changes: 35 additions & 3 deletions src/coap_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1328,7 +1328,7 @@ coap_io_prepare_io_lkd(coap_context_t *ctx,
unsigned int *num_sockets,
coap_tick_t now) {
coap_queue_t *nextpdu;
coap_session_t *s, *rtmp;
coap_session_t *s, *stmp;
coap_tick_t timeout = 0;
coap_tick_t s_timeout;
#if COAP_SERVER_SUPPORT
Expand Down Expand Up @@ -1403,14 +1403,15 @@ coap_io_prepare_io_lkd(coap_context_t *ctx,
sockets[(*num_sockets)++] = &ep->sock;
}
#endif /* ! COAP_EPOLL_SUPPORT && ! WITH_LWIP && ! RIOT_VERSION */
SESSIONS_ITER_SAFE(ep->sessions, s, rtmp) {
SESSIONS_ITER_SAFE(ep->sessions, s, stmp) {
/* Check whether any idle server sessions should be released */
if (s->type == COAP_SESSION_TYPE_SERVER && s->ref == 0 &&
s->delayqueue == NULL &&
(s->last_rx_tx + session_timeout <= now ||
s->state == COAP_SESSION_STATE_NONE)) {
coap_handle_event_lkd(ctx, COAP_EVENT_SERVER_SESSION_DEL, s);
coap_session_free(s);
continue;
} else {
if (s->type == COAP_SESSION_TYPE_SERVER && s->ref == 0 &&
s->delayqueue == NULL) {
Expand Down Expand Up @@ -1474,11 +1475,42 @@ coap_io_prepare_io_lkd(coap_context_t *ctx,
release_1:
coap_session_release_lkd(s);
}
if (s->type == COAP_SESSION_TYPE_SERVER &&
s->state == COAP_SESSION_STATE_ESTABLISHED &&
s->ref_subscriptions &&
ctx->ping_timeout > 0) {
/* Only do this if this session is observing */
if (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND <= now) {
/* Time to send a ping */
if ((s->last_ping_mid = coap_session_send_ping_lkd(s)) == COAP_INVALID_MID)
/* Some issue - not safe to continue processing */
continue;
if (s->last_ping > 0 && s->last_pong < s->last_ping) {
coap_handle_event_lkd(s->context, COAP_EVENT_KEEPALIVE_FAILURE, s);
coap_session_reference_lkd(s);
RESOURCES_ITER(s->context->resources, r) {
coap_cancel_all_messages(s->context, s, NULL);
coap_delete_observer(r, s, NULL);
}
coap_session_release_lkd(s);
/* Force session to go away */
coap_session_set_type_client_lkd(s);
coap_session_release_lkd(s);
/* check the next session */
continue;
}
s->last_rx_tx = now;
s->last_ping = now;
}
s_timeout = (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND) - now;
if (timeout == 0 || s_timeout < timeout)
timeout = s_timeout;
}
}
}
#endif /* COAP_SERVER_SUPPORT */
#if COAP_CLIENT_SUPPORT
SESSIONS_ITER_SAFE(ctx->sessions, s, rtmp) {
SESSIONS_ITER_SAFE(ctx->sessions, s, stmp) {
if (s->type == COAP_SESSION_TYPE_CLIENT &&
s->state == COAP_SESSION_STATE_ESTABLISHED &&
ctx->ping_timeout > 0) {
Expand Down
11 changes: 6 additions & 5 deletions src/coap_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -1844,10 +1844,10 @@ coap_send_internal(coap_session_t *session, coap_pdu_t *pdu) {
#if COAP_OSCORE_SUPPORT
if (session->oscore_encryption &&
pdu->type != COAP_MESSAGE_RST &&
!(pdu->type == COAP_MESSAGE_ACK && pdu->code == COAP_EMPTY_CODE)) {
!(pdu->type == COAP_MESSAGE_ACK && pdu->code == COAP_EMPTY_CODE) &&
!(COAP_PROTO_RELIABLE(session->proto) && pdu->code == COAP_SIGNALING_CODE_PONG)) {
/* Refactor PDU as appropriate RFC8613 */
coap_pdu_t *osc_pdu = coap_oscore_new_pdu_encrypted_lkd(session, pdu, NULL,
0);
coap_pdu_t *osc_pdu = coap_oscore_new_pdu_encrypted_lkd(session, pdu, NULL, 0);

if (osc_pdu == NULL) {
coap_log_warn("OSCORE: PDU could not be encrypted\n");
Expand Down Expand Up @@ -2773,7 +2773,7 @@ coap_cancel_all_messages(coap_context_t *context, coap_session_t *session,

while (q) {
if (q->session == session &&
coap_binary_equal(&q->pdu->actual_token, token)) {
(!token || coap_binary_equal(&q->pdu->actual_token, token))) {
*p = q->next;
coap_log_debug("** %s: mid=0x%04x: removed (6)\n",
coap_session_str(session), q->id);
Expand Down Expand Up @@ -4282,7 +4282,8 @@ coap_dispatch(coap_context_t *context, coap_session_t *session,
coap_remove_from_queue(&context->sendqueue, session, pdu->mid, &sent);

if (sent) {
coap_cancel(context, sent);
if (!is_ping_rst)
coap_cancel(context, sent);

if (!is_ping_rst && !is_ext_token_rst) {
if (sent->pdu->type==COAP_MESSAGE_CON) {
Expand Down
3 changes: 3 additions & 0 deletions src/coap_resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ coap_add_observer(coap_resource_t *resource,
}
s->cache_key = cache_key;
s->session = coap_session_reference_lkd(session);
session->ref_subscriptions++;

/* add subscriber to resource */
LL_PREPEND(resource->subscribers, s);
Expand Down Expand Up @@ -1022,6 +1023,8 @@ coap_delete_observer_internal(coap_resource_t *resource, coap_session_t *session
if (resource->subscribers) {
LL_DELETE(resource->subscribers, s);
coap_session_release_lkd(session);
assert(session->ref_subscriptions > 0);
session->ref_subscriptions--;
coap_delete_pdu(s->pdu);
coap_delete_cache_key(s->cache_key);
coap_free_type(COAP_SUBSCRIPTION, s);
Expand Down
14 changes: 11 additions & 3 deletions src/coap_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -1985,9 +1985,18 @@ coap_session_get_type(const coap_session_t *session) {
return 0;
}

#if COAP_CLIENT_SUPPORT
int
COAP_API int
coap_session_set_type_client(coap_session_t *session) {
int ret;

coap_lock_lock(session->context, return 0);
ret = coap_session_set_type_client_lkd(session);
coap_lock_unlock(session->context);
return ret;
}

int
coap_session_set_type_client_lkd(coap_session_t *session) {
#if COAP_SERVER_SUPPORT
if (session && session->type == COAP_SESSION_TYPE_SERVER) {
coap_session_reference_lkd(session);
Expand All @@ -1999,7 +2008,6 @@ coap_session_set_type_client(coap_session_t *session) {
#endif /* ! COAP_SERVER_SUPPORT */
return 0;
}
#endif /* COAP_CLIENT_SUPPORT */

coap_session_state_t
coap_session_get_state(const coap_session_t *session) {
Expand Down

0 comments on commit 4d84859

Please sign in to comment.