From 4ed6b48d9dd469d43d810fa285257043e9ce9779 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Thu, 9 Nov 2023 10:27:13 +0000 Subject: QUIC PORT, CHANNEL: Move DEMUX and default packet handling out of CHANNEL Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22674) --- include/internal/quic_channel.h | 5 ++ include/internal/quic_port.h | 10 +++ ssl/quic/quic_channel.c | 137 ++++++--------------------------------- ssl/quic/quic_channel_local.h | 9 +-- ssl/quic/quic_port.c | 138 ++++++++++++++++++++++++++++++++++++---- ssl/quic/quic_port_local.h | 11 ++++ ssl/quic/quic_tserver.c | 1 + 7 files changed, 172 insertions(+), 139 deletions(-) diff --git a/include/internal/quic_channel.h b/include/internal/quic_channel.h index 59caf58b30..b8d3edf23f 100644 --- a/include/internal/quic_channel.h +++ b/include/internal/quic_channel.h @@ -262,6 +262,11 @@ void ossl_quic_channel_on_remote_conn_close(QUIC_CHANNEL *ch, void ossl_quic_channel_on_new_conn_id(QUIC_CHANNEL *ch, OSSL_QUIC_FRAME_NEW_CONN_ID *f); +/* Temporarily exposed during QUIC_PORT transition. */ +int ossl_quic_channel_on_new_conn(QUIC_CHANNEL *ch, const BIO_ADDR *peer, + const QUIC_CONN_ID *peer_scid, + const QUIC_CONN_ID *peer_dcid); + /* * Queries and Accessors * ===================== diff --git a/include/internal/quic_port.h b/include/internal/quic_port.h index 08740f5afa..75fcaad87f 100644 --- a/include/internal/quic_port.h +++ b/include/internal/quic_port.h @@ -57,6 +57,13 @@ typedef struct quic_port_args_st { * inside newly created channels. */ SSL_CTX *channel_ctx; + + /* + * If 1, this port is to be used for multiple connections, so + * non-zero-length CIDs should be used. If 0, this port will only be used + * for a single connection, so a zero-length local CID can be used. + */ + int is_multi_conn; } QUIC_PORT_ARGS; typedef struct quic_port_st QUIC_PORT; @@ -104,6 +111,9 @@ CRYPTO_MUTEX *ossl_quic_port_get0_mutex(QUIC_PORT *port); /* Gets the current time. */ OSSL_TIME ossl_quic_port_get_time(QUIC_PORT *port); +int ossl_quic_port_get_rx_short_dcid_len(const QUIC_PORT *port); +int ossl_quic_port_get_tx_init_dcid_len(const QUIC_PORT *port); + # endif #endif diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index 1ea69584c5..2d7784ff04 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -26,7 +26,6 @@ * TODO(QUIC SERVER): Implement retry logic */ -#define INIT_DCID_LEN 8 #define INIT_CRYPTO_RECV_BUF_LEN 16384 #define INIT_CRYPTO_SEND_BUF_LEN 16384 #define INIT_APP_BUF_LEN 8192 @@ -99,10 +98,6 @@ 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_default_packet_handler(QUIC_URXE *e, void *arg); -static int ch_server_on_new_conn(QUIC_CHANNEL *ch, const BIO_ADDR *peer, - const QUIC_CONN_ID *peer_scid, - const QUIC_CONN_ID *peer_dcid); 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); @@ -220,7 +215,7 @@ static void chan_remove_reset_token(QUIC_CHANNEL *ch, uint64_t seq_num) * * TODO(QUIC FUTURE): optimise this to only be called for unparsable packets */ -static int ch_stateless_reset_token_handler(const unsigned char *data, +static int ossl_unused ch_stateless_reset_token_handler(const unsigned char *data, size_t datalen, void *arg) { QUIC_SRT_ELEM srte; @@ -275,7 +270,8 @@ static int ch_init(QUIC_CHANNEL *ch) OSSL_QRX_ARGS qrx_args = {0}; QUIC_TLS_ARGS tls_args = {0}; uint32_t pn_space; - size_t rx_short_cid_len = ch->is_server ? INIT_DCID_LEN : 0; + size_t rx_short_dcid_len = ossl_quic_port_get_rx_short_dcid_len(ch->port); + size_t tx_init_dcid_len = ossl_quic_port_get_tx_init_dcid_len(ch->port); if (ch->port == NULL) goto err; @@ -288,7 +284,7 @@ static int ch_init(QUIC_CHANNEL *ch) /* For clients, generate our initial DCID. */ if (!ch->is_server - && !gen_rand_conn_id(ch->port->libctx, INIT_DCID_LEN, &ch->init_dcid)) + && !gen_rand_conn_id(ch->port->libctx, tx_init_dcid_len, &ch->init_dcid)) goto err; /* We plug in a network write BIO to the QTX later when we get one. */ @@ -395,31 +391,16 @@ static int ch_init(QUIC_CHANNEL *ch) ossl_quic_tx_packetiser_set_ack_tx_cb(ch->txp, ch_on_txp_ack_tx, ch); - if ((ch->demux = ossl_quic_demux_new(/*BIO=*/NULL, - /*Short CID Len=*/rx_short_cid_len, - get_time, ch)) == NULL) - goto err; - /* * Setup a handler to detect stateless reset tokens. */ - ossl_quic_demux_set_stateless_reset_handler(ch->demux, - &ch_stateless_reset_token_handler, - ch); - - /* - * If we are a server, setup our handler for packets not corresponding to - * any known DCID on our end. This is for handling clients establishing new - * connections. - */ - if (ch->is_server) - ossl_quic_demux_set_default_handler(ch->demux, - ch_default_packet_handler, - ch); + //ossl_quic_demux_set_stateless_reset_handler(ch->demux, + // &ch_stateless_reset_token_handler, + // ch); qrx_args.libctx = ch->port->libctx; - qrx_args.demux = ch->demux; - qrx_args.short_conn_id_len = rx_short_cid_len; + qrx_args.demux = ch->port->demux; + qrx_args.short_conn_id_len = rx_short_dcid_len; qrx_args.max_deferred = 32; if ((ch->qrx = ossl_qrx_new(&qrx_args)) == NULL) @@ -531,7 +512,6 @@ static void ch_cleanup(QUIC_CHANNEL *ch) ossl_quic_tls_free(ch->qtls); ossl_qrx_free(ch->qrx); - ossl_quic_demux_free(ch->demux); OPENSSL_free(ch->local_transport_params); OPENSSL_free((char *)ch->terminate_cause.reason); OSSL_ERR_STATE_free(ch->err_state); @@ -690,7 +670,7 @@ int ossl_quic_channel_is_handshake_confirmed(const QUIC_CHANNEL *ch) QUIC_DEMUX *ossl_quic_channel_get0_demux(QUIC_CHANNEL *ch) { - return ch->demux; + return ch->port->demux; } QUIC_PORT *ossl_quic_channel_get0_port(QUIC_CHANNEL *ch) @@ -705,7 +685,7 @@ CRYPTO_MUTEX *ossl_quic_channel_get_mutex(QUIC_CHANNEL *ch) int ossl_quic_channel_has_pending(const QUIC_CHANNEL *ch) { - return ossl_quic_demux_has_pending(ch->demux) + return ossl_quic_demux_has_pending(ch->port->demux) || ossl_qrx_processed_read_pending(ch->qrx); } @@ -2032,7 +2012,7 @@ static void ch_rx_pre(QUIC_CHANNEL *ch) * Get DEMUX to BIO_recvmmsg from the network and queue incoming datagrams * to the appropriate QRX instance. */ - ret = ossl_quic_demux_pump(ch->demux); + ret = ossl_quic_demux_pump(ch->port->demux); if (ret == QUIC_DEMUX_PUMP_RES_STATELESS_RESET) ch_stateless_reset(ch); else if (ret == QUIC_DEMUX_PUMP_RES_PERMANENT_FAIL) @@ -2423,87 +2403,6 @@ static void ch_raise_version_neg_failure(QUIC_CHANNEL *ch) ch_start_terminating(ch, &tcause, 1); } -/* - * This is called by the demux when we get a packet not destined for any known - * DCID. - */ -static void ch_default_packet_handler(QUIC_URXE *e, void *arg) -{ - QUIC_CHANNEL *ch = arg; - PACKET pkt; - QUIC_PKT_HDR hdr; - - if (!ossl_assert(ch->is_server)) - goto undesirable; - - /* - * We only support one connection to our server currently, so if we already - * started one, ignore any new connection attempts. - */ - if (ch->state != QUIC_CHANNEL_STATE_IDLE) - goto undesirable; - - /* - * We have got a packet for an unknown DCID. This might be an attempt to - * open a new connection. - */ - if (e->data_len < QUIC_MIN_INITIAL_DGRAM_LEN) - goto undesirable; - - if (!PACKET_buf_init(&pkt, ossl_quic_urxe_data(e), e->data_len)) - goto err; - - /* - * We set short_conn_id_len to SIZE_MAX here which will cause the decode - * operation to fail if we get a 1-RTT packet. This is fine since we only - * care about Initial packets. - */ - if (!ossl_quic_wire_decode_pkt_hdr(&pkt, SIZE_MAX, 1, 0, &hdr, NULL)) - goto undesirable; - - switch (hdr.version) { - case QUIC_VERSION_1: - break; - - case QUIC_VERSION_NONE: - default: - /* Unknown version or proactive version negotiation request, bail. */ - /* TODO(QUIC SERVER): Handle version negotiation on server side */ - goto undesirable; - } - - /* - * We only care about Initial packets which might be trying to establish a - * connection. - */ - if (hdr.type != QUIC_PKT_TYPE_INITIAL) - goto undesirable; - - /* - * Assume this is a valid attempt to initiate a connection. - * - * We do not register the DCID in the initial packet we received and that - * DCID is not actually used again, thus after provisioning the correct - * Initial keys derived from it (which is done in the call below) we pass - * the received packet directly to the QRX so that it can process it as a - * one-time thing, instead of going through the usual DEMUX DCID-based - * routing. - */ - if (!ch_server_on_new_conn(ch, &e->peer, - &hdr.src_conn_id, - &hdr.dst_conn_id)) - goto err; - - ossl_qrx_inject_urxe(ch->qrx, e); - return; - -err: - ossl_quic_channel_raise_protocol_error(ch, QUIC_ERR_INTERNAL_ERROR, 0, - "internal error"); -undesirable: - ossl_quic_demux_release_urxe(ch->demux, e); -} - /* Try to generate packets and if possible, flush them to the network. */ static int ch_tx(QUIC_CHANNEL *ch) { @@ -2741,7 +2640,7 @@ int ossl_quic_channel_set_net_rbio(QUIC_CHANNEL *ch, BIO *net_rbio) if (!ch_update_poll_desc(ch, net_rbio, /*for_write=*/0)) return 0; - ossl_quic_demux_set_bio(ch->demux, net_rbio); + ossl_quic_demux_set_bio(ch->port->demux, net_rbio); ch->net_rbio = net_rbio; return 1; } @@ -3497,15 +3396,17 @@ static void ch_on_idle_timeout(QUIC_CHANNEL *ch) } /* Called when we, as a server, get a new incoming connection. */ -static int ch_server_on_new_conn(QUIC_CHANNEL *ch, const BIO_ADDR *peer, - const QUIC_CONN_ID *peer_scid, - const QUIC_CONN_ID *peer_dcid) +int ossl_quic_channel_on_new_conn(QUIC_CHANNEL *ch, const BIO_ADDR *peer, + const QUIC_CONN_ID *peer_scid, + const QUIC_CONN_ID *peer_dcid) { if (!ossl_assert(ch->state == QUIC_CHANNEL_STATE_IDLE && ch->is_server)) return 0; + /* Generate a SCID we will use for the connection. */ - if (!gen_rand_conn_id(ch->port->libctx, INIT_DCID_LEN, + if (!gen_rand_conn_id(ch->port->libctx, + ossl_quic_port_get_tx_init_dcid_len(ch->port), &ch->cur_local_cid)) return 0; diff --git a/ssl/quic/quic_channel_local.h b/ssl/quic/quic_channel_local.h index 37cf73c67a..e2aea16f68 100644 --- a/ssl/quic/quic_channel_local.h +++ b/ssl/quic/quic_channel_local.h @@ -87,14 +87,7 @@ struct quic_channel_st { const OSSL_CC_METHOD *cc_method; OSSL_ACKM *ackm; - /* - * RX demuxer. We register incoming DCIDs with this. Since we currently only - * support client operation and use one L4 port per connection, we own the - * demuxer and register a single zero-length DCID with it. - */ - QUIC_DEMUX *demux; - - /* Record layers in the TX and RX directions, plus the RX demuxer. */ + /* Record layers in the TX and RX directions. */ OSSL_QTX *qtx; OSSL_QRX *qrx; diff --git a/ssl/quic/quic_port.c b/ssl/quic/quic_port.c index 66e0d3b0d5..5c5cde8ecd 100644 --- a/ssl/quic/quic_port.c +++ b/ssl/quic/quic_port.c @@ -17,11 +17,13 @@ * QUIC Port Structure * =================== */ +#define INIT_DCID_LEN 8 + static int port_init(QUIC_PORT *port); static void port_cleanup(QUIC_PORT *port); static OSSL_TIME get_time(void *arg); static void port_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags); -//static void port_default_packet_handler(QUIC_URXE *e, void *arg); +static void port_default_packet_handler(QUIC_URXE *e, void *arg); DEFINE_LIST_OF_IMPL(ch, QUIC_CHANNEL); @@ -32,12 +34,13 @@ QUIC_PORT *ossl_quic_port_new(const QUIC_PORT_ARGS *args) if ((port = OPENSSL_zalloc(sizeof(QUIC_PORT))) == NULL) return NULL; - port->libctx = args->libctx; - port->propq = args->propq; - port->mutex = args->mutex; - port->now_cb = args->now_cb; - port->now_cb_arg = args->now_cb_arg; - port->channel_ctx = args->channel_ctx; + port->libctx = args->libctx; + port->propq = args->propq; + port->mutex = args->mutex; + port->now_cb = args->now_cb; + port->now_cb_arg = args->now_cb_arg; + port->channel_ctx = args->channel_ctx; + port->is_multi_conn = args->is_multi_conn; if (!port_init(port)) { OPENSSL_free(port); @@ -58,13 +61,13 @@ void ossl_quic_port_free(QUIC_PORT *port) static int port_init(QUIC_PORT *port) { - size_t rx_short_cid_len = 8; + size_t rx_short_dcid_len = (port->is_multi_conn ? INIT_DCID_LEN : 0); if (port->channel_ctx == NULL) goto err; if ((port->demux = ossl_quic_demux_new(/*BIO=*/NULL, - /*Short CID Len=*/rx_short_cid_len, + /*Short CID Len=*/rx_short_dcid_len, get_time, port)) == NULL) goto err; @@ -74,11 +77,13 @@ static int port_init(QUIC_PORT *port) * connections. */ // if (is_server) - //ossl_quic_demux_set_default_handler(port->demux, - // port_default_packet_handler, - // port); + ossl_quic_demux_set_default_handler(port->demux, + port_default_packet_handler, + port); ossl_quic_reactor_init(&port->rtor, port_tick, port, ossl_time_zero()); + port->rx_short_dcid_len = (unsigned char)rx_short_dcid_len; + port->tx_init_dcid_len = INIT_DCID_LEN; return 1; err: @@ -121,6 +126,15 @@ static OSSL_TIME get_time(void *port) return ossl_quic_port_get_time(port); } +int ossl_quic_port_get_rx_short_dcid_len(const QUIC_PORT *port) +{ + return port->rx_short_dcid_len; +} + +int ossl_quic_port_get_tx_init_dcid_len(const QUIC_PORT *port) +{ + return port->tx_init_dcid_len; +} /* * QUIC Port: Network BIO Configuration @@ -266,7 +280,13 @@ QUIC_CHANNEL *ossl_quic_port_create_outgoing(QUIC_PORT *port, SSL *tls) QUIC_CHANNEL *ossl_quic_port_create_incoming(QUIC_PORT *port, SSL *tls) { - return port_make_channel(port, tls, /*is_server=*/1); + QUIC_CHANNEL *ch; + + assert(port->tserver_ch == NULL); + + ch = port_make_channel(port, tls, /*is_server=*/1); + port->tserver_ch = ch; + return ch; } /* @@ -283,3 +303,95 @@ static void port_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) { /* TODO */ } + +/* + * Handles an incoming connection request and potentially decides to make a + * connection from it. If a new connection is made, the new channel is written + * to *new_ch. + */ +static void port_on_new_conn(QUIC_PORT *port, const BIO_ADDR *peer, + const QUIC_CONN_ID *scid, + const QUIC_CONN_ID *dcid, + QUIC_CHANNEL **new_ch) +{ + if (port->tserver_ch != NULL) { + /* Specially assign to existing channel */ + if (!ossl_quic_channel_on_new_conn(port->tserver_ch, peer, scid, dcid)) + return; + + *new_ch = port->tserver_ch; + port->tserver_ch = NULL; + return; + } +} + +/* + * This is called by the demux when we get a packet not destined for any known + * DCID. + */ +static void port_default_packet_handler(QUIC_URXE *e, void *arg) +{ + QUIC_PORT *port = arg; + PACKET pkt; + QUIC_PKT_HDR hdr; + QUIC_CHANNEL *new_ch = NULL; + + if (port->tserver_ch == NULL) + goto undesirable; + + /* + * We have got a packet for an unknown DCID. This might be an attempt to + * open a new connection. + */ + if (e->data_len < QUIC_MIN_INITIAL_DGRAM_LEN) + goto undesirable; + + if (!PACKET_buf_init(&pkt, ossl_quic_urxe_data(e), e->data_len)) + goto undesirable; + + /* + * We set short_conn_id_len to SIZE_MAX here which will cause the decode + * operation to fail if we get a 1-RTT packet. This is fine since we only + * care about Initial packets. + */ + if (!ossl_quic_wire_decode_pkt_hdr(&pkt, SIZE_MAX, 1, 0, &hdr, NULL)) + goto undesirable; + + switch (hdr.version) { + case QUIC_VERSION_1: + break; + + case QUIC_VERSION_NONE: + default: + /* Unknown version or proactive version negotiation request, bail. */ + /* TODO(QUIC SERVER): Handle version negotiation on server side */ + goto undesirable; + } + + /* + * We only care about Initial packets which might be trying to establish a + * connection. + */ + if (hdr.type != QUIC_PKT_TYPE_INITIAL) + goto undesirable; + + /* + * Try to process this as a valid attempt to initiate a connection. + * + * We do not register the DCID in the Initial packet we received as + * that DCID is not actually used again, thus after provisioning + * the new connection and associated Initial keys, we inject the + * received packet directly to the new channel's QRX so that it can + * process it as a one-time thing, instead of going through the usual + * DEMUX DCID-based routing. + */ + port_on_new_conn(port, &e->peer, &hdr.src_conn_id, &hdr.dst_conn_id, + &new_ch); + if (new_ch != NULL) + ossl_qrx_inject_urxe(new_ch->qrx, e); + + return; + +undesirable: + ossl_quic_demux_release_urxe(port->demux, e); +} diff --git a/ssl/quic/quic_port_local.h b/ssl/quic/quic_port_local.h index fc0521d02b..ff109365aa 100644 --- a/ssl/quic/quic_port_local.h +++ b/ssl/quic/quic_port_local.h @@ -48,6 +48,17 @@ struct quic_port_st { /* List of all child channels. */ OSSL_LIST(ch) channel_list; + + /* Special TSERVER channel. To be removed in the future. */ + QUIC_CHANNEL *tserver_ch; + + /* DCID length used for incoming short header packets. */ + unsigned char rx_short_dcid_len; + /* For clients, CID length used for outgoing Initial packets. */ + unsigned char tx_init_dcid_len; + + /* Is this port created to support multiple connections? */ + unsigned int is_multi_conn : 1; }; # endif diff --git a/ssl/quic/quic_tserver.c b/ssl/quic/quic_tserver.c index 7246963253..bc6668ef90 100644 --- a/ssl/quic/quic_tserver.c +++ b/ssl/quic/quic_tserver.c @@ -122,6 +122,7 @@ QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args, port_args.channel_ctx = srv->ctx; port_args.now_cb = srv->args.now_cb; port_args.now_cb_arg = srv->args.now_cb_arg; + port_args.is_multi_conn = 1; if ((srv->port = ossl_quic_port_new(&port_args)) == NULL) goto err; -- cgit v1.2.3