diff options
author | Vinicius Costa Gomes <vinicius.gomes@intel.com> | 2016-09-14 11:39:15 -0300 |
---|---|---|
committer | Anas Nashif <nashif@linux.intel.com> | 2016-10-25 12:56:51 +0000 |
commit | b7833b9851863b64416b39c93d4540b7ff152bdb (patch) | |
tree | 27344e17241774b1b3d10ba1b9dc03275f8e51ce /lib | |
parent | 669d581e0adb05209eb342b06968d372af194ffc (diff) |
iot/zoap: Add support for block sized transfers
This will add basic support for sending bodies of data that exceed the
size of a single UDP packet.
Block-wise transfers are defined in the recently adopted RFC 7959[1].
The RFC defines four options, two negotiating the block transfer, and
two for informing the size of the transfer. Depending whether the packet
is a request or a response, each option is defined as
"control" (informative) or descriptive (describes what's in the
payload).
Change-Id: Ic71275558c4afed0298d20e8712f76d53904f89f
Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/iot/zoap/zoap.c | 216 | ||||
-rw-r--r-- | lib/iot/zoap/zoap.h | 89 |
2 files changed, 304 insertions, 1 deletions
diff --git a/lib/iot/zoap/zoap.c b/lib/iot/zoap/zoap.c index d50b00410..2e2d90d9e 100644 --- a/lib/iot/zoap/zoap.c +++ b/lib/iot/zoap/zoap.c @@ -1028,3 +1028,219 @@ void zoap_header_set_id(struct zoap_packet *pkt, uint16_t id) sys_put_be16(id, &appdata[2]); } + +int zoap_block_transfer_init(struct zoap_block_context *ctx, + enum zoap_block_size block_size, + size_t total_size) +{ + ctx->block_size = block_size; + ctx->total_size = total_size; + ctx->current = 0; + + return 0; +} + +#define GET_BLOCK_SIZE(v) (((v) & 0x7)) +#define GET_MORE(v) (!!((v) & 0x08)) +#define GET_NUM(v) ((v) >> 4) + +#define SET_BLOCK_SIZE(v, b) (v |= ((b) & 0x07)) +#define SET_MORE(v, m) ((v) |= (m) ? 0x08 : 0x00) +#define SET_NUM(v, n) ((v) |= ((n) << 4)) + +static bool is_request(struct zoap_packet *pkt) +{ + uint8_t code = zoap_header_get_code(pkt); + + return !(code & ~ZOAP_REQUEST_MASK); +} + +int zoap_add_block1_option(struct zoap_packet *pkt, + struct zoap_block_context *ctx) +{ + uint16_t bytes = zoap_block_size_to_bytes(ctx->block_size); + unsigned int val = 0; + int r; + + if (is_request(pkt)) { + SET_BLOCK_SIZE(val, ctx->block_size); + SET_MORE(val, ctx->current + bytes < ctx->total_size); + SET_NUM(val, ctx->current / bytes); + } else { + SET_BLOCK_SIZE(val, ctx->block_size); + SET_NUM(val, ctx->current / bytes); + } + + r = zoap_add_option_int(pkt, ZOAP_OPTION_BLOCK1, val); + + return r; +} + +int zoap_add_block2_option(struct zoap_packet *pkt, + struct zoap_block_context *ctx) +{ + int r, val = 0; + uint16_t bytes = zoap_block_size_to_bytes(ctx->block_size); + + if (is_request(pkt)) { + SET_BLOCK_SIZE(val, ctx->block_size); + SET_NUM(val, ctx->current / bytes); + } else { + SET_BLOCK_SIZE(val, ctx->block_size); + SET_MORE(val, ctx->current + bytes < ctx->total_size); + SET_NUM(val, ctx->current / bytes); + } + + r = zoap_add_option_int(pkt, ZOAP_OPTION_BLOCK2, val); + + return r; +} + +int zoap_add_size1_option(struct zoap_packet *pkt, + struct zoap_block_context *ctx) +{ + return zoap_add_option_int(pkt, ZOAP_OPTION_SIZE1, ctx->total_size); +} + +int zoap_add_size2_option(struct zoap_packet *pkt, + struct zoap_block_context *ctx) +{ + return zoap_add_option_int(pkt, ZOAP_OPTION_SIZE2, ctx->total_size); +} + +struct block_transfer { + int num; + int block_size; + bool more; +}; + +static unsigned int get_block_option(struct zoap_packet *pkt, uint16_t code) +{ + struct zoap_option option; + unsigned int val; + int count = 1; + + count = zoap_find_options(pkt, code, &option, count); + if (count <= 0) + return 0; + + val = zoap_option_value_to_int(&option); + + return val; +} + +static int update_descriptive_block(struct zoap_block_context *ctx, + int block, int size) +{ + size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4); + + if (block == 0) { + return -ENOENT; + } + + if (size && ctx->total_size && ctx->total_size != size) { + return -EINVAL; + } + + if (ctx->block_size > 0 && GET_BLOCK_SIZE(block) > ctx->block_size) { + return -EINVAL; + } + + if (ctx->total_size && new_current > ctx->total_size) { + return -EINVAL; + } + + if (size) { + ctx->total_size = size; + } + ctx->current = new_current; + ctx->block_size = GET_BLOCK_SIZE(block); + + return 0; +} + +static int update_control_block1(struct zoap_block_context *ctx, + int block, int size) +{ + size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4); + + if (block == 0) { + return 0; + } + + if (new_current != ctx->current) { + return -EINVAL; + } + + if (GET_BLOCK_SIZE(block) > ctx->block_size) { + return -EINVAL; + } + + ctx->block_size = GET_BLOCK_SIZE(block); + ctx->total_size = size; + + return 0; +} + +static int update_control_block2(struct zoap_block_context *ctx, + int block, int size) +{ + size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4); + + if (block == 0) { + return 0; + } + + if (GET_MORE(block)) { + return -EINVAL; + } + + if (GET_NUM(block) > 0 && GET_BLOCK_SIZE(block) != ctx->block_size) { + return -EINVAL; + } + + ctx->current = new_current; + ctx->block_size = GET_BLOCK_SIZE(block); + ctx->total_size = size; + + return 0; +} + +int zoap_update_from_block(struct zoap_packet *pkt, + struct zoap_block_context *ctx) +{ + unsigned int block1, block2, size1, size2; + int r; + + block1 = get_block_option(pkt, ZOAP_OPTION_BLOCK1); + block2 = get_block_option(pkt, ZOAP_OPTION_BLOCK2); + size1 = get_block_option(pkt, ZOAP_OPTION_SIZE1); + size2 = get_block_option(pkt, ZOAP_OPTION_SIZE2); + + if (is_request(pkt)) { + r = update_control_block2(ctx, block2, size2); + if (r) { + return r; + } + + return update_descriptive_block(ctx, block1, size1); + } + + r = update_control_block1(ctx, block1, size1); + if (r) { + return r; + } + + return update_descriptive_block(ctx, block2, size2); +} + +size_t zoap_next_block(struct zoap_block_context *ctx) +{ + if (ctx->current >= ctx->total_size) { + return 0; + } + + ctx->current += zoap_block_size_to_bytes(ctx->block_size); + + return ctx->current; +} diff --git a/lib/iot/zoap/zoap.h b/lib/iot/zoap/zoap.h index 6f0b32e77..d5e518cfe 100644 --- a/lib/iot/zoap/zoap.h +++ b/lib/iot/zoap/zoap.h @@ -54,8 +54,12 @@ enum zoap_option_num { ZOAP_OPTION_URI_QUERY = 15, ZOAP_OPTION_ACCEPT = 17, ZOAP_OPTION_LOCATION_QUERY = 20, + ZOAP_OPTION_BLOCK2 = 23, + ZOAP_OPTION_BLOCK1 = 27, + ZOAP_OPTION_SIZE2 = 28, ZOAP_OPTION_PROXY_URI = 35, - ZOAP_OPTION_PROXY_SCHEME = 39 + ZOAP_OPTION_PROXY_SCHEME = 39, + ZOAP_OPTION_SIZE1 = 60, }; /** @@ -127,6 +131,7 @@ enum zoap_response_code { ZOAP_RESPONSE_CODE_NOT_FOUND = zoap_make_response_code(4, 4), ZOAP_RESPONSE_CODE_NOT_ALLOWED = zoap_make_response_code(4, 5), ZOAP_RESPONSE_CODE_NOT_ACCEPTABLE = zoap_make_response_code(4, 6), + ZOAP_RESPONSE_CODE_INCOMPLETE = zoap_make_response_code(4, 8), ZOAP_RESPONSE_CODE_PRECONDITION_FAILED = zoap_make_response_code(4, 12), ZOAP_RESPONSE_CODE_REQUEST_TOO_LARGE = zoap_make_response_code(4, 13), ZOAP_RESPONSE_CODE_INTERNAL_ERROR = zoap_make_response_code(5, 0), @@ -403,6 +408,88 @@ int zoap_find_options(const struct zoap_packet *pkt, uint16_t code, struct zoap_option *options, uint16_t veclen); /** + * Represents the size of each block that will be transferred using + * block-wise transfers [RFC7959]: + * + * Each entry maps directly to the value that is used in the wire. + * + * https://tools.ietf.org/html/rfc7959 + */ +enum zoap_block_size { + ZOAP_BLOCK_16, + ZOAP_BLOCK_32, + ZOAP_BLOCK_64, + ZOAP_BLOCK_128, + ZOAP_BLOCK_256, + ZOAP_BLOCK_512, + ZOAP_BLOCK_1024, +}; + +/** + * Helper for converting the enumeration to the size expressed in bytes. + */ +static inline uint16_t zoap_block_size_to_bytes( + enum zoap_block_size block_size) +{ + return (1 << (block_size + 4)); +} + +/** + * Represents the current state of a block-wise transaction. + */ +struct zoap_block_context { + size_t total_size; + size_t current; + enum zoap_block_size block_size; +}; + +/** + * Initializes the context of a block-wise transfer. + */ +int zoap_block_transfer_init(struct zoap_block_context *ctx, + enum zoap_block_size block_size, + size_t total_size); + +/** + * Add BLOCK1 option to the packet. + */ +int zoap_add_block1_option(struct zoap_packet *pkt, + struct zoap_block_context *ctx); + +/** + * Add BLOCK2 option to the packet. + */ +int zoap_add_block2_option(struct zoap_packet *pkt, + struct zoap_block_context *ctx); + +/** + * Add SIZE1 option to the packet. + */ +int zoap_add_size1_option(struct zoap_packet *pkt, + struct zoap_block_context *ctx); + +/** + * Add SIZE2 option to the packet. + */ +int zoap_add_size2_option(struct zoap_packet *pkt, + struct zoap_block_context *ctx); + +/** + * Retrieves BLOCK{1,2} and SIZE{1,2} from @a pkt and updates + * @a ctx accordingly. + * + * Returns an error if the packet contains invalid options. + */ +int zoap_update_from_block(struct zoap_packet *pkt, + struct zoap_block_context *ctx); +/** + * Updates @a ctx so after this is called the current entry + * indicates the correct offset in the body of data being + * transferred. + */ +size_t zoap_next_block(struct zoap_block_context *ctx); + +/** * Returns the version present in a CoAP packet. */ uint8_t zoap_header_get_version(const struct zoap_packet *pkt); |