From 632b0c7e8c9700b4f3fe49ccccda9caa1fbd390f Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Thu, 9 Nov 2023 10:27:13 +0000 Subject: QUIC PORT, CHANNEL: Move ticking code into QUIC_PORT Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22674) --- include/internal/quic_channel.h | 16 ++-- include/internal/quic_port.h | 18 +++++ ssl/quic/quic_channel.c | 161 ++++++---------------------------------- ssl/quic/quic_channel_local.h | 9 --- ssl/quic/quic_impl.c | 4 +- ssl/quic/quic_port.c | 74 +++++++++++++++++- ssl/quic/quic_port_local.h | 3 + test/quic_multistream_test.c | 6 +- 8 files changed, 128 insertions(+), 163 deletions(-) diff --git a/include/internal/quic_channel.h b/include/internal/quic_channel.h index b8d3edf23f..d0046d07d1 100644 --- a/include/internal/quic_channel.h +++ b/include/internal/quic_channel.h @@ -267,6 +267,13 @@ 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); +/* For use by QUIC_PORT. You should not need to call this directly. */ +void ossl_quic_channel_subtick(QUIC_CHANNEL *ch, QUIC_TICK_RESULT *r, + uint32_t flags); + +/* For use by QUIC_PORT only. */ +void ossl_quic_channel_raise_net_error(QUIC_CHANNEL *ch); + /* * Queries and Accessors * ===================== @@ -294,12 +301,6 @@ BIO *ossl_quic_channel_get_net_wbio(QUIC_CHANNEL *ch); int ossl_quic_channel_set_net_rbio(QUIC_CHANNEL *ch, BIO *net_rbio); int ossl_quic_channel_set_net_wbio(QUIC_CHANNEL *ch, BIO *net_wbio); -/* - * Re-poll the network BIOs already set to determine if their support - * for polling has changed. - */ -int ossl_quic_channel_update_poll_descriptors(QUIC_CHANNEL *ch); - /* * Returns an existing stream by stream ID. Returns NULL if the stream does not * exist. @@ -396,9 +397,6 @@ int ossl_quic_channel_has_pending(const QUIC_CHANNEL *ch); /* Force transmission of an ACK-eliciting packet. */ int ossl_quic_channel_ping(QUIC_CHANNEL *ch); -/* For testing use. While enabled, ticking is not performed. */ -void ossl_quic_channel_set_inhibit_tick(QUIC_CHANNEL *ch, int inhibit); - /* * These queries exist for diagnostic purposes only. They may roll over. * Do not rely on them for non-testing purposes. diff --git a/include/internal/quic_port.h b/include/internal/quic_port.h index 75fcaad87f..3d9e5b0211 100644 --- a/include/internal/quic_port.h +++ b/include/internal/quic_port.h @@ -97,6 +97,10 @@ BIO *ossl_quic_port_get_net_wbio(QUIC_PORT *port); int ossl_quic_port_set_net_rbio(QUIC_PORT *port, BIO *net_rbio); int ossl_quic_port_set_net_wbio(QUIC_PORT *port, BIO *net_wbio); +/* + * Re-poll the network BIOs already set to determine if their support + * for polling has changed. + */ int ossl_quic_port_update_poll_descriptors(QUIC_PORT *port); /* Gets the reactor which can be used to tick/poll on the port. */ @@ -114,6 +118,20 @@ 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); +/* For testing use. While enabled, ticking is not performed. */ +void ossl_quic_port_set_inhibit_tick(QUIC_PORT *port, int inhibit); + +/* + * Events + * ====== + */ + +/* + * Called if a permanent network error occurs. Terminates all channels + * immediately. + */ +void ossl_quic_port_raise_net_error(QUIC_PORT *port); + # endif #endif diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index 2d7784ff04..24f428a3f8 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -48,10 +48,8 @@ DEFINE_LIST_OF_IMPL(ch, QUIC_CHANNEL); static void ch_save_err_state(QUIC_CHANNEL *ch); -static void ch_rx_pre(QUIC_CHANNEL *ch); static int ch_rx(QUIC_CHANNEL *ch, int channel_only); static int ch_tx(QUIC_CHANNEL *ch); -static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags); static int ch_tick_tls(QUIC_CHANNEL *ch, int channel_only); static void ch_rx_handle_packet(QUIC_CHANNEL *ch, int channel_only); static OSSL_TIME ch_determine_next_tick_deadline(QUIC_CHANNEL *ch); @@ -92,7 +90,6 @@ 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_raise_net_error(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, @@ -467,8 +464,6 @@ static int ch_init(QUIC_CHANNEL *ch) goto err; ch_update_idle(ch); - ossl_quic_reactor_init(&ch->rtor, ch_tick, ch, - ch_determine_next_tick_deadline(ch)); ossl_list_ch_insert_tail(&ch->port->channel_list, ch); ch->on_port_list = 1; return 1; @@ -601,7 +596,7 @@ int ossl_quic_channel_set_peer_addr(QUIC_CHANNEL *ch, const BIO_ADDR *peer_addr) QUIC_REACTOR *ossl_quic_channel_get_reactor(QUIC_CHANNEL *ch) { - return &ch->rtor; + return ossl_quic_port_get0_reactor(ch->port); } QUIC_STREAM_MAP *ossl_quic_channel_get_qsm(QUIC_CHANNEL *ch) @@ -1839,20 +1834,20 @@ err: * at least everything network I/O related. Best effort - not allowed to fail * "loudly". */ -static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) +void ossl_quic_channel_subtick(QUIC_CHANNEL *ch, QUIC_TICK_RESULT *res, + uint32_t flags) { OSSL_TIME now, deadline; - QUIC_CHANNEL *ch = arg; int channel_only = (flags & QUIC_REACTOR_TICK_FLAG_CHANNEL_ONLY) != 0; /* * When we tick the QUIC connection, we do everything we need to do - * periodically. In order, we: + * periodically. Network I/O handling will already have been performed + * as necessary by the QUIC port. Thus, in order, we: * - * - handle any incoming data from the network; - * - handle any timer events which are due to fire (ACKM, etc.) - * - write any data to the network due to be sent, to the extent - * possible; + * - handle any packets the DEMUX has queued up for us; + * - handle any timer events which are due to fire (ACKM, etc.); + * - generate any packets which need to be sent; * - determine the time at which we should next be ticked. */ @@ -1880,13 +1875,10 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) } } - if (!ch->inhibit_tick) { + if (!ch->port->inhibit_tick) { /* Handle RXKU timeouts. */ ch_rxku_tick(ch); - /* Handle any incoming data from network. */ - ch_rx_pre(ch); - do { /* Process queued incoming packets. */ ch->did_tls_tick = 0; @@ -1923,7 +1915,7 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) * Idle timeout differs from normal protocol violation because we do * not send a CONN_CLOSE frame; go straight to TERMINATED. */ - if (!ch->inhibit_tick) + if (!ch->port->inhibit_tick) ch_on_idle_timeout(ch); res->net_read_desired = 0; @@ -1932,7 +1924,7 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) return; } - if (!ch->inhibit_tick) { + if (!ch->port->inhibit_tick) { deadline = ossl_ackm_get_loss_detection_deadline(ch->ackm); if (!ossl_time_is_zero(deadline) && ossl_time_compare(now, deadline) >= 0) @@ -1954,7 +1946,7 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) ch_update_ping_deadline(ch); } - /* Write any data to the network due to be sent. */ + /* Queue any data to be sent for transmission. */ ch_tx(ch); /* Do stream GC. */ @@ -1965,14 +1957,13 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) res->tick_deadline = ch_determine_next_tick_deadline(ch); /* - * Always process network input unless we are now terminated. - * Although we had not terminated at the beginning of this tick, network - * errors in ch_rx_pre() or ch_tx() may have caused us to transition to the - * Terminated state. + * Always process network input unless we are now terminated. Although we + * had not terminated at the beginning of this tick, network errors in + * ch_tx() may have caused us to transition to the Terminated state. */ res->net_read_desired = !ossl_quic_channel_is_terminated(ch); - /* We want to write to the network if we have any in our queue. */ + /* We want to write to the network if we have any data in our TX queue. */ res->net_write_desired = (!ossl_quic_channel_is_terminated(ch) && ossl_qtx_get_queue_len_datagrams(ch->qtx) > 0); @@ -2000,32 +1991,6 @@ static int ch_tick_tls(QUIC_CHANNEL *ch, int channel_only) return 1; } -/* Process incoming datagrams, if any. */ -static void ch_rx_pre(QUIC_CHANNEL *ch) -{ - int ret; - - if (!ch->is_server && !ch->have_sent_any_pkt) - return; - - /* - * Get DEMUX to BIO_recvmmsg from the network and queue incoming datagrams - * to the appropriate QRX instance. - */ - 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) - /* - * We don't care about transient failure, but permanent failure means we - * should tear down the connection as though a protocol violation - * occurred. Skip straight to the Terminating state as there is no point - * trying to send CONNECTION_CLOSE frames if the network BIO is not - * operating correctly. - */ - ch_raise_net_error(ch); -} - /* Check incoming forged packet limit and terminate connection if needed. */ static void ch_rx_check_forged_pkt_limit(QUIC_CHANNEL *ch) { @@ -2508,7 +2473,7 @@ static int ch_tx(QUIC_CHANNEL *ch) case QTX_FLUSH_NET_RES_PERMANENT_FAIL: default: /* Permanent underlying network BIO, start terminating. */ - ch_raise_net_error(ch); + ossl_quic_port_raise_net_error(ch->port); break; } @@ -2571,91 +2536,14 @@ static OSSL_TIME ch_determine_next_tick_deadline(QUIC_CHANNEL *ch) * ======================================= */ -/* Determines whether we can support a given poll descriptor. */ -static int validate_poll_descriptor(const BIO_POLL_DESCRIPTOR *d) -{ - if (d->type == BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD && d->value.fd < 0) { - ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT); - return 0; - } - - return 1; -} - -BIO *ossl_quic_channel_get_net_rbio(QUIC_CHANNEL *ch) -{ - return ch->net_rbio; -} - -BIO *ossl_quic_channel_get_net_wbio(QUIC_CHANNEL *ch) -{ - return ch->net_wbio; -} - -static int ch_update_poll_desc(QUIC_CHANNEL *ch, BIO *net_bio, int for_write) -{ - BIO_POLL_DESCRIPTOR d = {0}; - - if (net_bio == NULL - || (!for_write && !BIO_get_rpoll_descriptor(net_bio, &d)) - || (for_write && !BIO_get_wpoll_descriptor(net_bio, &d))) - /* Non-pollable BIO */ - d.type = BIO_POLL_DESCRIPTOR_TYPE_NONE; - - if (!validate_poll_descriptor(&d)) - return 0; - - if (for_write) - ossl_quic_reactor_set_poll_w(&ch->rtor, &d); - else - ossl_quic_reactor_set_poll_r(&ch->rtor, &d); - - return 1; -} - -int ossl_quic_channel_update_poll_descriptors(QUIC_CHANNEL *ch) -{ - int ok = 1; - - if (!ch_update_poll_desc(ch, ch->net_rbio, /*for_write=*/0)) - ok = 0; - - if (!ch_update_poll_desc(ch, ch->net_wbio, /*for_write=*/1)) - ok = 0; - - return ok; -} - -/* - * QUIC_CHANNEL does not ref any BIO it is provided with, nor is any ref - * transferred to it. The caller (i.e., QUIC_CONNECTION) is responsible for - * ensuring the BIO lasts until the channel is freed or the BIO is switched out - * for another BIO by a subsequent successful call to this function. - */ int ossl_quic_channel_set_net_rbio(QUIC_CHANNEL *ch, BIO *net_rbio) { - if (ch->net_rbio == net_rbio) - return 1; - - if (!ch_update_poll_desc(ch, net_rbio, /*for_write=*/0)) - return 0; - - ossl_quic_demux_set_bio(ch->port->demux, net_rbio); - ch->net_rbio = net_rbio; - return 1; + return ossl_quic_port_set_net_rbio(ch->port, net_rbio); } int ossl_quic_channel_set_net_wbio(QUIC_CHANNEL *ch, BIO *net_wbio) { - if (ch->net_wbio == net_wbio) - return 1; - - if (!ch_update_poll_desc(ch, net_wbio, /*for_write=*/1)) - return 0; - - ossl_qtx_set_bio(ch->qtx, net_wbio); - ch->net_wbio = net_wbio; - return 1; + return ossl_quic_port_set_net_wbio(ch->port, net_wbio); } /* @@ -2695,7 +2583,7 @@ int ossl_quic_channel_start(QUIC_CHANNEL *ch) if (!ch_tick_tls(ch, /*channel_only=*/0)) return 0; - ossl_quic_reactor_tick(&ch->rtor, 0); /* best effort */ + ossl_quic_reactor_tick(ossl_quic_port_get0_reactor(ch->port), 0); /* best effort */ return 1; } @@ -3199,7 +3087,7 @@ static void ch_save_err_state(QUIC_CHANNEL *ch) OSSL_ERR_STATE_save(ch->err_state); } -static void ch_stateless_reset(QUIC_CHANNEL *ch) +static void ossl_unused ch_stateless_reset(QUIC_CHANNEL *ch) { QUIC_TERMINATE_CAUSE tcause = {0}; @@ -3207,7 +3095,7 @@ static void ch_stateless_reset(QUIC_CHANNEL *ch) ch_start_terminating(ch, &tcause, 1); } -static void ch_raise_net_error(QUIC_CHANNEL *ch) +void ossl_quic_channel_raise_net_error(QUIC_CHANNEL *ch) { QUIC_TERMINATE_CAUSE tcause = {0}; @@ -3698,11 +3586,6 @@ int ossl_quic_channel_ping(QUIC_CHANNEL *ch) return 1; } -void ossl_quic_channel_set_inhibit_tick(QUIC_CHANNEL *ch, int inhibit) -{ - ch->inhibit_tick = (inhibit != 0); -} - uint16_t ossl_quic_channel_get_diag_num_rx_ack(QUIC_CHANNEL *ch) { return ch->diag_num_rx_ack; diff --git a/ssl/quic/quic_channel_local.h b/ssl/quic/quic_channel_local.h index e2aea16f68..dafe03fe8a 100644 --- a/ssl/quic/quic_channel_local.h +++ b/ssl/quic/quic_channel_local.h @@ -58,15 +58,9 @@ struct quic_channel_st { */ unsigned char *local_transport_params; - /* Asynchronous I/O reactor. */ - QUIC_REACTOR rtor; - /* Our current L4 peer address, if any. */ BIO_ADDR cur_peer_addr; - /* Network-side read and write BIOs. */ - BIO *net_rbio, *net_wbio; - /* * Subcomponents of the connection. All of these components are instantiated * and owned by us. @@ -442,9 +436,6 @@ struct quic_channel_st { */ unsigned int protocol_error : 1; - /* Inhibit tick for testing purposes? */ - unsigned int inhibit_tick : 1; - /* Are we using addressed mode? */ unsigned int addressed_mode : 1; diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index d1138f89f5..751d5b846a 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -64,7 +64,7 @@ static int block_until_pred(QUIC_CONNECTION *qc, * Any attempt to block auto-disables tick inhibition as otherwise we will * hang around forever. */ - ossl_quic_channel_set_inhibit_tick(qc->ch, 0); + ossl_quic_port_set_inhibit_tick(qc->port, 0); rtor = ossl_quic_channel_get_reactor(qc->ch); return ossl_quic_reactor_block_until_pred(rtor, pred, pred_arg, flags, @@ -856,7 +856,7 @@ static int qc_can_support_blocking_cached(QUIC_CONNECTION *qc) static void qc_update_can_support_blocking(QUIC_CONNECTION *qc) { - ossl_quic_channel_update_poll_descriptors(qc->ch); /* best effort */ + ossl_quic_port_update_poll_descriptors(qc->port); /* best effort */ } static void qc_update_blocking_mode(QUIC_CONNECTION *qc) diff --git a/ssl/quic/quic_port.c b/ssl/quic/quic_port.c index 5c5cde8ecd..ee4b2900e0 100644 --- a/ssl/quic/quic_port.c +++ b/ssl/quic/quic_port.c @@ -24,6 +24,7 @@ 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_rx_pre(QUIC_PORT *port); DEFINE_LIST_OF_IMPL(ch, QUIC_CHANNEL); @@ -217,13 +218,17 @@ int ossl_quic_port_set_net_rbio(QUIC_PORT *port, BIO *net_rbio) int ossl_quic_port_set_net_wbio(QUIC_PORT *port, BIO *net_wbio) { + QUIC_CHANNEL *ch; + if (port->net_wbio == net_wbio) return 1; if (!port_update_poll_desc(port, net_wbio, /*for_write=*/1)) return 0; - //ossl_qtx_set_bio(port->qtx, net_wbio); + LIST_FOREACH(ch, ch, &port->channel_list) + ossl_qtx_set_bio(ch->qtx, net_wbio); + port->net_wbio = net_wbio; return 1; } @@ -301,7 +306,49 @@ QUIC_CHANNEL *ossl_quic_port_create_incoming(QUIC_PORT *port, SSL *tls) */ static void port_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) { - /* TODO */ + QUIC_PORT *port = arg; + QUIC_CHANNEL *ch; + + res->net_read_desired = 0; + res->net_write_desired = 0; + res->tick_deadline = ossl_time_infinite(); + + if (!port->inhibit_tick) { + /* Handle any incoming data from network. */ + port_rx_pre(port); + + /* Iterate through all channels and service them. */ + LIST_FOREACH(ch, ch, &port->channel_list) { + QUIC_TICK_RESULT subr = {0}; + + ossl_quic_channel_subtick(ch, &subr, flags); + ossl_quic_tick_result_merge_into(res, &subr); + } + } +} + +/* Process incoming datagrams, if any. */ +static void port_rx_pre(QUIC_PORT *port) +{ + int ret; + + // TODO !have_sent_any_pkt + + /* + * Get DEMUX to BIO_recvmmsg from the network and queue incoming datagrams + * to the appropriate QRX instances. + */ + ret = ossl_quic_demux_pump(port->demux); + // TODO: handle ret, stateless reset + + if (ret == QUIC_DEMUX_PUMP_RES_PERMANENT_FAIL) + /* + * We don't care about transient failure, but permanent failure means we + * should tear down the port. All connections skip straight to the + * Terminated state as there is no point trying to send CONNECTION_CLOSE + * frames if the network BIO is not operating correctly. + */ + ossl_quic_port_raise_net_error(port); } /* @@ -336,9 +383,17 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg) QUIC_PKT_HDR hdr; QUIC_CHANNEL *new_ch = NULL; + // TODO review this if (port->tserver_ch == NULL) goto undesirable; + // TODO allow_incoming + //if (!ossl_assert(ch->is_server)) + // goto undesirable; + + //TODO 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. @@ -395,3 +450,18 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg) undesirable: ossl_quic_demux_release_urxe(port->demux, e); } + +void ossl_quic_port_set_inhibit_tick(QUIC_PORT *port, int inhibit) +{ + port->inhibit_tick = (inhibit != 0); +} + +void ossl_quic_port_raise_net_error(QUIC_PORT *port) +{ + QUIC_CHANNEL *ch; + + // TODO fsm + + LIST_FOREACH(ch, ch, &port->channel_list) + ossl_quic_channel_raise_net_error(ch); +} diff --git a/ssl/quic/quic_port_local.h b/ssl/quic/quic_port_local.h index ff109365aa..ad6638eb2f 100644 --- a/ssl/quic/quic_port_local.h +++ b/ssl/quic/quic_port_local.h @@ -59,6 +59,9 @@ struct quic_port_st { /* Is this port created to support multiple connections? */ unsigned int is_multi_conn : 1; + + /* Inhibit tick for testing purposes? */ + unsigned int inhibit_tick : 1; }; # endif diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index d0560bb041..604c2f986b 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -14,6 +14,7 @@ #include "internal/quic_ssl.h" #include "internal/quic_error.h" #include "internal/quic_stream_map.h" +#include "internal/quic_port.h" #include "testutil.h" #include "helpers/quictestlib.h" #if defined(OPENSSL_THREADS) @@ -1594,7 +1595,7 @@ static int run_script_worker(struct helper *h, const struct script_op *script, QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn); SSL_SHUTDOWN_EX_ARGS args = {0}; - ossl_quic_channel_set_inhibit_tick(ch, 0); + ossl_quic_port_set_inhibit_tick(ossl_quic_channel_get0_port(ch), 0); if (!TEST_ptr(c_tgt)) goto out; @@ -1918,7 +1919,8 @@ static int run_script_worker(struct helper *h, const struct script_op *script, { QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn); - ossl_quic_channel_set_inhibit_tick(ch, op->arg1); + ossl_quic_port_set_inhibit_tick(ossl_quic_channel_get0_port(ch), + op->arg1); } break; -- cgit v1.2.3