diff --git a/include/coap3/coap_block_internal.h b/include/coap3/coap_block_internal.h index 7bbedcca5c..cb945bc5c1 100644 --- a/include/coap3/coap_block_internal.h +++ b/include/coap3/coap_block_internal.h @@ -30,8 +30,14 @@ extern "C" { * @{ */ -/* Use the top 20 bits of a 64 bit token for tracking current block in large transfer */ +/* + * Use the top 20 bits of a 64 bit token for tracking current block in large transfer. + * However, if smaller tokens are required. this vould be set up to 48, leaving 16 bits + * for unique tokens. + */ +#ifndef STATE_MAX_BLK_CNT_BITS #define STATE_MAX_BLK_CNT_BITS 20 +#endif /* STATE_MAX_BLK_CNT_BITS */ #define STATE_TOKEN_BASE(t) ((t) & (0xffffffffffffffffULL >> STATE_MAX_BLK_CNT_BITS)) #define STATE_TOKEN_RETRY(t) ((uint64_t)(t) >> (64 - STATE_MAX_BLK_CNT_BITS)) #define STATE_TOKEN_FULL(t,r) (STATE_TOKEN_BASE(t) + ((uint64_t)(r) << (64 - STATE_MAX_BLK_CNT_BITS))) @@ -165,7 +171,6 @@ struct coap_lg_xmit_t { int last_block; /**< last acknowledged block number Block1 last transmitted Q-Block2 */ coap_lg_xmit_data_t *data_info; /**< Pointer to large data information */ - size_t offset; /**< large data next offset to transmit */ union { coap_l_block1_t b1; coap_l_block2_t b2; @@ -177,6 +182,7 @@ struct coap_lg_xmit_t { coap_tick_t last_obs; /**< Last time used (Observe tracking) or 0 */ #if COAP_Q_BLOCK_SUPPORT coap_tick_t non_timeout_random_ticks; /** Used for Q-Block */ + coap_rblock_t send_blocks; /**< list of blocks still to send */ #endif /* COAP_Q_BLOCK_SUPPORT */ uint32_t ref; /**< Reference count */ }; @@ -330,6 +336,21 @@ coap_mid_t coap_block_test_q_block(coap_session_t *session, coap_pdu_t *actual); #endif /* COAP_CLIENT_SUPPORT */ #if COAP_Q_BLOCK_SUPPORT +/** + * Send the next set of Q-blocks - one if CON, else to the next appropriate MAX_PAYLOAD + * boundary. Handles if blocks are reported as not being received. + * + * If rate limiting when called returns COAP_INVALID_MID. + * + * @param session The current session. + * @param lg_xmit Contains information about the list of blocks to send. + * @param block The first block to send using @p pdu template. + * @param pdu The PDU to model the blocks on. @p pdu is always released + * if send_pdu == COAP_SEND_INC_PDU + * @param send_pdu Whether @p pdu should be sent first or just used as a PDU model. + * + * @return COAP_INVALID_MID if transmission error, else the last transmitted block mid. + */ coap_mid_t coap_send_q_blocks(coap_session_t *session, coap_lg_xmit_t *lg_xmit, coap_block_b_t block, diff --git a/src/coap_block.c b/src/coap_block.c index 7fdf788de8..aae0763fb6 100644 --- a/src/coap_block.c +++ b/src/coap_block.c @@ -29,6 +29,12 @@ #error COAP_ETAG_MAX_BYTES byte size invalid #endif +#define COAP_LG_XMIT_TXT_SCALAR (8) + +#if COAP_Q_BLOCK_SUPPORT +static int blocks_delete_entry(coap_rblock_t *rec_blocks, uint32_t block_num); +#endif /* COAP_Q_BLOCK_SUPPORT */ + #if COAP_Q_BLOCK_SUPPORT int coap_q_block_is_supported(void) { @@ -1226,6 +1232,11 @@ coap_add_data_large_internal(coap_session_t *session, (block.num << 4) | (block.m << 3) | lg_xmit->blk_size), buf); } +#if COAP_Q_BLOCK_SUPPORT + /* Set up send_blocks */ + lg_xmit->send_blocks.used = 1; + lg_xmit->send_blocks.range[0].end = (uint32_t)(lg_xmit->data_info->length / chunk); +#endif /* COAP_Q_BLOCK_SUPPORT */ rem = block.chunk_size; if (rem > lg_xmit->data_info->length - block.num * chunk) @@ -1525,7 +1536,7 @@ coap_block_check_lg_xmit_timeouts(coap_session_t *session, coap_tick_t now, coap_lg_xmit_t *lg_xmit; coap_lg_xmit_t *q; #if COAP_Q_BLOCK_SUPPORT - coap_tick_t idle_timeout = 4 * COAP_NON_TIMEOUT_TICKS(session); + coap_tick_t idle_timeout = COAP_LG_XMIT_TXT_SCALAR * COAP_NON_TIMEOUT_TICKS(session); #else /* ! COAP_Q_BLOCK_SUPPORT */ coap_tick_t idle_timeout = 8 * COAP_TICKS_PER_SECOND; #endif /* ! COAP_Q_BLOCK_SUPPORT */ @@ -1895,6 +1906,87 @@ check_any_blocks_next_payload_set(coap_session_t *session, #endif /* COAP_CLIENT_SUPPORT */ #if COAP_SERVER_SUPPORT +#if COAP_Q_BLOCK_SUPPORT +static int +request_missing_blocks(coap_session_t *session, coap_lg_srcv_t *lg_srcv, size_t final_block) { + uint32_t i; + int block = -1; /* Last one seen */ + size_t block_size = (size_t)1 << (lg_srcv->szx + 4); + size_t cur_payload; + size_t last_payload_block; + coap_pdu_t *pdu = NULL; + size_t no_blocks = 0; + + /* Need to count the number of missing blocks */ + for (i = 0; i < lg_srcv->rec_blocks.used; i++) { + if (block < (int)lg_srcv->rec_blocks.range[i].begin && + lg_srcv->rec_blocks.range[i].begin != 0) { + block++; + no_blocks += lg_srcv->rec_blocks.range[i].begin - block; + } + if (block < (int)lg_srcv->rec_blocks.range[i].end) { + block = lg_srcv->rec_blocks.range[i].end; + } + } + if (no_blocks == 0 && block == (int)final_block) + return 0; + + /* Include missing up to end of current payload or total amount */ + cur_payload = block / COAP_MAX_PAYLOADS(session); + last_payload_block = (cur_payload + 1) * COAP_MAX_PAYLOADS(session) - 1; + if (final_block > last_payload_block) { + final_block = last_payload_block; + } + no_blocks += final_block - block; + if (no_blocks == 0) { + /* Add in the blocks out of the next payload */ + final_block = (lg_srcv->total_len + block_size - 1)/block_size - 1; + last_payload_block += COAP_MAX_PAYLOADS(session); + if (final_block > last_payload_block) { + final_block = last_payload_block; + } + } + /* Ask for the missing blocks */ + block = -1; + for (i = 0; i < lg_srcv->rec_blocks.used; i++) { + if (block < (int)lg_srcv->rec_blocks.range[i].begin && + lg_srcv->rec_blocks.range[i].begin != 0) { + /* Report on missing blocks */ + if (pdu == NULL) { + pdu = pdu_408_build(session, lg_srcv); + if (!pdu) + continue; + } + block++; + for (; block < (int)lg_srcv->rec_blocks.range[i].begin; block++) { + if (!add_408_block(pdu, block)) { + break; + } + } + } + if (block < (int)lg_srcv->rec_blocks.range[i].end) { + block = lg_srcv->rec_blocks.range[i].end; + } + } + block++; + for (; block <= (int)final_block; block++) { + if (pdu == NULL) { + pdu = pdu_408_build(session, lg_srcv); + if (!pdu) + continue; + } + if (!add_408_block(pdu, block)) { + break; + } + } + if (pdu) + coap_send_internal(session, pdu, NULL); + lg_srcv->rec_blocks.retry++; + coap_ticks(&lg_srcv->rec_blocks.last_seen); + return 1; +} +#endif /* COAP_Q_BLOCK_SUPPORT */ + /* * return 1 if there is a future expire time, else 0. * update tim_rem with remaining value if return is 1. @@ -1937,81 +2029,19 @@ coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now, goto expire; } if (lg_srcv->rec_blocks.last_seen + scaled_timeout <= now) { - uint32_t i; - int block = -1; /* Last one seen */ size_t block_size = (size_t)1 << (lg_srcv->szx + 4); size_t final_block = (lg_srcv->total_len + block_size - 1)/block_size - 1; - size_t cur_payload; - size_t last_payload_block; - coap_pdu_t *pdu = NULL; - size_t no_blocks = 0; - - /* Need to count the number of missing blocks */ - for (i = 0; i < lg_srcv->rec_blocks.used; i++) { - if (block < (int)lg_srcv->rec_blocks.range[i].begin && - lg_srcv->rec_blocks.range[i].begin != 0) { - block++; - no_blocks += lg_srcv->rec_blocks.range[i].begin - block; - } - if (block < (int)lg_srcv->rec_blocks.range[i].end) { - block = lg_srcv->rec_blocks.range[i].end; - } - } - if (no_blocks == 0 && block == (int)final_block) - goto expire; - /* Include missing up to end of current payload or total amount */ - cur_payload = block / COAP_MAX_PAYLOADS(session); - last_payload_block = (cur_payload + 1) * COAP_MAX_PAYLOADS(session) - 1; - if (final_block > last_payload_block) { - final_block = last_payload_block; - } - no_blocks += final_block - block; - if (no_blocks == 0) { - /* Add in the blocks out of the next payload */ - final_block = (lg_srcv->total_len + block_size - 1)/block_size - 1; - last_payload_block += COAP_MAX_PAYLOADS(session); - if (final_block > last_payload_block) { - final_block = last_payload_block; - } + if (lg_srcv->rec_blocks.used > 1 || + final_block != lg_srcv->rec_blocks.range[lg_srcv->rec_blocks.used - 1].end) { + /* Missing sone blocks before the latest block received */ + if (final_block > lg_srcv->rec_blocks.range[lg_srcv->rec_blocks.used - 1].end + 1) + final_block = lg_srcv->rec_blocks.range[lg_srcv->rec_blocks.used - 1].end + 1; } - /* Ask for the missing blocks */ - block = -1; - for (i = 0; i < lg_srcv->rec_blocks.used; i++) { - if (block < (int)lg_srcv->rec_blocks.range[i].begin && - lg_srcv->rec_blocks.range[i].begin != 0) { - /* Report on missing blocks */ - if (pdu == NULL) { - pdu = pdu_408_build(session, lg_srcv); - if (!pdu) - continue; - } - block++; - for (; block < (int)lg_srcv->rec_blocks.range[i].begin; block++) { - if (!add_408_block(pdu, block)) { - break; - } - } - } - if (block < (int)lg_srcv->rec_blocks.range[i].end) { - block = lg_srcv->rec_blocks.range[i].end; - } - } - block++; - for (; block <= (int)final_block; block++) { - if (pdu == NULL) { - pdu = pdu_408_build(session, lg_srcv); - if (!pdu) - continue; - } - if (!add_408_block(pdu, block)) { - break; - } - } - if (pdu) - coap_send_internal(session, pdu, NULL); - lg_srcv->rec_blocks.retry++; - coap_ticks(&lg_srcv->rec_blocks.last_seen); + coap_log_debug("** %s: lg_srcv %p timeout\n", + coap_session_str(session), (void *)lg_srcv); + if (!request_missing_blocks(session, lg_srcv, final_block)) + goto expire; } if (*tim_rem > lg_srcv->rec_blocks.last_seen + scaled_timeout - now) { *tim_rem = lg_srcv->rec_blocks.last_seen + scaled_timeout - now; @@ -2072,7 +2102,7 @@ coap_send_q_blocks(coap_session_t *session, coap_block_b_t block, coap_pdu_t *pdu, coap_send_pdu_t send_pdu) { - coap_pdu_t *block_pdu = NULL; + coap_pdu_t *block_pdu = NULL; /* The next block to send after any initial PDU */ coap_opt_filter_t drop_options; coap_mid_t mid = COAP_INVALID_MID; uint64_t token; @@ -2080,10 +2110,21 @@ coap_send_q_blocks(coap_session_t *session, uint8_t ltoken[8]; size_t ltoken_length; uint32_t delayqueue_cnt = 0; + coap_block_b_t l_block; - if (!lg_xmit) { - if (send_pdu == COAP_SEND_INC_PDU) - return coap_send_internal(session, pdu, NULL); + if (!lg_xmit || session->is_rate_limiting) { + if (send_pdu == COAP_SEND_INC_PDU) { + if (lg_xmit && + (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &l_block) || + coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &l_block))) { + mid = coap_send_internal(session, pdu, NULL); + if (mid != COAP_INVALID_MID) { + blocks_delete_entry(&lg_xmit->send_blocks, l_block.num); + } + } else { + return coap_send_internal(session, pdu, NULL); + } + } return COAP_INVALID_MID; } @@ -2097,16 +2138,23 @@ coap_send_q_blocks(coap_session_t *session, } } pdu->lg_xmit = lg_xmit; - if (block.m && + if ((block.m || lg_xmit->send_blocks.used) && + COAP_MAX_PAYLOADS(session) && ((pdu->type == COAP_MESSAGE_NON && - ((block.num + 1) % COAP_MAX_PAYLOADS(session)) + 1 != - COAP_MAX_PAYLOADS(session)) || + (lg_xmit->send_blocks.used > 1 || + ((block.num + 1) % COAP_MAX_PAYLOADS(session)) + 1 != + COAP_MAX_PAYLOADS(session))) || (pdu->type == COAP_MESSAGE_ACK && lg_xmit->option == COAP_OPTION_Q_BLOCK2) || (pdu->type == COAP_MESSAGE_CON && delayqueue_cnt < COAP_NSTART(session)) || COAP_PROTO_RELIABLE(session->proto))) { - /* Allocate next pdu if there is headroom */ + /* + * Allocate next block pdu if there is headroom and if one of + * NON and more in MAX_PAYLOADS + * CON and NSTART allows it (based on number in delayqueue) + * Reliable transport + */ if (COAP_PDU_IS_RESPONSE(pdu)) { ptoken = pdu->actual_token.s; ltoken_length = pdu->actual_token.length; @@ -2126,25 +2174,40 @@ coap_send_q_blocks(coap_session_t *session, } /* Send initial pdu (which deletes 'pdu') */ - if (send_pdu == COAP_SEND_INC_PDU && - (mid = coap_send_internal(session, pdu, NULL)) == COAP_INVALID_MID) { - /* Not expected, underlying issue somewhere */ - coap_delete_pdu_lkd(block_pdu); - return COAP_INVALID_MID; + if (send_pdu == COAP_SEND_INC_PDU) { + if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &l_block) || + coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &l_block)) { + mid = coap_send_internal(session, pdu, NULL); + if (mid != COAP_INVALID_MID) { + blocks_delete_entry(&lg_xmit->send_blocks, l_block.num); + } + } else { + mid = coap_send_internal(session, pdu, NULL); + } + if (mid == COAP_INVALID_MID) { + /* Not expected, underlying issue somewhere */ + coap_delete_pdu_lkd(block_pdu); + return COAP_INVALID_MID; + } } + /* Unsafe to use pdu in later code */ coap_lg_xmit_reference_lkd(lg_xmit); - while (block_pdu) { + while (block_pdu && lg_xmit->send_blocks.used) { coap_pdu_t *t_pdu = NULL; uint8_t buf[8]; size_t chunk = ((size_t)1 << (lg_xmit->blk_size + 4)); + size_t offset; - block.num++; - lg_xmit->offset = block.num * chunk; - block.m = lg_xmit->offset + chunk < lg_xmit->data_info->length; + /* Get the next block to transmit (which could be a retry */ + block.num = lg_xmit->send_blocks.range[0].begin; + + offset = block.num * chunk; + block.m = offset + chunk < lg_xmit->data_info->length; if (block.m && ((block_pdu->type == COAP_MESSAGE_NON && - (block.num % COAP_MAX_PAYLOADS(session)) + 1 != - COAP_MAX_PAYLOADS(session)) || + (lg_xmit->send_blocks.used > 1 || + (block.num % COAP_MAX_PAYLOADS(session)) + 1 != + COAP_MAX_PAYLOADS(session))) || (block_pdu->type == COAP_MESSAGE_CON && delayqueue_cnt + 1 < COAP_NSTART(session)) || COAP_PROTO_RELIABLE(session->proto))) { @@ -2221,8 +2284,11 @@ coap_send_q_blocks(coap_session_t *session, coap_delete_pdu_lkd(t_pdu); return COAP_INVALID_MID; } + /* This block has now been transmitted */ + blocks_delete_entry(&lg_xmit->send_blocks, block.num); block_pdu = t_pdu; } + coap_delete_pdu_lkd(block_pdu); if (!block.m) { lg_xmit->last_payload = 0; coap_ticks(&lg_xmit->last_all_sent); @@ -2255,15 +2321,17 @@ coap_block_check_q_block1_xmit(coap_session_t *session, coap_tick_t now, coap_ti } timed_out = now - non_timeout; - if (lg_xmit->last_payload) { + if (lg_xmit->last_payload && lg_xmit->send_blocks.used) { if (lg_xmit->last_payload <= timed_out) { /* Send off the next MAX_PAYLOAD set */ coap_block_b_t block; size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4); + size_t offset; memset(&block, 0, sizeof(block)); - block.num = (uint32_t)(lg_xmit->offset / chunk); - block.m = lg_xmit->offset + chunk < lg_xmit->data_info->length; + block.num = lg_xmit->send_blocks.range[0].begin; + offset = block.num * chunk; + block.m = offset + chunk < lg_xmit->data_info->length; block.szx = lg_xmit->blk_size; coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU); if (*tim_rem > non_timeout) { @@ -2279,14 +2347,14 @@ coap_block_check_q_block1_xmit(coap_session_t *session, coap_tick_t now, coap_ti } } else if (lg_xmit->last_all_sent) { non_timeout = COAP_NON_TIMEOUT_TICKS(session); - if (lg_xmit->last_all_sent + 4 * non_timeout <= now) { + if (lg_xmit->last_all_sent + COAP_LG_XMIT_TXT_SCALAR * non_timeout <= now) { /* Expire this entry */ LL_DELETE(session->lg_xmit, lg_xmit); coap_block_delete_lg_xmit(session, lg_xmit); } else { /* Delay until the lg_xmit needs to expire */ - if (*tim_rem > lg_xmit->last_all_sent + 4 * non_timeout - now) { - *tim_rem = lg_xmit->last_all_sent + 4 * non_timeout - now; + if (*tim_rem > lg_xmit->last_all_sent + COAP_LG_XMIT_TXT_SCALAR * non_timeout - now) { + *tim_rem = lg_xmit->last_all_sent + COAP_LG_XMIT_TXT_SCALAR * non_timeout - now; ret = 1; } } @@ -2319,15 +2387,17 @@ coap_block_check_q_block2_xmit(coap_session_t *session, coap_tick_t now, coap_ti } timed_out = now - non_timeout; - if (lg_xmit->last_payload) { + if (lg_xmit->last_payload && lg_xmit->send_blocks.used) { if (lg_xmit->last_payload <= timed_out) { /* Send off the next MAX_PAYLOAD set */ coap_block_b_t block; size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4); + size_t offset; memset(&block, 0, sizeof(block)); - block.num = (uint32_t)(lg_xmit->offset / chunk); - block.m = lg_xmit->offset + chunk < lg_xmit->data_info->length; + block.num = lg_xmit->send_blocks.range[0].begin; + offset = block.num * chunk; + block.m = offset + chunk < lg_xmit->data_info->length; block.szx = lg_xmit->blk_size; if (block.num == (uint32_t)lg_xmit->last_block) coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU); @@ -2847,24 +2917,23 @@ coap_handle_request_send_block(coap_session_t *session, } if (block.bert == 0 && block.szx != lg_xmit->blk_size) { if (block.num == 0) { - if ((lg_xmit->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) { + if (block.szx < lg_xmit->blk_size) { /* * Recompute the block number of the previous packet given * the new block size */ - block.num = (uint32_t)(((lg_xmit->offset + chunk) >> (block.szx + 4)) - 1); - lg_xmit->blk_size = block.szx; + block.num = (uint32_t)((chunk >> (block.szx + 4)) - 1); chunk = (size_t)1 << (lg_xmit->blk_size + 4); - lg_xmit->offset = block.num * chunk; +#if COAP_Q_BLOCK_SUPPORT + lg_xmit->send_blocks.range[0].begin = block.num; + lg_xmit->send_blocks.range[0].end = (uint32_t)(lg_xmit->data_info->length / chunk); +#endif /* COAP_Q_BLOCK_SUPPORT */ + lg_xmit->blk_size = block.szx; coap_log_debug("new Block size is %u, block number %u completed\n", - 1 << (block.szx + 4), block.num); + (1 << (block.szx + 4)), block.num); } else { - coap_log_debug("ignoring request to increase Block size, " - "next block is not aligned on requested block size " - "boundary. (%" PRIuS " x %u mod %u = %" PRIuS " (which is not 0)\n", - lg_xmit->offset/chunk + 1, (1 << (lg_xmit->blk_size + 4)), - (1 << (block.szx + 4)), - (lg_xmit->offset + chunk) % ((size_t)1 << (block.szx + 4))); + coap_log_debug("ignoring request to increase Block size from %u to %u\n", + (1 << (lg_xmit->blk_size + 4)), (1 << (lg_xmit->blk_size + 4))); } } else { coap_log_debug("ignoring request to change Block size from %u to %u\n", @@ -2932,7 +3001,6 @@ coap_handle_request_send_block(coap_session_t *session, if (request_cnt == 0) { /* Block2 or Q-Block2 not found - give them the first block */ block.szx = lg_xmit->blk_size; - lg_xmit->offset = 0; out_blocks[0].num = 0; out_blocks[0].is_continue = 0; request_cnt = 1; @@ -2940,9 +3008,9 @@ coap_handle_request_send_block(coap_session_t *session, for (i = 0; i < request_cnt; i++) { uint8_t buf[8]; + size_t offset; block.num = out_blocks[i].num; - lg_xmit->offset = block.num * chunk; if (i + 1 < request_cnt) { /* Need to set up a copy of the pdu to send */ @@ -2988,12 +3056,13 @@ coap_handle_request_send_block(coap_session_t *session, } if (pdu->type == COAP_MESSAGE_NON) out_pdu->type = COAP_MESSAGE_NON; + offset = block.num * chunk; if (block.bert) { size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; - block.m = (lg_xmit->data_info->length - lg_xmit->offset) > + block.m = (lg_xmit->data_info->length - offset) > ((out_pdu->max_size - token_options) /1024) * 1024; } else { - block.m = (lg_xmit->offset + chunk) < lg_xmit->data_info->length; + block.m = (offset + chunk) < lg_xmit->data_info->length; } if (!coap_update_option(out_pdu, lg_xmit->option, coap_encode_var_safe(buf, @@ -3004,7 +3073,7 @@ coap_handle_request_send_block(coap_session_t *session, buf)) { goto internal_issue; } - if (!(lg_xmit->offset + chunk < lg_xmit->data_info->length)) { + if (!(offset + chunk < lg_xmit->data_info->length)) { /* Last block - keep in cache for 4 * ACK_TIMOUT */ coap_ticks(&lg_xmit->last_all_sent); } @@ -3012,7 +3081,7 @@ coap_handle_request_send_block(coap_session_t *session, coap_tick_t now; coap_time_t rem; - if (!(lg_xmit->offset + chunk < lg_xmit->data_info->length)) { + if (!(offset + chunk < lg_xmit->data_info->length)) { /* Last block - keep in cache for 4 * ACK_TIMOUT */ coap_ticks(&lg_xmit->last_all_sent); } @@ -3068,8 +3137,62 @@ coap_handle_request_send_block(coap_session_t *session, } #endif /* COAP_SERVER_SUPPORT */ +#if COAP_Q_BLOCK_SUPPORT +static int +blocks_delete_entry(coap_rblock_t *rec_blocks, uint32_t block_num) { + uint32_t i; + + if (rec_blocks->total_blocks && block_num + 1 > rec_blocks->total_blocks) { + /* received block number greater than Block No defined when More bit unset */ + return 0; + } + + for (i = 0; i < rec_blocks->used; i++) { + if (block_num >= rec_blocks->range[i].begin && + block_num <= rec_blocks->range[i].end) { + /* In this block */ + if (block_num == rec_blocks->range[i].begin) { + if (block_num == rec_blocks->range[i].end) { + /* Need to delete this range */ + if (i + 1 < rec_blocks->used) { + memmove(&rec_blocks->range[i], &rec_blocks->range[i+1], + (rec_blocks->used - i) * sizeof(rec_blocks->range[0])); + } + rec_blocks->used--; + break; + } + rec_blocks->range[i].begin++; + } else if (block_num == rec_blocks->range[i].end) { + rec_blocks->range[i].end--; + if (rec_blocks->range[i].begin == rec_blocks->range[i].end) { + /* Need to delete this range */ + rec_blocks->used--; + if (i == rec_blocks->used) + break; + memmove(&rec_blocks->range[i], &rec_blocks->range[i+1], + sizeof(rec_blocks->range[i]) * (rec_blocks->used - i)); + } + } else { + /* Need to split the range */ + if (rec_blocks->used == COAP_RBLOCK_CNT -1) + /* Too many losses */ + return 0; + memmove(&rec_blocks->range[i+1], &rec_blocks->range[i], + (rec_blocks->used - i) * sizeof(rec_blocks->range[0])); + rec_blocks->range[i].end = block_num - 1; + rec_blocks->range[i+1].begin = block_num + 1; + rec_blocks->used++; + } + break; + } + } + coap_ticks(&rec_blocks->last_seen); + return 1; +} +#endif /* COAP_Q_BLOCK_SUPPORT */ + static int -update_received_blocks(coap_rblock_t *rec_blocks, uint32_t block_num, uint32_t block_m) { +blocks_add_entry(coap_rblock_t *rec_blocks, uint32_t block_num, uint32_t block_m) { uint32_t i; if (rec_blocks->total_blocks && block_num + 1 > rec_blocks->total_blocks) { @@ -3410,7 +3533,7 @@ coap_handle_request_put_block(coap_context_t *context, while (offset < saved_offset + length) { if (!check_if_received_block(&lg_srcv->rec_blocks, block.num)) { /* Update list of blocks received */ - if (!update_received_blocks(&lg_srcv->rec_blocks, block.num, block.m)) { + if (!blocks_add_entry(&lg_srcv->rec_blocks, block.num, block.m)) { coap_handle_event_lkd(context, COAP_EVENT_PARTIAL_BLOCK, session); coap_add_data(response, sizeof("Too many missing blocks")-1, (const uint8_t *)"Too many missing blocks"); @@ -3462,6 +3585,8 @@ coap_handle_request_put_block(coap_context_t *context, goto skip_app_handler; } } + } else { + goto skip_app_handler; } if (block.m || @@ -3483,6 +3608,17 @@ coap_handle_request_put_block(coap_context_t *context, block.num = lg_srcv->rec_blocks.range[0].end; goto skip_app_handler; } + } else if (lg_srcv->rec_blocks.used > 1 && + (block.num / COAP_MAX_PAYLOADS(session)) > + (lg_srcv->rec_blocks.range[0].end / COAP_MAX_PAYLOADS(session)) && + (lg_srcv->rec_blocks.range[0].end % COAP_MAX_PAYLOADS(session)) + 1 != + COAP_MAX_PAYLOADS(session)) { + /* + * Current MAX_PAYLOAD chunk is different to last MAX_PAYLOAD chunk + * with some missing packets in last MAX_PAYLOAD chunk + */ + request_missing_blocks(session, lg_srcv, block.num); + goto skip_app_handler; } else { /* The remote end will be sending the next one unless this is a MAX_PAYLOADS and all previous have been received */ @@ -3568,6 +3704,8 @@ coap_handle_request_put_block(coap_context_t *context, * Fall through to skip_app_handler. */ #if COAP_Q_BLOCK_SUPPORT + } else { + request_missing_blocks(session, lg_srcv, block.num); } #endif /* COAP_Q_BLOCK_SUPPORT */ } @@ -3805,26 +3943,22 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, coap_log_info("ignoring request to increase Block size, " "(%u > %u)\n", 1 << (block.szx + 4), 1 << (lg_xmit->blk_size + 4)); - } else if ((lg_xmit->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) { + } else { /* * Recompute the block number of the previous packet given the * new block size */ - block.num = (uint32_t)(((lg_xmit->offset + chunk) >> (block.szx + 4)) - 1); - lg_xmit->blk_size = block.szx; + block.num = (uint32_t)((chunk >> (block.szx + 4)) - 1); chunk = (size_t)1 << (lg_xmit->blk_size + 4); - lg_xmit->offset = block.num * chunk; +#if COAP_Q_BLOCK_SUPPORT + lg_xmit->send_blocks.range[0].begin = block.num; + lg_xmit->send_blocks.range[0].end = (uint32_t)(lg_xmit->data_info->length / chunk); +#endif /* COAP_Q_BLOCK_SUPPORT */ + lg_xmit->blk_size = block.szx; coap_log_debug("new Block size is %u, block number %u completed\n", 1 << (block.szx + 4), block.num); block.bert = 0; block.aszx = block.szx; - } else { - coap_log_debug("ignoring request to increase Block size, " - "next block is not aligned on requested block size boundary. " - "(%" PRIuS " x %u mod %u = %" PRIuS " != 0)\n", - lg_xmit->offset/chunk + 1, (1 << (lg_xmit->blk_size + 4)), - (1 << (block.szx + 4)), - (lg_xmit->offset + chunk) % ((size_t)1 << (block.szx + 4))); } } track_echo(session, rcvd); @@ -3845,13 +3979,13 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, if (block.bert) block.num += (unsigned int)(lg_xmit->b.b1.bert_size / 1024 - 1); lg_xmit->last_block = block.num; - lg_xmit->offset = (block.num + 1) * chunk; - if (lg_xmit->offset < lg_xmit->data_info->length) { + if ((block.num + 1) * chunk < lg_xmit->data_info->length) { /* Build the next PDU request based off the skeletal PDU */ uint8_t buf[8]; coap_pdu_t *pdu; uint64_t token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token, ++lg_xmit->b.b1.count); size_t len = coap_encode_var_safe8(buf, sizeof(token), token); + size_t offset; if (lg_xmit->sent_pdu->code == COAP_REQUEST_CODE_FETCH) { /* Need to handle Observe for large FETCH */ @@ -3884,14 +4018,15 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, pdu->type = COAP_MESSAGE_CON; } + offset = block.num * chunk; block.num++; if (block.bert) { size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; - block.m = (lg_xmit->data_info->length - lg_xmit->offset) > + block.m = (lg_xmit->data_info->length - offset) > ((pdu->max_size - token_options) /1024) * 1024; } else { - block.m = (lg_xmit->offset + chunk) < lg_xmit->data_info->length; + block.m = (offset + chunk) < lg_xmit->data_info->length; } coap_update_option(pdu, lg_xmit->option, coap_encode_var_safe(buf, sizeof(buf), @@ -4006,11 +4141,6 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, /* Need to decode CBOR to work out what blocks to re-send */ const uint8_t *bp = data; uint32_t i; - uint8_t buf[8]; - coap_pdu_t *pdu; - uint64_t token; - uint8_t ltoken[8]; - size_t ltoken_length; for (i = 0; (bp < data + length) && i < COAP_MAX_PAYLOADS(session); i++) { @@ -4023,29 +4153,13 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, block.m = (block.num + 1) * chunk < lg_xmit->data_info->length; block.szx = lg_xmit->blk_size; - /* Build the next PDU request based off the skeletal PDU */ - token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count); - ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token); - pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, ltoken_length, - ltoken, NULL, COAP_BOOL_FALSE); - if (!pdu) - goto fail_body; - - coap_update_option(pdu, lg_xmit->option, - coap_encode_var_safe(buf, sizeof(buf), - (block.num << 4) | - (block.m << 3) | - block.szx), - buf); - - if (!coap_add_block(pdu, - lg_xmit->data_info->length, - lg_xmit->data_info->data, - block.num, - block.szx)) - goto fail_body; - if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID) - goto fail_body; + blocks_add_entry(&lg_xmit->send_blocks, block.num, block.m); + } + if (lg_xmit->send_blocks.used) { + /* Flush out the first one */ + block.num = lg_xmit->send_blocks.range[0].begin; + block.m = (block.num + 1) * chunk < lg_xmit->data_info->length; + coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU); } goto skip_app_handler; } @@ -4420,7 +4534,7 @@ coap_handle_response_get_block(coap_context_t *context, lg_crcv->rec_blocks.latest_payload_set = this_payload_set; #endif /* COAP_Q_BLOCK_SUPPORT */ /* Update list of blocks received */ - if (!update_received_blocks(&lg_crcv->rec_blocks, block.num, block.m)) { + if (!blocks_add_entry(&lg_crcv->rec_blocks, block.num, block.m)) { coap_handle_event_lkd(context, COAP_EVENT_PARTIAL_BLOCK, session); goto fail_resp; } diff --git a/src/coap_debug.c b/src/coap_debug.c index 7c74a39baa..afe8338d62 100644 --- a/src/coap_debug.c +++ b/src/coap_debug.c @@ -1467,7 +1467,7 @@ coap_debug_send_packet(void) { for (i = 0; i < num_packet_loss_intervals; i++) { if (send_packet_count >= packet_loss_intervals[i].start && send_packet_count <= packet_loss_intervals[i].end) { - coap_log_debug("Packet %u dropped\n", send_packet_count); + coap_log_debug("Following packet no %u dropped\n", send_packet_count); return 0; } } @@ -1476,7 +1476,7 @@ coap_debug_send_packet(void) { uint16_t r = 0; coap_prng_lkd((uint8_t *)&r, 2); if (r < packet_loss_level) { - coap_log_debug("Packet %u dropped\n", send_packet_count); + coap_log_debug("Following packet no %u dropped\n", send_packet_count); return 0; } } @@ -1485,7 +1485,7 @@ coap_debug_send_packet(void) { for (i = 0; i < num_packet_fail_intervals; i++) { if (send_packet_count >= packet_fail_intervals[i].start && send_packet_count <= packet_fail_intervals[i].end) { - coap_log_debug("Packet %u failed\n", send_packet_count); + coap_log_debug("Following packet no %u failed\n", send_packet_count); errno = ECONNREFUSED; return -1; } @@ -1495,7 +1495,7 @@ coap_debug_send_packet(void) { uint16_t r = 0; coap_prng_lkd((uint8_t *)&r, 2); if (r < packet_fail_level) { - coap_log_debug("Packet %u failed\n", send_packet_count); + coap_log_debug("Following packet no %u failed\n", send_packet_count); errno = ECONNREFUSED; return -1; } diff --git a/src/coap_net.c b/src/coap_net.c index bca9645217..8b9d2bf93c 100644 --- a/src/coap_net.c +++ b/src/coap_net.c @@ -103,6 +103,8 @@ /** creates a Qx.FRAC_BITS from session's 'ack_timeout' */ #define ACK_TIMEOUT Q(FRAC_BITS, session->ack_timeout) +static int send_recv_terminate = 0; + COAP_STATIC_INLINE coap_queue_t * coap_malloc_node(void) { return (coap_queue_t *)coap_malloc_type(COAP_NODE, sizeof(coap_queue_t)); @@ -2046,9 +2048,22 @@ coap_send_internal(coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *request if (!session->is_rate_limiting) { coap_ticks(&now); +#if (COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_DEBUG) + if (now - session->last_tx < session->rl_ticks_per_packet) { + uint32_t rem = (uint32_t)(session->rl_ticks_per_packet - + (now - session->last_tx)) * 1000 / COAP_TICKS_PER_SECOND; + coap_log_debug("** %s: mid 0x%04x: delaying transmission (%d.%03ds)\n", + coap_session_str(session), pdu->mid, rem / 1000, rem %1000); + coap_show_pdu(COAP_LOG_DEBUG, pdu); + } +#endif /* COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_DEBUG */ while (1) { uint32_t timeout_ms; + if (send_recv_terminate) { + goto error; + } + if (now - session->last_tx >= session->rl_ticks_per_packet) { break; } @@ -2058,11 +2073,14 @@ coap_send_internal(coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *request if (timeout_ms == 0) { timeout_ms = COAP_IO_NO_WAIT; } + session->is_rate_limiting = 1; coap_io_process_lkd(session->context, timeout_ms); session->is_rate_limiting = 0; coap_ticks(&now); } + coap_log_debug("** %s: mid 0x%04x: now transmitting\n", + coap_session_str(session), pdu->mid); session->last_tx = now; } } @@ -2215,8 +2233,6 @@ coap_send_internal(coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *request return COAP_INVALID_MID; } -static int send_recv_terminate = 0; - void coap_send_recv_terminate(void) { send_recv_terminate = 1; @@ -4222,6 +4238,10 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu /* Use this to delay transmission */ coap_wait_ack(session->context, session, node); } + } else if (COAP_PDU_IS_EMPTY(response) && + (response->type == COAP_MESSAGE_NON || + COAP_PROTO_RELIABLE(session->proto))) { + coap_delete_pdu_lkd(response); } else { coap_log_debug(" %s: mid=0x%04x: response dropped\n", coap_session_str(session),