From 6107619899e50b307e9644625a8050de88c603cc Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Thu, 9 Nov 2023 10:27:13 +0000 Subject: QUIC PORT: Partially move stateless reset handling to port Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22674) --- include/internal/quic_channel.h | 3 +++ ssl/quic/quic_channel.c | 50 ++++------------------------------------- ssl/quic/quic_port.c | 49 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 46 deletions(-) diff --git a/include/internal/quic_channel.h b/include/internal/quic_channel.h index c696f93324..bdc48e5084 100644 --- a/include/internal/quic_channel.h +++ b/include/internal/quic_channel.h @@ -273,6 +273,9 @@ void ossl_quic_channel_subtick(QUIC_CHANNEL *ch, QUIC_TICK_RESULT *r, /* For use by QUIC_PORT only. */ void ossl_quic_channel_raise_net_error(QUIC_CHANNEL *ch); +/* For use by QUIC_PORT only. */ +void ossl_quic_channel_on_stateless_reset(QUIC_CHANNEL *ch); + /* * Queries and Accessors * ===================== diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index aa5ea0971e..c363b3bdde 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -89,12 +89,10 @@ static int ch_discard_el(QUIC_CHANNEL *ch, static void ch_on_idle_timeout(QUIC_CHANNEL *ch); static void ch_update_idle(QUIC_CHANNEL *ch); static void ch_update_ping_deadline(QUIC_CHANNEL *ch); -static void ch_stateless_reset(QUIC_CHANNEL *ch); static void ch_on_terminating_timeout(QUIC_CHANNEL *ch); static void ch_start_terminating(QUIC_CHANNEL *ch, const QUIC_TERMINATE_CAUSE *tcause, int force_immediate); -static int ch_stateless_reset_token_handler(const unsigned char *data, size_t datalen, void *arg); static void ch_on_txp_ack_tx(const OSSL_QUIC_FRAME_ACK *ack, uint32_t pn_space, void *arg); static void ch_rx_handle_version_neg(QUIC_CHANNEL *ch, OSSL_QRX_PKT *pkt); @@ -207,47 +205,6 @@ static void chan_remove_reset_token(QUIC_CHANNEL *ch, uint64_t seq_num) } } -/* - * This is called by the demux whenever a new datagram arrives - * - * TODO(QUIC FUTURE): optimise this to only be called for unparsable packets - */ -static int ossl_unused ch_stateless_reset_token_handler(const unsigned char *data, - size_t datalen, void *arg) -{ - QUIC_SRT_ELEM srte; - QUIC_CHANNEL *ch = (QUIC_CHANNEL *)arg; - - /* - * Perform some fast and cheap checks for a packet not being a stateless - * reset token. RFC 9000 s. 10.3 specifies this layout for stateless - * reset packets: - * - * Stateless Reset { - * Fixed Bits (2) = 1, - * Unpredictable Bits (38..), - * Stateless Reset Token (128), - * } - * - * It also specifies: - * However, endpoints MUST treat any packet ending in a valid - * stateless reset token as a Stateless Reset, as other QUIC - * versions might allow the use of a long header. - * - * We can rapidly check for the minimum length and that the first pair - * of bits in the first byte are 01 or 11. - * - * The function returns 1 if it is a stateless reset packet, 0 if it isn't - * and -1 if an error was encountered. - */ - if (datalen < QUIC_STATELESS_RESET_TOKEN_LEN + 5 || (0100 & *data) != 0100) - return 0; - memset(&srte, 0, sizeof(srte)); - if (!reset_token_obfuscate(&srte, data + datalen - sizeof(srte.token))) - return -1; - return lh_QUIC_SRT_ELEM_retrieve(ch->srt_hash_tok, &srte) != NULL; -} - /* * QUIC Channel Initialization and Teardown * ======================================== @@ -3072,12 +3029,13 @@ static void ch_save_err_state(QUIC_CHANNEL *ch) OSSL_ERR_STATE_save(ch->err_state); } -static void ossl_unused ch_stateless_reset(QUIC_CHANNEL *ch) +void ossl_quic_channel_on_stateless_reset(QUIC_CHANNEL *ch) { QUIC_TERMINATE_CAUSE tcause = {0}; - tcause.error_code = QUIC_ERR_NO_ERROR; - ch_start_terminating(ch, &tcause, 1); + tcause.error_code = QUIC_ERR_NO_ERROR; + tcause.remote = 1; + ch_start_terminating(ch, &tcause, 0); } void ossl_quic_channel_raise_net_error(QUIC_CHANNEL *ch) diff --git a/ssl/quic/quic_port.c b/ssl/quic/quic_port.c index b128477c0a..bf351e923c 100644 --- a/ssl/quic/quic_port.c +++ b/ssl/quic/quic_port.c @@ -381,6 +381,52 @@ static void port_on_new_conn(QUIC_PORT *port, const BIO_ADDR *peer, } } +static int port_try_handle_stateless_reset(QUIC_PORT *port, const QUIC_URXE *e) +{ + size_t i; + const unsigned char *data = ossl_quic_urxe_data(e); + void *opaque = NULL; + + /* + * Perform some fast and cheap checks for a packet not being a stateless + * reset token. RFC 9000 s. 10.3 specifies this layout for stateless + * reset packets: + * + * Stateless Reset { + * Fixed Bits (2) = 1, + * Unpredictable Bits (38..), + * Stateless Reset Token (128), + * } + * + * It also specifies: + * However, endpoints MUST treat any packet ending in a valid + * stateless reset token as a Stateless Reset, as other QUIC + * versions might allow the use of a long header. + * + * We can rapidly check for the minimum length and that the first pair + * of bits in the first byte are 01 or 11. + * + * The function returns 1 if it is a stateless reset packet, 0 if it isn't + * and -1 if an error was encountered. + */ + if (e->data_len < QUIC_STATELESS_RESET_TOKEN_LEN + 5 + || (0100 & *data) != 0100) + return 0; + + for (i = 0;; ++i) { + if (!ossl_quic_srtm_lookup(port->srtm, + (QUIC_STATELESS_RESET_TOKEN *)(data + e->data_len + - sizeof(QUIC_STATELESS_RESET_TOKEN)), + i, &opaque, NULL)) + break; + + assert(opaque != NULL); + ossl_quic_channel_on_stateless_reset((QUIC_CHANNEL *)opaque); + } + + return i > 0; +} + /* * This is called by the demux when we get a packet not destined for any known * DCID. @@ -392,6 +438,9 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg) QUIC_PKT_HDR hdr; QUIC_CHANNEL *new_ch = NULL; + if (port_try_handle_stateless_reset(port, e)) + goto undesirable; + // TODO review this if (port->tserver_ch == NULL) goto undesirable; -- cgit v1.2.3