Skip to content

Commit

Permalink
RFC9175: Add in support for the Echo and Request-Tag options
Browse files Browse the repository at this point in the history
A unique Request-Tag is added to any PUT/POST/FETCH request that contains
a large body.  The server is then able to differentiate between concurrent
sending of large data using the same session.

If a server responds with a 4.01 and Echo option, then the libcoap client
retransmits the request adding in the received option.

If the server responds normally with an Echo option, then the next request
from the client (internally added by libcoap) will contain that Echo option.

It is the responsibility of the server application to add in the Echo
option unless this is triggered by OSCORE.
  • Loading branch information
mrdeep1 committed Feb 9, 2022
1 parent afb4f2b commit 0a1c37b
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 46 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![Static Analysis](https://scan.coverity.com/projects/10970/badge.svg?flat=1)](https://scan.coverity.com/projects/obgm-libcoap)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/libcoap.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:libcoap)

Copyright (C) 2010—2021 by Olaf Bergmann <[email protected]> and others
Copyright (C) 2010—2022 by Olaf Bergmann <[email protected]> and others

ABOUT LIBCOAP
=============
Expand Down Expand Up @@ -51,6 +51,8 @@ The following RFCs are supported

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

* RFC9175: CoAP: Echo, Request-Tag, and Token Processing

There is (D)TLS support for the following libraries

* OpenSSL (Minimum version 1.1.0) [PKI, PSK and PKCS11]
Expand Down
4 changes: 3 additions & 1 deletion doc/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ libcoap {#mainpage}
A C implementation of the Constrained Application Protocol (RFC 7252)
=====================================================================

Copyright (C) 2010--2021 by Olaf Bergmann <[email protected]> and others
Copyright (C) 2010--2022 by Olaf Bergmann <[email protected]> and others

About libcoap
=============
Expand Down Expand Up @@ -36,6 +36,8 @@ The following RFCs are supported

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

* RFC9175: CoAP: Echo, Request-Tag, and Token Processing

There is (D)TLS support for the following libraries

* OpenSSL (Minimum version 1.1.0) [PKI, PSK and PKCS11]
Expand Down
2 changes: 1 addition & 1 deletion include/coap3/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ typedef void (*coap_release_large_data_t)(coap_session_t *session,
* Used for a client request.
*
* If the data spans multiple PDUs, then the data will get transmitted using
* BLOCK1 option with the addition of the SIZE1 option.
* BLOCK1 option with the addition of the SIZE1 and RTAG options.
* The underlying library will handle the transmission of the individual blocks.
* Once the body of data has been transmitted (or a failure occurred), then
* @p release_func (if not NULL) will get called so the application can
Expand Down
3 changes: 2 additions & 1 deletion include/coap3/coap_block_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ int coap_handle_request_put_block(coap_context_t *context,
#endif /* COAP_SERVER_SUPPORT */

#if COAP_CLIENT_SUPPORT
int coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd);
int coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent,
coap_pdu_t *rcvd);

int coap_handle_response_get_block(coap_context_t *context,
coap_session_t *session,
Expand Down
2 changes: 2 additions & 0 deletions include/coap3/coap_session_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ struct coap_session_t {
int dtls_event; /**< Tracking any (D)TLS events on this
sesison */
uint8_t block_mode; /**< Zero or more COAP_BLOCK_ or'd options */
uint32_t tx_rtag; /**< Next Request-Tag number to use */
uint64_t tx_token; /**< Next token number to use */
coap_bin_const_t *echo; /**< Echo value to send with next request */
};

#if COAP_SERVER_SUPPORT
Expand Down
2 changes: 2 additions & 0 deletions include/coap3/pdu.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ typedef enum coap_request_t {
#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_ECHO 252 /* _N__E_U, opaque, 0-40 B, RFC9175 */
#define COAP_OPTION_NORESPONSE 258 /* _U-_E_U, uint, 0-1 B, RFC7967 */
#define COAP_OPTION_RTAG 292 /* ___RE_U, opaque, 0-8 B, RFC9175 */

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

Expand Down
2 changes: 2 additions & 0 deletions man/coap.txt.in
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ See

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

"RFC9175: CoAP: Echo, Request-Tag, and Token Processing"

for further information.

BUGS
Expand Down
2 changes: 1 addition & 1 deletion man/coap_block.txt.in
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ but supports the transmission of data that has a body size that is potentially
larger than can be fitted into a single client request PDU. The specified
payload _data_ of length _length_ is associated with the _session_ with the
first block of data added to the PDU _pdu_ along with the appropriate CoAP
options such as BLOCK1, and SIZE1 if the data does not fit in
options such as BLOCK1, SIZE1 and RTAG if the data does not fit in
a single PDU.

When the block receipt has been acknowledged by the peer, the library
Expand Down
6 changes: 5 additions & 1 deletion man/coap_pdu_setup.txt.in
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ The following is the current list of options with their numeric value
COAP_OPTION_IF_MATCH 1 /* C__RE__, opaque, 0-8 B, RFC7252 */
COAP_OPTION_URI_HOST 3 /* CU-___U, String, 1-255 B, RFC7252 */
COAP_OPTION_ETAG 4 /* ___RE__, opaque, 1-8 B, RFC7252 */
ION_IF_NONE_MATCH 5 /* C___E__, empty, 0 B, RFC7252 */
COAP_OPTION_IF_NONE_MATCH 5 /* C___E__, empty, 0 B, RFC7252 */
COAP_OPTION_OBSERVE 6 /* _U-_E_U, empty/uint, 0 B/0-3 B, RFC7641 */
COAP_OPTION_URI_PORT 7 /* CU-___U, uint, 0-2 B, RFC7252 */
COAP_OPTION_LOCATION_PATH 8 /* ___RE__, String, 0-255 B, RFC7252 */
Expand All @@ -301,7 +301,9 @@ COAP_OPTION_SIZE2 28 /* __N_E_U, uint, 0-4 B, RFC7959 */
COAP_OPTION_PROXY_URI 35 /* CU-___U, String, 1-1034 B, RFC7252 */
COAP_OPTION_PROXY_SCHEME 39 /* CU-___U, String, 1-255 B, RFC7252 */
COAP_OPTION_SIZE1 60 /* __N_E_U, uint, 0-4 B, RFC7252 */
COAP_OPTION_ECHO 252 /* _N__E_U, opaque, 0-40 B, RFC9175 */
COAP_OPTION_NORESPONSE 258 /* _U-_E_U, uint, 0-1 B, RFC7967 */
COAP_OPTION_RTAG 292 /* ___RE_U, opaque, 0-8 B, RFC9175 */
----
See FURTHER INFORMATION as to how to get the latest list.

Expand Down Expand Up @@ -570,6 +572,8 @@ See

"RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)"

"RFC9175: CoAP: Echo, Request-Tag, and Token Processing"

for further information.

See https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#option-numbers
Expand Down
157 changes: 141 additions & 16 deletions src/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ coap_add_data_large_internal(coap_session_t *session,
}

avail = pdu->max_size - pdu->used_size - pdu->hdr_size;
/* There may be a response with Echo option */
avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40);
/* May need token of length 8, so account for this */
avail -= (pdu->token_length < 8) ? 8 - pdu->token_length : 0;
blk_size = coap_flsll((long long)avail) - 4 - 1;
Expand Down Expand Up @@ -494,8 +496,12 @@ coap_add_data_large_internal(coap_session_t *session,
coap_encode_var_safe(buf, sizeof(buf),
(unsigned int)length),
buf);
}
else {
coap_update_option(pdu,
COAP_OPTION_RTAG,
coap_encode_var_safe8(buf, sizeof(buf),
++session->tx_rtag),
buf);
} else {
/*
* resource+query match is used for BLOCK2 large body transmissions
* token match is used for BLOCK1 large body transmissions
Expand Down Expand Up @@ -561,6 +567,8 @@ coap_add_data_large_internal(coap_session_t *session,

/* Check we still have space after adding in some options */
avail = pdu->max_size - pdu->used_size - pdu->hdr_size;
/* There may be a response with Echo option */
avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40);
/* May need token of length 8, so account for this */
avail -= (pdu->token_length < 8) ? 8 - pdu->token_length : 0;
if (avail < (ssize_t)chunk) {
Expand Down Expand Up @@ -839,8 +847,6 @@ coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now) {
coap_lg_crcv_t *
coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu) {
coap_lg_crcv_t *lg_crcv;
uint8_t buf[8];
size_t length;
uint64_t state_token = STATE_TOKEN_FULL(++session->tx_token, 1);

lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t));
Expand Down Expand Up @@ -881,12 +887,6 @@ coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu) {
/* Need to set up a base token for actual communications if retries needed */
lg_crcv->retry_counter = 1;
lg_crcv->state_token = state_token;
length = coap_encode_var_safe8(buf, sizeof(lg_crcv->state_token),
lg_crcv->state_token);
if (!coap_update_token(pdu, length, buf)) {
coap_block_delete_lg_crcv(session, lg_crcv);
return NULL;
}

/* In case it is there - must not be in continuing request PDUs */
coap_remove_option(&lg_crcv->pdu, COAP_OPTION_BLOCK1);
Expand Down Expand Up @@ -1301,12 +1301,24 @@ coap_handle_request_put_block(coap_context_t *context,
uint16_t fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt),
coap_opt_length(fmt_opt)) :
COAP_MEDIATYPE_TEXT_PLAIN;
coap_opt_t *rtag_opt = coap_check_option(pdu,
COAP_OPTION_RTAG,
&opt_iter);
size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;

total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt),
coap_opt_length(size_opt)) : 0;
offset = block.num << (block.szx + 4);

LL_FOREACH(session->lg_srcv, p) {
if (rtag_opt || p->rtag_set == 1) {
if (!(rtag_opt && p->rtag_set == 1))
continue;
if (p->rtag_length != rtag_length ||
memcmp(p->rtag, rtag, rtag_length) != 0)
continue;
}
if (resource == p->resource) {
break;
}
Expand Down Expand Up @@ -1348,6 +1360,11 @@ coap_handle_request_put_block(coap_context_t *context,
memcpy(p->observe, coap_opt_value(observe), p->observe_length);
p->observe_set = 1;
}
if (rtag_opt) {
p->rtag_length = (uint8_t)rtag_length;
memcpy(p->rtag, rtag, rtag_length);
p->rtag_set = 1;
}
p->body_data = NULL;
LL_PREPEND(session->lg_srcv, p);
}
Expand Down Expand Up @@ -1483,6 +1500,90 @@ coap_handle_request_put_block(coap_context_t *context,
#endif /* COAP_SERVER_SUPPORT */

#if COAP_CLIENT_SUPPORT
static int
check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent,
coap_lg_xmit_t *lg_xmit, coap_lg_crcv_t *lg_crcv)
{
/* Check for ECHO option for freshness */
coap_opt_iterator_t opt_iter;
coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);

if (opt) {
if (sent || lg_xmit || lg_crcv) {
/* Need to retransmit original request with ECHO added */
coap_pdu_t *echo_pdu;
coap_mid_t mid;
const uint8_t *data;
size_t data_len;
int have_data = 0;
uint8_t ltoken[8];
size_t ltoken_len;
uint64_t token;

if (sent) {
if (coap_get_data(sent, &data_len, &data))
have_data = 1;
} else if (lg_xmit) {
sent = &lg_xmit->pdu;
if (lg_xmit->length) {
size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4);
size_t offset = (lg_xmit->last_block + 1) * blk_size;
have_data = 1;
data = &lg_xmit->data[offset];
data_len = (lg_xmit->length - offset) > blk_size ? blk_size :
lg_xmit->length - offset;
}
} else /* lg_crcv */ {
sent = &lg_crcv->pdu;
if (coap_get_data(sent, &data_len, &data))
have_data = 1;
}
if (lg_xmit) {
token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,
++lg_xmit->b.b1.count);
} else {
token = STATE_TOKEN_FULL(lg_crcv->state_token,
++lg_crcv->retry_counter);
}
ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
echo_pdu = coap_pdu_duplicate(sent, session, ltoken_len, ltoken, NULL);
if (!echo_pdu)
return 0;
if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO,
coap_opt_length(opt), coap_opt_value(opt)))
goto no_sent;
if (have_data) {
coap_add_data(echo_pdu, data_len, data);
}

mid = coap_send_internal(session, echo_pdu);
if (mid == COAP_INVALID_MID)
goto no_sent;
return 1;
} else {
/* Need to save ECHO value to add to next reansmission */
no_sent:
coap_delete_bin_const(session->echo);
session->echo = coap_new_bin_const(coap_opt_value(opt),
coap_opt_length(opt));
}
}
return 0;
}

static void
track_echo(coap_session_t *session, coap_pdu_t *rcvd)
{
coap_opt_iterator_t opt_iter;
coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);

if (opt) {
coap_delete_bin_const(session->echo);
session->echo = coap_new_bin_const(coap_opt_value(opt),
coap_opt_length(opt));
}
}

/*
* Need to see if this is a response to a large body request transfer. If so,
* need to initiate the request containing the next block and not trouble the
Expand All @@ -1498,15 +1599,20 @@ coap_handle_request_put_block(coap_context_t *context,
* 1 Do not call application handler - just send the built response
*/
int
coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd) {
coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent,
coap_pdu_t *rcvd)
{
coap_lg_xmit_t *p;
coap_lg_xmit_t *q;
uint64_t token_match = STATE_TOKEN_BASE(coap_decode_var_bytes8(rcvd->token,
rcvd->token_length));

LL_FOREACH_SAFE(session->lg_xmit, p, q) {
if (!COAP_PDU_IS_REQUEST(&p->pdu) ||
token_match != STATE_TOKEN_BASE(p->b.b1.state_token)) {
(token_match != STATE_TOKEN_BASE(p->b.b1.state_token) &&
token_match !=
STATE_TOKEN_BASE(coap_decode_var_bytes8(p->b.b1.app_token->s,
p->b.b1.app_token->length)))) {
/* try out the next one */
continue;
}
Expand Down Expand Up @@ -1542,6 +1648,7 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd) {
(p->offset + chunk) % ((size_t)1 << (block.szx + 4)));
}
}
track_echo(session, rcvd);
if (p->last_block == (int)block.num) {
/*
* Duplicate BLOCK ACK
Expand Down Expand Up @@ -1586,6 +1693,9 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd) {
goto fail_body;
return 1;
}
} else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
if (check_freshness(session, rcvd, sent, p, NULL))
return 1;
}
fail_body:
if (session->lg_crcv) {
Expand Down Expand Up @@ -1677,10 +1787,11 @@ coap_block_build_body(coap_binary_t *body_data, size_t length,
*/
int
coap_handle_response_get_block(coap_context_t *context,
coap_session_t *session,
coap_pdu_t *sent,
coap_pdu_t *rcvd,
coap_recurse_t recursive) {
coap_session_t *session,
coap_pdu_t *sent,
coap_pdu_t *rcvd,
coap_recurse_t recursive)
{
coap_lg_crcv_t *p;
int app_has_response = 0;
coap_block_t block = {0, 0, 0};
Expand Down Expand Up @@ -1720,6 +1831,7 @@ coap_handle_response_get_block(coap_context_t *context,
have_block = 1;
block_opt = COAP_OPTION_BLOCK2;
}
track_echo(session, rcvd);
if (have_block) {
coap_opt_t *fmt_opt = coap_check_option(rcvd,
COAP_OPTION_CONTENT_FORMAT,
Expand Down Expand Up @@ -1955,6 +2067,10 @@ coap_handle_response_get_block(coap_context_t *context,
goto call_app_handler;
}
}
} else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
if (check_freshness(session, rcvd, sent, NULL, p))
goto skip_app_handler;
goto fail_resp;
}
if (!block.m && !p->observe_set) {
fail_resp:
Expand Down Expand Up @@ -2006,6 +2122,15 @@ coap_handle_response_get_block(coap_context_t *context,
coap_block_delete_lg_crcv(session, lg_crcv);
}
}
track_echo(session, rcvd);
} else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent);

if (lg_crcv) {
LL_PREPEND(session->lg_crcv, lg_crcv);
return coap_handle_response_get_block(context, session, sent, rcvd,
COAP_RECURSE_NO);
}
}
}
return app_has_response;
Expand Down
Loading

0 comments on commit 0a1c37b

Please sign in to comment.