Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CoAP Packet Deduplication #791

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ Please note: LwM2M version 1.0 is only supported by clients, while servers are b
- WAKAAMA_COAP_RAW_BLOCK1_REQUESTS For low memory client devices where it is not possible to keep a large post or put request in memory to be parsed (typically a firmware write).
This option enable each unprocessed block 1 payload to be passed to the application, typically to be stored to a flash memory.
- WAKAAMA_COAP_DEFAULT_BLOCK_SIZE CoAP block size used by CoAP layer when performing block-wise transfers. Possible values: 16, 32, 64, 128, 256, 512 and 1024. Defaults to 1024.

- WAKAAMA_COAP_MESSAGE_EXCHANGE_LIFETIME "The time from starting to send a Confirmable message to the time when an acknowledgement is no longer expected [...]" rfc7252#section-4.8.2.
Used for message deduplication window.

### Logging

Expand Down
107 changes: 107 additions & 0 deletions coap/message_dedup.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* CoAP message deduplication tracking.
* RFC7252 section 4.2
*/

#include <stdbool.h>

#include "message_dedup.h"
#include <internals.h>
#include <liblwm2m.h>

void coap_cleanup_message_deduplication_step(coap_msg_dedup_t **message_dedup, const time_t current_time,
time_t *timeout) {
LOG_DBG("Entering");
coap_msg_dedup_t *message_dedup_check = *message_dedup;
coap_msg_dedup_t *message_dedup_check_prev = *message_dedup;
while (message_dedup_check != NULL) {
time_t diff = current_time - message_dedup_check->timestamp;
if (diff >= LWM2M_COAP_MESSAGE_EXCHANGE_LIFETIME) {
LOG_ARG_DBG("Message %d deduplication period ended", message_dedup_check->mid);
if (message_dedup_check_prev != *message_dedup) {
message_dedup_check_prev->next = message_dedup_check->next;
} else {
*message_dedup = message_dedup_check->next;
message_dedup_check_prev = message_dedup_check->next;
}
coap_msg_dedup_t *message_dedup_check_next = message_dedup_check->next;
lwm2m_free(message_dedup_check);
message_dedup_check = message_dedup_check_next;
} else {
LOG_ARG_DBG("Message %d check deduplication", message_dedup_check->mid);
time_t message_dedup_timeout;
if ((message_dedup_timeout =
(message_dedup_check->timestamp + LWM2M_COAP_MESSAGE_EXCHANGE_LIFETIME) - current_time) < 0) {
message_dedup_timeout = 0;
}
if (message_dedup_timeout < *timeout) {
LOG_ARG_DBG("Message %d check again in %ds deduplication", message_dedup_check->mid,
message_dedup_timeout);
*timeout = message_dedup_timeout;
}
message_dedup_check_prev = message_dedup_check;
message_dedup_check = message_dedup_check->next;
}
}
}

bool coap_check_message_duplication(coap_msg_dedup_t **message_dedup, const uint16_t mid, const void *session,
uint8_t *dedup_coap_error_code) {
LOG_DBG("Entering");
coap_msg_dedup_t *message_dedup_check = *message_dedup;
while (message_dedup_check != NULL) {
bool is_equal = lwm2m_session_is_equal(message_dedup_check->session, (void *)session, NULL);
if (message_dedup_check->mid == mid && is_equal) {
LOG_ARG_DBG("Duplicate, ignore mid %d (session: %p)", mid, session);
*dedup_coap_error_code = message_dedup_check->coap_response_code;
return true;
}
message_dedup_check = message_dedup_check->next;
}
LOG_ARG_DBG("Register mid %d (session: %p) for deduplication check", mid, session);
/* The message was not received in the past. Remember for future checks. */
coap_msg_dedup_t *new_message;
new_message = lwm2m_malloc(sizeof(coap_msg_dedup_t));
if (new_message == NULL) {
/* Memory allocation failed, mark packet as duplicate. Further allocations during packet processing would fail
* anyway. */
return true;
}
memset(new_message, 0, sizeof(coap_msg_dedup_t));
new_message->mid = mid;
new_message->session = (void *)session;
new_message->timestamp = lwm2m_gettime();

/* Add message id to deduplication list */
coap_msg_dedup_t *message_dedup_temp = *message_dedup;
*message_dedup = new_message;
(*message_dedup)->next = message_dedup_temp;

return false;
}

bool coap_deduplication_set_response_code(coap_msg_dedup_t **message_dedup, const uint16_t mid, const void *session,
const uint8_t coap_response_code) {
LOG_DBG("Entering");
coap_msg_dedup_t *message_dedup_check = *message_dedup;
while (message_dedup_check != NULL) {
bool is_equal = lwm2m_session_is_equal(message_dedup_check->session, (void *)session, NULL);
if (message_dedup_check->mid == mid && is_equal) {
LOG_ARG_DBG("Set response code %" PRIu8 " to message mid %" PRIu16, coap_response_code, mid);
message_dedup_check->coap_response_code = coap_response_code;
return true;
}
message_dedup_check = message_dedup_check->next;
}
return false;
}

void coap_deduplication_free(lwm2m_context_t *ctx) {
LOG_DBG("Remove and free the whole message deduplication list");
while (ctx->message_dedup != NULL) {
coap_msg_dedup_t *msg_dedup;
msg_dedup = ctx->message_dedup;
ctx->message_dedup = ctx->message_dedup->next;
lwm2m_free(msg_dedup);
}
}
54 changes: 54 additions & 0 deletions coap/message_dedup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#ifndef _COAP_MESSAGE_DEDUP_H_
#define _COAP_MESSAGE_DEDUP_H_

#include <stdint.h>
#include <time.h>

#include <liblwm2m.h>

typedef struct _coap_msg_dedup_ {
struct _coap_msg_dedup_ *next;
uint16_t mid;
void *session;
uint8_t coap_response_code;
time_t timestamp;
} coap_msg_dedup_t;

/**
* Cleanup message ids after EXCHANGE_LIFETIME.
* @param message_dedup list of message ids for deduplication
* @param current_time current timestamp
* @param timeout next timeout in main loop
*/
void coap_cleanup_message_deduplication_step(coap_msg_dedup_t **message_dedup, time_t current_time, time_t *timeout);

/**
* Check whether a message was already received. Add new messages to the deduplication tracking list.
* @param message_dedup list of message ids for deduplication
* @param mid message id
* @param session pointer to the session the message was received from
* @param coap_response_code CoAP response code to be used for answering duplicate messages
* @return true if the message was already seen within in the EXCHANGE_LIFETIME window, false otherwise.
*/
bool coap_check_message_duplication(coap_msg_dedup_t **message_dedup, uint16_t mid, const void *session,
uint8_t *coap_response_code);

/**
* Set response code to be used in acks to a duplicate message. Acknowledgements to duplicate messages must have the
* same CoAP return code as the relies to the first received message.
* @param message_dedup list of message ids for deduplication
* @param mid message id
* @param session pointer to the session the message was received from
* @param coap_response_code CoAP response code to be used for answering duplicate messages
* @return false if no matching message was found, this is an internal error and should not happen
*/
bool coap_deduplication_set_response_code(coap_msg_dedup_t **message_dedup, uint16_t mid, const void *session,
uint8_t coap_response_code);

/**
* Remove and free the whole message deduplication list
* @param ctx lwm2m context
*/
void coap_deduplication_free(lwm2m_context_t *ctx);

#endif // _COAP_MESSAGE_DEDUP_H_
3 changes: 3 additions & 0 deletions core/liblwm2m.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
*/

#include "internals.h"
#include "message_dedup.h"

#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -223,6 +224,7 @@ void lwm2m_close(lwm2m_context_t * contextP)
#endif

prv_deleteTransactionList(contextP);
coap_deduplication_free(contextP);
lwm2m_free(contextP);
}

Expand Down Expand Up @@ -493,6 +495,7 @@ int lwm2m_step(lwm2m_context_t * contextP,

registration_step(contextP, tv_sec, timeoutP);
transaction_step(contextP, tv_sec, timeoutP);
coap_cleanup_message_deduplication_step(&contextP->message_dedup, tv_sec, timeoutP);

LOG_ARG_DBG("Final timeoutP: %d", (int)*timeoutP);
#ifdef LWM2M_CLIENT_MODE
Expand Down
39 changes: 37 additions & 2 deletions core/packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Contains code snippets which are:


#include "internals.h"
#include "message_dedup.h"

#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -493,6 +494,26 @@ void lwm2m_handle_packet(lwm2m_context_t *contextP, uint8_t *buffer, size_t leng
message->type, message->token_len, message->code >> 5, message->code & 0x1F, message->mid,
message->content_type);
LOG_ARG_DBG("Payload: %.*s", (int)message->payload_len, STR_NULL2EMPTY(message->payload));

uint8_t dedup_coap_error_code = NO_ERROR;
if (coap_check_message_duplication(&contextP->message_dedup, message->mid, fromSessionH,
&dedup_coap_error_code)) {
if (dedup_coap_error_code == NO_ERROR) {
/* Internal error occurred, return silently. */
return;
}
LOG_WARN("Message %d already seen in transmission window", message->mid);
coap_init_message(response, COAP_TYPE_ACK, dedup_coap_error_code, message->mid);
if (message->token_len) {
coap_set_header_token(response, message->token, message->token_len);
}
coap_error_code = message_send(contextP, response, fromSessionH);
if (coap_error_code != NO_ERROR) {
LOG_ERR("Warning: Message already seen in transmission window");
}
return;
}

if (message->code >= COAP_GET && message->code <= COAP_DELETE)
{
uint32_t block_num = 0;
Expand Down Expand Up @@ -639,7 +660,10 @@ void lwm2m_handle_packet(lwm2m_context_t *contextP, uint8_t *buffer, size_t leng
lwm2m_get_coap_block_size());
coap_set_payload(response, response->payload, lwm2m_get_coap_block_size());
}

if (!coap_deduplication_set_response_code(&contextP->message_dedup, response->mid, fromSessionH,
response->code)) {
LOG_ARG_ERR("Message %" PRIu16 " duplication not tracked", response->mid);
}
coap_error_code = message_send(contextP, response, fromSessionH);

lwm2m_free(payload);
Expand All @@ -664,6 +688,10 @@ void lwm2m_handle_packet(lwm2m_context_t *contextP, uint8_t *buffer, size_t leng
}
if (1 == coap_set_status_code(response, coap_error_code))
{
if (!coap_deduplication_set_response_code(&contextP->message_dedup, response->mid, fromSessionH,
response->code)) {
LOG_ARG_ERR("Message %" PRIu16 " duplication not tracked", response->mid);
}
coap_error_code = message_send(contextP, response, fromSessionH);
}
}
Expand Down Expand Up @@ -714,6 +742,10 @@ void lwm2m_handle_packet(lwm2m_context_t *contextP, uint8_t *buffer, size_t leng
if (message->payload_len > lwm2m_get_coap_block_size()) {
coap_set_status_code(response, COAP_413_ENTITY_TOO_LARGE);
}
if (!coap_deduplication_set_response_code(&contextP->message_dedup, response->mid, fromSessionH,
response->code)) {
LOG_ARG_ERR("Message %" PRIu16 " duplication not tracked", response->mid);
}
coap_error_code = message_send(contextP, response, fromSessionH);
}
}
Expand Down Expand Up @@ -855,6 +887,10 @@ void lwm2m_handle_packet(lwm2m_context_t *contextP, uint8_t *buffer, size_t leng
/* Reuse input buffer for error message. */
coap_init_message(message, COAP_TYPE_ACK, coap_error_code, message->mid);
coap_set_payload(message, coap_error_message, strlen(coap_error_message));
if (!coap_deduplication_set_response_code(&contextP->message_dedup, response->mid, fromSessionH,
response->code)) {
LOG_ARG_ERR("Message %" PRIu16 " duplication not tracked", response->mid);
}
message_send(contextP, message, fromSessionH);
}
}
Expand Down Expand Up @@ -887,4 +923,3 @@ uint8_t message_send(lwm2m_context_t * contextP,

return result;
}

3 changes: 3 additions & 0 deletions include/liblwm2m.h
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,8 @@ typedef int (*lwm2m_bootstrap_callback_t)(lwm2m_context_t *contextP, void *sessi
void *userData);
#endif

typedef struct _coap_msg_dedup_ coap_msg_dedup_t;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear if this really belongs here. Probably it does together with the complete struct definition as we need it in the context. But then it should probably be prefixed by lwm2m_

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to store the list somewhere, the lwm2m context was the easiest place, for that I needed this forward declaration. The message duplication tracking list is an implementation detail of the CoAP layer and might fit better there. The context structure was available everywhere I needed access though. Problem is, there is no clean separation between LwM2M and CoAP in Wakaama.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we don't have that nice separation between CoAP and LwM2M. But the forward declaration doesn't work properly together with the typedef.


struct _lwm2m_context_
{
#ifdef LWM2M_CLIENT_MODE
Expand All @@ -842,6 +844,7 @@ struct _lwm2m_context_
#endif
uint16_t nextMID;
lwm2m_transaction_t * transactionList;
coap_msg_dedup_t *message_dedup;
void * userData;
};

Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(TEST_SOURCES
coap_block1tests.c
coap_block2tests.c
coap_parse_message.c
coap_message_deduplication.c
data_cbor_tests.c
core_convert_numbers_test.c
core_list_tests.c
Expand Down
Loading
Loading