1
0
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:
Leandro Lanzieri 2022-10-14 11:23:48 +02:00 committed by GitHub
commit 70acefaa55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 180 additions and 48 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
/** @} */

View File

@ -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);
}
/** @} */