From: Neil Horman Date: Tue, 28 Jan 2025 13:58:19 +0000 (-0500) Subject: Reduce our NEW_TOKEN send rate. X-Git-Tag: openssl-3.5.0-alpha1~229 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=642fde861688581e0be672fe89735477102a562e;p=thirdparty%2Fopenssl.git Reduce our NEW_TOKEN send rate. Currently, we send a NEW_TOKEN frame on every new validated connection, but thats not necessecary. Since NEW_TOKEN tokens have a lifetime of 1 hour currently, we really only need to send a NEW_TOKEN if: 1) We validated a RETRY token or 2) We validated a NEW_TOKEN for which the lifetime is nearing its limit So lets do that. When we validate a token, only generate a NEW_TOKEN if the current token is a RETRY token, or if its a NEW_TOKEN, and there is less than 10% of the tokens lifetime remaining. This lets clients use NEW_TOKENS repeatedly (as per the RFC), and saves us some network bandwith. Reviewed-by: Matt Caswell Reviewed-by: Saša Nedvědický (Merged from https://github.com/openssl/openssl/pull/26517) --- diff --git a/ssl/quic/quic_port.c b/ssl/quic/quic_port.c index 3276ec885c4..ff3c327758c 100644 --- a/ssl/quic/quic_port.c +++ b/ssl/quic/quic_port.c @@ -1240,6 +1240,15 @@ static void port_send_version_negotiation(QUIC_PORT *port, BIO_ADDR *peer, "port version negotiation send failed"); } +/** + * @brief defintions of token lifetimes + * + * RETRY tokens are only valid for 10 seconds + * NEW_TOKEN tokens have a lifetime of 3600 sec (1 hour) + */ + +#define RETRY_LIFETIME 10 +#define NEW_TOKEN_LIFETIME 3600 /** * @brief Validates a received token in a QUIC packet header. * @@ -1272,15 +1281,17 @@ static void port_send_version_negotiation(QUIC_PORT *port, BIO_ADDR *peer, */ static int port_validate_token(QUIC_PKT_HDR *hdr, QUIC_PORT *port, BIO_ADDR *peer, QUIC_CONN_ID *odcid, - QUIC_CONN_ID *scid) + QUIC_CONN_ID *scid, uint8_t *gen_new_token) { int ret = 0; QUIC_VALIDATION_TOKEN token = { 0 }; - unsigned long long time_diff; + uint64_t time_diff; size_t remote_addr_len, dec_token_len; unsigned char *remote_addr = NULL, dec_token[MARSHALLED_TOKEN_MAX_LEN]; OSSL_TIME now = ossl_time_now(); + *gen_new_token = 0; + if (!decrypt_validation_token(port, hdr->token, hdr->token_len, NULL, &dec_token_len) || dec_token_len > MARSHALLED_TOKEN_MAX_LEN @@ -1297,8 +1308,8 @@ static int port_validate_token(QUIC_PKT_HDR *hdr, QUIC_PORT *port, goto err; time_diff = ossl_time2seconds(ossl_time_abs_difference(token.timestamp, now)); - if ((token.is_retry && time_diff > 10) - || (!token.is_retry && time_diff > 3600)) + if ((token.is_retry && time_diff > RETRY_LIFETIME) + || (!token.is_retry && time_diff > NEW_TOKEN_LIFETIME)) goto err; /* Validate remote address */ @@ -1336,6 +1347,24 @@ static int port_validate_token(QUIC_PKT_HDR *hdr, QUIC_PORT *port, *scid = hdr->src_conn_id; } + /* + * Determine if we need to send a NEW_TOKEN frame + * If we validated a retry token, we should always + * send a NEW_TOKEN frame to the client + * + * If however, we validated a NEW_TOKEN, which may be + * reused multiple times, only send a NEW_TOKEN frame + * if the existing received token has 10% of its lifetime + * remaining. This prevents us from constantly sending + * NEW_TOKEN frames on every connection when not needed + */ + if (token.is_retry) { + *gen_new_token = 1; + } else { + if (time_diff > ((NEW_TOKEN_LIFETIME * 9) / 10)) + *gen_new_token = 1; + } + ret = 1; err: cleanup_validation_token(&token); @@ -1402,6 +1431,7 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg, QUIC_PKT_HDR hdr; QUIC_CHANNEL *ch = NULL, *new_ch = NULL; QUIC_CONN_ID odcid, scid; + uint8_t gen_new_token = 0; uint64_t cause_flags = 0; /* Don't handle anything if we are no longer running. */ @@ -1504,7 +1534,7 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg, */ if (hdr.token != NULL) { if (port_validate_token(&hdr, port, &e->peer, - &odcid, &scid) == 0) { + &odcid, &scid, &gen_new_token) == 0) { /* * RFC 9000 s 8.1.3 * When a server receives an Initial packet with an address @@ -1529,7 +1559,8 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg, /* * Generate a token for sending in a later NEW_TOKEN frame */ - generate_new_token(new_ch, &e->peer); + if (gen_new_token == 1) + generate_new_token(new_ch, &e->peer); /* * The channel will do all the LCID registration needed, but as an