--- /dev/null
+/*
+ * 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
* 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;
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);
*/
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);
*/
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
* ======
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
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
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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
#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)
{
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;
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:
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)
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)
*/
/*
- * 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;
};
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;
/* 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