diff --git a/sys/net/application_layer/gcoap/forward_proxy.c b/sys/net/application_layer/gcoap/forward_proxy.c index b96c32e31944..ab3c3cb31130 100644 --- a/sys/net/application_layer/gcoap/forward_proxy.c +++ b/sys/net/application_layer/gcoap/forward_proxy.c @@ -23,10 +23,10 @@ typedef struct { uint16_t in_use; - uint16_t validating; + uint8_t req_etag_len; sock_udp_ep_t ep; -#if IS_ACTIVE(MODULE_NANOCOAP_CACHE) - uint8_t cache_key[CONFIG_NANOCOAP_CACHE_KEY_LENGTH]; +#if IS_USED(MODULE_NANOCOAP_CACHE) + uint8_t req_etag[COAP_ETAG_LENGTH_MAX]; #endif } client_ep_t; @@ -55,81 +55,8 @@ gcoap_listener_t forward_proxy_listener = { void gcoap_forward_proxy_init(void) { gcoap_register_listener(&forward_proxy_listener); - - /* initialize the nanocoap cache operation, if compiled */ - if (IS_ACTIVE(MODULE_NANOCOAP_CACHE)) { - nanocoap_cache_init(); - } -} - -static int _cache_build_response(nanocoap_cache_entry_t *ce, - coap_pkt_t *pdu, - uint8_t *buf, - size_t len) -{ - if ((pdu->hdr->code == COAP_METHOD_GET) || (pdu->hdr->code == COAP_METHOD_FETCH)) { - uint8_t *req_etag; - /* Searching for more ETags might become necessary in the future */ - ssize_t req_etag_len = coap_opt_get_opaque(pdu, COAP_OPT_ETAG, &req_etag); - - if (req_etag_len > 0) { - /* ETag found, validate from cache entry */ - uint8_t *cache_etag; - ssize_t cache_etag_len = coap_opt_get_opaque(&ce->response_pkt, COAP_OPT_ETAG, - &cache_etag); - - if ((cache_etag_len == req_etag_len) && - (memcmp(req_etag, cache_etag, req_etag_len) == 0)) { - gcoap_resp_init(pdu, buf, len, COAP_CODE_VALID); - coap_opt_add_opaque(pdu, COAP_OPT_ETAG, req_etag, req_etag_len); - return coap_get_total_hdr_len(pdu); - } - } - } - /* Use the same code from the cached content. Use other header - * fields from the incoming request */ - gcoap_resp_init(pdu, buf, len, ce->response_pkt.hdr->code); - /* copy all options and possible payload from the cached response - * to the new response */ - unsigned header_len_req = coap_get_total_hdr_len(pdu); - unsigned header_len_cached = coap_get_total_hdr_len(&ce->response_pkt); - unsigned opt_payload_len = ce->response_len - header_len_cached; - - memcpy((buf + header_len_req), - (ce->response_buf + header_len_cached), - opt_payload_len); - return header_len_req + opt_payload_len; } -static int _cache_lookup_and_process(coap_pkt_t *pdu, - uint8_t *buf, - size_t len, - client_ep_t *cep, - nanocoap_cache_entry_t **ce) -{ - (void) cep; - - uint8_t cache_key[SHA256_DIGEST_LENGTH]; - ztimer_now_t now = ztimer_now(ZTIMER_SEC); - nanocoap_cache_key_generate(pdu, cache_key); - *ce = nanocoap_cache_key_lookup(cache_key); - - /* cache hit, methods are equal, and cache entry is not stale */ - if (*ce && - ((*ce)->request_method == coap_get_code(pdu)) && - ((*ce)->max_age > now)) { - /* use response from cache */ - return _cache_build_response(*ce, pdu, buf, len); - } - -#if IS_ACTIVE(MODULE_NANOCOAP_CACHE) - memcpy(cep->cache_key, cache_key, CONFIG_NANOCOAP_CACHE_KEY_LENGTH); -#endif - - return 0; -} - - static client_ep_t *_allocate_client_ep(sock_udp_ep_t *ep) { client_ep_t *cep; @@ -138,7 +65,7 @@ static client_ep_t *_allocate_client_ep(sock_udp_ep_t *ep) cep++) { if (!cep->in_use) { cep->in_use = 1U; - cep->validating = 0U; + cep->req_etag_len = 0U; memcpy(&cep->ep, ep, sizeof(*ep)); return cep; } @@ -268,60 +195,30 @@ static void _forward_resp_handler(const gcoap_request_memo_t *memo, (void) remote; /* this is the origin server */ client_ep_t *cep = (client_ep_t *)memo->context; - if (memo->state == GCOAP_MEMO_RESP) { - if (!IS_USED(MODULE_NANOCOAP_CACHE) || - /* only forward 2.03 Valid, if client endpoint sent an ETag to validate cached content */ - (pdu->hdr->code != COAP_CODE_VALID) || cep->validating) { - /* forward the response packet as-is to the client */ - gcoap_forward_proxy_dispatch((uint8_t *)pdu->hdr, - (pdu->payload - - (uint8_t *)pdu->hdr + pdu->payload_len), - &cep->ep); - } - #if IS_USED(MODULE_NANOCOAP_CACHE) - /* if response is a 2.03, but client endpoint did not send an ETag to validate content */ - if ((pdu->hdr->code == COAP_CODE_VALID) && !cep->validating) { - nanocoap_cache_entry_t *ce = NULL; - - if ((ce = nanocoap_cache_key_lookup(cep->cache_key))) { - /* update max_age from response and send cached response */ - uint32_t max_age = 60; - - coap_opt_get_uint(pdu, COAP_OPT_MAX_AGE, &max_age); - ce->max_age = ztimer_now(ZTIMER_SEC) + max_age; - /* copy all options and possible payload from the cached response - * to the new response */ - unsigned header_len_req = coap_get_total_hdr_len(pdu); - unsigned header_len_cached = coap_get_total_hdr_len(&ce->response_pkt); - uint8_t *buf = (uint8_t *)pdu->hdr; - size_t len = pdu->payload_len + header_len_req; - gcoap_resp_init(pdu, buf, len, ce->response_pkt.hdr->code); - unsigned opt_payload_len = ce->response_len - header_len_cached; - - memcpy((buf + header_len_req), - (ce->response_buf + header_len_cached), - opt_payload_len); - gcoap_forward_proxy_dispatch(buf, header_len_req + opt_payload_len, &cep->ep); - } - else { - /* cache entry to be validated cached out while trying to validate */ - /* TODO: re-request real response (without ETag) */ - } - return; - } - coap_pkt_t req; - if (memo->send_limit == GCOAP_SEND_LIMIT_NON) { - req.hdr = (coap_hdr_t *) &memo->msg.hdr_buf[0]; + /* 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)) { + gcoap_resp_init(pdu, (uint8_t *)pdu->hdr, + (pdu->payload - (uint8_t *)pdu->hdr + pdu->payload_len), + COAP_CODE_VALID); + coap_opt_add_opaque(pdu, COAP_OPT_ETAG, cep->req_etag, cep->req_etag_len); + coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); } - else { - req.hdr = (coap_hdr_t *) memo->msg.data.pdu_buf; - } - size_t pdu_len = pdu->payload_len + - (pdu->payload - (uint8_t *)pdu->hdr); - nanocoap_cache_process(cep->cache_key, coap_get_code(&req), pdu, pdu_len); -#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 */ + gcoap_forward_proxy_dispatch((uint8_t *)pdu->hdr, + (pdu->payload - + (uint8_t *)pdu->hdr + pdu->payload_len), + &cep->ep); + _free_client_ep(cep); } @@ -348,38 +245,46 @@ static int _gcoap_forward_proxy_add_uri_path(coap_pkt_t *pkt, static int _gcoap_forward_proxy_copy_options(coap_pkt_t *pkt, coap_pkt_t *client_pkt, client_ep_t *cep, - uri_parser_result_t *urip, - nanocoap_cache_entry_t *ce) + uri_parser_result_t *urip) { /* copy all options from client_pkt to pkt */ coap_optpos_t opt = {0, 0}; uint8_t *value; - bool uri_path_added = false, etag_added = false; + bool uri_path_added = false; + bool etag_added = false; for (int i = 0; i < client_pkt->options_len; i++) { ssize_t optlen = coap_opt_get_next(client_pkt, &opt, &value, !i); - /* wrt to ETag: we always have at least the Proxy-URI option in the client_pkt, so - * we should hit at least once (and it's opt_num is also >= COAP_OPT_ETAG) */ + /* wrt to ETag option slack: we always have at least the Proxy-URI option in the client_pkt, + * so * we should hit at least once (and it's opt_num is also >= COAP_OPT_ETAG) */ if (optlen >= 0) { - /* Add ETag before any larger opt num, but skip old ETag if ETag in cache */ - if (!etag_added && (opt.opt_num >= COAP_OPT_ETAG)) { - if (IS_USED(MODULE_NANOCOAP_CACHE) && ce) { - uint8_t *etag; - /* Searching for more ETags might become necessary in the future */ - ssize_t etag_len = coap_opt_get_opaque(&ce->response_pkt, COAP_OPT_ETAG, &etag); - - if (etag_len > 0) { - coap_opt_add_opaque(pkt, COAP_OPT_ETAG, etag, etag_len); - } + 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()) */ + if (coap_opt_add_opaque(pkt, COAP_OPT_ETAG, tmp, sizeof(tmp))) { etag_added = true; } } - /* skip original ETag of request, otherwise we might accidentally fill the cache - * with 2.03 Valid responses which would require additional handling */ +#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) { - cep->validating = 1U; + uint8_t *etag; + ssize_t etag_len = coap_opt_get_opaque(pkt, COAP_OPT_ETAG, &etag); + if (etag_len > 0) { + /* only set request ETag if it can be properly parsed */ + cep->req_etag_len = (uint8_t)etag_len; + memcpy(cep->req_etag, etag, etag_len); + } + /* skip original ETag of request, otherwise we might accidentally fill the cache + * with 2.03 Valid responses which would require additional handling. + * For upstream validation, gcoap_req_send() will add an ETag, if the response + * 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) { @@ -410,8 +315,7 @@ static int _gcoap_forward_proxy_copy_options(coap_pkt_t *pkt, static int _gcoap_forward_proxy_via_coap(coap_pkt_t *client_pkt, client_ep_t *client_ep, - uri_parser_result_t *urip, - nanocoap_cache_entry_t *ce) + uri_parser_result_t *urip) { coap_pkt_t pkt; sock_udp_ep_t origin_server_ep; @@ -447,7 +351,7 @@ static int _gcoap_forward_proxy_via_coap(coap_pkt_t *client_pkt, } /* copy all options from client_pkt to pkt */ - len = _gcoap_forward_proxy_copy_options(&pkt, client_pkt, client_ep, urip, ce); + len = _gcoap_forward_proxy_copy_options(&pkt, client_pkt, client_ep, urip); if (len == -EINVAL) { return -EINVAL; @@ -466,27 +370,11 @@ int gcoap_forward_proxy_request_process(coap_pkt_t *pkt, ssize_t optlen = 0; client_ep_t *cep = _allocate_client_ep(client); - nanocoap_cache_entry_t *ce = NULL; if (!cep) { return -ENOMEM; } - if (IS_ACTIVE(MODULE_NANOCOAP_CACHE)) { - int pdu_len = _cache_lookup_and_process(pkt, - (uint8_t *)pkt->hdr, - CONFIG_GCOAP_PDU_BUF_SIZE, - cep, - &ce); - /* if a valid cache entry was found, then pdu_len contains the - * length of that response message */ - if (pdu_len > 0) { - _free_client_ep(cep); - return pdu_len; - } - /* if there was no cache hit, then we continue forwarding */ - } - optlen = coap_get_proxy_uri(pkt, &uri); if (optlen < 0) { @@ -505,7 +393,7 @@ int gcoap_forward_proxy_request_process(coap_pkt_t *pkt, /* target is using CoAP */ if (!strncmp("coap", urip.scheme, urip.scheme_len)) { - int res = _gcoap_forward_proxy_via_coap(pkt, cep, &urip, ce); + int res = _gcoap_forward_proxy_via_coap(pkt, cep, &urip); if (res < 0) { _free_client_ep(cep); return -EINVAL; diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index c8c1df08d0d1..cfaca4a56bb6 100644 --- a/sys/net/application_layer/gcoap/gcoap.c +++ b/sys/net/application_layer/gcoap/gcoap.c @@ -1283,14 +1283,13 @@ kernel_pid_t gcoap_init(void) /* randomize initial value */ atomic_init(&_coap_state.next_message_id, (unsigned)random_uint32()); + if (IS_USED(MODULE_NANOCOAP_CACHE)) { + nanocoap_cache_init(); + } /* initialize the forward proxy operation, if compiled */ if (IS_ACTIVE(MODULE_GCOAP_FORWARD_PROXY)) { gcoap_forward_proxy_init(); } - /* gcoap_forward_proxy_init() also initializes nanocoap_cache_init() */ - else if (IS_USED(MODULE_NANOCOAP_CACHE)) { - nanocoap_cache_init(); - } return _pid; }