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

Merge pull request #17509 from benpicco/nanocoap-rework

sys/net/nanocoap: refactor to remove work-buffer requirement
This commit is contained in:
benpicco 2022-04-14 01:17:30 +02:00 committed by GitHub
commit 32790eb8f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 263 additions and 118 deletions

View File

@ -155,19 +155,13 @@ extern "C" {
/**
* @brief Maximum length of a CoAP header for a blockwise message
*
* Value obtained experimentally when using SUIT
*/
#ifndef CONFIG_NANOCOAP_BLOCK_HEADER_MAX
#define CONFIG_NANOCOAP_BLOCK_HEADER_MAX (64)
#define CONFIG_NANOCOAP_BLOCK_HEADER_MAX (80)
#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
*
@ -250,6 +244,18 @@ typedef ssize_t (*coap_handler_t)(coap_pkt_t *pkt, uint8_t *buf, size_t len, voi
*/
typedef int (*coap_blockwise_cb_t)(void *arg, size_t offset, uint8_t *buf, size_t len, int more);
/**
* @brief Coap equest callback descriptor
*
* @param[in] arg Pointer to be passed as arguments to the callback
* @param[in] pkt The received CoAP response.
* Buffers point to network stack internal memory.
*
* @returns >=0 on success
* @returns <0 on error
*/
typedef int (*coap_request_cb_t)(void *arg, coap_pkt_t *pkt);
/**
* @brief Method flag type
*
@ -324,7 +330,7 @@ static inline uint8_t coap_code(unsigned cls, unsigned detail)
*
* @returns message code class
*/
static inline unsigned coap_get_code_class(coap_pkt_t *pkt)
static inline unsigned coap_get_code_class(const coap_pkt_t *pkt)
{
return pkt->hdr->code >> 5;
}
@ -348,7 +354,7 @@ static inline unsigned coap_get_code_detail(const coap_pkt_t *pkt)
*
* @returns message code in decimal format
*/
static inline unsigned coap_get_code(coap_pkt_t *pkt)
static inline unsigned coap_get_code(const coap_pkt_t *pkt)
{
return (coap_get_code_class(pkt) * 100) + coap_get_code_detail(pkt);
}
@ -401,6 +407,18 @@ static inline unsigned coap_get_total_hdr_len(const coap_pkt_t *pkt)
return sizeof(coap_hdr_t) + coap_get_token_len(pkt);
}
/**
* @brief Get the total length of a CoAP packet
*
* @param[in] pkt CoAP packet
*
* @returns total CoAP length
*/
static inline unsigned coap_get_total_len(const coap_pkt_t *pkt)
{
return (uintptr_t)pkt->payload - (uintptr_t)pkt->hdr + pkt->payload_len;
}
/**
* @brief Get the message type
*

View File

@ -206,16 +206,14 @@ ssize_t nanocoap_sock_get(nanocoap_sock_t *sock, const char *path, void *buf,
* @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_blksize_t blksize,
coap_blockwise_cb_t callback, void *arg);
/**
@ -228,7 +226,6 @@ int nanocoap_sock_get_blockwise(nanocoap_sock_t *sock, const char *path,
* @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
*
@ -237,7 +234,7 @@ int nanocoap_sock_get_blockwise(nanocoap_sock_t *sock, const char *path,
* @returns 0 on success
*/
int nanocoap_get_blockwise_url(const char *url,
coap_blksize_t blksize, void *work_buf,
coap_blksize_t blksize,
coap_blockwise_cb_t callback, void *arg);
/**
@ -251,7 +248,6 @@ int nanocoap_get_blockwise_url(const char *url,
* @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] buf Target buffer
* @param[in] len Target buffer length
*
@ -260,7 +256,7 @@ int nanocoap_get_blockwise_url(const char *url,
* @returns size of the response payload on success
*/
ssize_t nanocoap_get_blockwise_url_to_buf(const char *url,
coap_blksize_t blksize, void *work_buf,
coap_blksize_t blksize,
void *buf, size_t len);
/**
@ -277,6 +273,24 @@ ssize_t nanocoap_get_blockwise_url_to_buf(const char *url,
*/
ssize_t nanocoap_sock_request(nanocoap_sock_t *sock, coap_pkt_t *pkt, size_t len);
/**
* @brief Simple synchronous CoAP request with callback
*
* The response will be handled by a callback, which avoids copying the
* response packet out of the network stack internal buffer.
*
* @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] cb Callback executed for response packet
* @param[in] arg Optional callback argumnent
*
* @returns length of response on success
* @returns <0 on error
*/
ssize_t nanocoap_sock_request_cb(sock_udp_t *sock, coap_pkt_t *pkt,
coap_request_cb_t cb, void *arg);
/**
* @brief Simple synchronous CoAP request
*

View File

@ -16,6 +16,7 @@
* @brief Nanocoap sock helpers
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*
* @}
*/
@ -27,11 +28,23 @@
#include "net/nanocoap_sock.h"
#include "net/sock/util.h"
#include "net/sock/udp.h"
#include "sys/uio.h"
#include "timex.h"
#define ENABLE_DEBUG 0
#include "debug.h"
enum {
STATE_SEND_REQUEST,
STATE_AWAIT_RESPONSE,
};
typedef struct {
coap_blockwise_cb_t callback;
void *arg;
bool more;
} _block_ctx_t;
int nanocoap_sock_connect(nanocoap_sock_t *sock, sock_udp_ep_t *local, sock_udp_ep_t *remote)
{
if (!remote->port) {
@ -41,59 +54,90 @@ int nanocoap_sock_connect(nanocoap_sock_t *sock, sock_udp_ep_t *local, sock_udp_
return sock_udp_create(sock, local, remote, 0);
}
ssize_t nanocoap_sock_request(nanocoap_sock_t *sock, coap_pkt_t *pkt, size_t len)
static int _get_error(const coap_pkt_t *pkt)
{
ssize_t res = -EAGAIN;
switch (coap_get_code_class(pkt)) {
case COAP_CLASS_CLIENT_FAILURE:
return -ENXIO;
case COAP_CLASS_SERVER_FAILURE:
return -ENETRESET;
default:
return 0;
}
}
static int _send_ack(nanocoap_sock_t *sock, coap_pkt_t *pkt)
{
coap_hdr_t ack;
unsigned tkl = coap_get_token_len(pkt);
coap_build_hdr(&ack, COAP_TYPE_ACK, pkt->token, tkl,
COAP_CODE_VALID, ntohs(pkt->hdr->id));
return sock_udp_send(sock, &ack, sizeof(ack), NULL);
}
ssize_t nanocoap_sock_request_cb(nanocoap_sock_t *sock, coap_pkt_t *pkt,
coap_request_cb_t cb, void *arg)
{
ssize_t tmp, res = 0;
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);
void *payload, *ctx = NULL;
/* TODO: timeout random between between ACK_TIMEOUT and (ACK_TIMEOUT *
* ACK_RANDOM_FACTOR) */
unsigned state = STATE_SEND_REQUEST;
/* TODO: timeout random between between ACK_TIMEOUT and (ACK_TIMEOUT * ACK_RANDOM_FACTOR) */
uint32_t timeout = CONFIG_COAP_ACK_TIMEOUT_MS * US_PER_MS;
/* 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;
const bool confirmable = coap_get_type(pkt) == COAP_TYPE_CON;
while (1) {
switch (state) {
case STATE_SEND_REQUEST:
if (--tries_left == 0) {
DEBUG("nanocoap: maximum retries reached\n");
return -ETIMEDOUT;
}
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 res;
}
}
/* return if no response is expected */
if (!confirmable) {
pkt->payload_len = 0;
break;
}
res = sock_udp_recv(sock, buf, len, timeout, NULL);
if (res <= 0) {
if (confirmable || cb) {
state = STATE_AWAIT_RESPONSE;
} else {
return 0;
}
/* fall-through */
case STATE_AWAIT_RESPONSE:
tmp = sock_udp_recv_buf(sock, &payload, &ctx, timeout, NULL);
if (tmp == 0) {
/* no more data */
return res;
}
res = tmp;
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;
}
state = STATE_SEND_REQUEST;
continue;
}
DEBUG("nanocoap: error receiving coap response, %d\n", (int)res);
break;
}
else {
if (coap_parse(pkt, (uint8_t *)buf, res) < 0) {
if (res < 0) {
DEBUG("nanocoap: error receiving coap response, %d\n", (int)res);
return res;
}
/* parse response */
if (coap_parse(pkt, payload, res) < 0) {
DEBUG("nanocoap: error parsing packet\n");
res = -EBADMSG;
}
@ -102,41 +146,105 @@ ssize_t nanocoap_sock_request(nanocoap_sock_t *sock, coap_pkt_t *pkt, size_t len
continue;
}
break;
switch (coap_get_type(pkt)) {
case COAP_TYPE_RST:
/* TODO: handle different? */
return -EBADMSG;
case COAP_TYPE_CON:
_send_ack(sock, pkt);
/* fall-through */
case COAP_TYPE_NON:
case COAP_TYPE_ACK:
/* call user callback */
if (cb) {
res = cb(arg, pkt);
} else {
res = 0;
}
break;
}
}
}
return res;
}
static int _request_cb(void *arg, coap_pkt_t *pkt)
{
struct iovec *buf = arg;
size_t pkt_len = coap_get_total_len(pkt);
int res = _get_error(pkt);
if (res) {
return res;
}
if (pkt_len > buf->iov_len) {
return -ENOBUFS;
}
memcpy(buf->iov_base, pkt->hdr, pkt_len);
pkt->hdr = buf->iov_base;
pkt->token = (uint8_t*)pkt->hdr + sizeof(coap_hdr_t);
pkt->payload = (uint8_t*)pkt->hdr + (pkt_len - pkt->payload_len);
return pkt_len;
}
ssize_t nanocoap_sock_request(sock_udp_t *sock, coap_pkt_t *pkt, size_t len)
{
struct iovec buf = {
.iov_base = pkt->hdr,
.iov_len = len,
};
return nanocoap_sock_request_cb(sock, pkt, _request_cb, &buf);
}
static int _get_cb(void *arg, coap_pkt_t *pkt)
{
struct iovec *buf = arg;
int res = _get_error(pkt);
if (res) {
return res;
}
if (pkt->payload_len > buf->iov_len) {
return -ENOBUFS;
}
memcpy(buf->iov_base, pkt->payload, pkt->payload_len);
return pkt->payload_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;
struct iovec ctx = {
.iov_base = buf,
.iov_len = len,
};
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_sock_request(sock, &pkt, len);
res = nanocoap_sock_request_cb(sock, &pkt, _get_cb, &ctx);
if (res < 0) {
return res;
}
else {
res = coap_get_code(&pkt);
if (res != 205) {
res = -res;
}
else {
if (pkt.payload_len) {
memmove(buf, pkt.payload, pkt.payload_len);
}
res = pkt.payload_len;
}
if (coap_get_code(&pkt) != 205) {
return -ENOENT;
}
return res;
}
@ -173,13 +281,33 @@ ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, void *buf, size_t
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)
static int _block_cb(void *arg, coap_pkt_t *pkt)
{
uint8_t *pktpos = buf;
uint16_t lastonum = 0;
_block_ctx_t *ctx = arg;
coap_block1_t block2;
pkt->hdr = (coap_hdr_t *)buf;
int res = _get_error(pkt);
if (res) {
return res;
}
/* response was not block-wise */
if (!coap_get_block2(pkt, &block2)) {
block2.offset = 0;
ctx->more = false;
} else {
ctx->more = block2.more;
}
return ctx->callback(ctx->arg, block2.offset, pkt->payload, pkt->payload_len, block2.more);
}
static int _fetch_block(coap_pkt_t *pkt, sock_udp_t *sock,
const char *path, coap_blksize_t blksize, unsigned num,
_block_ctx_t *ctx)
{
uint8_t *pktpos = (void *)pkt->hdr;
uint16_t lastonum = 0;
pktpos += coap_build_hdr(pkt->hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET,
num);
@ -190,61 +318,50 @@ static int _fetch_block(coap_pkt_t *pkt, uint8_t *buf, nanocoap_sock_t *sock,
pkt->payload = pktpos;
pkt->payload_len = 0;
int res = nanocoap_sock_request(sock, pkt, NANOCOAP_BLOCKWISE_BUF(blksize));
int res = nanocoap_sock_request_cb(sock, pkt, _block_cb, ctx);
if (res < 0) {
return res;
}
res = coap_get_code(pkt);
DEBUG("code=%i\n", res);
if (res != 205) {
return -res;
}
DEBUG("code=%i\n", coap_get_code(pkt));
return 0;
return _get_error(pkt);
}
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_blksize_t blksize,
coap_blockwise_cb_t callback, void *arg)
{
coap_pkt_t pkt;
coap_block1_t block2;
uint8_t buf[CONFIG_NANOCOAP_BLOCK_HEADER_MAX];
_block_ctx_t ctx = {
.callback = callback,
.arg = arg,
.more = true,
};
unsigned num = 0;
while (ctx.more) {
coap_pkt_t pkt = {
.hdr = (void *)buf,
};
DEBUG("fetching block %u\n", num);
int res = _fetch_block(&pkt, sock, path, blksize, num, &ctx);
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);
DEBUG("error fetching block %u: %d\n", num, 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_blksize_t blksize,
coap_blockwise_cb_t callback, void *arg)
{
char hostport[CONFIG_SOCK_HOSTPORT_MAXLEN];
@ -273,7 +390,7 @@ int nanocoap_get_blockwise_url(const char *url,
return res;
}
res = nanocoap_sock_get_blockwise(&sock, urlpath, blksize, buf, callback, arg);
res = nanocoap_sock_get_blockwise(&sock, urlpath, blksize, callback, arg);
nanocoap_sock_close(&sock);
return res;
@ -302,11 +419,11 @@ static int _2buf(void *arg, size_t offset, uint8_t *buf, size_t len, int more)
}
ssize_t nanocoap_get_blockwise_url_to_buf(const char *url,
coap_blksize_t blksize, void *work_buf,
coap_blksize_t blksize,
void *buf, size_t len)
{
_buf_t _buf = { .ptr = buf, .len = len };
int res = nanocoap_get_blockwise_url(url, blksize, work_buf, _2buf, &_buf);
int res = nanocoap_get_blockwise_url(url, blksize, _2buf, &_buf);
return (res < 0) ? (ssize_t)res : (ssize_t)_buf.len;
}

View File

@ -49,8 +49,8 @@ static int _get_component_size(suit_manifest_t *manifest,
{
nanocbor_value_t param_size;
if ((suit_param_ref_to_cbor(manifest, &comp->param_size, &param_size) == 0)
|| (nanocbor_get_uint32(&param_size, img_size) < 0)) { return
SUIT_ERR_INVALID_MANIFEST;
|| (nanocbor_get_uint32(&param_size, img_size) < 0)) {
return SUIT_ERR_INVALID_MANIFEST;
}
return SUIT_OK;
}
@ -359,9 +359,8 @@ 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) {
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,
suit_storage_helper,
manifest);
}
#endif

View File

@ -112,10 +112,10 @@ ssize_t coap_subtree_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len,
subtree->resources_numof);
}
static void _suit_handle_url(const char *url, coap_blksize_t blksize, void *work_buf)
static void _suit_handle_url(const char *url, coap_blksize_t blksize)
{
LOG_INFO("suit_coap: downloading \"%s\"\n", url);
ssize_t size = nanocoap_get_blockwise_url_to_buf(url, blksize, work_buf,
ssize_t size = nanocoap_get_blockwise_url_to_buf(url, blksize,
_manifest_buf,
SUIT_MANIFEST_BUFSIZE);
if (size >= 0) {
@ -204,8 +204,6 @@ 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);
@ -219,7 +217,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, CONFIG_SUIT_COAP_BLOCKSIZE, buffer);
_suit_handle_url(_url, CONFIG_SUIT_COAP_BLOCKSIZE);
break;
default:
LOG_WARNING("suit_coap: warning: unhandled msg\n");

View File

@ -204,9 +204,8 @@ int nanotest_client_url_cmd(int argc, char **argv)
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);
return nanocoap_get_blockwise_url(argv[2], COAP_BLOCKSIZE_32,
_blockwise_cb, NULL);
error:
printf("usage: %s <get|post|put> <url> [data]\n", argv[0]);
return -1;