QUIC ENGINE: Add unused QUIC_ENGINE object
authorHugo Landau <hlandau@openssl.org>
Fri, 10 Nov 2023 12:53:39 +0000 (12:53 +0000)
committerHugo Landau <hlandau@openssl.org>
Thu, 21 Dec 2023 08:12:06 +0000 (08:12 +0000)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22674)

include/internal/quic_engine.h [new file with mode: 0644]
include/internal/quic_port.h
include/internal/quic_predef.h
ssl/quic/build.info
ssl/quic/quic_engine.c [new file with mode: 0644]
ssl/quic/quic_engine_local.h [new file with mode: 0644]
ssl/quic/quic_port.c
ssl/quic/quic_port_local.h

diff --git a/include/internal/quic_engine.h b/include/internal/quic_engine.h
new file mode 100644 (file)
index 0000000..a08f76e
--- /dev/null
@@ -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 <openssl/ssl.h>
+
+# 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
index 9e52a1e77ccea8cc0a078bfade1ca1257d062705..74578e9267af8919e413ec746d6f49f32f214542 100644 (file)
  * 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
  * ======
index 6f8f8e5c93f548c412119723286e4f5978de0dea..7c7567b9c52e56c10872ebd18df7912cf9e59844 100644 (file)
@@ -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
 
index d837fc764b80f6c0cc340a0b5d08846d678091ff..bb6739b18b038b504e281c1698b8773d220811c4 100644 (file)
@@ -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 (file)
index 0000000..26c859e
--- /dev/null
@@ -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 (file)
index 0000000..6896e30
--- /dev/null
@@ -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
index 46f4b34f9bd6e9565ad1bf201ebe2828a0452f95..897edc4a795025702476cf7566147698e238cb85 100644 (file)
@@ -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"
 
 /*
 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;
index 8df3c4ca946ad6f5bf0245bc73b34bf8a1528f1a..ec12872b7217f678263a41d38d582371772467ff 100644 (file)
@@ -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