net: lwm2m: Fix block transfer retransmissions

During FW update, the application expects a consecutive data stream.
Therefore retransmitted blocks shall not be forwarded to the
application, but ignored. In case blocks are received out of order,
return an error and do not handle this block.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2020-06-18 11:32:34 +02:00 committed by Carles Cufí
commit b080dfbd12

View file

@ -142,6 +142,7 @@ struct block_context {
struct coap_block_context ctx; struct coap_block_context ctx;
int64_t timestamp; int64_t timestamp;
uint32_t remaining_len; uint32_t remaining_len;
uint32_t expected;
uint8_t token[8]; uint8_t token[8];
uint8_t tkl; uint8_t tkl;
uint8_t opaque_header_len; uint8_t opaque_header_len;
@ -277,6 +278,7 @@ init_block_ctx(const uint8_t *token, uint8_t tkl, struct block_context **ctx)
(*ctx)->timestamp = timestamp; (*ctx)->timestamp = timestamp;
(*ctx)->remaining_len = 0; (*ctx)->remaining_len = 0;
(*ctx)->opaque_header_len = 0; (*ctx)->opaque_header_len = 0;
(*ctx)->expected = 0;
(*ctx)->last_block = false; (*ctx)->last_block = false;
return 0; return 0;
@ -300,7 +302,6 @@ get_block_ctx(const uint8_t *token, uint8_t tkl, struct block_context **ctx)
} }
if (*ctx == NULL) { if (*ctx == NULL) {
LOG_ERR("Cannot find block context");
return -ENOENT; return -ENOENT;
} }
@ -3329,10 +3330,12 @@ static int handle_request(struct coap_packet *request,
uint16_t format = LWM2M_FORMAT_NONE, accept; uint16_t format = LWM2M_FORMAT_NONE, accept;
int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */ int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */
bool well_known = false; bool well_known = false;
int block_opt, block_num;
struct block_context *block_ctx = NULL; struct block_context *block_ctx = NULL;
enum coap_block_size block_size; enum coap_block_size block_size;
uint16_t payload_len = 0U; uint16_t payload_len = 0U;
bool last_block = false; bool last_block = false;
bool ignore = false;
/* set CoAP request / message */ /* set CoAP request / message */
msg->in.in_cpkt = request; msg->in.in_cpkt = request;
@ -3512,12 +3515,13 @@ static int handle_request(struct coap_packet *request,
coap_packet_get_payload(msg->in.in_cpkt, &payload_len); coap_packet_get_payload(msg->in.in_cpkt, &payload_len);
/* Check for block transfer */ /* Check for block transfer */
r = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1);
if (r > 0) { block_opt = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1);
last_block = !GET_MORE(r); if (block_opt > 0) {
last_block = !GET_MORE(block_opt);
/* RFC7252: 4.6. Message Size */ /* RFC7252: 4.6. Message Size */
block_size = GET_BLOCK_SIZE(r); block_size = GET_BLOCK_SIZE(block_opt);
if (!last_block && if (!last_block &&
coap_block_size_to_bytes(block_size) > payload_len) { coap_block_size_to_bytes(block_size) > payload_len) {
LOG_DBG("Trailing payload is discarded!"); LOG_DBG("Trailing payload is discarded!");
@ -3525,16 +3529,31 @@ static int handle_request(struct coap_packet *request,
goto error; goto error;
} }
if (GET_BLOCK_NUM(r) == 0) { block_num = GET_BLOCK_NUM(block_opt);
r = init_block_ctx(token, tkl, &block_ctx);
} else { /* Try to retrieve existing block context. If one not exists,
* and we've received first block, allocate new context.
*/
r = get_block_ctx(token, tkl, &block_ctx); r = get_block_ctx(token, tkl, &block_ctx);
if (r < 0 && block_num == 0) {
r = init_block_ctx(token, tkl, &block_ctx);
} }
if (r < 0) { if (r < 0) {
LOG_ERR("Cannot find block context");
goto error; goto error;
} }
if (block_num < block_ctx->expected) {
LOG_WRN("Block already handled %d, expected %d",
block_num, block_ctx->expected);
ignore = true;
} else if (block_num > block_ctx->expected) {
LOG_WRN("Block out of order %d, expected %d",
block_num, block_ctx->expected);
r = -EFAULT;
goto error;
} else {
r = coap_update_from_block(msg->in.in_cpkt, &block_ctx->ctx); r = coap_update_from_block(msg->in.in_cpkt, &block_ctx->ctx);
if (r < 0) { if (r < 0) {
LOG_ERR("Error from block update: %d", r); LOG_ERR("Error from block update: %d", r);
@ -3543,6 +3562,15 @@ static int handle_request(struct coap_packet *request,
block_ctx->last_block = last_block; block_ctx->last_block = last_block;
/* Initial block sent by the server might be larger than
* our block size therefore it is needed to take this
* into account when calculating next expected block
* number.
*/
block_ctx->expected += GET_BLOCK_SIZE(block_opt) -
block_ctx->ctx.block_size + 1;
}
/* Handle blockwise 1 (Part 1): Set response code */ /* Handle blockwise 1 (Part 1): Set response code */
if (!last_block) { if (!last_block) {
msg->code = COAP_RESPONSE_CODE_CONTINUE; msg->code = COAP_RESPONSE_CODE_CONTINUE;
@ -3555,13 +3583,16 @@ static int handle_request(struct coap_packet *request,
goto error; goto error;
} }
if (!ignore) {
switch (msg->operation) { switch (msg->operation) {
case LWM2M_OP_READ: case LWM2M_OP_READ:
if (observe == 0) { if (observe == 0) {
/* add new observer */ /* add new observer */
if (msg->token) { if (msg->token) {
r = coap_append_option_int(msg->out.out_cpkt, r = coap_append_option_int(
msg->out.out_cpkt,
COAP_OPTION_OBSERVE, COAP_OPTION_OBSERVE,
1); 1);
if (r < 0) { if (r < 0) {
@ -3620,6 +3651,7 @@ static int handle_request(struct coap_packet *request,
if (r < 0) { if (r < 0) {
goto error; goto error;
} }
}
/* Handle blockwise 1 (Part 2): Append BLOCK1 option / free context */ /* Handle blockwise 1 (Part 2): Append BLOCK1 option / free context */
if (block_ctx) { if (block_ctx) {