Skip to content

Commit

Permalink
draft-ietf-core-echo-request-tag: Add in support
Browse files Browse the repository at this point in the history
Add in support for the Echo and Request-Tag options.

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 (added by libcoap) will contain that request 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 Dec 9, 2021
1 parent cca9c85 commit 69fe0b7
Show file tree
Hide file tree
Showing 13 changed files with 205 additions and 31 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ The following RFCs are supported

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

* draft-ietf-core-echo-request-tag-14: 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: 2 additions & 0 deletions doc/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ The following RFCs are supported

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

* draft-ietf-core-echo-request-tag-14: 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 @@ -213,7 +213,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
4 changes: 4 additions & 0 deletions include/coap3/coap_session_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ struct coap_session_t {
sesison */
uint8_t block_mode; /**< Zero or more COAP_BLOCK_ or'd options */
uint64_t tx_token; /**< Next token number to use */
uint64_t tx_rtag; /**< Next Request-Tag number to use */
uint8_t echo[8]; /**< Echo value to send with next request */
uint32_t echo_len; /**< Echo value length */
int echo_send; /**< Send Echo in next request */
};

#if COAP_SERVER_SUPPORT
Expand Down
4 changes: 4 additions & 0 deletions include/coap3/pdu.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ typedef enum coap_request_t {
#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 */

/* selected option types from draft-ietf-core-echo-request-tag */
#define COAP_OPTION_ECHO 252 /* _N__E_U, opaque, 0-40 B, RFC???? */
#define COAP_OPTION_RTAG 292 /* ___RE_U, opaque, 0-8 B, RFC???? */

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

/* CoAP result codes (HTTP-Code / 100 * 40 + HTTP-Code % 100) */
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"

"draft-ietf-core-echo-request-tag-14: 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 @@ -165,7 +165,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 has been acknowledged by the peer, the library
will then send the next block of data until all the data has been transmitted.
This function must only be called once per _pdu_. The _data_ passed to the
Expand Down
2 changes: 2 additions & 0 deletions man/coap_pdu_setup.txt.in
Original file line number Diff line number Diff line change
Expand Up @@ -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, RFC???? */
COAP_OPTION_NORESPONSE 258 /* _U-_E_U, uint, 0-1 B, RFC7967 */
COAP_OPTION_RTAG 292 /* ___RE_U, opaque, 0-8 B, RFC???? */
----
See FURTHER INFORMATION as to how to get the latest list.

Expand Down
144 changes: 139 additions & 5 deletions src/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,11 @@ coap_add_data_large_internal(coap_session_t *session,
coap_encode_var_safe(buf, sizeof(buf),
(unsigned int)length),
buf);
coap_update_option(pdu,
COAP_OPTION_RTAG,
coap_encode_var_safe8(buf, sizeof(buf),
++session->tx_rtag),
buf);
}
else {
/*
Expand Down Expand Up @@ -1294,12 +1299,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 @@ -1341,6 +1358,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 = 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 @@ -1470,6 +1492,94 @@ 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 = 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:
session->echo_len = coap_opt_length(opt);
session->echo_send = 1;
memcpy(session->echo, coap_opt_value(opt), session->echo_len);
}
}
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) {
session->echo_len = coap_opt_length(opt);
session->echo_send = 1;
memcpy(session->echo, coap_opt_value(opt), session->echo_len);
}
}

/*
* 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 @@ -1485,7 +1595,9 @@ 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,
Expand Down Expand Up @@ -1529,6 +1641,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 @@ -1574,6 +1687,10 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd) {
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) {
LL_FOREACH(session->lg_crcv, lg_crcv) {
Expand Down Expand Up @@ -1664,10 +1781,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 @@ -1707,6 +1825,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 @@ -1943,6 +2062,11 @@ coap_handle_response_get_block(coap_context_t *context,
}
}
}
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:
/* lg_crcv no longer required - cache it */
Expand Down Expand Up @@ -1993,6 +2117,16 @@ 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
11 changes: 8 additions & 3 deletions src/coap_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,14 @@ msg_option_string(uint8_t code, uint16_t option_type) {
{ COAP_OPTION_PROXY_URI, "Proxy-Uri" },
{ COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme" },
{ COAP_OPTION_SIZE1, "Size1" },
{ COAP_OPTION_NORESPONSE, "No-Response" }
{ COAP_OPTION_ECHO, "Echo" },
{ COAP_OPTION_NORESPONSE, "No-Response" },
{ COAP_OPTION_RTAG, "RTag" }
};

static struct option_desc_t options_csm[] = {
{ COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, "Max-Message-Size" },
{ COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-wise-Transfer" }
{ COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-Wise-Transfer" }
};

static struct option_desc_t options_pingpong[] = {
Expand Down Expand Up @@ -634,6 +636,9 @@ coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) {

case COAP_OPTION_IF_MATCH:
case COAP_OPTION_ETAG:
case COAP_OPTION_ECHO:
case COAP_OPTION_NORESPONSE:
case COAP_OPTION_RTAG:
opt_len = coap_opt_length(option);
opt_val = coap_opt_value(option);
snprintf((char *)buf, sizeof(buf), "0x");
Expand Down Expand Up @@ -856,7 +861,7 @@ char *coap_string_tls_support(char *buffer, size_t bufsize)
if (have_dtls == 0 && have_tls == 0) {
snprintf(buffer, bufsize, "(No DTLS or TLS support)");
return buffer;
}
}
switch (tls_version->type) {
case COAP_TLS_LIBRARY_NOTLS:
snprintf(buffer, bufsize, "(No DTLS or TLS support)");
Expand Down
14 changes: 10 additions & 4 deletions src/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,12 @@ coap_send_internal(coap_session_t *session, coap_pdu_t *pdu) {
}
}

if (session->echo_send) {
if (coap_insert_option(pdu, COAP_OPTION_ECHO, session->echo_len,
session->echo))
session->echo_send = 0;
}

if (!coap_pdu_encode_header(pdu, session->proto)) {
goto error;
}
Expand Down Expand Up @@ -2842,7 +2848,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu
if (observe)
coap_delete_observer(resource, session, &token);
if (added_block)
coap_remove_option(pdu, COAP_OPTION_BLOCK1);
coap_remove_option(response, COAP_OPTION_BLOCK1);
}

/* If original request contained a token, and the registered
Expand Down Expand Up @@ -2909,8 +2915,8 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu
#if COAP_CLIENT_SUPPORT
static void
handle_response(coap_context_t *context, coap_session_t *session,
coap_pdu_t *sent, coap_pdu_t *rcvd) {

coap_pdu_t *sent, coap_pdu_t *rcvd)
{
/* In a lossy context, the ACK of a separate response may have
* been lost, so we need to stop retransmitting requests with the
* same token.
Expand All @@ -2919,7 +2925,7 @@ handle_response(coap_context_t *context, coap_session_t *session,

if (session->block_mode & COAP_BLOCK_USE_LIBCOAP) {
/* See if need to send next block to server */
if (coap_handle_response_send_block(session, rcvd)) {
if (coap_handle_response_send_block(session, sent, rcvd)) {
/* Next block transmitted, no need to inform app */
coap_send_ack(session, rcvd);
return;
Expand Down
Loading

0 comments on commit 69fe0b7

Please sign in to comment.