From 55aeaa6cc162fcd37e35b1804fcba671a9c97e43 Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Wed, 10 Jul 2019 14:55:22 +0200 Subject: [PATCH] Update Keepalive support to include unreliable sessions Add in support for both reliable and unreliable sessions when calling coap_context_set_keepalive(). include/coap2/coap_session.h: Track the last ping mid in coap_session_t. src/coap_io.c: src/coap_session.c: Support both reliable and unreliable sending of pings in coap_write(). src/net.c: Determine whether RST packet response is associated with a CoAP Empty Ping that was sent and handle accordingly. configure.ac: examples/client.c: include/coap2/net.h: man/Makefile.am: man/coap-client.txt.in: man/coap.txt.in: man/coap_handler.txt.in: man/coap_keepalive.txt.in: (New) man/coap_observe.txt.in: Add in a new man page (coap_keepalive). Update documentation to remove references to TCP and reliable only. --- configure.ac | 3 +- examples/client.c | 1 - include/coap2/coap_session.h | 1 + include/coap2/net.h | 12 ++++-- man/Makefile.am | 1 + man/coap-client.txt.in | 4 +- man/coap.txt.in | 22 +++++++++-- man/coap_handler.txt.in | 2 +- man/coap_keepalive.txt.in | 73 ++++++++++++++++++++++++++++++++++++ man/coap_observe.txt.in | 1 + src/coap_io.c | 3 +- src/coap_session.c | 11 +++++- src/net.c | 22 +++++++++-- 13 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 man/coap_keepalive.txt.in diff --git a/configure.ac b/configure.ac index 2116e463..df00e081 100644 --- a/configure.ac +++ b/configure.ac @@ -663,8 +663,9 @@ man/coap_attribute.txt man/coap_context.txt man/coap_encryption.txt man/coap_handler.txt -man/coap_observe.txt +man/coap_keepalive.txt man/coap_logging.txt +man/coap_observe.txt man/coap_pdu_setup.txt man/coap_recovery.txt man/coap_resource.txt diff --git a/examples/client.c b/examples/client.c index 218ed562..1840b3ef 100644 --- a/examples/client.c +++ b/examples/client.c @@ -646,7 +646,6 @@ usage( const char *program, const char *version) { "\t-B seconds\tBreak operation after waiting given seconds\n" "\t \t\t(default is %d)\n" "\t-K interval\tsend a ping after interval seconds of inactivity\n" - "\t \t\t(TCP only)\n" "\t-N \t\tSend NON-confirmable message\n" "\t-O num,text\tAdd option num with contents text to request\n" "\t-P addr[:port]\tUse proxy (automatically adds Proxy-Uri option to\n" diff --git a/include/coap2/coap_session.h b/include/coap2/coap_session.h index 1dc0103e..fa630450 100644 --- a/include/coap2/coap_session.h +++ b/include/coap2/coap_session.h @@ -71,6 +71,7 @@ typedef struct coap_session_t { void *tls; /**< security parameters */ uint16_t tx_mid; /**< the last message id that was used in this session */ uint8_t con_active; /**< Active CON request sent */ + coap_tid_t last_ping_mid; /**< the last keepalive message id that was used in this session */ struct coap_queue_t *delayqueue; /**< list of delayed messages waiting to be sent */ size_t partial_write; /**< if > 0 indicates number of bytes already written from the pdu at the head of sendqueue */ uint8_t read_header[8]; /**< storage space for header of incoming message header */ diff --git a/include/coap2/net.h b/include/coap2/net.h index 7fccf332..231b17db 100644 --- a/include/coap2/net.h +++ b/include/coap2/net.h @@ -347,12 +347,16 @@ coap_context_set_pki_root_cas(coap_context_t *context, * Set the context keepalive timer for sessions. * A keepalive message will be sent after if a session has been inactive, * i.e. no packet sent or received, for the given number of seconds. - * For reliable protocols, a PING message will be sent. If a PONG has not - * been received before the next PING is due to be sent, the session will - * considered as disconnected. + * For unreliable protocols, a CoAP Empty message will be sent. If a + * CoAP RST is not received, the CoAP Empty messages will get resent based + * on the Confirmable retry parameters until there is a failure timeout, + * at which point the session will be considered as disconnected. + * For reliable protocols, a CoAP PING message will be sent. If a CoAP PONG + * has not been received before the next PING is due to be sent, the session + * will be considered as disconnected. * * @param context The coap_context_t object. - * @param seconds Number of seconds for the inactivity timer, or zero + * @param seconds Number of seconds for the inactivity timer, or zero * to disable CoAP-level keepalive messages. * * @return 1 if successful, else 0 diff --git a/man/Makefile.am b/man/Makefile.am index 48eba65d..9be7e25c 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -14,6 +14,7 @@ TXT3 = coap_attribute.txt \ coap_context.txt \ coap_encryption.txt \ coap_handler.txt \ + coap_keepalive.txt \ coap_logging.txt \ coap_observe.txt \ coap_pdu_setup.txt \ diff --git a/man/coap-client.txt.in b/man/coap-client.txt.in index fe613ebc..8f93de6a 100644 --- a/man/coap-client.txt.in +++ b/man/coap-client.txt.in @@ -109,8 +109,8 @@ OPTIONS - General Break operation after waiting given seconds (default is 90). *-K* interval:: - Send a ping after interval seconds of inactivity (TCP only). - If not specified, keep-alive is disabled (default). + Send a ping after interval seconds of inactivity. + If not specified (or 0), keep-alive is disabled (default). *-N* :: Send NON-confirmable message. If option *-N* is not specified, a diff --git a/man/coap.txt.in b/man/coap.txt.in index 17b69ffb..727f3c48 100644 --- a/man/coap.txt.in +++ b/man/coap.txt.in @@ -39,15 +39,29 @@ examples directory. SEE ALSO -------- *coap_attribute*(3), *coap_context*(3), *coap_encryption*(3), *coap_handler*(3), -*coap_logging*(3), *coap_observe*(3), *coap_pdu_setup*(3), *coap_recovery*(3), -*coap_resource*(3), *coap_session*(3) and *coap_tls_library*(3) +*coap_keepalive*(3), *coap_logging*(3), *coap_observe*(3), *coap_pdu_setup*(3), +*coap_recovery*(3), *coap_resource*(3), *coap_session*(3) +and *coap_tls_library*(3) For example executables, see *coap-client*(5), *coap-rd*(5) and *coap-server*(5) FURTHER INFORMATION ------------------- -See "RFC7252: The Constrained Application Protocol (CoAP)" for further -information. +See + +"RFC7252: The Constrained Application Protocol (CoAP)" + +"RFC7641: Observing Resources in the Constrained Application Protocol (CoAP)" + +"RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)" + +"RFC7967: Constrained Application Protocol (CoAP) Option for No Server Response" + +"RFC8132: PATCH and FETCH Methods for the Constrained Application Protocol (CoAP)" + +"RFC8323: CoAP (Constrained Application Protocol) over TCP, TLS, and WebSockets" + +for further information. BUGS ---- diff --git a/man/coap_handler.txt.in b/man/coap_handler.txt.in index 9560181f..40284b85 100644 --- a/man/coap_handler.txt.in +++ b/man/coap_handler.txt.in @@ -114,7 +114,7 @@ typedef void (*coap_ping_handler_t)(coap_context_t *context, ---- The *coap_register_pong_handler*() function defines a _handler_ for tracking -CoAP TCP ping response traffic associated with the _context_. +CoAP ping response traffic associated with the _context_. If _handler_ is NULL, then the handler is de-registered. The handler function prototype is defined as: diff --git a/man/coap_keepalive.txt.in b/man/coap_keepalive.txt.in new file mode 100644 index 00000000..207660d2 --- /dev/null +++ b/man/coap_keepalive.txt.in @@ -0,0 +1,73 @@ +// -*- mode:doc; -*- +// vim: set syntax=asciidoc,tw=0: + +coap_keepalive(3) +================= +:doctype: manpage +:man source: coap_keepalive +:man version: @PACKAGE_VERSION@ +:man manual: libcoap Manual + +NAME +---- +coap_keepalive, coap_context_set_keepalive - work with CoAP keepalive + +SYNOPSIS +-------- +*#include * + +*void void coap_context_set_keepalive(coap_context_t *_context_, +unsigned int _seconds_);* + +Link with *-lcoap-@LIBCOAP_API_VERSION@*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, +*-lcoap-@LIBCOAP_API_VERSION@-openssl* or +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* depending on your (D)TLS library +type. + +DESCRIPTION +----------- +There may be a requirement to send out keepalive traffic when the CoAP session +is idle (no packets have been sent or received for a specified period) to keep, +say, an interim NAT device "warm" with the NAT translation state, or to +periodically check whether the device at the other end of the session +has "gone away". + +For DTLS, this is done with the confirmable CoAP (0.00) Ping packet, which +solicits a CoAP RST response. For TLS, this is done with Coap (7.02) Ping +packet, which solicits a CoAP (7.03) Pong response, all handled by libcoap. + +The *coap_context_set_keepalive*() function needs to be called to update the +_context_ with the keepalive for idle traffic timeout of _seconds_. If +_seconds_ is set to 0, then the sending of keepalives is disabled. Any sessions +created from this _context_ will use the same _seconds_ value to determine +whether a keepalive "ping" is to be sent out or not. + +Applications can track the usage of the receipt of "pings" and receipt of +"responses" by defining the respective handlers to use by using +coap_register_ping_handler() and coap_register_pong_handler(). + +If the keepalive fails to solicit a response, then this can be tracked by +defining the handler to use by using coap_register_nack_handler() which will +be called with a reason of COAP_NACK_TOO_MANY_RETRIES. + +NOTE: *coap_context_set_keepalive*() is supported by both the CoAP client and +CoAP server. + +SEE ALSO +-------- +*coap_handler*(3) + +FURTHER INFORMATION +------------------- +"RFC7252: The Constrained Application Protocol (CoAP)" + +"RFC8323: CoAP (Constrained Application Protocol) over TCP, TLS, and WebSockets" + +BUGS +---- +Please report bugs on the mailing list for libcoap: +libcoap-developers@lists.sourceforge.net + +AUTHORS +------- +The libcoap project diff --git a/man/coap_observe.txt.in b/man/coap_observe.txt.in index 4338a15a..35dcd78b 100644 --- a/man/coap_observe.txt.in +++ b/man/coap_observe.txt.in @@ -383,6 +383,7 @@ and *coap_resource*(3) FURTHER INFORMATION ------------------- "RFC7252: The Constrained Application Protocol (CoAP)" + "RFC7641: Observing Resources in the Constrained Application Protocol (CoAP)" BUGS diff --git a/src/coap_io.c b/src/coap_io.c index 50032b48..e21d534a 100644 --- a/src/coap_io.c +++ b/src/coap_io.c @@ -1206,14 +1206,13 @@ coap_write(coap_context_t *ctx, LL_FOREACH_SAFE(ctx->sessions, s, tmp) { if ( s->type == COAP_SESSION_TYPE_CLIENT - && COAP_PROTO_RELIABLE(s->proto) && s->state == COAP_SESSION_STATE_ESTABLISHED && ctx->ping_timeout > 0 ) { coap_tick_t s_timeout; if (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND <= now) { if ((s->last_ping > 0 && s->last_pong < s->last_ping) - || coap_session_send_ping(s) == COAP_INVALID_TID) + || ((s->last_ping_mid = coap_session_send_ping(s)) == COAP_INVALID_TID)) { /* Make sure the session object is not deleted in the callback */ coap_session_reference(s); diff --git a/src/coap_session.c b/src/coap_session.c index c7585f41..89d4ea14 100644 --- a/src/coap_session.c +++ b/src/coap_session.c @@ -137,6 +137,7 @@ coap_make_session(coap_proto_t proto, coap_session_type_t type, session->ack_timeout = COAP_DEFAULT_ACK_TIMEOUT; session->ack_random_factor = COAP_DEFAULT_ACK_RANDOM_FACTOR; session->dtls_event = -1; + session->last_ping_mid = COAP_INVALID_TID; /* initialize message id */ prng((unsigned char *)&session->tx_mid, sizeof(session->tx_mid)); @@ -316,8 +317,14 @@ void coap_session_send_csm(coap_session_t *session) { coap_tid_t coap_session_send_ping(coap_session_t *session) { coap_pdu_t *ping; if (session->state != COAP_SESSION_STATE_ESTABLISHED) - return 0; - ping = coap_pdu_init(COAP_MESSAGE_CON, COAP_SIGNALING_PING, 0, 1); + return COAP_INVALID_TID; + if (COAP_PROTO_NOT_RELIABLE(session->proto)) { + uint16_t tid = coap_new_message_id (session); + ping = coap_pdu_init(COAP_MESSAGE_CON, 0, tid, 0); + } + else { + ping = coap_pdu_init(COAP_MESSAGE_CON, COAP_SIGNALING_PING, 0, 1); + } if (!ping) return COAP_INVALID_TID; return coap_send(session, ping); diff --git a/src/net.c b/src/net.c index 7381c6ff..78abe1cd 100644 --- a/src/net.c +++ b/src/net.c @@ -2174,6 +2174,7 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, coap_queue_t *sent = NULL; coap_pdu_t *response; coap_opt_filter_t opt_filter; + int is_ping_rst; #ifndef NDEBUG if (LOG_DEBUG <= coap_get_log_level()) { @@ -2222,8 +2223,13 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, /* We have sent something the receiver disliked, so we remove * not only the transaction but also the subscriptions we might * have. */ + is_ping_rst = 0; + if (pdu->tid == session->last_ping_mid && + context->ping_timeout && session->last_ping > 0) + is_ping_rst = 1; - coap_log(LOG_ALERT, "got RST for message %d\n", pdu->tid); + if (!is_ping_rst) + coap_log(LOG_ALERT, "got RST for message %d\n", pdu->tid); if (session->con_active) { session->con_active--; @@ -2238,8 +2244,18 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, if (sent) { coap_cancel(context, sent); - if(sent->pdu->type==COAP_MESSAGE_CON && context->nack_handler) - context->nack_handler(context, sent->session, sent->pdu, COAP_NACK_RST, sent->id); + if (!is_ping_rst) { + if(sent->pdu->type==COAP_MESSAGE_CON && context->nack_handler) + context->nack_handler(context, sent->session, sent->pdu, + COAP_NACK_RST, sent->id); + } + else { + if (context->pong_handler) { + context->pong_handler(context, session, pdu, pdu->tid); + } + session->last_pong = session->last_rx_tx; + session->last_ping_mid = COAP_INVALID_TID; + } } else { /* Need to check is there is a subscription active and delete it */