mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #18386 from miri64/gcoap_forward_proxy/enh/empty-ack
gcoap_forward_proxy: send empty ACK when response takes too long
This commit is contained in:
commit
70acefaa55
@ -29,6 +29,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "net/coap.h"
|
||||
#include "net/nanocoap.h"
|
||||
#include "net/gcoap.h"
|
||||
|
||||
@ -36,6 +37,18 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @addtogroup net_gcoap_conf
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Timeout in milliseconds for the forward proxy to send an empty ACK without response
|
||||
*/
|
||||
#ifndef CONFIG_GCOAP_FORWARD_PROXY_EMPTY_ACK_MS
|
||||
#define CONFIG_GCOAP_FORWARD_PROXY_EMPTY_ACK_MS ((CONFIG_COAP_ACK_TIMEOUT_MS / 4) * 3)
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Registers a listener for forward proxy operation
|
||||
*/
|
||||
@ -68,21 +81,6 @@ void gcoap_forward_proxy_find_req_memo(gcoap_request_memo_t **memo_ptr,
|
||||
coap_pkt_t *src_pdu,
|
||||
const sock_udp_ep_t *remote);
|
||||
|
||||
/**
|
||||
* @brief Sends a buffer containing a CoAP message to the @p remote endpoint
|
||||
*
|
||||
* @param[in] buf Buffer that contains the CoAP message to be sent
|
||||
* @param[in] len Length of @p buf
|
||||
* @param[in] remote Remote endpoint to send the message to
|
||||
*
|
||||
* @note see sock_udp_send() for all return valus.
|
||||
*
|
||||
* @return length of the packet
|
||||
* @return < 0 on error
|
||||
*/
|
||||
ssize_t gcoap_forward_proxy_dispatch(const uint8_t *buf,
|
||||
size_t len, sock_udp_ep_t *remote);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -13,6 +13,18 @@ menuconfig KCONFIG_USEMODULE_GCOAP
|
||||
|
||||
if KCONFIG_USEMODULE_GCOAP
|
||||
|
||||
menuconfig KCONFIG_USEMODULE_GCOAP_FORWARD_PROXY
|
||||
bool "Configure forward proxy"
|
||||
depends on USEMODULE_GCOAP_FORWARD_PROXY
|
||||
help
|
||||
Configure forward proxy of Gcoap using Kconfig.
|
||||
|
||||
if KCONFIG_USEMODULE_GCOAP_FORWARD_PROXY
|
||||
config GCOAP_FORWARD_PROXY_EMPTY_ACK_MS
|
||||
int "Timeout in milliseconds for the forward proxy to send an empty ACK without response"
|
||||
default 1500
|
||||
endif # KCONFIG_USEMODULE_GCOAP_FORWARD_PROXY
|
||||
|
||||
menuconfig KCONFIG_USEMODULE_GCOAP_DNS
|
||||
bool "Configure DNS-over-CoAPS implementation in GCoAP"
|
||||
depends on USEMODULE_GCOAP_DNS
|
||||
|
@ -17,23 +17,37 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "event.h"
|
||||
#include "kernel_defines.h"
|
||||
#include "net/gcoap.h"
|
||||
#include "net/gcoap/forward_proxy.h"
|
||||
#include "uri_parser.h"
|
||||
#include "net/nanocoap/cache.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#define CLIENT_EP_FLAGS_IN_USE 0x80
|
||||
#define CLIENT_EP_FLAGS_RESP_TYPE_MASK 0x30
|
||||
#define CLIENT_EP_FLAGS_RESP_TYPE_POS 4U
|
||||
#define CLIENT_EP_FLAGS_ETAG_LEN_MASK 0x0f
|
||||
#define CLIENT_EP_FLAGS_ETAG_LEN_POS 0U
|
||||
|
||||
typedef struct {
|
||||
bool in_use;
|
||||
uint8_t req_etag_len;
|
||||
sock_udp_ep_t ep;
|
||||
uint16_t mid;
|
||||
uint8_t flags;
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
uint8_t req_etag[COAP_ETAG_LENGTH_MAX];
|
||||
#endif
|
||||
ztimer_t empty_ack_timer;
|
||||
event_t event;
|
||||
} client_ep_t;
|
||||
|
||||
extern uint16_t gcoap_next_msg_id(void);
|
||||
extern void gcoap_forward_proxy_post_event(void *arg);
|
||||
|
||||
static uint8_t proxy_req_buf[CONFIG_GCOAP_PDU_BUF_SIZE];
|
||||
static client_ep_t _client_eps[CONFIG_GCOAP_REQ_WAITING_MAX];
|
||||
|
||||
@ -42,6 +56,12 @@ static int _request_matcher_forward_proxy(gcoap_listener_t *listener,
|
||||
coap_pkt_t *pdu);
|
||||
static ssize_t _forward_proxy_handler(coap_pkt_t* pdu, uint8_t *buf,
|
||||
size_t len, coap_request_ctx_t *ctx);
|
||||
static bool _cep_in_use(client_ep_t *cep);
|
||||
static void _cep_set_in_use(client_ep_t *cep);
|
||||
static uint8_t _cep_get_response_type(client_ep_t *cep);
|
||||
static void _cep_set_response_type(client_ep_t *cep, uint8_t resp_type);
|
||||
static uint8_t _cep_get_req_etag_len(client_ep_t *cep);
|
||||
static void _cep_set_req_etag_len(client_ep_t *cep, uint8_t req_etag_len);
|
||||
|
||||
const coap_resource_t forward_proxy_resources[] = {
|
||||
{ "/", COAP_IGNORE, _forward_proxy_handler, NULL },
|
||||
@ -67,9 +87,9 @@ static client_ep_t *_allocate_client_ep(const sock_udp_ep_t *ep)
|
||||
for (cep = _client_eps;
|
||||
cep < (_client_eps + CONFIG_GCOAP_REQ_WAITING_MAX);
|
||||
cep++) {
|
||||
if (!cep->in_use) {
|
||||
cep->in_use = true;
|
||||
cep->req_etag_len = 0U;
|
||||
if (!_cep_in_use(cep)) {
|
||||
_cep_set_in_use(cep);
|
||||
_cep_set_req_etag_len(cep, 0);
|
||||
memcpy(&cep->ep, ep, sizeof(*ep));
|
||||
return cep;
|
||||
}
|
||||
@ -79,6 +99,7 @@ static client_ep_t *_allocate_client_ep(const sock_udp_ep_t *ep)
|
||||
|
||||
static void _free_client_ep(client_ep_t *cep)
|
||||
{
|
||||
ztimer_remove(ZTIMER_MSEC, &cep->empty_ack_timer);
|
||||
memset(cep, 0, sizeof(*cep));
|
||||
}
|
||||
|
||||
@ -184,24 +205,65 @@ static bool _parse_endpoint(sock_udp_ep_t *remote,
|
||||
return true;
|
||||
}
|
||||
|
||||
static ssize_t _dispatch_msg(const void *buf, size_t len, sock_udp_ep_t *remote)
|
||||
{
|
||||
/* Yes it's not a request -- but turns out there is nothing in
|
||||
* gcoap_req_send_tl that is actually request specific, especially if we
|
||||
* don't assign a callback. */
|
||||
ssize_t res = gcoap_req_send_tl(buf, len, remote, NULL, NULL,
|
||||
GCOAP_SOCKET_TYPE_UDP);
|
||||
if (res <= 0) {
|
||||
DEBUG("gcoap_forward_proxy: unable to dispatch message: %d\n", -res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void _send_empty_ack(event_t *event)
|
||||
{
|
||||
coap_hdr_t buf;
|
||||
client_ep_t *cep = container_of(event, client_ep_t, event);
|
||||
|
||||
if (_cep_get_response_type(cep) != COAP_TYPE_ACK) {
|
||||
return;
|
||||
}
|
||||
_cep_set_response_type(cep, COAP_TYPE_CON);
|
||||
|
||||
/* Flipping byte order as unlike in the other places where mid is
|
||||
* used, coap_build_hdr would actually flip it back */
|
||||
coap_build_hdr(&buf, COAP_TYPE_ACK, NULL, 0, 0, ntohs(cep->mid));
|
||||
_dispatch_msg(&buf, sizeof(buf), &cep->ep);
|
||||
}
|
||||
|
||||
static void _set_response_type(coap_pkt_t *pdu, uint8_t resp_type)
|
||||
{
|
||||
coap_hdr_set_type(pdu->hdr, resp_type);
|
||||
if (resp_type == COAP_TYPE_CON) {
|
||||
pdu->hdr->id = htons(gcoap_next_msg_id());
|
||||
}
|
||||
}
|
||||
|
||||
static void _forward_resp_handler(const gcoap_request_memo_t *memo,
|
||||
coap_pkt_t* pdu,
|
||||
const sock_udp_ep_t *remote)
|
||||
{
|
||||
(void) remote; /* this is the origin server */
|
||||
client_ep_t *cep = (client_ep_t *)memo->context;
|
||||
size_t buf_len = (pdu->payload - (uint8_t *)pdu->hdr) + pdu->payload_len;
|
||||
|
||||
size_t buf_len;
|
||||
|
||||
/* No harm done in removing a timer that's not active */
|
||||
ztimer_remove(ZTIMER_MSEC, &cep->empty_ack_timer);
|
||||
buf_len = coap_get_total_len(pdu);
|
||||
if (memo->state == GCOAP_MEMO_RESP) {
|
||||
uint8_t req_etag_len = _cep_get_req_etag_len(cep);
|
||||
|
||||
if (req_etag_len > 0) {
|
||||
/* req_tag in cep is pre-processor guarded so we need to as well */
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
/* req_tag in cep is pre-processor guarded so we need to as well */
|
||||
if (cep->req_etag_len > 0) {
|
||||
uint8_t *resp_etag;
|
||||
|
||||
/* check if we can just send 2.03 Valid instead */
|
||||
if ((cep->req_etag_len == coap_opt_get_opaque(pdu, COAP_OPT_ETAG, &resp_etag)) &&
|
||||
(memcmp(cep->req_etag, resp_etag, cep->req_etag_len) == 0)) {
|
||||
if ((req_etag_len == coap_opt_get_opaque(pdu, COAP_OPT_ETAG, &resp_etag)) &&
|
||||
(memcmp(cep->req_etag, resp_etag, req_etag_len) == 0)) {
|
||||
uint32_t max_age;
|
||||
|
||||
if (coap_opt_get_uint(pdu, COAP_OPT_MAX_AGE, &max_age) < 0) {
|
||||
@ -210,17 +272,17 @@ static void _forward_resp_handler(const gcoap_request_memo_t *memo,
|
||||
max_age = 60U;
|
||||
}
|
||||
gcoap_resp_init(pdu, (uint8_t *)pdu->hdr, buf_len, COAP_CODE_VALID);
|
||||
coap_opt_add_opaque(pdu, COAP_OPT_ETAG, cep->req_etag, cep->req_etag_len);
|
||||
coap_opt_add_opaque(pdu, COAP_OPT_ETAG, cep->req_etag, req_etag_len);
|
||||
if (max_age != 60U) {
|
||||
/* only include Max-Age option if it is not the default value */
|
||||
coap_opt_add_uint(pdu, COAP_OPT_MAX_AGE, max_age);
|
||||
}
|
||||
coap_opt_finish(pdu, COAP_OPT_FINISH_NONE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* we do not need to check if valid came from upstream as this is already automatically
|
||||
* converted by the client-side to the cached response */
|
||||
#endif
|
||||
/* else forward the response packet as-is to the client */
|
||||
}
|
||||
else if (memo->state == GCOAP_MEMO_RESP_TRUNC) {
|
||||
@ -231,11 +293,9 @@ static void _forward_resp_handler(const gcoap_request_memo_t *memo,
|
||||
gcoap_resp_init(pdu, (uint8_t *)pdu->hdr, buf_len, COAP_CODE_INTERNAL_SERVER_ERROR);
|
||||
coap_opt_finish(pdu, COAP_OPT_FINISH_NONE);
|
||||
}
|
||||
_set_response_type(pdu, _cep_get_response_type(cep));
|
||||
/* don't use buf_len here, in case the above `gcoap_resp_init`s changed `pdu` */
|
||||
gcoap_forward_proxy_dispatch((uint8_t *)pdu->hdr,
|
||||
(pdu->payload -
|
||||
(uint8_t *)pdu->hdr + pdu->payload_len),
|
||||
&cep->ep);
|
||||
_dispatch_msg(pdu->hdr, coap_get_total_len(pdu), &cep->ep);
|
||||
_free_client_ep(cep);
|
||||
}
|
||||
|
||||
@ -277,19 +337,20 @@ static int _gcoap_forward_proxy_copy_options(coap_pkt_t *pkt,
|
||||
if (optlen >= 0) {
|
||||
if (IS_USED(MODULE_NANOCOAP_CACHE) && !etag_added && (opt.opt_num >= COAP_OPT_ETAG)) {
|
||||
static const uint8_t tmp[COAP_ETAG_LENGTH_MAX] = { 0 };
|
||||
/* add slack to maybe add an ETag on stale cache hit later, as is done in gcoap_req_send()
|
||||
* (which we circumvented in _gcoap_forward_proxy_via_coap()) */
|
||||
/* add slack to maybe add an ETag on stale cache hit later, as is done in
|
||||
* gcoap_req_send() (which we circumvented in _gcoap_forward_proxy_via_coap()) */
|
||||
if (coap_opt_add_opaque(pkt, COAP_OPT_ETAG, tmp, sizeof(tmp))) {
|
||||
etag_added = true;
|
||||
}
|
||||
}
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
/* req_tag in cep is pre-processor guarded so we need to as well */
|
||||
if (opt.opt_num == COAP_OPT_ETAG) {
|
||||
if (cep->req_etag_len == 0) {
|
||||
if (IS_USED(MODULE_NANOCOAP_CACHE) && opt.opt_num == COAP_OPT_ETAG) {
|
||||
if (_cep_get_req_etag_len(cep) == 0) {
|
||||
/* TODO: what to do on multiple ETags? */
|
||||
cep->req_etag_len = (uint8_t)optlen;
|
||||
_cep_set_req_etag_len(cep, (uint8_t)optlen);
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
/* req_tag in cep is pre-processor guarded so we need to as well */
|
||||
memcpy(cep->req_etag, value, optlen);
|
||||
#endif
|
||||
}
|
||||
/* skip original ETag of request, otherwise we might accidentally fill the cache
|
||||
* with 2.03 Valid responses which would require additional handling.
|
||||
@ -297,9 +358,6 @@ static int _gcoap_forward_proxy_copy_options(coap_pkt_t *pkt,
|
||||
* was in cache */
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
(void)cep;
|
||||
#endif
|
||||
/* add URI-PATH before any larger opt num */
|
||||
if (!uri_path_added && (opt.opt_num > COAP_OPT_URI_PATH)) {
|
||||
if (_gcoap_forward_proxy_add_uri_path(pkt, urip) == -EINVAL) {
|
||||
@ -352,6 +410,14 @@ static int _gcoap_forward_proxy_via_coap(coap_pkt_t *client_pkt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (coap_get_type(client_pkt) == COAP_TYPE_CON) {
|
||||
client_ep->empty_ack_timer.callback = gcoap_forward_proxy_post_event;
|
||||
client_ep->empty_ack_timer.arg = &client_ep->event;
|
||||
client_ep->event.handler = _send_empty_ack;
|
||||
ztimer_set(ZTIMER_MSEC, &client_ep->empty_ack_timer,
|
||||
CONFIG_GCOAP_FORWARD_PROXY_EMPTY_ACK_MS);
|
||||
}
|
||||
|
||||
unsigned token_len = coap_get_token_len(client_pkt);
|
||||
|
||||
coap_pkt_init(&pkt, proxy_req_buf, CONFIG_GCOAP_PDU_BUF_SIZE,
|
||||
@ -390,6 +456,12 @@ int gcoap_forward_proxy_request_process(coap_pkt_t *pkt,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cep->mid = pkt->hdr->id;
|
||||
_cep_set_response_type(
|
||||
cep,
|
||||
(coap_get_type(pkt) == COAP_TYPE_CON) ? COAP_TYPE_ACK : COAP_TYPE_NON
|
||||
);
|
||||
|
||||
optlen = coap_get_proxy_uri(pkt, &uri);
|
||||
|
||||
if (optlen < 0) {
|
||||
@ -423,4 +495,42 @@ int gcoap_forward_proxy_request_process(coap_pkt_t *pkt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool _cep_in_use(client_ep_t *cep)
|
||||
{
|
||||
return cep->flags & CLIENT_EP_FLAGS_IN_USE;
|
||||
}
|
||||
|
||||
static void _cep_set_in_use(client_ep_t *cep)
|
||||
{
|
||||
cep->flags |= CLIENT_EP_FLAGS_IN_USE;
|
||||
}
|
||||
|
||||
static uint8_t _cep_get_response_type(client_ep_t *cep)
|
||||
{
|
||||
return (cep->flags & CLIENT_EP_FLAGS_RESP_TYPE_MASK) >> CLIENT_EP_FLAGS_RESP_TYPE_POS;
|
||||
}
|
||||
|
||||
static void _cep_set_response_type(client_ep_t *cep, uint8_t resp_type)
|
||||
{
|
||||
cep->flags &= ~CLIENT_EP_FLAGS_RESP_TYPE_MASK;
|
||||
cep->flags |= (resp_type << CLIENT_EP_FLAGS_RESP_TYPE_POS) & CLIENT_EP_FLAGS_RESP_TYPE_MASK;
|
||||
}
|
||||
|
||||
static uint8_t _cep_get_req_etag_len(client_ep_t *cep)
|
||||
{
|
||||
if (IS_USED(MODULE_NANOCOAP_CACHE)) {
|
||||
return (cep->flags & CLIENT_EP_FLAGS_ETAG_LEN_MASK) >> CLIENT_EP_FLAGS_ETAG_LEN_POS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _cep_set_req_etag_len(client_ep_t *cep, uint8_t req_etag_len)
|
||||
{
|
||||
if (IS_USED(MODULE_NANOCOAP_CACHE)) {
|
||||
cep->flags &= ~CLIENT_EP_FLAGS_ETAG_LEN_MASK;
|
||||
cep->flags |= (req_etag_len << CLIENT_EP_FLAGS_ETAG_LEN_POS)
|
||||
& CLIENT_EP_FLAGS_ETAG_LEN_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
@ -839,7 +839,8 @@ static bool _memo_ep_is_multicast(const gcoap_request_memo_t *memo)
|
||||
* memo_ptr[out] -- Registered request memo, or NULL if not found
|
||||
* src_pdu[in] -- PDU for token to match
|
||||
* remote[in] -- Remote endpoint to match
|
||||
* by_mid[in] -- true if matches are to be done based on Message ID, otherwise they are done by token
|
||||
* by_mid[in] -- true if matches are to be done based on Message ID, otherwise they are done by
|
||||
* token
|
||||
*/
|
||||
static void _find_req_memo(gcoap_request_memo_t **memo_ptr, coap_pkt_t *src_pdu,
|
||||
const sock_udp_ep_t *remote, bool by_mid)
|
||||
@ -1125,7 +1126,8 @@ static ssize_t _tl_authenticate(gcoap_socket_t *sock, const sock_udp_ep_t *remot
|
||||
uint32_t start = ztimer_now(ZTIMER_MSEC);
|
||||
res = ztimer_msg_receive_timeout(ZTIMER_MSEC, &msg, timeout);
|
||||
|
||||
/* ensure whole timeout time for the case we receive other messages than DTLS_EVENT_CONNECTED */
|
||||
/* ensure whole timeout time for the case we receive other messages than
|
||||
* DTLS_EVENT_CONNECTED */
|
||||
if (timeout != SOCK_NO_TIMEOUT) {
|
||||
uint32_t diff = (ztimer_now(ZTIMER_MSEC) - start);
|
||||
timeout = (diff > timeout) ? 0: timeout - diff;
|
||||
@ -1301,6 +1303,11 @@ static ssize_t _cache_check(const uint8_t *buf, size_t len,
|
||||
DEBUG("gcoap: parse failure for cache lookup: %d\n", (int)res);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (coap_get_code_class(&req) != COAP_CLASS_REQ) {
|
||||
/* Not a request so ignore, as gcoap_req_send might have been used with
|
||||
* its undocumented function to send a CON response from submodule */
|
||||
return len;
|
||||
}
|
||||
|
||||
*cache_hit = _cache_lookup(memo, &req, &ce);
|
||||
|
||||
@ -1376,6 +1383,11 @@ kernel_pid_t gcoap_init(void)
|
||||
return _pid;
|
||||
}
|
||||
|
||||
uint16_t gcoap_next_msg_id(void)
|
||||
{
|
||||
return (uint16_t)atomic_fetch_add(&_coap_state.next_message_id, 1);
|
||||
}
|
||||
|
||||
void gcoap_register_listener(gcoap_listener_t *listener)
|
||||
{
|
||||
/* That item will be overridden, ensure that the user expecting different
|
||||
@ -1402,7 +1414,7 @@ int gcoap_req_init_path_buffer(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
||||
pdu->hdr = (coap_hdr_t *)buf;
|
||||
|
||||
/* generate token */
|
||||
uint16_t msgid = (uint16_t)atomic_fetch_add(&_coap_state.next_message_id, 1);
|
||||
uint16_t msgid = gcoap_next_msg_id();
|
||||
ssize_t res;
|
||||
if (code) {
|
||||
#if CONFIG_GCOAP_TOKENLEN
|
||||
@ -1620,7 +1632,7 @@ int gcoap_obs_init(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
||||
}
|
||||
|
||||
pdu->hdr = (coap_hdr_t *)buf;
|
||||
uint16_t msgid = (uint16_t)atomic_fetch_add(&_coap_state.next_message_id, 1);
|
||||
uint16_t msgid = gcoap_next_msg_id();
|
||||
ssize_t hdrlen = coap_build_hdr(pdu->hdr, COAP_TYPE_NON, &memo->token[0],
|
||||
memo->token_len, COAP_CODE_CONTENT, msgid);
|
||||
|
||||
@ -1761,9 +1773,9 @@ void gcoap_forward_proxy_find_req_memo(gcoap_request_memo_t **memo_ptr,
|
||||
_find_req_memo(memo_ptr, src_pdu, remote, false);
|
||||
}
|
||||
|
||||
ssize_t gcoap_forward_proxy_dispatch(const uint8_t *buf, size_t len, sock_udp_ep_t *remote)
|
||||
void gcoap_forward_proxy_post_event(void *arg)
|
||||
{
|
||||
return sock_udp_send(&_sock_udp, buf, len, remote);
|
||||
event_post(&_queue, arg);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
Loading…
Reference in New Issue
Block a user