From 53f78eb7216e107102766636024d552f3c42b26d Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 10 Nov 2023 12:53:39 +0000 Subject: [PATCH] QUIC ENGINE: Add unused QUIC_ENGINE object Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22674) --- include/internal/quic_engine.h | 80 +++++++++++++++++++ include/internal/quic_port.h | 13 ++++ include/internal/quic_predef.h | 1 + ssl/quic/build.info | 2 +- ssl/quic/quic_engine.c | 137 +++++++++++++++++++++++++++++++++ ssl/quic/quic_engine_local.h | 58 ++++++++++++++ ssl/quic/quic_port.c | 37 +++++++-- ssl/quic/quic_port_local.h | 12 +++ 8 files changed, 332 insertions(+), 8 deletions(-) create mode 100644 include/internal/quic_engine.h create mode 100644 ssl/quic/quic_engine.c create mode 100644 ssl/quic/quic_engine_local.h diff --git a/include/internal/quic_engine.h b/include/internal/quic_engine.h new file mode 100644 index 0000000000..a08f76e53f --- /dev/null +++ b/include/internal/quic_engine.h @@ -0,0 +1,80 @@ +/* + * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ +#ifndef OSSL_QUIC_ENGINE_H +# define OSSL_QUIC_ENGINE_H + +# include + +# include "internal/quic_predef.h" +# include "internal/quic_port.h" +# include "internal/thread_arch.h" + +# ifndef OPENSSL_NO_QUIC + +/* + * QUIC Engine + * =========== + * + * A QUIC Engine (QUIC_ENGINE) represents an event processing domain for the + * purposes of QUIC and contains zero or more subsidiary QUIC_PORT instances + * (each of which currently represents a UDP socket), each of which in turn + * contains zero or more subsidiary QUIC_CHANNEL instances, each of which + * represents a single QUIC connection. All QUIC_PORT instances must belong + * to a QUIC_ENGINE. + * + * A QUIC engine is the root engine in a QUIC event domain, and is responsible + * for managing event processing for all QUIC ports and channels (e.g. timeouts, + * clock management, the QUIC_REACTOR instance, etc.). + */ +typedef struct quic_engine_args_st { + OSSL_LIB_CTX *libctx; + const char *propq; + + /* + * This must be a mutex the lifetime of which will exceed that of the port + * and all channels. The instantiator of the port is responsible for + * providing a mutex as this makes it easier to handle instantiation and + * teardown of channels in situations potentially requiring locking. + * + * Note that this is a MUTEX not a RWLOCK as it needs to be an OS mutex for + * compatibility with an OS's condition variable wait API, whereas RWLOCK + * may, depending on the build configuration, be implemented using an OS's + * mutex primitive or using its RW mutex primitive. + */ + CRYPTO_MUTEX *mutex; + + OSSL_TIME (*now_cb)(void *arg); + void *now_cb_arg; +} QUIC_ENGINE_ARGS; + +QUIC_ENGINE *ossl_quic_engine_new(const QUIC_ENGINE_ARGS *args); + +void ossl_quic_engine_free(QUIC_ENGINE *qeng); + +/* + * Create a port which is a child of the engine. args->engine shall be NULL. + */ +QUIC_PORT *ossl_quic_engine_create_port(QUIC_ENGINE *qeng, + const QUIC_PORT_ARGS *args); + +/* Gets the mutex used by the engine. */ +CRYPTO_MUTEX *ossl_quic_engine_get0_mutex(QUIC_ENGINE *qeng); + +/* Gets the current time. */ +OSSL_TIME ossl_quic_engine_get_time(QUIC_ENGINE *qeng); + +/* For testing use. While enabled, ticking is not performed. */ +void ossl_quic_engine_set_inhibit_tick(QUIC_ENGINE *qeng, int inhibit); + +/* Gets the reactor which can be used to tick/poll on the port. */ +QUIC_REACTOR *ossl_quic_engine_get0_reactor(QUIC_ENGINE *qeng); + +# endif + +#endif diff --git a/include/internal/quic_port.h b/include/internal/quic_port.h index 9e52a1e77c..74578e9267 100644 --- a/include/internal/quic_port.h +++ b/include/internal/quic_port.h @@ -35,8 +35,13 @@ * of legacy compatibility where a caller can create an incoming (server role) * channel and that channel will be automatically be bound to the next incoming * connection. In the future this will go away once QUIC_TSERVER is removed. + * + * All QUIC_PORT instances are created by a QUIC_ENGINE. */ typedef struct quic_port_args_st { + /* The engine which the QUIC port is to be a child of. */ + QUIC_ENGINE *engine; + /* All channels in a QUIC event domain share the same (libctx, propq). */ OSSL_LIB_CTX *libctx; const char *propq; @@ -75,6 +80,7 @@ typedef struct quic_port_args_st { int is_multi_conn; } QUIC_PORT_ARGS; +/* Only QUIC_ENGINE should use this function. */ QUIC_PORT *ossl_quic_port_new(const QUIC_PORT_ARGS *args); void ossl_quic_port_free(QUIC_PORT *port); @@ -110,6 +116,9 @@ int ossl_quic_port_set_net_wbio(QUIC_PORT *port, BIO *net_wbio); */ int ossl_quic_port_update_poll_descriptors(QUIC_PORT *port); +/* Gets the engine which this port is a child of. */ +QUIC_ENGINE *ossl_quic_port_get0_engine(QUIC_PORT *port); + /* Gets the reactor which can be used to tick/poll on the port. */ QUIC_REACTOR *ossl_quic_port_get0_reactor(QUIC_PORT *port); @@ -137,6 +146,10 @@ int ossl_quic_port_is_running(const QUIC_PORT *port); */ void ossl_quic_port_restore_err_state(const QUIC_PORT *port); +/* For use by QUIC_ENGINE. You should not need to call this directly. */ +void ossl_quic_port_subtick(QUIC_PORT *port, QUIC_TICK_RESULT *r, + uint32_t flags); + /* * Events * ====== diff --git a/include/internal/quic_predef.h b/include/internal/quic_predef.h index 6f8f8e5c93..7c7567b9c5 100644 --- a/include/internal/quic_predef.h +++ b/include/internal/quic_predef.h @@ -36,6 +36,7 @@ typedef struct quic_tick_result_st QUIC_TICK_RESULT; typedef struct quic_srtm_st QUIC_SRTM; typedef struct quic_lcidm_st QUIC_LCIDM; typedef struct quic_urxe_st QUIC_URXE; +typedef struct quic_engine_st QUIC_ENGINE; # endif diff --git a/ssl/quic/build.info b/ssl/quic/build.info index d837fc764b..bb6739b18b 100644 --- a/ssl/quic/build.info +++ b/ssl/quic/build.info @@ -9,7 +9,7 @@ SOURCE[$LIBSSL]=quic_cfq.c quic_txpim.c quic_fifd.c quic_txp.c SOURCE[$LIBSSL]=quic_stream_map.c SOURCE[$LIBSSL]=quic_sf_list.c quic_rstream.c quic_sstream.c SOURCE[$LIBSSL]=quic_reactor.c -SOURCE[$LIBSSL]=quic_channel.c quic_port.c +SOURCE[$LIBSSL]=quic_channel.c quic_port.c quic_engine.c SOURCE[$LIBSSL]=quic_tserver.c SOURCE[$LIBSSL]=quic_tls.c SOURCE[$LIBSSL]=quic_thread_assist.c diff --git a/ssl/quic/quic_engine.c b/ssl/quic/quic_engine.c new file mode 100644 index 0000000000..26c859e595 --- /dev/null +++ b/ssl/quic/quic_engine.c @@ -0,0 +1,137 @@ +/* + * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/quic_engine.h" +#include "internal/quic_port.h" +#include "quic_engine_local.h" +#include "quic_port_local.h" +#include "../ssl_local.h" + +/* + * QUIC Engine + * =========== + */ +static int qeng_init(QUIC_ENGINE *qeng); +static void qeng_cleanup(QUIC_ENGINE *qeng); +static void qeng_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags); + +DEFINE_LIST_OF_IMPL(port, QUIC_PORT); + +QUIC_ENGINE *ossl_quic_engine_new(const QUIC_ENGINE_ARGS *args) +{ + QUIC_ENGINE *qeng; + + if ((qeng = OPENSSL_zalloc(sizeof(QUIC_ENGINE))) == NULL) + return NULL; + + qeng->libctx = args->libctx; + qeng->propq = args->propq; + qeng->mutex = args->mutex; + qeng->now_cb = args->now_cb; + qeng->now_cb_arg = args->now_cb_arg; + + if (!qeng_init(qeng)) { + OPENSSL_free(qeng); + return NULL; + } + + return qeng; +} + +void ossl_quic_engine_free(QUIC_ENGINE *qeng) +{ + if (qeng == NULL) + return; + + qeng_cleanup(qeng); + OPENSSL_free(qeng); +} + +static int qeng_init(QUIC_ENGINE *qeng) +{ + ossl_quic_reactor_init(&qeng->rtor, qeng_tick, qeng, ossl_time_zero()); + return 1; +} + +static void qeng_cleanup(QUIC_ENGINE *qeng) +{ + assert(ossl_list_port_num(&qeng->port_list) == 0); +} + +QUIC_REACTOR *ossl_quic_engine_get0_reactor(QUIC_ENGINE *qeng) + +{ + return &qeng->rtor; +} + +CRYPTO_MUTEX *ossl_quic_engine_get0_mutex(QUIC_ENGINE *qeng) +{ + return qeng->mutex; +} + +OSSL_TIME ossl_quic_engine_get_time(QUIC_ENGINE *qeng) +{ + if (qeng->now_cb == NULL) + return ossl_time_now(); + + return qeng->now_cb(qeng->now_cb_arg); +} + +void ossl_quic_engine_set_inhibit_tick(QUIC_ENGINE *qeng, int inhibit) +{ + qeng->inhibit_tick = (inhibit != 0); +} + +/* + * QUIC Engine: Child Object Lifecycle Management + * ============================================== + */ + +QUIC_PORT *ossl_quic_engine_create_port(QUIC_ENGINE *qeng, + const QUIC_PORT_ARGS *args) +{ + QUIC_PORT_ARGS largs = *args; + + if (largs.engine != NULL) + return NULL; + + largs.engine = qeng; + return ossl_quic_port_new(&largs); +} + +/* + * QUIC Engine: Ticker-Mutator + * ========================== + */ + +/* + * The central ticker function called by the reactor. This does everything, or + * at least everything network I/O related. Best effort - not allowed to fail + * "loudly". + */ +static void qeng_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) +{ + QUIC_ENGINE *qeng = arg; + QUIC_PORT *port; + + res->net_read_desired = 0; + res->net_write_desired = 0; + res->tick_deadline = ossl_time_infinite(); + + if (qeng->inhibit_tick) + return; + + /* Iterate through all ports and service them. */ + LIST_FOREACH(port, port, &qeng->port_list) { + QUIC_TICK_RESULT subr = {0}; + + ossl_quic_port_subtick(port, &subr, flags); + ossl_quic_tick_result_merge_into(res, &subr); + } +} diff --git a/ssl/quic/quic_engine_local.h b/ssl/quic/quic_engine_local.h new file mode 100644 index 0000000000..6896e30d85 --- /dev/null +++ b/ssl/quic/quic_engine_local.h @@ -0,0 +1,58 @@ +/* + * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_QUIC_ENGINE_LOCAL_H +# define OSSL_QUIC_ENGINE_LOCAL_H + +# include "internal/quic_engine.h" +# include "internal/quic_reactor.h" + +# ifndef OPENSSL_NO_QUIC + +/* + * QUIC Engine Structure + * ===================== + * + * QUIC engine internals. It is intended that only the QUIC_ENGINE, QUIC_PORT + * and QUIC_CHANNEL implementations be allowed to access this structure + * directly. + * + * Other components should not include this header. + */ +DECLARE_LIST_OF(port, QUIC_PORT); + +struct quic_engine_st { + OSSL_LIB_CTX *libctx; + const char *propq; + + /* + * Master synchronisation mutex for the entire QUIC event domain. Used for + * thread assisted mode synchronisation. We don't own this; the instantiator + * of the port passes it to us and is responsible for freeing it after port + * destruction. + */ + CRYPTO_MUTEX *mutex; + + /* Callback used to get the current time. */ + OSSL_TIME (*now_cb)(void *arg); + void *now_cb_arg; + + /* Asynchronous I/O reactor. */ + QUIC_REACTOR rtor; + + /* List of all child ports. */ + OSSL_LIST(port) port_list; + + /* Inhibit tick for testing purposes? */ + unsigned int inhibit_tick : 1; +}; + +# endif + +#endif diff --git a/ssl/quic/quic_port.c b/ssl/quic/quic_port.c index 46f4b34f9b..897edc4a79 100644 --- a/ssl/quic/quic_port.c +++ b/ssl/quic/quic_port.c @@ -13,6 +13,7 @@ #include "internal/quic_srtm.h" #include "quic_port_local.h" #include "quic_channel_local.h" +#include "quic_engine_local.h" #include "../ssl_local.h" /* @@ -24,12 +25,13 @@ 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, const QUIC_CONN_ID *dcid); static void port_rx_pre(QUIC_PORT *port); +static void port_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags); DEFINE_LIST_OF_IMPL(ch, QUIC_CHANNEL); +DEFINE_LIST_OF_IMPL(port, QUIC_PORT); QUIC_PORT *ossl_quic_port_new(const QUIC_PORT_ARGS *args) { @@ -38,6 +40,7 @@ QUIC_PORT *ossl_quic_port_new(const QUIC_PORT_ARGS *args) if ((port = OPENSSL_zalloc(sizeof(QUIC_PORT))) == NULL) return NULL; + port->engine = args->engine; port->libctx = args->libctx; port->propq = args->propq; port->mutex = args->mutex; @@ -88,10 +91,16 @@ static int port_init(QUIC_PORT *port) if ((port->lcidm = ossl_quic_lcidm_new(port->libctx, rx_short_dcid_len)) == NULL) goto err; - ossl_quic_reactor_init(&port->rtor, port_tick, port, ossl_time_zero()); + if (port->engine == NULL) + 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; port->state = QUIC_PORT_STATE_RUNNING; + if (port->engine != NULL) { + ossl_list_port_insert_tail(&port->engine->port_list, port); + port->on_engine_list = 1; + } return 1; err: @@ -114,6 +123,11 @@ static void port_cleanup(QUIC_PORT *port) OSSL_ERR_STATE_free(port->err_state); port->err_state = NULL; + + if (port->on_engine_list) { + ossl_list_port_remove(&port->engine->port_list, port); + port->on_engine_list = 0; + } } static void port_transition_failed(QUIC_PORT *port) @@ -129,9 +143,14 @@ int ossl_quic_port_is_running(const QUIC_PORT *port) return port->state == QUIC_PORT_STATE_RUNNING; } +QUIC_ENGINE *ossl_quic_port_get0_engine(QUIC_PORT *port) +{ + return port->engine; +} + QUIC_REACTOR *ossl_quic_port_get0_reactor(QUIC_PORT *port) { - return &port->rtor; + return port->engine != NULL ? &port->engine->rtor : &port->rtor; } QUIC_DEMUX *ossl_quic_port_get0_demux(QUIC_PORT *port) @@ -333,13 +352,17 @@ QUIC_CHANNEL *ossl_quic_port_create_incoming(QUIC_PORT *port, SSL *tls) */ /* - * The central ticker function called by the reactor. This does everything, or - * at least everything network I/O related. Best effort - not allowed to fail - * "loudly". + * Tick function for this port. This does everything related to network I/O for + * this port's network BIOs, and services child channels. */ static void port_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) { - QUIC_PORT *port = arg; + ossl_quic_port_subtick(arg, res, flags); +} + +void ossl_quic_port_subtick(QUIC_PORT *port, QUIC_TICK_RESULT *res, + uint32_t flags) +{ QUIC_CHANNEL *ch; res->net_read_desired = 0; diff --git a/ssl/quic/quic_port_local.h b/ssl/quic/quic_port_local.h index 8df3c4ca94..ec12872b72 100644 --- a/ssl/quic/quic_port_local.h +++ b/ssl/quic/quic_port_local.h @@ -41,6 +41,15 @@ enum { }; struct quic_port_st { + /* The engine which this port is a child of. */ + QUIC_ENGINE *engine; + + /* + * QUIC_ENGINE keeps the channels which belong to it on a list for + * bookkeeping purposes. + */ + OSSL_LIST_MEMBER(port, QUIC_PORT); + OSSL_LIB_CTX *libctx; const char *propq; @@ -102,6 +111,9 @@ struct quic_port_st { /* Does this port allow incoming connections? */ unsigned int is_server : 1; + + /* Are we on the QUIC_ENGINE linked list of ports? */ + unsigned int on_engine_list : 1; }; # endif -- 2.34.1