Skip to content

Commit

Permalink
RFC8768: Add in support for CoAP Option Hop Limit loop detection
Browse files Browse the repository at this point in the history
All this is handled by libcoap with coap-client having the ability to define
an initial Hop Count value for testing. No other application support is
required.

Incoming requests are checked for the Hop Limit option - if found, the value
is decremented in the PDU before passing it up to the application. If it
decrements to 0, a 5.08 response is generated.

coap_add_option() checks if this is a Proxy option for a request. If so, and
Hop Limit option does not exist, then the Hop Limit option is inserted into
the PDU with the default value (16).

coap_send() checks if it is a 5.08 response and if so, prepends the diagnostic
data with the IP address of the transmitting interface. If the IP address
already exists, then the PDU is dropped as the return path for the 5.08 response
is looping.  An alternative method for return path looping check is also
used by initially setting, then testing and then decrementing the Hop Limit
value as response packets pass back through the proxies.  If a proxy does not
copy the Hop Limit options when returning the 5.08 response, then the IP address
duplication is used.

Makefile.am:

Hide the coap_insert_option(), coap_update_option and coap_pdu_check_resize()
function from applications.

README.md:

Add in RFC8768 is supported by libcoap.

examples/client.c:

Add in the -H hoplimit option.

include/coap2/pdu.h:

Add in the default Hop-Limit: option COAP_OPTION_HOP_LIMIT.
Re-organize the COAP_OPTION_ descriptions to include
the C, U, N, R, E, I and U flags.
Expose the internal coap_pdu_check_resize() function so src/net.c can use it.
Define the two new internal functions coap_insert_option() and
coap_update_option() and expose them so that the test suites can use them.

man/coap-client.txt.in:

Document the new -H hoplimit option.

man/coap.txt.in:

Include the new RFC8768.

man/coap_pdu_setup.txt.in:

Update documentation for the CoAP options.

src/coap_debug.c:

Add in support for printing out COAP_OPTION_HOP_LIMIT.

src/net.c:

Make sure that COAP_OPTION_HOP_LIMIT is not returned in an error response.
Decrement received Hop-limit in handle_request() and generate 5.08 response
if appropriate.
Update coap_send() to prepend IP in 5.08 response.
Support COAP_OPTION_HOP_LIMIT being used in the 5.08 response message.

src/pdu.c:

Report on CoAP options that are repeated, but not defined as repeatable.
Define new (5.08) Hop Limit Reached error response.

Add in new internal coap_insert_option() function. This now means that
callers of coap_add_option() no longer have to have the options in the
correct order.

Add in new internal coap_update_option() function that updates the value of
an option.

Make coap_pdu_check_resize() non-static.

tests/test_pdu.c:

Check that options are getting inserted in the correct order when using
coap_insert_option(), even though they are provided in a random order.

Test the coap_update_option() function.
  • Loading branch information
mrdeep1 committed Sep 7, 2020
1 parent 1739507 commit 7033555
Show file tree
Hide file tree
Showing 11 changed files with 694 additions and 89 deletions.
3 changes: 3 additions & 0 deletions Makefile.am
Expand Up @@ -174,9 +174,11 @@ coap_dtls_session_update_mtu \
coap_dtls_shutdown \
coap_dtls_startup \
coap_epoll_ctl_mod \
coap_insert_option \
coap_io_do_events \
coap_mfree_endpoint \
coap_packet_extract_pbuf \
coap_pdu_check_resize \
coap_pdu_from_pbuf \
coap_session_mfree \
coap_session_new_dtls_session \
Expand All @@ -199,6 +201,7 @@ coap_tls_new_client_session \
coap_tls_new_server_session \
coap_tls_read \
coap_tls_write \
coap_update_option \
"

# This helper is called by libcoap-$(LIBCOAP_API_VERSION).{map,sym} to see if
Expand Down
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -48,6 +48,9 @@ The following RFCs are supported
* RFC8132: PATCH and FETCH Methods for the Constrained Application Protocol (CoAP)

* RFC8323: CoAP (Constrained Application Protocol) over TCP, TLS, and WebSockets
[No WebSockets support]

* RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option

There is (D)TLS support for the following libraries

Expand Down
65 changes: 47 additions & 18 deletions examples/client.c
Expand Up @@ -251,15 +251,16 @@ clear_obs(coap_context_t *ctx, coap_session_t *session) {

for (option = optlist; option; option = option->next ) {
switch (option->number) {
case COAP_OPTION_URI_PORT :
case COAP_OPTION_URI_PATH :
case COAP_OPTION_URI_QUERY :
case COAP_OPTION_URI_PORT : /* 7 */
case COAP_OPTION_URI_PATH : /* 11 */
case COAP_OPTION_URI_QUERY : /* 15 */
case COAP_OPTION_HOP_LIMIT : /* 16 */
if (!coap_add_option(pdu, option->number, option->length,
option->data)) {
goto error;
}
break;
default:
default:
;
}
}
Expand Down Expand Up @@ -453,13 +454,14 @@ message_handler(struct coap_context_t *ctx,
/* create pdu with request for next block */
pdu = coap_new_request(ctx, session, method, NULL, NULL, 0); /* first, create bare PDU w/o any option */
if ( pdu ) {
/* add URI components from optlist */
/* add URI components from optlist in ascending order */
for (option = optlist; option; option = option->next ) {
switch (option->number) {
case COAP_OPTION_URI_HOST :
case COAP_OPTION_URI_PORT :
case COAP_OPTION_URI_PATH :
case COAP_OPTION_URI_QUERY :
case COAP_OPTION_URI_HOST : /* 3 */
case COAP_OPTION_URI_PORT : /* 7 */
case COAP_OPTION_URI_PATH : /* 11 */
case COAP_OPTION_URI_QUERY : /* 15 */
case COAP_OPTION_HOP_LIMIT : /* 16 */
coap_add_option(pdu, option->number, option->length,
option->data);
break;
Expand Down Expand Up @@ -550,11 +552,12 @@ message_handler(struct coap_context_t *ctx,
/* add URI components from optlist */
for (option = optlist; option; option = option->next ) {
switch (option->number) {
case COAP_OPTION_URI_HOST :
case COAP_OPTION_URI_PORT :
case COAP_OPTION_URI_PATH :
case COAP_OPTION_CONTENT_FORMAT :
case COAP_OPTION_URI_QUERY :
case COAP_OPTION_URI_HOST : /* 3 */
case COAP_OPTION_URI_PORT : /* 7 */
case COAP_OPTION_URI_PATH : /* 11 */
case COAP_OPTION_CONTENT_FORMAT : /* 12 */
case COAP_OPTION_URI_QUERY : /* 15 */
case COAP_OPTION_HOP_LIMIT : /* 16 */
coap_add_option(pdu, option->number, option->length,
option->data);
break;
Expand Down Expand Up @@ -641,8 +644,8 @@ usage( const char *program, const char *version) {
"%s\n\n"
"Usage: %s [-a addr] [-b [num,]size] [-e text] [-f file] [-l loss]\n"
"\t\t[-m method] [-o file] [-p port] [-r] [-s duration] [-t type]\n"
"\t\t[-v num] [-A type] [-B seconds] [-K interval] [-N] [-O num,text]\n"
"\t\t[-P addr[:port]] [-T token] [-U]\n"
"\t\t[-v num] [-A type] [-B seconds] [-H hoplimit] [-K interval] [-N]\n"
"\t\t[-O num,text] [-P addr[:port]] [-T token] [-U]\n"
"\t\t[[-h match_hint_file] [-k key] [-u user]]\n"
"\t\t[[-c certfile] [-j keyfile] [-C cafile] [-J pkcs11_pin]\n"
"\t\t[-R root_cafile] [-S match_pki_sni_file]] URI\n"
Expand Down Expand Up @@ -674,6 +677,9 @@ usage( const char *program, const char *version) {
"\t-A type\t\tAccepted media type\n"
"\t-B seconds\tBreak operation after waiting given seconds\n"
"\t \t\t(default is %d)\n"
"\t-H hoplimit\tSet the Hop Limit count to hoplimit for proxies. Must\n"
"\t \t\thave a value between 1 and 255 inclusive.\n"
"\t \t\tDefault is '16'\n"
"\t-K interval\tsend a ping after interval seconds of inactivity\n"
"\t-N \t\tSend NON-confirmable message\n"
"\t-O num,text\tAdd option num with contents text to request. If the\n"
Expand Down Expand Up @@ -722,7 +728,8 @@ usage( const char *program, const char *version) {
"\t \t\t'trusted' for the verification.\n"
"\t \t\tAlternatively, this can point to a directory containing\n"
"\t \t\ta set of CA PEM files\n"
"\n"
);
fprintf( stderr,
"Examples:\n"
"\tcoap-client -m get coap://[::1]/\n"
"\tcoap-client -m get coap://[::1]/.well-known/core\n"
Expand Down Expand Up @@ -788,6 +795,24 @@ cmdline_content_type(char *arg, uint16_t key) {
}
}

static int
cmdline_hop_limit(char *arg) {
coap_optlist_t *node;
uint32_t value;
uint8_t buf[4];

value = strtol(arg, NULL, 10);
if (value < 1 || value > 255) {
return 0;
}
node = coap_new_optlist(COAP_OPTION_HOP_LIMIT, coap_encode_var_safe(buf, sizeof(buf), value), buf);
if (node) {
coap_insert_optlist(&optlist, node);
}
return 1;
}


static uint16_t
get_default_port(const coap_uri_t *u) {
return coap_uri_scheme_is_secure(u) ? COAPS_DEFAULT_PORT : COAP_DEFAULT_PORT;
Expand Down Expand Up @@ -1533,7 +1558,7 @@ main(int argc, char **argv) {
struct sigaction sa;
#endif

while ((opt = getopt(argc, argv, "a:b:c:e:f:h:j:k:l:m:o:p:rs:t:u:v:A:B:C:J:K:NO:P:R:T:U")) != -1) {
while ((opt = getopt(argc, argv, "a:b:c:e:f:h:j:k:l:m:o:p:rs:t:u:v:A:B:C:J:K:H:NO:P:R:T:U")) != -1) {
switch (opt) {
case 'a':
strncpy(node_str, optarg, NI_MAXHOST - 1);
Expand Down Expand Up @@ -1643,6 +1668,10 @@ main(int argc, char **argv) {
exit(1);
}
break;
case 'H':
if (!cmdline_hop_limit(optarg))
fprintf(stderr, "Hop Limit has to be > 0 and < 256\n");
break;
default:
usage( argv[0], LIBCOAP_PACKAGE_VERSION );
exit( 1 );
Expand Down
123 changes: 89 additions & 34 deletions include/coap2/pdu.h
Expand Up @@ -32,6 +32,10 @@ struct coap_session_t;
#define COAP_DEFAULT_MTU 1152
#endif /* COAP_DEFAULT_MTU */

#ifndef COAP_DEFAULT_HOP_LIMIT
#define COAP_DEFAULT_HOP_LIMIT 16
#endif /* COAP_DEFAULT_HOP_LIMIT */

/* TCP Message format constants, do not modify */
#define COAP_MESSAGE_SIZE_OFFSET_TCP8 13
#define COAP_MESSAGE_SIZE_OFFSET_TCP16 269 /* 13 + 256 */
Expand Down Expand Up @@ -87,41 +91,60 @@ typedef enum coap_request_t {
} coap_request_t;

/*
* CoAP option types (be sure to update coap_option_check_critical() when
* adding options
* CoAP option types (be sure to update coap_option_check_critical() and
* coap_add_option() when adding options
*/

#define COAP_OPTION_IF_MATCH 1 /* C, opaque, 0-8 B, (none) */
#define COAP_OPTION_URI_HOST 3 /* C, String, 1-255 B, destination address */
#define COAP_OPTION_ETAG 4 /* E, opaque, 1-8 B, (none) */
#define COAP_OPTION_IF_NONE_MATCH 5 /* empty, 0 B, (none) */
#define COAP_OPTION_URI_PORT 7 /* C, uint, 0-2 B, destination port */
#define COAP_OPTION_LOCATION_PATH 8 /* E, String, 0-255 B, - */
#define COAP_OPTION_URI_PATH 11 /* C, String, 0-255 B, (none) */
#define COAP_OPTION_CONTENT_FORMAT 12 /* E, uint, 0-2 B, (none) */
#define COAP_OPTION_CONTENT_TYPE COAP_OPTION_CONTENT_FORMAT
#define COAP_OPTION_MAXAGE 14 /* E, uint, 0--4 B, 60 Seconds */
#define COAP_OPTION_URI_QUERY 15 /* C, String, 1-255 B, (none) */
#define COAP_OPTION_ACCEPT 17 /* C, uint, 0-2 B, (none) */
#define COAP_OPTION_LOCATION_QUERY 20 /* E, String, 0-255 B, (none) */
#define COAP_OPTION_SIZE2 28 /* E, uint, 0-4 B, (none) */
#define COAP_OPTION_PROXY_URI 35 /* C, String, 1-1034 B, (none) */
#define COAP_OPTION_PROXY_SCHEME 39 /* C, String, 1-255 B, (none) */
#define COAP_OPTION_SIZE1 60 /* E, uint, 0-4 B, (none) */

/* option types from RFC 7641 */

#define COAP_OPTION_OBSERVE 6 /* E, empty/uint, 0 B/0-3 B, (none) */
#define COAP_OPTION_SUBSCRIPTION COAP_OPTION_OBSERVE

/* selected option types from RFC 7959 */

#define COAP_OPTION_BLOCK2 23 /* C, uint, 0--3 B, (none) */
#define COAP_OPTION_BLOCK1 27 /* C, uint, 0--3 B, (none) */

/* selected option types from RFC 7967 */
/*
* The C, U, and N flags indicate the properties
* Critical, Unsafe, and NoCacheKey, respectively.
* If U is set, then N has no meaning as per
* https://tools.ietf.org/html/rfc7252#section-5.10
* and is set to a -.
*
* Separately, R is for the options that can be repeated
*
* The least significant byte of the option is set as followed
* as per https://tools.ietf.org/html/rfc7252#section-5.4.6
*
* 0 1 2 3 4 5 6 7
* --+---+---+---+---+---+---+---+
* | NoCacheKey| U | C |
* --+---+---+---+---+---+---+---+
*
* https://tools.ietf.org/html/rfc8613#section-4 goes on to define E, I and U
* properties Encrypted and Integrity Protected, Integrity Protected Only and
* Unprotected respectively. Integretity Protected Only is not currently used.
*
* An Option is tagged with CUNREIU with any of the letters replaced with _ if
* not set, or - for N if U is set (see above) for aiding understanding of the
* Option.
*/

#define COAP_OPTION_NORESPONSE 258 /* N, uint, 0--1 B, 0 */
#define COAP_OPTION_IF_MATCH 1 /* C__RE__, opaque, 0-8 B, RFC7252 */
#define COAP_OPTION_URI_HOST 3 /* CU-___U, String, 1-255 B, RFC7252 */
#define COAP_OPTION_ETAG 4 /* ___RE__, opaque, 1-8 B, RFC7252 */
#define COAP_OPTION_IF_NONE_MATCH 5 /* C___E__, empty, 0 B, RFC7252 */
#define COAP_OPTION_OBSERVE 6 /* _U-_E_U, empty/uint,0/0-3 B, RFC7641 */
#define COAP_OPTION_URI_PORT 7 /* CU-___U, uint, 0-2 B, RFC7252 */
#define COAP_OPTION_LOCATION_PATH 8 /* ___RE__, String, 0-255 B, RFC7252 */
#define COAP_OPTION_OSCORE 9 /* C_____U, *, 0-255 B, RFC8613 */
#define COAP_OPTION_URI_PATH 11 /* CU-RE__, String, 0-255 B, RFC7252 */
#define COAP_OPTION_CONTENT_FORMAT 12 /* ____E__, uint, 0-2 B, RFC7252 */
#define COAP_OPTION_CONTENT_TYPE COAP_OPTION_CONTENT_FORMAT
/* COAP_OPTION_MAXAGE default 60 seconds if not set */
#define COAP_OPTION_MAXAGE 14 /* _U-_E_U, uint, 0-4 B, RFC7252 */
#define COAP_OPTION_URI_QUERY 15 /* CU-RE__, String, 1-255 B, RFC7252 */
#define COAP_OPTION_HOP_LIMIT 16 /* ______U, uint, 1 B, RFC8768 */
#define COAP_OPTION_ACCEPT 17 /* C___E__, uint, 0-2 B, RFC7252 */
#define COAP_OPTION_LOCATION_QUERY 20 /* ___RE__, String, 0-255 B, RFC7252 */
#define COAP_OPTION_BLOCK2 23 /* CU-_E_U, uint, 0-3 B, RFC7959 */
#define COAP_OPTION_BLOCK1 27 /* CU-_E_U, uint, 0-3 B, RFC7959 */
#define COAP_OPTION_SIZE2 28 /* __N_E_U, uint, 0-4 B, RFC7959 */
#define COAP_OPTION_PROXY_URI 35 /* CU-___U, String, 1-1034 B, RFC7252 */
#define COAP_OPTION_PROXY_SCHEME 39 /* CU-___U, String, 1-255 B, RFC7252 */
#define COAP_OPTION_SIZE1 60 /* __N_E_U, uint, 0-4 B, RFC7252 */
#define COAP_OPTION_NORESPONSE 258 /* _U-_E_U, uint, 0-1 B, RFC7967 */

#define COAP_MAX_OPT 65535 /**< the highest option number we know */

Expand Down Expand Up @@ -273,7 +296,7 @@ typedef struct coap_pdu_t {
uint8_t type; /**< message type */
uint8_t code; /**< request method (value 1--31) or response code (value 64-255) */
uint8_t max_hdr_size; /**< space reserved for protocol-specific header */
uint8_t hdr_size; /**< actaul size used for protocol-specific header */
uint8_t hdr_size; /**< actual size used for protocol-specific header */
uint8_t token_length; /**< length of Token */
uint16_t tid; /**< transaction id, if any, in regular host byte order */
uint16_t max_delta; /**< highest option number */
Expand Down Expand Up @@ -359,6 +382,19 @@ coap_pdu_init(uint8_t type, uint8_t code, uint16_t tid, size_t size);
*/
int coap_pdu_resize(coap_pdu_t *pdu, size_t new_size);

/**
* Dynamically grows the size of @p pdu to @p new_size if needed. The new size
* must not exceed the PDU's configure maximum size. On success, this
* function returns 1, otherwise 0.
*
* Internal use only.
*
* @param pdu The PDU to resize.
* @param new_size The new size in bytes.
* @return 1 if the operation succeeded, 0 otherwise.
*/
int coap_pdu_check_resize(coap_pdu_t *pdu, size_t new_size);

/**
* Clears any contents from @p pdu and resets @c used_size,
* and @c data pointers. @c max_size is set to @p size, any
Expand Down Expand Up @@ -489,6 +525,25 @@ uint8_t *coap_add_option_later(coap_pdu_t *pdu,
uint16_t type,
size_t len);


/**
* Inserts option of given type in the @p pdu with the appropriate data.
* The option will be inserted in the appropriate place in the options in the pdu.
*
* Internal use only.
*/
size_t coap_insert_option(coap_pdu_t *pdu, uint16_t type,
size_t len, const uint8_t *data);

/**
* Updates existing first option of given type in the @p pdu with the new data.
*
* Internal use only.
*/
int coap_update_option(coap_pdu_t *pdu,
uint16_t type,
size_t len,
const uint8_t *data);
/**
* Adds given data to the pdu that is passed as first parameter. Note that the
* PDU's data is destroyed by coap_add_option(). coap_add_data() must be called
Expand All @@ -500,7 +555,7 @@ int coap_add_data(coap_pdu_t *pdu,

/**
* Adds given data to the pdu that is passed as first parameter but does not
* copyt it. Note that the PDU's data is destroyed by coap_add_option().
* copy it. Note that the PDU's data is destroyed by coap_add_option().
* coap_add_data() must be have been called once for this PDU, otherwise the
* result is undefined.
* The actual data must be copied at the returned location.
Expand Down
9 changes: 7 additions & 2 deletions man/coap-client.txt.in
Expand Up @@ -16,8 +16,9 @@ SYNOPSIS
--------
*coap-client* [*-a* addr] [*-b* [num,]size] [*-e* text] [*-f* file] [*-l* loss]
[*-m* method] [*-o* file] [*-p* port] [*-r*] [*-s duration*]
[*-t* type] [*-v* num] [*-A* type] [*-B* seconds] [*-K* interval]
[*-N*] [*-O* num,text] [*-P* addr[:port]] [*-T* token] [*-U*]
[*-t* type] [*-v* num] [*-A* type] [*-B* seconds]
[*-H* hoplimit] [*-K* interval] [*-N*] [*-O* num,text]
[*-P* addr[:port]] [*-T* token] [*-U*]
[[*-h* match_hint_file] [*-k* key] [*-u* user]]
[[*-c* certfile] [*-j* keyfile] [*-C* cafile] [*-J* pkcs11_pin]
[*-R* root_cafile]] URI
Expand Down Expand Up @@ -109,6 +110,10 @@ OPTIONS - General
*-B* seconds::
Break operation after waiting given seconds (default is 90).

*-H* hoplimit::
Set the Hop Limit count to hoplimit for proxies. Must have a value between
1 and 255 inclusive. Default is '16'.

*-K* interval::
Send a ping after interval seconds of inactivity.
If not specified (or 0), keep-alive is disabled (default).
Expand Down
2 changes: 2 additions & 0 deletions man/coap.txt.in
Expand Up @@ -61,6 +61,8 @@ See

"RFC8323: CoAP (Constrained Application Protocol) over TCP, TLS, and WebSockets"

"RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option"

for further information.

BUGS
Expand Down

0 comments on commit 7033555

Please sign in to comment.