1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #17474 from benpicco/nanocoap-suit

sys/net/nanocoap: introduce `nanocoap_sock_*()`, use in suit/transport/coap
This commit is contained in:
benpicco 2022-02-27 02:07:50 +01:00 committed by GitHub
commit bbfa69153e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 493 additions and 296 deletions

View File

@ -590,6 +590,7 @@ endif
ifneq (,$(filter nanocoap_sock,$(USEMODULE)))
USEMODULE += sock_udp
USEMODULE += sock_util
endif
ifneq (,$(filter nanocoap_%,$(USEMODULE)))

View File

@ -183,16 +183,16 @@ extern "C" {
* @{
*/
/**
* @brief Timeout in seconds for a response to a confirmable request
* @brief Timeout in milliseconds for a response to a confirmable request
*
* This value is for the response to the *initial* confirmable message. The
* timeout doubles for subsequent retries. To avoid synchronization of resends
* across hosts, the actual timeout is chosen randomly between
* @ref CONFIG_COAP_ACK_TIMEOUT and
* (@ref CONFIG_COAP_ACK_TIMEOUT * @ref CONFIG_COAP_RANDOM_FACTOR_1000 / 1000).
* @ref CONFIG_COAP_ACK_TIMEOUT_MS and
* (@ref CONFIG_COAP_ACK_TIMEOUT_MS * @ref CONFIG_COAP_RANDOM_FACTOR_1000 / 1000).
*/
#ifndef CONFIG_COAP_ACK_TIMEOUT
#define CONFIG_COAP_ACK_TIMEOUT (2U)
#ifndef CONFIG_COAP_ACK_TIMEOUT_MS
#define CONFIG_COAP_ACK_TIMEOUT_MS (2000U)
#endif
/**
@ -202,7 +202,7 @@ extern "C" {
* ([RFC 7252, section 4.2](https://tools.ietf.org/html/rfc7252#section-4.2))
* multiplied by 1000, to avoid floating point arithmetic.
*
* See @ref CONFIG_COAP_ACK_TIMEOUT
* See @ref CONFIG_COAP_ACK_TIMEOUT_MS
*/
#ifndef CONFIG_COAP_RANDOM_FACTOR_1000
#define CONFIG_COAP_RANDOM_FACTOR_1000 (1500)
@ -233,6 +233,19 @@ extern "C" {
#define COAP_BLOCKWISE_SZX_MAX (7)
/** @} */
/**
* @brief Coap block-wise-transfer size SZX
*/
typedef enum {
COAP_BLOCKSIZE_16 = 0,
COAP_BLOCKSIZE_32,
COAP_BLOCKSIZE_64,
COAP_BLOCKSIZE_128,
COAP_BLOCKSIZE_256,
COAP_BLOCKSIZE_512,
COAP_BLOCKSIZE_1024,
} coap_blksize_t;
#ifdef __cplusplus
}
#endif

View File

@ -531,7 +531,7 @@ extern "C" {
* In normal operations the timeout between retransmissions doubles. When
* CONFIG_GCOAP_NO_RETRANS_BACKOFF is defined this doubling does not happen.
*
* @see CONFIG_COAP_ACK_TIMEOUT
* @see CONFIG_COAP_ACK_TIMEOUT_MS
*/
#define CONFIG_GCOAP_NO_RETRANS_BACKOFF
#endif
@ -702,7 +702,7 @@ typedef struct gcoap_listener gcoap_listener_t;
*/
typedef int (*gcoap_request_matcher_t)(gcoap_listener_t *listener,
const coap_resource_t **resource,
const coap_pkt_t *pdu);
coap_pkt_t *pdu);
/**
* @brief A modular collection of resources for a server

View File

@ -86,6 +86,7 @@
#include <unistd.h>
#ifdef RIOT_VERSION
#include "bitfield.h"
#include "byteorder.h"
#include "net/coap.h"
#else
@ -150,6 +151,21 @@ extern "C" {
#endif
/** @} */
/**
* @brief Maximum length of a CoAP header for a blockwise message
*/
#ifndef CONFIG_NANOCOAP_BLOCK_HEADER_MAX
#define CONFIG_NANOCOAP_BLOCK_HEADER_MAX (64)
#endif
/**
* @brief Work buffer size for blockwise operation
*
* @param[in] blksize CoAP blocksize
*/
#define NANOCOAP_BLOCKWISE_BUF(blksize) (CONFIG_NANOCOAP_BLOCK_HEADER_MAX \
+ (0x1 << (blksize + 4)))
/**
* @name coap_opt_finish() flag parameter values
*
@ -189,6 +205,7 @@ typedef struct {
uint16_t payload_len; /**< length of payload */
uint16_t options_len; /**< length of options array */
coap_optpos_t options[CONFIG_NANOCOAP_NOPTS_MAX]; /**< option offset array */
BITFIELD(opt_crit, CONFIG_NANOCOAP_NOPTS_MAX); /**< unhandled critical option */
#ifdef MODULE_GCOAP
uint32_t observe_value; /**< observe value */
#endif
@ -210,6 +227,20 @@ typedef struct {
*/
typedef ssize_t (*coap_handler_t)(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *context);
/**
* @brief Coap blockwise request callback descriptor
*
* @param[in] arg Pointer to be passed as arguments to the callback
* @param[in] offset Offset of received data
* @param[in] buf Pointer to the received data
* @param[in] len Length of the received data
* @param[in] more -1 for no option, 0 for last block, 1 for more blocks
*
* @returns 0 on success
* @returns -1 on error
*/
typedef int (*coap_blockwise_cb_t)(void *arg, size_t offset, uint8_t *buf, size_t len, int more);
/**
* @brief Method flag type
*
@ -433,8 +464,30 @@ static inline void coap_hdr_set_type(coap_hdr_t *hdr, unsigned type)
* @name Functions -- Options Read
*
* Read options from a parsed packet.
*
* Packets accessed through @ref coap_find_option or any of the `coap_opt_get_*` functions
* track their access in bit field created at parsing time to enable checking for critical
* options in @ref coap_has_unprocessed_critical_options.
* These functions thus have a side effect, and code that calls them on critical options
* needs to ensure that failure to process the accessed option is propagated into failure
* to process the message.
* For example, a server helper that tries to read the If-None-Match option (which is critical)
* and finds it to be longer than it can process must not return as if no If-None-Match option
* was present, as it has already triggered the side effect of marking the option as processed.
*/
/**@{*/
/**
* @brief Get pointer to an option field by type
*
* @param[in] pkt packet to work on
* @param[in] opt_num the option number to search for
*
* @returns pointer to the option data
* NULL if option number was not found
*/
uint8_t *coap_find_option(coap_pkt_t *pkt, unsigned opt_num);
/**
* @brief Get content type from packet
*
@ -457,7 +510,7 @@ unsigned coap_get_content_type(coap_pkt_t *pkt);
* @return -ENOSPC if option length is greater than 4 octets
* @return -EBADMSG if option value is invalid
*/
int coap_opt_get_uint(const coap_pkt_t *pkt, uint16_t optnum, uint32_t *value);
int coap_opt_get_uint(coap_pkt_t *pkt, uint16_t optnum, uint32_t *value);
/**
* @brief Read a full option as null terminated string into the target buffer
@ -476,7 +529,7 @@ int coap_opt_get_uint(const coap_pkt_t *pkt, uint16_t optnum, uint32_t *value);
* @return -ENOSPC if the complete option does not fit into @p target
* @return nr of bytes written to @p target (including '\0')
*/
ssize_t coap_opt_get_string(const coap_pkt_t *pkt, uint16_t optnum,
ssize_t coap_opt_get_string(coap_pkt_t *pkt, uint16_t optnum,
uint8_t *target, size_t max_len, char separator);
/**
@ -494,7 +547,7 @@ ssize_t coap_opt_get_string(const coap_pkt_t *pkt, uint16_t optnum,
* @returns -ENOSPC if URI option is larger than @p max_len
* @returns nr of bytes written to @p target (including '\0')
*/
static inline ssize_t coap_get_location_path(const coap_pkt_t *pkt,
static inline ssize_t coap_get_location_path(coap_pkt_t *pkt,
uint8_t *target, size_t max_len)
{
return coap_opt_get_string(pkt, COAP_OPT_LOCATION_PATH,
@ -516,7 +569,7 @@ static inline ssize_t coap_get_location_path(const coap_pkt_t *pkt,
* @returns -ENOSPC if URI option is larger than @p max_len
* @returns nr of bytes written to @p target (including '\0')
*/
static inline ssize_t coap_get_location_query(const coap_pkt_t *pkt,
static inline ssize_t coap_get_location_query(coap_pkt_t *pkt,
uint8_t *target, size_t max_len)
{
return coap_opt_get_string(pkt, COAP_OPT_LOCATION_QUERY,
@ -537,7 +590,7 @@ static inline ssize_t coap_get_location_query(const coap_pkt_t *pkt,
* @returns -ENOSPC if URI option is larger than CONFIG_NANOCOAP_URI_MAX
* @returns nr of bytes written to @p target (including '\0')
*/
static inline ssize_t coap_get_uri_path(const coap_pkt_t *pkt, uint8_t *target)
static inline ssize_t coap_get_uri_path(coap_pkt_t *pkt, uint8_t *target)
{
return coap_opt_get_string(pkt, COAP_OPT_URI_PATH, target,
CONFIG_NANOCOAP_URI_MAX, '/');
@ -557,7 +610,7 @@ static inline ssize_t coap_get_uri_path(const coap_pkt_t *pkt, uint8_t *target)
* @returns -ENOSPC if URI option is larger than CONFIG_NANOCOAP_URI_MAX
* @returns nr of bytes written to @p target (including '\0')
*/
static inline ssize_t coap_get_uri_query(const coap_pkt_t *pkt, uint8_t *target)
static inline ssize_t coap_get_uri_query(coap_pkt_t *pkt, uint8_t *target)
{
return coap_opt_get_string(pkt, COAP_OPT_URI_QUERY, target,
CONFIG_NANOCOAP_URI_MAX, '&');
@ -611,7 +664,7 @@ ssize_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt,
* @return -ENOENT if option not found
* @return -EINVAL if option cannot be parsed
*/
ssize_t coap_opt_get_opaque(const coap_pkt_t *pkt, unsigned opt_num, uint8_t **value);
ssize_t coap_opt_get_opaque(coap_pkt_t *pkt, unsigned opt_num, uint8_t **value);
/**@}*/
/**
@ -626,7 +679,7 @@ ssize_t coap_opt_get_opaque(const coap_pkt_t *pkt, unsigned opt_num, uint8_t **v
* @return -ENOENT if Proxy-Uri option not found
* @return -EINVAL if Proxy-Uri option cannot be parsed
*/
static inline ssize_t coap_get_proxy_uri(const coap_pkt_t *pkt, char **target)
static inline ssize_t coap_get_proxy_uri(coap_pkt_t *pkt, char **target)
{
return coap_opt_get_opaque(pkt, COAP_OPT_PROXY_URI, (uint8_t **)target);
}
@ -831,6 +884,25 @@ static inline int coap_get_block2(coap_pkt_t *pkt, coap_block1_t *block)
*/
int coap_get_blockopt(coap_pkt_t *pkt, uint16_t option, uint32_t *blknum, unsigned *szx);
/**
* @brief Check whether any of the packet's options that are critical
*
* (i.e must be understood by the receiver, indicated by a 1 in the option number's least
* significant bit) were not accessed since the packet was parsed.
*
* Call this in a server on requests after all their option processing has happened,
* and stop processing the request if it returns true, returning a 4.02 Bad Option response.
*
* Call this in a client when receiving a response before acting on it;
* consider the response unprocessable if it returns true.
*
* @param[in] pkt pkt to work on
*
* @returns true if any of the options marked as critical at parse time have not been accessed.
* @returns false if there are no critical options, or all have been accessed.
*/
bool coap_has_unprocessed_critical_options(const coap_pkt_t *pkt);
/**
* @brief Helper to decode SZX value to size in bytes
*

View File

@ -139,6 +139,12 @@
extern "C" {
#endif
/**
* @brief nanocoap socket type
*
*/
typedef sock_udp_t nanocoap_sock_t;
/**
* @brief Start a nanocoap server instance
*
@ -153,10 +159,33 @@ extern "C" {
*/
int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize);
/**
* @brief Create a CoAP client socket
*
* @param[out] sock CoAP UDP socket
* @param[in] local Local UDP endpoint, may be NULL
* @param[in] remote remote UDP endpoint
*
* @returns 0 on success
* @returns <0 on error
*/
int nanocoap_sock_connect(nanocoap_sock_t *sock, sock_udp_ep_t *local,
sock_udp_ep_t *remote);
/**
* @brief Close a CoAP client socket
*
* @param[in] sock CoAP UDP socket
*/
static inline void nanocoap_sock_close(nanocoap_sock_t *sock)
{
sock_udp_close(sock);
}
/**
* @brief Simple synchronous CoAP (confirmable) get
*
* @param[in] remote remote UDP endpoint
* @param[in] sock socket to use for the request
* @param[in] path remote path
* @param[out] buf buffer to write response to
* @param[in] len length of @p buffer
@ -164,8 +193,66 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize);
* @returns length of response payload on success
* @returns <0 on error
*/
ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, uint8_t *buf,
size_t len);
ssize_t nanocoap_sock_get(nanocoap_sock_t *sock, const char *path, void *buf,
size_t len);
/**
* @brief Performs a blockwise coap get request on a socket.
*
* This function will fetch the content of the specified resource path via
* block-wise-transfer. A coap_blockwise_cb_t will be called on each received
* block.
*
* @param[in] sock socket to use for the request
* @param[in] path pointer to source path
* @param[in] blksize sender suggested SZX for the COAP block request
* @param[in] work_buf Work buffer, must be `NANOCOAP_BLOCKWISE_BUF(blksize)` bytes
* @param[in] callback callback to be executed on each received block
* @param[in] arg optional function arguments
*
* @returns -EINVAL if an invalid url is provided
* @returns -1 if failed to fetch the url content
* @returns 0 on success
*/
int nanocoap_sock_get_blockwise(nanocoap_sock_t *sock, const char *path,
coap_blksize_t blksize, void *work_buf,
coap_blockwise_cb_t callback, void *arg);
/**
* @brief Performs a blockwise coap get request to the specified url.
*
* This function will fetch the content of the specified resource path via
* block-wise-transfer. A coap_blockwise_cb_t will be called on each received
* block.
*
* @param[in] url Absolute URL pointer to source path (i.e. not containing
* a fragment identifier)
* @param[in] blksize sender suggested SZX for the COAP block request
* @param[in] work_buf Work buffer, must be `NANOCOAP_BLOCKWISE_BUF(blksize)` bytes
* @param[in] callback callback to be executed on each received block
* @param[in] arg optional function arguments
*
* @returns -EINVAL if an invalid url is provided
* @returns -1 if failed to fetch the url content
* @returns 0 on success
*/
int nanocoap_get_blockwise_url(const char *url,
coap_blksize_t blksize, void *work_buf,
coap_blockwise_cb_t callback, void *arg);
/**
* @brief Simple synchronous CoAP request
*
* @param[in] sock socket to use for the request
* @param[in,out] pkt Packet struct containing the request. Is reused for
* the response
* @param[in] len Total length of the buffer associated with the
* request
*
* @returns length of response on success
* @returns <0 on error
*/
ssize_t nanocoap_sock_request(nanocoap_sock_t *sock, coap_pkt_t *pkt, size_t len);
/**
* @brief Simple synchronous CoAP request
@ -183,6 +270,20 @@ ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, uint8_t *buf,
ssize_t nanocoap_request(coap_pkt_t *pkt, sock_udp_ep_t *local,
sock_udp_ep_t *remote, size_t len);
/**
* @brief Simple synchronous CoAP (confirmable) get
*
* @param[in] remote remote UDP endpoint
* @param[in] path remote path
* @param[out] buf buffer to write response to
* @param[in] len length of @p buffer
*
* @returns length of response payload on success
* @returns <0 on error
*/
ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, void *buf,
size_t len);
#ifdef __cplusplus
}
#endif

View File

@ -90,37 +90,11 @@ typedef const struct {
const size_t resources_numof; /**< nr of entries in array */
} coap_resource_subtree_t;
/**
* @brief Coap blockwise request callback descriptor
*
* @param[in] arg Pointer to be passed as arguments to the callback
* @param[in] offset Offset of received data
* @param[in] buf Pointer to the received data
* @param[in] len Length of the received data
* @param[in] more -1 for no option, 0 for last block, 1 for more blocks
*
* @returns 0 on success
* @returns -1 on error
*/
typedef int (*coap_blockwise_cb_t)(void *arg, size_t offset, uint8_t *buf, size_t len, int more);
/**
* @brief Reference to the coap resource subtree
*/
extern const coap_resource_subtree_t coap_resource_subtree_suit;
/**
* @brief Coap block-wise-transfer size SZX
*/
typedef enum {
COAP_BLOCKSIZE_32 = 1,
COAP_BLOCKSIZE_64,
COAP_BLOCKSIZE_128,
COAP_BLOCKSIZE_256,
COAP_BLOCKSIZE_512,
COAP_BLOCKSIZE_1024,
} coap_blksize_t;
/**
* @brief Coap block-wise-transfer size used for SUIT
*/
@ -128,26 +102,6 @@ typedef enum {
#define CONFIG_SUIT_COAP_BLOCKSIZE COAP_BLOCKSIZE_64
#endif
/**
* @brief Performs a blockwise coap get request to the specified url.
*
* This function will fetch the content of the specified resource path via
* block-wise-transfer. A coap_blockwise_cb_t will be called on each received
* block.
*
* @param[in] url url pointer to source path
* @param[in] blksize sender suggested SZX for the COAP block request
* @param[in] callback callback to be executed on each received block
* @param[in] arg optional function arguments
*
* @returns -EINVAL if an invalid url is provided
* @returns -1 if failed to fetch the url content
* @returns 0 on success
*/
int suit_coap_get_blockwise_url(const char *url,
coap_blksize_t blksize,
coap_blockwise_cb_t callback, void *arg);
/**
* @brief Trigger a SUIT udate
*

View File

@ -18,15 +18,15 @@ menuconfig KCONFIG_COAP
if KCONFIG_COAP
config COAP_ACK_TIMEOUT
int "Timeout in seconds for a response to a confirmable request"
default 2
config COAP_ACK_TIMEOUT_MS
int "Timeout in milliseconds for a response to a confirmable request"
default 2000
help
This value is for the response to the initial confirmable message. The
timeout doubles for subsequent retries. To avoid synchronization of
resends across hosts, the actual timeout is chosen randomly between
@ref CONFIG_COAP_ACK_TIMEOUT and
(@ref CONFIG_COAP_ACK_TIMEOUT * @ref CONFIG_COAP_RANDOM_FACTOR_1000 / 1000).
@ref CONFIG_COAP_ACK_TIMEOUT_MS and
(@ref CONFIG_COAP_ACK_TIMEOUT_MS * @ref CONFIG_COAP_RANDOM_FACTOR_1000 / 1000).
The default of 2 seconds is taken from
[RFC 7252, section 4.8](https://tools.ietf.org/html/rfc7252#section-4.8).

View File

@ -47,7 +47,7 @@
#define NO_IMMEDIATE_REPLY (-1)
/* End of the range to pick a random timeout */
#define TIMEOUT_RANGE_END (CONFIG_COAP_ACK_TIMEOUT * CONFIG_COAP_RANDOM_FACTOR_1000 / 1000)
#define TIMEOUT_RANGE_END (CONFIG_COAP_ACK_TIMEOUT_MS * CONFIG_COAP_RANDOM_FACTOR_1000 / 1000)
/* Internal functions */
static void *_event_loop(void *arg);
@ -66,7 +66,7 @@ static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len,
static void _expire_request(gcoap_request_memo_t *memo);
static void _find_req_memo(gcoap_request_memo_t **memo_ptr, coap_pkt_t *pdu,
const sock_udp_ep_t *remote, bool by_mid);
static int _find_resource(const coap_pkt_t *pdu,
static int _find_resource(coap_pkt_t *pdu,
const coap_resource_t **resource_ptr,
gcoap_listener_t **listener_ptr);
static int _find_observer(sock_udp_ep_t **observer, sock_udp_ep_t *remote);
@ -77,7 +77,7 @@ static void _find_obs_memo_resource(gcoap_observe_memo_t **memo,
static int _request_matcher_default(gcoap_listener_t *listener,
const coap_resource_t **resource,
const coap_pkt_t *pdu);
coap_pkt_t *pdu);
#if IS_USED(MODULE_GCOAP_DTLS)
static void _on_sock_dtls_evt(sock_dtls_t *sock, sock_async_flags_t type, void *arg);
@ -472,9 +472,9 @@ static void _on_resp_timeout(void *arg) {
#else
unsigned i = CONFIG_COAP_MAX_RETRANSMIT - memo->send_limit;
#endif
uint32_t timeout = ((uint32_t)CONFIG_COAP_ACK_TIMEOUT << i) * MS_PER_SEC;
uint32_t timeout = (uint32_t)CONFIG_COAP_ACK_TIMEOUT_MS << i;
#if CONFIG_COAP_RANDOM_FACTOR_1000 > 1000
uint32_t end = ((uint32_t)TIMEOUT_RANGE_END << i) * MS_PER_SEC;
uint32_t end = (uint32_t)TIMEOUT_RANGE_END << i;
timeout = random_uint32_range(timeout, end);
#endif
event_timeout_set(&memo->resp_evt_tmout, timeout);
@ -538,7 +538,7 @@ static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len,
gcoap_observe_memo_t *memo = NULL;
gcoap_observe_memo_t *resource_memo = NULL;
switch (_find_resource((const coap_pkt_t *)pdu, &resource, &listener)) {
switch (_find_resource(pdu, &resource, &listener)) {
case GCOAP_RESOURCE_WRONG_METHOD:
return gcoap_response(pdu, buf, len, COAP_CODE_METHOD_NOT_ALLOWED);
case GCOAP_RESOURCE_NO_PATH:
@ -640,7 +640,7 @@ static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len,
static int _request_matcher_default(gcoap_listener_t *listener,
const coap_resource_t **resource,
const coap_pkt_t *pdu)
coap_pkt_t *pdu)
{
uint8_t uri[CONFIG_NANOCOAP_URI_MAX];
int ret = GCOAP_RESOURCE_NO_PATH;
@ -696,7 +696,7 @@ static int _request_matcher_default(gcoap_listener_t *listener,
* code didn't match and `GCOAP_RESOURCE_NO_PATH` if no matching
* resource was found.
*/
static int _find_resource(const coap_pkt_t *pdu,
static int _find_resource(coap_pkt_t *pdu,
const coap_resource_t **resource_ptr,
gcoap_listener_t **listener_ptr)
{
@ -1158,9 +1158,9 @@ ssize_t gcoap_req_send(const uint8_t *buf, size_t len,
}
if (memo->msg.data.pdu_buf) {
memo->send_limit = CONFIG_COAP_MAX_RETRANSMIT;
timeout = (uint32_t)CONFIG_COAP_ACK_TIMEOUT * MS_PER_SEC;
timeout = (uint32_t)CONFIG_COAP_ACK_TIMEOUT_MS;
#if CONFIG_COAP_RANDOM_FACTOR_1000 > 1000
timeout = random_uint32_range(timeout, TIMEOUT_RANGE_END * MS_PER_SEC);
timeout = random_uint32_range(timeout, TIMEOUT_RANGE_END);
#endif
memo->state = GCOAP_MEMO_RETRANSMIT;
}

View File

@ -68,6 +68,7 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
pkt->payload = NULL;
pkt->payload_len = 0;
memset(pkt->opt_crit, 0, sizeof(pkt->opt_crit));
if (len < sizeof(coap_hdr_t)) {
DEBUG("msg too short\n");
@ -126,6 +127,10 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
return -ENOMEM;
}
/* check if option is critical */
if (option_nr & 1) {
bf_set(pkt->opt_crit, option_count);
}
optpos->opt_num = option_nr;
optpos->offset = (uintptr_t)option_start - (uintptr_t)hdr;
DEBUG("optpos option_nr=%u %u\n", (unsigned)option_nr, (unsigned)optpos->offset);
@ -176,13 +181,15 @@ int coap_match_path(const coap_resource_t *resource, uint8_t *uri)
return res;
}
uint8_t *coap_find_option(const coap_pkt_t *pkt, unsigned opt_num)
uint8_t *coap_find_option(coap_pkt_t *pkt, unsigned opt_num)
{
const coap_optpos_t *optpos = pkt->options;
unsigned opt_count = pkt->options_len;
while (opt_count--) {
if (optpos->opt_num == opt_num) {
unsigned idx = index_of(pkt->options, optpos);
bf_unset(pkt->opt_crit, idx);
return (uint8_t*)pkt->hdr + optpos->offset;
}
optpos++;
@ -219,7 +226,7 @@ static uint8_t *_parse_option(const coap_pkt_t *pkt,
return pkt_pos;
}
ssize_t coap_opt_get_opaque(const coap_pkt_t *pkt, unsigned opt_num, uint8_t **value)
ssize_t coap_opt_get_opaque(coap_pkt_t *pkt, unsigned opt_num, uint8_t **value)
{
uint8_t *start = coap_find_option(pkt, opt_num);
if (!start) {
@ -237,7 +244,7 @@ ssize_t coap_opt_get_opaque(const coap_pkt_t *pkt, unsigned opt_num, uint8_t **v
return len;
}
int coap_opt_get_uint(const coap_pkt_t *pkt, uint16_t opt_num, uint32_t *target)
int coap_opt_get_uint(coap_pkt_t *pkt, uint16_t opt_num, uint32_t *target)
{
assert(target);
@ -262,7 +269,7 @@ int coap_opt_get_uint(const coap_pkt_t *pkt, uint16_t opt_num, uint32_t *target)
return -ENOENT;
}
uint8_t *coap_iterate_option(const coap_pkt_t *pkt, uint8_t **optpos,
uint8_t *coap_iterate_option(coap_pkt_t *pkt, uint8_t **optpos,
int *opt_len, int first)
{
uint8_t *data_start;
@ -325,7 +332,7 @@ ssize_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt,
return len;
}
ssize_t coap_opt_get_string(const coap_pkt_t *pkt, uint16_t optnum,
ssize_t coap_opt_get_string(coap_pkt_t *pkt, uint16_t optnum,
uint8_t *target, size_t max_len, char separator)
{
assert(pkt && target && (max_len > 1));
@ -392,6 +399,17 @@ int coap_get_blockopt(coap_pkt_t *pkt, uint16_t option, uint32_t *blknum, unsign
return (blkopt & 0x8) ? 1 : 0;
}
bool coap_has_unprocessed_critical_options(const coap_pkt_t *pkt)
{
for (unsigned i = 0; i < sizeof(pkt->opt_crit); ++i){
if (pkt->opt_crit[i]) {
return true;
}
}
return false;
}
ssize_t coap_handle_req(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_len)
{
if (coap_get_code_class(pkt) != COAP_REQ) {

View File

@ -25,51 +25,69 @@
#include <stdio.h>
#include "net/nanocoap_sock.h"
#include "net/sock/util.h"
#include "net/sock/udp.h"
#include "timex.h"
#define ENABLE_DEBUG 0
#include "debug.h"
ssize_t nanocoap_request(coap_pkt_t *pkt, sock_udp_ep_t *local, sock_udp_ep_t *remote, size_t len)
int nanocoap_sock_connect(nanocoap_sock_t *sock, sock_udp_ep_t *local, sock_udp_ep_t *remote)
{
ssize_t res;
size_t pdu_len = (pkt->payload - (uint8_t *)pkt->hdr) + pkt->payload_len;
uint8_t *buf = (uint8_t*)pkt->hdr;
sock_udp_t sock;
if (!remote->port) {
remote->port = COAP_PORT;
}
res = sock_udp_create(&sock, local, remote, 0);
if (res < 0) {
return res;
}
return sock_udp_create(sock, local, remote, 0);
}
ssize_t nanocoap_sock_request(nanocoap_sock_t *sock, coap_pkt_t *pkt, size_t len)
{
ssize_t res = -EAGAIN;
size_t pdu_len = (pkt->payload - (uint8_t *)pkt->hdr) + pkt->payload_len;
uint8_t *buf = (uint8_t*)pkt->hdr;
uint32_t id = coap_get_id(pkt);
/* TODO: timeout random between between ACK_TIMEOUT and (ACK_TIMEOUT *
* ACK_RANDOM_FACTOR) */
uint32_t timeout = CONFIG_COAP_ACK_TIMEOUT * US_PER_SEC;
unsigned tries_left = CONFIG_COAP_MAX_RETRANSMIT + 1; /* add 1 for initial transmit */
while (tries_left) {
uint32_t timeout = CONFIG_COAP_ACK_TIMEOUT_MS * US_PER_MS;
res = sock_udp_send(&sock, buf, pdu_len, NULL);
if (res <= 0) {
DEBUG("nanocoap: error sending coap request, %d\n", (int)res);
/* add 1 for initial transmit */
unsigned tries_left = CONFIG_COAP_MAX_RETRANSMIT + 1;
/* check if we expect a reply */
bool confirmable = coap_get_type(pkt) == COAP_TYPE_CON;
while (tries_left) {
if (res == -EAGAIN) {
res = sock_udp_send(sock, buf, pdu_len, NULL);
if (res <= 0) {
DEBUG("nanocoap: error sending coap request, %d\n", (int)res);
break;
}
}
/* return if no response is expected */
if (!confirmable) {
pkt->payload_len = 0;
break;
}
res = sock_udp_recv(&sock, buf, len, timeout, NULL);
res = sock_udp_recv(sock, buf, len, timeout, NULL);
if (res <= 0) {
if (res == -ETIMEDOUT) {
DEBUG("nanocoap: timeout\n");
timeout *= 2;
tries_left--;
if (!tries_left) {
DEBUG("nanocoap: maximum retries reached\n");
break;
}
else {
timeout *= 2;
res = -EAGAIN;
continue;
}
continue;
}
DEBUG("nanocoap: error receiving coap response, %d\n", (int)res);
break;
@ -79,28 +97,31 @@ ssize_t nanocoap_request(coap_pkt_t *pkt, sock_udp_ep_t *local, sock_udp_ep_t *r
DEBUG("nanocoap: error parsing packet\n");
res = -EBADMSG;
}
else if (coap_get_id(pkt) != id) {
res = -EBADMSG;
continue;
}
break;
}
}
sock_udp_close(&sock);
return res;
}
ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, uint8_t *buf, size_t len)
ssize_t nanocoap_sock_get(nanocoap_sock_t *sock, const char *path, void *buf, size_t len)
{
ssize_t res;
coap_pkt_t pkt;
uint8_t *pktpos = buf;
pkt.hdr = (coap_hdr_t*)buf;
pkt.hdr = buf;
pktpos += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET, 1);
pktpos += coap_opt_put_uri_path(pktpos, 0, path);
pkt.payload = pktpos;
pkt.payload_len = 0;
res = nanocoap_request(&pkt, NULL, remote, len);
res = nanocoap_sock_request(sock, &pkt, len);
if (res < 0) {
return res;
}
@ -119,9 +140,148 @@ ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, uint8_t *buf, size
return res;
}
ssize_t nanocoap_request(coap_pkt_t *pkt, sock_udp_ep_t *local,
sock_udp_ep_t *remote, size_t len)
{
int res;
nanocoap_sock_t sock;
res = nanocoap_sock_connect(&sock, local, remote);
if (res) {
return res;
}
res = nanocoap_sock_request(&sock, pkt, len);
nanocoap_sock_close(&sock);
return res;
}
ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, void *buf, size_t len)
{
int res;
nanocoap_sock_t sock;
res = nanocoap_sock_connect(&sock, NULL, remote);
if (res) {
return res;
}
res = nanocoap_sock_get(&sock, path, buf, len);
nanocoap_sock_close(&sock);
return res;
}
static int _fetch_block(coap_pkt_t *pkt, uint8_t *buf, nanocoap_sock_t *sock,
const char *path, coap_blksize_t blksize, size_t num)
{
uint8_t *pktpos = buf;
uint16_t lastonum = 0;
pkt->hdr = (coap_hdr_t *)buf;
pktpos += coap_build_hdr(pkt->hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET,
num);
pktpos += coap_opt_put_uri_pathquery(pktpos, &lastonum, path);
pktpos += coap_opt_put_uint(pktpos, lastonum, COAP_OPT_BLOCK2,
(num << 4) | blksize);
pkt->payload = pktpos;
pkt->payload_len = 0;
int res = nanocoap_sock_request(sock, pkt, NANOCOAP_BLOCKWISE_BUF(blksize));
if (res < 0) {
return res;
}
res = coap_get_code(pkt);
DEBUG("code=%i\n", res);
if (res != 205) {
return -res;
}
return 0;
}
int nanocoap_sock_get_blockwise(nanocoap_sock_t *sock, const char *path,
coap_blksize_t blksize, void *buf,
coap_blockwise_cb_t callback, void *arg)
{
coap_pkt_t pkt;
coap_block1_t block2;
size_t num = 0;
do {
DEBUG("fetching block %u\n", (unsigned)num);
int res = _fetch_block(&pkt, buf, sock, path, blksize, num);
if (res) {
DEBUG("error fetching block: %d\n", res);
return -1;
}
/* no block option in response - directly use paylaod */
if (!coap_get_block2(&pkt, &block2)) {
block2.more = 0;
block2.offset = 0;
}
if (coap_has_unprocessed_critical_options(&pkt)) {
return -ENOTSUP;
}
res = callback(arg, block2.offset, pkt.payload, pkt.payload_len,
block2.more);
if (res) {
DEBUG("callback %d != 0, aborting.\n", res);
return res;
}
num += 1;
} while (block2.more == 1);
return 0;
}
int nanocoap_get_blockwise_url(const char *url,
coap_blksize_t blksize, void *buf,
coap_blockwise_cb_t callback, void *arg)
{
char hostport[CONFIG_SOCK_HOSTPORT_MAXLEN];
char urlpath[CONFIG_SOCK_URLPATH_MAXLEN];
sock_udp_ep_t remote;
nanocoap_sock_t sock;
int res;
if (strncmp(url, "coap://", 7)) {
DEBUG("nanocoap: URL doesn't start with \"coap://\"\n");
return -EINVAL;
}
if (sock_urlsplit(url, hostport, urlpath) < 0) {
DEBUG("nanocoap: invalid URL\n");
return -EINVAL;
}
if (sock_udp_str2ep(&remote, hostport) < 0) {
DEBUG("nanocoap: invalid URL\n");
return -EINVAL;
}
res = nanocoap_sock_connect(&sock, NULL, &remote);
if (res) {
return res;
}
res = nanocoap_sock_get_blockwise(&sock, urlpath, blksize, buf, callback, arg);
nanocoap_sock_close(&sock);
return res;
}
int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize)
{
sock_udp_t sock;
nanocoap_sock_t sock;
sock_udp_ep_t remote;
if (!local->port) {

View File

@ -37,6 +37,7 @@
#ifdef MODULE_SUIT_TRANSPORT_COAP
#include "suit/transport/coap.h"
#include "net/nanocoap_sock.h"
#endif
#include "suit/transport/mock.h"
@ -358,9 +359,10 @@ static int _dtv_fetch(suit_manifest_t *manifest, int key,
if (0) {}
#ifdef MODULE_SUIT_TRANSPORT_COAP
else if (strncmp(manifest->urlbuf, "coap://", 7) == 0) {
res = suit_coap_get_blockwise_url(manifest->urlbuf, CONFIG_SUIT_COAP_BLOCKSIZE,
suit_storage_helper,
manifest);
uint8_t buffer[NANOCOAP_BLOCKWISE_BUF(CONFIG_SUIT_COAP_BLOCKSIZE)];
res = nanocoap_get_blockwise_url(manifest->urlbuf, CONFIG_SUIT_COAP_BLOCKSIZE,
buffer, suit_storage_helper,
manifest);
}
#endif
#ifdef MODULE_SUIT_TRANSPORT_MOCK

View File

@ -133,181 +133,6 @@ static inline uint32_t deadline_left(uint32_t deadline)
return left;
}
static ssize_t _nanocoap_request(sock_udp_t *sock, coap_pkt_t *pkt, size_t len)
{
ssize_t res = -EAGAIN;
size_t pdu_len = (pkt->payload - (uint8_t *)pkt->hdr) + pkt->payload_len;
uint8_t *buf = (uint8_t *)pkt->hdr;
uint32_t id = coap_get_id(pkt);
/* TODO: timeout random between between ACK_TIMEOUT and (ACK_TIMEOUT *
* ACK_RANDOM_FACTOR) */
uint32_t timeout = CONFIG_COAP_ACK_TIMEOUT * US_PER_SEC;
uint32_t deadline = deadline_from_interval(timeout);
/* add 1 for initial transmit */
unsigned tries_left = CONFIG_COAP_MAX_RETRANSMIT + 1;
while (tries_left) {
if (res == -EAGAIN) {
res = sock_udp_send(sock, buf, pdu_len, NULL);
if (res <= 0) {
DEBUG("nanocoap: error sending coap request, %d\n", (int)res);
break;
}
}
res = sock_udp_recv(sock, buf, len, deadline_left(deadline), NULL);
if (res <= 0) {
if (res == -ETIMEDOUT) {
DEBUG("nanocoap: timeout\n");
tries_left--;
if (!tries_left) {
DEBUG("nanocoap: maximum retries reached\n");
break;
}
else {
timeout *= 2;
deadline = deadline_from_interval(timeout);
res = -EAGAIN;
continue;
}
}
DEBUG("nanocoap: error receiving coap response, %d\n", (int)res);
break;
}
else {
if (coap_parse(pkt, (uint8_t *)buf, res) < 0) {
DEBUG("nanocoap: error parsing packet\n");
res = -EBADMSG;
}
else if (coap_get_id(pkt) != id) {
res = -EBADMSG;
continue;
}
break;
}
}
return res;
}
static int _fetch_block(coap_pkt_t *pkt, uint8_t *buf, sock_udp_t *sock,
const char *path, coap_blksize_t blksize, size_t num)
{
uint8_t *pktpos = buf;
uint16_t lastonum = 0;
pkt->hdr = (coap_hdr_t *)buf;
pktpos += coap_build_hdr(pkt->hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET,
num);
pktpos += coap_opt_put_uri_pathquery(pktpos, &lastonum, path);
pktpos +=
coap_opt_put_uint(pktpos, lastonum, COAP_OPT_BLOCK2,
(num << 4) | blksize);
pkt->payload = pktpos;
pkt->payload_len = 0;
int res = _nanocoap_request(sock, pkt, 64 + (0x1 << (blksize + 4)));
if (res < 0) {
return res;
}
res = coap_get_code(pkt);
DEBUG("code=%i\n", res);
if (res != 205) {
return -res;
}
return 0;
}
int suit_coap_get_blockwise(sock_udp_ep_t *remote, const char *path,
coap_blksize_t blksize,
coap_blockwise_cb_t callback, void *arg)
{
/* mmmmh dynamically sized array */
uint8_t buf[64 + (0x1 << (blksize + 4))];
sock_udp_ep_t local = SOCK_IPV6_EP_ANY;
coap_pkt_t pkt;
/* HACK: use random local port */
local.port = 0x8000 + (xtimer_now_usec() % 0XFFF);
sock_udp_t sock;
int res = sock_udp_create(&sock, &local, remote, 0);
if (res < 0) {
return res;
}
int more = 1;
size_t num = 0;
res = -1;
while (more == 1) {
DEBUG("fetching block %u\n", (unsigned)num);
res = _fetch_block(&pkt, buf, &sock, path, blksize, num);
DEBUG("res=%i\n", res);
if (!res) {
coap_block1_t block2;
coap_get_block2(&pkt, &block2);
more = block2.more;
if (callback(arg, block2.offset, pkt.payload, pkt.payload_len,
more)) {
DEBUG("callback res != 0, aborting.\n");
res = -1;
goto out;
}
}
else {
DEBUG("error fetching block\n");
res = -1;
goto out;
}
num += 1;
}
out:
sock_udp_close(&sock);
return res;
}
int suit_coap_get_blockwise_url(const char *url,
coap_blksize_t blksize,
coap_blockwise_cb_t callback, void *arg)
{
char hostport[CONFIG_SOCK_HOSTPORT_MAXLEN];
char urlpath[CONFIG_SOCK_URLPATH_MAXLEN];
sock_udp_ep_t remote;
if (strncmp(url, "coap://", 7)) {
LOG_INFO("suit: URL doesn't start with \"coap://\"\n");
return -EINVAL;
}
if (sock_urlsplit(url, hostport, urlpath) < 0) {
LOG_INFO("suit: invalid URL\n");
return -EINVAL;
}
if (sock_udp_str2ep(&remote, hostport) < 0) {
LOG_INFO("suit: invalid URL\n");
return -EINVAL;
}
if (!remote.port) {
remote.port = COAP_PORT;
}
return suit_coap_get_blockwise(&remote, urlpath, blksize, callback, arg);
}
typedef struct {
size_t offset;
uint8_t *ptr;
@ -334,20 +159,20 @@ static int _2buf(void *arg, size_t offset, uint8_t *buf, size_t len, int more)
}
}
ssize_t suit_coap_get_blockwise_url_buf(const char *url,
coap_blksize_t blksize,
uint8_t *buf, size_t len)
static ssize_t suit_coap_get_blockwise_url_buf(const char *url,
coap_blksize_t blksize, void *work_buf,
uint8_t *buf, size_t len)
{
_buf_t _buf = { .ptr = buf, .len = len };
int res = suit_coap_get_blockwise_url(url, blksize, _2buf, &_buf);
int res = nanocoap_get_blockwise_url(url, blksize, work_buf, _2buf, &_buf);
return (res < 0) ? (ssize_t)res : (ssize_t)_buf.offset;
}
static void _suit_handle_url(const char *url)
static void _suit_handle_url(const char *url, coap_blksize_t blksize, void *work_buf)
{
LOG_INFO("suit_coap: downloading \"%s\"\n", url);
ssize_t size = suit_coap_get_blockwise_url_buf(url, CONFIG_SUIT_COAP_BLOCKSIZE,
ssize_t size = suit_coap_get_blockwise_url_buf(url, blksize, work_buf,
_manifest_buf,
SUIT_MANIFEST_BUFSIZE);
if (size >= 0) {
@ -436,6 +261,8 @@ static void *_suit_coap_thread(void *arg)
{
(void)arg;
uint8_t buffer[NANOCOAP_BLOCKWISE_BUF(CONFIG_SUIT_COAP_BLOCKSIZE)];
LOG_INFO("suit_coap: started.\n");
msg_t msg_queue[4];
msg_init_queue(msg_queue, 4);
@ -449,7 +276,7 @@ static void *_suit_coap_thread(void *arg)
switch (m.content.value) {
case SUIT_MSG_TRIGGER:
LOG_INFO("suit_coap: trigger received\n");
_suit_handle_url(_url);
_suit_handle_url(_url, CONFIG_SUIT_COAP_BLOCKSIZE, buffer);
break;
default:
LOG_WARNING("suit_coap: warning: unhandled msg\n");

View File

@ -29,11 +29,13 @@
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
extern int nanotest_client_cmd(int argc, char **argv);
extern int nanotest_client_url_cmd(int argc, char **argv);
extern int nanotest_server_cmd(int argc, char **argv);
static int _list_all_inet6(int argc, char **argv);
static const shell_command_t shell_commands[] = {
{ "client", "CoAP client", nanotest_client_cmd },
{ "url", "CoAP client URL request", nanotest_client_url_cmd },
{ "server", "CoAP server", nanotest_server_cmd },
{ "inet6", "IPv6 addresses", _list_all_inet6 },
{ NULL, NULL, NULL }

View File

@ -80,7 +80,7 @@ static ssize_t _send(coap_pkt_t *pkt, size_t len, char *addr_str, char *port_str
int nanotest_client_cmd(int argc, char **argv)
{
/* Ordered like the RFC method code numbers, but off by 1. GET is code 0. */
char *method_codes[] = {"get", "post", "put"};
const char *method_codes[] = {"get", "post", "put"};
unsigned buflen = 128;
uint8_t buf[buflen];
coap_pkt_t pkt;
@ -161,3 +161,50 @@ int nanotest_client_cmd(int argc, char **argv)
argv[0]);
return 1;
}
static int _blockwise_cb(void *arg, size_t offset, uint8_t *buf, size_t len, int more)
{
(void)arg;
(void)more;
printf("offset %03u: ", (unsigned)offset);
for (unsigned i = 0; i < len; ++i) {
putchar(buf[i]);
}
puts("");
return 0;
}
int nanotest_client_url_cmd(int argc, char **argv)
{
/* Ordered like the RFC method code numbers, but off by 1. GET is code 0. */
const char *method_codes[] = {"get", "post", "put"};
if (argc < 3) {
goto error;
}
int code_pos = -1;
for (size_t i = 0; i < ARRAY_SIZE(method_codes); i++) {
if (strcmp(argv[1], method_codes[i]) == 0) {
code_pos = i;
break;
}
}
if (code_pos == -1) {
goto error;
}
if (code_pos != 0) {
printf("TODO: implement %s request\n", method_codes[code_pos]);
return -1;
}
uint8_t buffer[NANOCOAP_BLOCKWISE_BUF(COAP_BLOCKSIZE_32)];
return nanocoap_get_blockwise_url(argv[2], COAP_BLOCKSIZE_32, buffer,
_blockwise_cb, NULL);
error:
printf("usage: %s <get|post|put> <url> [data]\n", argv[0]);
return -1;
}