From ac9009e1a15be3db621a4282e2cedab2270343fa Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Thu, 23 Nov 2023 10:20:43 +0000 Subject: [PATCH] coap_block.c: Support server setting max block size for Block1/Block2 --- examples/coap-client.c | 14 +++--- examples/coap-server.c | 18 +++++-- include/coap3/coap_block.h | 37 ++++++++++++++ include/coap3/coap_threadsafe_internal.h | 3 ++ libcoap-3.map | 1 + libcoap-3.sym | 1 + man/coap-client.txt.in | 10 ++-- man/coap-server.txt.in | 7 ++- man/coap_block.txt.in | 12 +++++ src/coap_block.c | 64 ++++++++++++++++++++---- src/coap_threadsafe.c | 11 ++++ 11 files changed, 150 insertions(+), 28 deletions(-) diff --git a/examples/coap-client.c b/examples/coap-client.c index 3d01321a38..3ce9f7df20 100644 --- a/examples/coap-client.c +++ b/examples/coap-client.c @@ -509,7 +509,7 @@ usage(const char *program, const char *version) { "General Options\n" "\t-a addr\t\tThe local interface address to use\n" "\t-b [num,]size\tBlock size to be used in GET/PUT/POST requests\n" - "\t \t\t(value must be a multiple of 16 not larger than 1024)\n" + "\t \t\t(value must be 16, 32, 64, 128, 256, 512 or 1024)\n" "\t \t\tIf num is present, the request chain will start at\n" "\t \t\tblock num\n" "\t-e text\t\tInclude text as payload (use percent-encoding for\n" @@ -591,7 +591,8 @@ usage(const char *program, const char *version) { "\t \t\tcertificate in '-c certfile' if the parameter is\n" "\t \t\tdifferent from certfile in '-c certfile'\n" "\t-n \t\tDisable remote peer certificate checking\n" - "\t-C cafile\tPEM file or PKCS11 URI for the CA certificate that was\n" + "\t-C cafile\tPEM file or PKCS11 URI for the CA certificate and any\n" + "\t \t\tintermediate CAs that was\n" "\t \t\tused to sign the server certfile. Ideally the client\n" "\t \t\tcertificate should be signed by the same CA so that\n" "\t \t\tmutual authentication can take place. The contents of\n" @@ -612,7 +613,7 @@ usage(const char *program, const char *version) { "\t \t\tUsing '-R trust_casfile' disables common CA mutual\n" "\t \t\tauthentication which can only be done by using\n" "\t \t\t'-C cafile'.\n" - "\t \t\tUsing the -C or -R options will will trigger the\n" + "\t \t\tUsing the -C or -R options will trigger the\n" "\t \t\tvalidation of the server certificate unless overridden\n" "\t \t\tby the -n option\n" ); @@ -888,8 +889,8 @@ cmdline_blocksize(char *arg) { } else if (size > 1024) { coap_log_warn("Maximum block size is 1024\n"); return 0; - } else if ((size % 16) != 0) { - coap_log_warn("Block size %u is not a multiple of 16\n", size); + } else if (size != ((1 << (coap_fls(size >> 4) - 1) << 4))) { + coap_log_warn("Block size %u invalid\n", size); return 0; } if (size) @@ -1617,7 +1618,8 @@ main(int argc, char **argv) { node_str[NI_MAXHOST - 1] = '\0'; break; case 'b': - cmdline_blocksize(optarg); + if (!cmdline_blocksize(optarg)) + goto failed; break; case 'B': wait_seconds = atoi(optarg); diff --git a/examples/coap-server.c b/examples/coap-server.c index b1c07be01a..ad72047bc4 100644 --- a/examples/coap-server.c +++ b/examples/coap-server.c @@ -2132,10 +2132,10 @@ usage(const char *program, const char *version) { coap_string_tls_version(buffer, sizeof(buffer))); fprintf(stderr, "%s\n", coap_string_tls_support(buffer, sizeof(buffer))); fprintf(stderr, "\n" - "Usage: %s [-a priority] [-d max] [-e] [-g group] [-l loss] [-p port]\n" - "\t\t[-r] [-v num] [-w [port][,secure_port]] [-A address]\n" - "\t\t[-E oscore_conf_file[,seq_file]] [-G group_if] [-L value] [-N]\n" - "\t\t[-P scheme://address[:port],[name1[,name2..]]]\n" + "Usage: %s [-a priority] [-b max_block_size] [-d max] [-e] [-g group]\n" + "\t\t[-l loss] [-p port] [-r] [-v num] [-w [port][,secure_port]]\n" + "\t\t[-A address] [-E oscore_conf_file[,seq_file]] [-G group_if]\n" + "\t\t[-L value] [-N] [-P scheme://address[:port],[name1[,name2..]]]\n" "\t\t[-T max_token_size] [-U type] [-V num] [-X size]\n" "\t\t[[-h hint] [-i match_identity_file] [-k key]\n" "\t\t[-s match_psk_sni_file] [-u user]]\n" @@ -2144,6 +2144,9 @@ usage(const char *program, const char *version) { "\t\t[-S match_pki_sni_file]]\n" "General Options\n" "\t-a priority\tSend logging output to syslog at priority (0-7) level\n" + "\t-b max_block_size\n" + "\t \t\tMaximum block size server supports (16, 32, 64,\n" + "\t \t\t128, 256, 512 or 1024) in bytes\n" "\t-d max \t\tAllow dynamic creation of up to a total of max\n" "\t \t\tresources. If max is reached, a 4.06 code is returned\n" "\t \t\tuntil one of the dynamic resources has been deleted\n" @@ -2742,6 +2745,7 @@ main(int argc, char **argv) { int nfds = 0; size_t i; int exit_code = 0; + uint32_t max_block_size = 0; #ifndef _WIN32 int use_syslog = 0; #endif /* ! _WIN32 */ @@ -2762,7 +2766,7 @@ main(int argc, char **argv) { clock_offset = time(NULL); while ((opt = getopt(argc, argv, - "a:c:d:eg:G:h:i:j:J:k:l:mnp:rs:tu:v:w:A:C:E:L:M:NP:R:S:T:U:V:X:")) != -1) { + "a:b:c:d:eg:G:h:i:j:J:k:l:mnp:rs:tu:v:w:A:C:E:L:M:NP:R:S:T:U:V:X:")) != -1) { switch (opt) { #ifndef _WIN32 case 'a': @@ -2776,6 +2780,9 @@ main(int argc, char **argv) { strncpy(addr_str, optarg, NI_MAXHOST-1); addr_str[NI_MAXHOST - 1] = '\0'; break; + case 'b': + max_block_size = atoi(optarg); + break; case 'c' : cert_file = optarg; break; @@ -2961,6 +2968,7 @@ main(int argc, char **argv) { if (mcast_per_resource) coap_mcast_per_resource(ctx); coap_context_set_block_mode(ctx, block_mode); + coap_context_set_max_block_size(ctx, max_block_size); if (csm_max_message_size) coap_context_set_csm_max_message_size(ctx, csm_max_message_size); if (doing_oscore) { diff --git a/include/coap3/coap_block.h b/include/coap3/coap_block.h index 92bd99c55d..51d17be2af 100644 --- a/include/coap3/coap_block.h +++ b/include/coap3/coap_block.h @@ -65,6 +65,27 @@ typedef struct { #define COAP_BLOCK_NO_PREEMPTIVE_RTAG 0x10 /* (cl) Don't use pre-emptive Request-Tags */ #define COAP_BLOCK_STLESS_FETCH 0x20 /* (cl) Assume server supports stateless FETCH */ #define COAP_BLOCK_STLESS_BLOCK2 0x40 /* (svr)Server is stateless for handling Block2 */ + +#if COAP_Q_BLOCK_SUPPORT +#define COAP_BLOCK_SET_MASK (COAP_BLOCK_USE_LIBCOAP | \ + COAP_BLOCK_SINGLE_BODY | \ + COAP_BLOCK_TRY_Q_BLOCK | \ + COAP_BLOCK_USE_M_Q_BLOCK | \ + COAP_BLOCK_NO_PREEMPTIVE_RTAG | \ + COAP_BLOCK_STLESS_FETCH | \ + COAP_BLOCK_STLESS_BLOCK2) +#else /* ! COAP_Q_BLOCK_SUPPORT */ +#define COAP_BLOCK_SET_MASK (COAP_BLOCK_USE_LIBCOAP | \ + COAP_BLOCK_SINGLE_BODY | \ + COAP_BLOCK_NO_PREEMPTIVE_RTAG | \ + COAP_BLOCK_STLESS_FETCH | \ + COAP_BLOCK_STLESS_BLOCK2) +#endif /* ! COAP_Q_BLOCK_SUPPORT */ + +#define COAP_BLOCK_MAX_SIZE_MASK 0x700 /* (svr)Mask to get the max supported block size */ +#define COAP_BLOCK_MAX_SIZE_SHIFT 8 /* (svr)Mask shift to get the max supported block size */ +#define COAP_BLOCK_MAX_SIZE_GET(a) (((a) & COAP_BLOCK_MAX_SIZE_MASK) >> COAP_BLOCK_MAX_SIZE_SHIFT) +#define COAP_BLOCK_MAX_SIZE_SET(a) (((a) << COAP_BLOCK_MAX_SIZE_SHIFT) & COAP_BLOCK_MAX_SIZE_MASK) /* Note 0x4000 and 0x8000 are internally defined elsewhere */ /** @@ -424,6 +445,22 @@ int coap_add_data_large_response(coap_resource_t *resource, void coap_context_set_block_mode(coap_context_t *context, uint32_t block_mode); +/** + * Set the context level maximum block size that the server supports when sending + * or receiving packets with Block1 or Block2 options. + * This maximum block size flows down to a session when a session is created. + * + * Note: This function must be called before the session is set up. + * + * Note: COAP_BLOCK_USE_LIBCOAP must be set using coap_context_set_block_mode() + * if libcoap is to do this work. + * + * @param context The coap_context_t object. + * @param max_block_size The maximum block size a server supports. Can be 0 + * (reset), or must be 16, 32, 64, 128, 256, 512 or 1024. + */ +int coap_context_set_max_block_size(coap_context_t *context, size_t max_block_size); + /** * Cancel an observe that is being tracked by the client large receive logic. * (coap_context_set_block_mode() has to be called) diff --git a/include/coap3/coap_threadsafe_internal.h b/include/coap3/coap_threadsafe_internal.h index 62ffaf76f6..c2356a0df5 100644 --- a/include/coap3/coap_threadsafe_internal.h +++ b/include/coap3/coap_threadsafe_internal.h @@ -35,6 +35,7 @@ #define coap_check_notify(s) coap_check_notify_locked(s) #define coap_context_oscore_server(c,o) coap_context_oscore_server_locked(c,o) #define coap_context_set_block_mode(c,b) coap_context_set_block_mode_locked(c,b) +#define coap_context_set_max_block_size(c,m) coap_context_set_max_block_size_locked(c,m) #define coap_context_set_pki(c,s) coap_context_set_pki_locked(c,s) #define coap_context_set_pki_root_cas(c,f,d) coap_context_set_pki_root_cas_locked(c,f,d) #define coap_context_set_psk(c,h,k,l) coap_context_set_psk_locked(c,h,k,l) @@ -125,6 +126,8 @@ int coap_context_oscore_server_locked(coap_context_t *context, coap_oscore_conf_t *oscore_conf); void coap_context_set_block_mode_locked(coap_context_t *context, uint32_t block_mode); +int coap_context_set_max_block_size_locked(coap_context_t *context, + size_t max_block_size); int coap_context_set_pki_locked(coap_context_t *ctx, const coap_dtls_pki_t *setup_data); int coap_context_set_pki_root_cas_locked(coap_context_t *ctx, diff --git a/libcoap-3.map b/libcoap-3.map index 21f7463ce3..c3b5cefcd5 100644 --- a/libcoap-3.map +++ b/libcoap-3.map @@ -54,6 +54,7 @@ global: coap_context_set_csm_timeout; coap_context_set_csm_timeout_ms; coap_context_set_keepalive; + coap_context_set_max_block_size; coap_context_set_max_handshake_sessions; coap_context_set_max_idle_sessions; coap_context_set_max_token_size; diff --git a/libcoap-3.sym b/libcoap-3.sym index 7bb2fb82c2..7e52f14d45 100644 --- a/libcoap-3.sym +++ b/libcoap-3.sym @@ -52,6 +52,7 @@ coap_context_set_csm_max_message_size coap_context_set_csm_timeout coap_context_set_csm_timeout_ms coap_context_set_keepalive +coap_context_set_max_block_size coap_context_set_max_handshake_sessions coap_context_set_max_idle_sessions coap_context_set_max_token_size diff --git a/man/coap-client.txt.in b/man/coap-client.txt.in index a96cdb5eb8..3231f4c278 100644 --- a/man/coap-client.txt.in +++ b/man/coap-client.txt.in @@ -63,9 +63,8 @@ OPTIONS - General cause "No such device" errors on transmission. *-b* [num,]size:: - The block size to be used in GET/PUT/POST requests (value must be a - multiple of 16 not larger than 1024 as libcoap uses a fixed maximum - PDU size of 1400 bytes). If 'num' is present, the request + The block size to be used in GET/PUT/POST requests (value must be 16, 32, + 64, 128, 256, 512 or 1024). If 'num' is present, the request chain will start at block 'num'. When the server includes a Block2 option in its response to a GET request, coap-client will automatically retrieve the subsequent block from the server until there are no more @@ -232,7 +231,8 @@ definitions have to be in DER, not PEM, format. Otherwise all of Disable remote peer certificate checking. *-C* cafile:: -PEM file or PKCS11 URI for the CA certificate that was used to sign the server +PEM file or PKCS11 URI for the CA certificate and any intermediate CAs that was + used to sign the server certfile. Ideally the client certificate should be signed by the same CA so that mutual authentication can take place. The contents of cafile are added to the trusted store of root CAs. Using the *-C* or *-R* options will trigger @@ -252,7 +252,7 @@ PEM file or PKCS11 URI for the CA certificate that was used to sign the server directory containing a set of CA PEM files. The *-C cafile* CA does not have to be in this list and is trusted for the validation. Using *-R trust_casfile* disables common CA mutual authentication which can only - be done by using *-C cafile*. Using the *-C* or *-R* options will will + be done by using *-C cafile*. Using the *-C* or *-R* options will trigger the validation of the server certificate unless overridden by the *-n* option. diff --git a/man/coap-server.txt.in b/man/coap-server.txt.in index 24e08e6fd6..d09864b09b 100644 --- a/man/coap-server.txt.in +++ b/man/coap-server.txt.in @@ -19,7 +19,8 @@ coap-server-notls SYNOPSIS -------- -*coap-server* [*-a* priority] [*-d* max] [*-e*] [*-g* group] [*-l* loss] [*-p* port] +*coap-server* [*-a* priority] [*-b* max_block_size] [*-d* max] [*-e*] + [*-g* group] [*-l* loss] [*-p* port] [*-r*] [*-t*] [*-v* num] [*-w* [port][,secure_port]] [*-A* address] [*-E* oscore_conf_file[,seq_file]] [*-G* group_if] [*-L* value] [*-N*] @@ -46,6 +47,10 @@ OPTIONS - General *-a* priority:: Send logging output to syslog at 'priority' (0-7) level. +*-b* max_block_size:: + Maximum block size server supports (16, 32, 64, 128, 256, 512 or 1024) in + bytes. + *-d* max:: Enable support for creation of dynamic resources when doing a PUT up to a limit of 'max'. If 'max' is reached, a 4.06 code is returned until one of diff --git a/man/coap_block.txt.in b/man/coap_block.txt.in index 0fea8d55e7..14f28d1dae 100644 --- a/man/coap_block.txt.in +++ b/man/coap_block.txt.in @@ -12,6 +12,7 @@ NAME ---- coap_block, coap_context_set_block_mode, +coap_context_set_max_block_size, coap_add_data_large_request, coap_add_data_large_response, coap_get_data_large, @@ -26,6 +27,9 @@ SYNOPSIS *void coap_context_set_block_mode(coap_context_t *_context_, uint32_t _block_mode_);* +*int coap_context_set_max_block_size(coap_context_t *_context_, +size_t _max_block_size_);* + *int coap_add_data_large_request(coap_session_t *_session_, coap_pdu_t *_pdu_, size_t _length_, const uint8_t *_data_, coap_release_large_data_t _release_func_, void *_app_ptr_);* @@ -209,6 +213,12 @@ for any data returned that uses Block2 to split up the data chunks. The application is called for every request to get the complete set of data which is then split into the separate Block2 responses as appropriate. +*Function: coap_context_set_max_block_size()* + +The *coap_context_set_max_block_size*() function is used to set the +_max_block_size_ in the _context_ that a server supports when the Block1 or +Block2 options are used. This must be set before a server session is created. + *Function: coap_add_data_large_request()* The *coap_add_data_large_request*() function is similar to *coap_add_data*(), @@ -316,6 +326,8 @@ RETURN VALUES *coap_block_build_body*() returns the current state of the body's data (which may have some missing gaps) or NULL on error. +*coap_context_set_max_block_size*() returns 0 on failure, 1 on success. + *coap_q_block_is_supported*() returns 0 on failure, 1 on success. EXAMPLES diff --git a/src/coap_block.c b/src/coap_block.c index 8153f763bd..5fe6544f38 100644 --- a/src/coap_block.c +++ b/src/coap_block.c @@ -379,23 +379,40 @@ void coap_context_set_block_mode(coap_context_t *context, uint32_t block_mode) { coap_lock_check_locked(context); - context->block_mode = (block_mode & (COAP_BLOCK_USE_LIBCOAP | - COAP_BLOCK_SINGLE_BODY | -#if COAP_Q_BLOCK_SUPPORT - COAP_BLOCK_TRY_Q_BLOCK | - COAP_BLOCK_USE_M_Q_BLOCK | -#endif /* COAP_Q_BLOCK_SUPPORT */ - COAP_BLOCK_NO_PREEMPTIVE_RTAG | - COAP_BLOCK_STLESS_FETCH | - COAP_BLOCK_STLESS_BLOCK2)); if (!(block_mode & COAP_BLOCK_USE_LIBCOAP)) - context->block_mode = 0; + block_mode = 0; + context->block_mode &= ~COAP_BLOCK_SET_MASK; + context->block_mode |= block_mode & COAP_BLOCK_SET_MASK; #if ! COAP_Q_BLOCK_SUPPORT if (block_mode & (COAP_BLOCK_TRY_Q_BLOCK|COAP_BLOCK_USE_M_Q_BLOCK)) coap_log_debug("Q-Block support not compiled in - ignored\n"); #endif /* ! COAP_Q_BLOCK_SUPPORT */ } +int +coap_context_set_max_block_size(coap_context_t *context, size_t max_block_size) { + switch (max_block_size) { + case 0: + case 16: + case 32: + case 64: + case 128: + case 256: + case 512: + case 1024: + break; + default: + coap_log_info("coap_context_set_max_block_size: Invalid max block size (%zu)\n", + max_block_size); + return 0; + } + coap_lock_check_locked(context); + max_block_size = (coap_fls((uint32_t)max_block_size >> 4) - 1) & 0x07; + context->block_mode &= ~COAP_BLOCK_MAX_SIZE_MASK; + context->block_mode |= COAP_BLOCK_MAX_SIZE_SET(max_block_size); + return 1; +} + COAP_STATIC_INLINE int full_match(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen) { @@ -616,6 +633,7 @@ coap_add_data_large_internal(coap_session_t *session, uint8_t buf[8]; int have_block_defined = 0; uint8_t blk_size; + uint8_t max_blk_size; uint16_t option; size_t token_options; coap_opt_t *opt; @@ -739,6 +757,10 @@ coap_add_data_large_internal(coap_session_t *session, if (blk_size > 6) blk_size = 6; + max_blk_size = COAP_BLOCK_MAX_SIZE_GET(session->block_mode); + if (max_blk_size && blk_size > max_blk_size) + blk_size = max_blk_size; + /* see if BlockX defined - if so update blk_size as given by app */ if (coap_get_block_b(session, pdu, option, &block)) { if (block.szx < blk_size) @@ -2677,6 +2699,7 @@ coap_handle_request_put_block(coap_context_t *context, &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; + uint32_t max_block_szx; if (length > block.chunk_size) { coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %zu\n", @@ -2734,7 +2757,14 @@ coap_handle_request_put_block(coap_context_t *context, p->uri_path = coap_new_str_const(uri_path->s, uri_path->length); p->content_format = fmt; p->total_len = total; - p->szx = block.szx; + p->amount_so_far = length; + max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode); + if (!block.bert && block.num == 0 && max_block_szx != 0 && + max_block_szx < block.szx) { + p->szx = max_block_szx; + } else { + p->szx = block.szx; + } p->block_option = block_option; if (observe) { p->observe_length = min(coap_opt_length(observe), 3); @@ -2857,6 +2887,12 @@ coap_handle_request_put_block(coap_context_t *context, goto skip_app_handler; } #endif /* COAP_Q_BLOCK_SUPPORT */ + /* Check to see if block size is getting forced down */ + max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode); + if (!block.bert && saved_num == 0 && max_block_szx != 0 && + max_block_szx < block.aszx) { + block.aszx = max_block_szx; + } /* Ask for the next block */ coap_insert_option(response, block_option, coap_encode_var_safe(buf, sizeof(buf), @@ -2912,6 +2948,12 @@ coap_handle_request_put_block(coap_context_t *context, else pdu->body_total = offset + length + block.m; + /* Check to see if block size is getting forced down */ + max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode); + if (!block.bert && block.num == 0 && max_block_szx != 0 && + max_block_szx < block.aszx) { + block.aszx = max_block_szx; + } coap_insert_option(response, block_option, coap_encode_var_safe(buf, sizeof(buf), (block.num << 4) | diff --git a/src/coap_threadsafe.c b/src/coap_threadsafe.c index 25fedbbe34..acfc17b647 100644 --- a/src/coap_threadsafe.c +++ b/src/coap_threadsafe.c @@ -457,6 +457,17 @@ coap_context_set_block_mode(coap_context_t *context, coap_lock_unlock(context); } +int +coap_context_set_max_block_size(coap_context_t *context, + size_t max_block_size) { + int ret; + + coap_lock_lock(context, return 0); + ret = coap_context_set_max_block_size_locked(context, max_block_size); + coap_lock_unlock(context); + return ret; +} + int coap_context_set_pki_root_cas(coap_context_t *ctx, const char *ca_file,