Skip to content

Commit

Permalink
gcoap: support separate response
Browse files Browse the repository at this point in the history
  • Loading branch information
fabian18 committed May 28, 2024
1 parent 66138ad commit c28df13
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 0 deletions.
66 changes: 66 additions & 0 deletions sys/include/net/gcoap.h
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,23 @@ typedef struct {
gcoap_socket_t socket; /**< Transport type to observer */
} gcoap_observe_memo_t;

/**
* @brief Context for a separate response
*/
typedef struct {
event_t ev; /**< Event for the separate response */
gcoap_socket_type_t tl; /**< Transport type on which the request was received */
sock_udp_ep_t remote; /**< Remote to send response to */
#if defined(MODULE_SOCK_AUX_LOCAL) || DOXYGEN
sock_udp_ep_t local; /**< Local endpoint from which to send the response */
#endif
int result; /**< Result of precessing the request */

Check failure on line 857 in sys/include/net/gcoap.h

View workflow job for this annotation

GitHub Actions / static-tests

There is a typo: precessing ==> processing If this is a false positive, add it to dist/tools/codespell/ignored_words.txt. You can fix this interactively by calling CODESPELL_INTERACTIVE=1 BASE_BRANCH=master ./dist/tools/codespell/check.sh
uint8_t *req_buf; /**< Buffer to cache request */
size_t req_buf_size; /**< Size of the buffer containing the request */
uint8_t *resp_buf; /**< Buffer to write response to */
size_t resp_buf_size; /**< Size of the buffer to write the response to */
} gcoap_separate_response_ctx_t;

/**
* @brief Initializes the gcoap thread and device
*
Expand Down Expand Up @@ -1028,6 +1045,20 @@ static inline ssize_t gcoap_req_send_tl(const uint8_t *buf, size_t len,
*/
int gcoap_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code);

/**
* @brief Initialize a CoAP separate response packet in a buffer
*
* @param[out] pdu Response metadata
* @param[in] buf Buffer containing the PDU
* @param[in] len Length of the buffer
* @param[in] type Type of the response (CON/NON)
* @param[in] code Response code
*
* @return 0 on success
* @return < 0 on error
*/
int gcoap_separate_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned type, unsigned code);

Check warning on line 1060 in sys/include/net/gcoap.h

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters

/**
* @brief Writes a complete CoAP response PDU when there is no payload
*
Expand All @@ -1047,6 +1078,41 @@ static inline ssize_t gcoap_response(coap_pkt_t *pdu, uint8_t *buf,
: -1;
}

/**
* @brief Prepare a context to be able to send a separate response
*
* @param[out] ctx Separate response context to prepare
* @param[in] req_buf Buffer to cache the request
* @param[in] req_buf_size Size of the request buffer
* @param[in] resp_buf Buffer to cache the request
* @param[in] resp_buf_size Size of the request buffer
* @param[in] req_pkt Request packet that initiated the separate response
* @param[in] req_ctx CoAP request context
*
* @return < 0 on error or 0 on success
*/
int gcoap_resp_prepare_separate(gcoap_separate_response_ctx_t *ctx,
void *req_buf, size_t req_buf_size,
void *resp_buf, size_t resp_buf_size,
const coap_pkt_t *req_pkt,
const coap_request_ctx_t *req_ctx);

/**
* @brief Sends the PDU buffer containing a separate response
*
* @param[in] buf Separate response buffer
* @param[in] len Length of the buffer
* @param[in] remote Destination endpoint to reply to
* @param[in] local Local endpoint to send from, may be NULL
* @param[in] tl_type Transport type to use for the reply
*
* @return length of the packet
* @return -1 on error or 0 if cannot send
*/
ssize_t gcoap_resp_send_separate(const uint8_t *buf, size_t len,
const sock_udp_ep_t *remote, const sock_udp_ep_t *local,
gcoap_socket_type_t tl_type);

/**
* @brief Initializes a CoAP Observe notification packet on a buffer, for the
* observer registered for a resource
Expand Down
17 changes: 17 additions & 0 deletions sys/include/net/nanocoap.h
Original file line number Diff line number Diff line change
Expand Up @@ -1982,6 +1982,23 @@ ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, const void *token,
ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code,
uint8_t *rbuf, unsigned rlen, unsigned payload_len);

/**
* @brief Initialize a separate response PDU from a request PDU
*
* @param[in,out] pkt Response PDU to initialize
* @param[in] type Response type (CON or NON)
* @param[in] code Response code
* @param[out] rbuf Response buffer
* @param[in] rlen Size of response buffer
* @param[in] payload_len Response payload length
*
* @returns 0 if no response should be sent due to a No-Response option in the request
* @returns <0 on error
* @returns -ENOSPC if @p rbuf too small
*/
ssize_t coap_build_separate_reply(coap_pkt_t *pkt, unsigned type, unsigned code,
uint8_t *rbuf, unsigned rlen, unsigned payload_len);

/**
* @brief Build empty reply to CoAP request
*
Expand Down
143 changes: 143 additions & 0 deletions sys/net/application_layer/gcoap/gcoap.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ static void _process_coap_pdu(gcoap_socket_t *sock, sock_udp_ep_t *remote, sock_
memo = _find_req_memo_by_mid(remote, pdu.hdr->id);
if ((memo != NULL) && (memo->send_limit != GCOAP_SEND_LIMIT_NON)) {
DEBUG("gcoap: empty ACK processed, stopping retransmissions\n");
/* for a separate CON response the memo is deleted because there is no response handler set */
_cease_retransmission(memo);
} else {
DEBUG("gcoap: empty ACK matches no known CON, ignoring\n");
Expand Down Expand Up @@ -1783,6 +1784,28 @@ int gcoap_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code)
return -1;
}

pdu->hdr = (coap_hdr_t *)buf;
pdu->options_len = 0;
pdu->payload = buf + header_len;
pdu->payload_len = len - header_len;

if (coap_get_observe(pdu) == COAP_OBS_REGISTER) {
_add_generated_observe_option(pdu);
}

return 0;
}

int gcoap_separate_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned type, unsigned code)
{
int header_len = coap_build_separate_reply(pdu, type, code, buf, len, 0);

/* request contained no-response option or not enough space for response */
if (header_len <= 0) {
return -1;
}

pdu->hdr = (coap_hdr_t *)buf;
pdu->options_len = 0;
pdu->payload = buf + header_len;
pdu->payload_len = len - header_len;
Expand Down Expand Up @@ -1954,4 +1977,124 @@ void gcoap_forward_proxy_post_event(void *arg)
event_post(&_queue, arg);
}

/* separate response API */

int gcoap_resp_prepare_separate(gcoap_separate_response_ctx_t *ctx,
void *req_buf, size_t req_buf_size,
void *resp_buf, size_t resp_buf_size,
const coap_pkt_t *req_pkt,
const coap_request_ctx_t *req_ctx)
{
size_t len = coap_get_total_len(req_pkt);
if (req_buf_size < len) {
return -ENOSPC;
}
ctx->tl = req_ctx->tl_type;
ctx->req_buf = req_buf;
ctx->req_buf_size = req_buf_size;
ctx->resp_buf = resp_buf;
ctx->resp_buf_size = resp_buf_size;
memcpy(ctx->req_buf, req_pkt->hdr, len);
memcpy(&ctx->remote, req_ctx->remote, sizeof(ctx->remote));
#ifdef MODULE_SOCK_AUX_LOCAL
assert(req_ctx->local);
memcpy(&ctx->local, req_ctx->local, sizeof(ctx->local));
#endif
return 0;
}

ssize_t gcoap_resp_send_separate(const uint8_t *buf, size_t len,
const sock_udp_ep_t *remote, const sock_udp_ep_t *local,
gcoap_socket_type_t tl_type)
{
/* mostly copied from gcoap_req_send() */
gcoap_socket_t socket = { 0 };
gcoap_request_memo_t *memo = NULL;
unsigned msg_type = (*buf & 0x30) >> 4;
uint32_t timeout = 0;

assert(remote != NULL);

ssize_t res = _tl_init_coap_socket(&socket, tl_type);
if (res < 0) {
return -1;
}
if (msg_type == COAP_TYPE_CON) {
mutex_lock(&_coap_state.lock);
/* Find empty slot in list of open requests. */
for (int i = 0; i < CONFIG_GCOAP_REQ_WAITING_MAX; i++) {
if (_coap_state.open_reqs[i].state == GCOAP_MEMO_UNUSED) {
memo = &_coap_state.open_reqs[i];
memo->state = GCOAP_MEMO_WAIT;
break;
}
}
if (!memo) {
mutex_unlock(&_coap_state.lock);
DEBUG("gcoap: dropping response; no space for response tracking\n");
return 0;
}
memo->resp_handler = NULL;
memo->context = NULL;
memcpy(&memo->remote_ep, remote, sizeof(sock_udp_ep_t));
memo->socket = socket;
/* copy buf to resend_bufs record */
memo->msg.data.pdu_buf = NULL;
for (int i = 0; i < CONFIG_GCOAP_RESEND_BUFS_MAX; i++) {
if (!_coap_state.resend_bufs[i][0]) {
memo->msg.data.pdu_buf = &_coap_state.resend_bufs[i][0];
memcpy(memo->msg.data.pdu_buf, buf, CONFIG_GCOAP_PDU_BUF_SIZE);
memo->msg.data.pdu_len = len;
break;
}
}
if (memo->msg.data.pdu_buf) {
memo->send_limit = CONFIG_COAP_MAX_RETRANSMIT;
timeout = (uint32_t)CONFIG_COAP_ACK_TIMEOUT_MS;
#if CONFIG_COAP_RANDOM_FACTOR_1000 > 1000
timeout = random_uint32_range(timeout, TIMEOUT_RANGE_END);
#endif
memo->state = GCOAP_MEMO_RETRANSMIT;
}
else {
memo->state = GCOAP_MEMO_UNUSED;
DEBUG("gcoap: no space for PDU in resend bufs\n");
}
mutex_unlock(&_coap_state.lock);
}

_tl_init_coap_socket(&socket, tl_type);
/* set response timeout; may be zero for non-confirmable */
if (memo) {
if (timeout > 0) {
event_callback_init(&memo->resp_tmout_cb, _on_resp_timeout, memo);
event_timeout_ztimer_init(&memo->resp_evt_tmout, ZTIMER_MSEC, &_queue,
&memo->resp_tmout_cb.super);
event_timeout_set(&memo->resp_evt_tmout, timeout);
}
else {
memset(&memo->resp_evt_tmout, 0, sizeof(event_timeout_t));
}
}

sock_udp_aux_tx_t aux = { 0 };
if (local) {
memcpy(&aux.local, local, sizeof(sock_udp_ep_t));
aux.flags = SOCK_AUX_SET_LOCAL;
}
if ((res = _tl_send(&socket, buf, len, remote, &aux)) <= 0) {
if (memo) {
if (msg_type == COAP_TYPE_CON) {
*memo->msg.data.pdu_buf = 0; /* clear resend buffer */
}
if (timeout > 0) {
event_timeout_clear(&memo->resp_evt_tmout);
}
memo->state = GCOAP_MEMO_UNUSED;
}
DEBUG("gcoap: sock send failed: %" PRIdSIZE "\n", res);
}
return res < 0 ? -1 : res;
}

/** @} */
34 changes: 34 additions & 0 deletions sys/net/application_layer/nanocoap/nanocoap.c
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,40 @@ ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code,
return len;
}

ssize_t coap_build_separate_reply(coap_pkt_t *pkt, unsigned type, unsigned code,
uint8_t *rbuf, unsigned rlen, unsigned payload_len)
{
assert(type == COAP_TYPE_CON || type == COAP_TYPE_NON);

unsigned tkl = coap_get_token_len(pkt);
unsigned len = sizeof(coap_hdr_t) + tkl;

if ((len + payload_len) > rlen) {
return -ENOSPC;
}

uint32_t no_response;
if (coap_opt_get_uint(pkt, COAP_OPT_NO_RESPONSE, &no_response) == 0) {

const uint8_t no_response_index = (code >> 5) - 1;
/* If the handler code misbehaved here, we'd face UB otherwise */
assert(no_response_index < 7);

const uint8_t mask = 1 << no_response_index;

/* option contains bitmap of disinterest */
if (no_response & mask) {
return 0;
}
}

coap_build_hdr((coap_hdr_t *)rbuf, type, coap_get_token(pkt), tkl, code,
ntohs(pkt->hdr->id));
len += payload_len;

return len;
}

ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, const void *token,
size_t token_len, unsigned code, uint16_t id)
{
Expand Down

0 comments on commit c28df13

Please sign in to comment.