From: Hugo Landau Date: Thu, 9 Nov 2023 10:27:13 +0000 (+0000) Subject: QUIC PORT: Add basic unwired QUIC_PORT object X-Git-Tag: openssl-3.3.0-alpha1~445 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=154131da112ed28b619dc6a7a0bec255e2a79316;p=thirdparty%2Fopenssl.git QUIC PORT: Add basic unwired QUIC_PORT object Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22674) --- diff --git a/include/internal/quic_port.h b/include/internal/quic_port.h new file mode 100644 index 00000000000..f8d774c1534 --- /dev/null +++ b/include/internal/quic_port.h @@ -0,0 +1,81 @@ +/* + * 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_PORT_H +# define OSSL_QUIC_PORT_H + +# include +# include "internal/quic_types.h" +# include "internal/quic_reactor.h" +# include "internal/quic_demux.h" +# include "internal/thread_arch.h" + +# ifndef OPENSSL_NO_QUIC + +/* + * QUIC Port + * ========= + * + * A QUIC Port (QUIC_PORT) represents a single UDP network socket and contains + * zero or more subsidiary QUIC_CHANNEL instances, each of which represents a + * single QUIC connection. All QUIC_CHANNEL instances must belong to a + * QUIC_PORT. + */ +typedef struct quic_port_args_st { + /* All channels in a QUIC event domain share the same (libctx, propq). */ + 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_PORT_ARGS; + +typedef struct quic_port_st QUIC_PORT; + +QUIC_PORT *ossl_quic_port_new(const QUIC_PORT_ARGS *args); + +void ossl_quic_port_free(QUIC_PORT *port); + +/* + * Queries and Accessors + * ===================== + */ + +/* Gets/sets the underlying network read and write BIO. */ +BIO *ossl_quic_port_get_net_rbio(QUIC_PORT *port); +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); + +int ossl_quic_port_update_poll_descriptors(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); + +/* Gets the demuxer belonging to the port. */ +QUIC_DEMUX *ossl_quic_port_get0_demux(QUIC_PORT *port); + +/* Gets the mutex used by the port. */ +CRYPTO_MUTEX *ossl_quic_port_get0_mutex(QUIC_PORT *port); + +# endif + +#endif diff --git a/ssl/quic/build.info b/ssl/quic/build.info index e8016dae91f..d837fc764b8 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 +SOURCE[$LIBSSL]=quic_channel.c quic_port.c SOURCE[$LIBSSL]=quic_tserver.c SOURCE[$LIBSSL]=quic_tls.c SOURCE[$LIBSSL]=quic_thread_assist.c diff --git a/ssl/quic/quic_port.c b/ssl/quic/quic_port.c new file mode 100644 index 00000000000..a5858d009ac --- /dev/null +++ b/ssl/quic/quic_port.c @@ -0,0 +1,219 @@ +/* + * 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_port.h" +#include "internal/quic_channel.h" +#include "quic_port_local.h" +#include "quic_channel_local.h" +#include "../ssl_local.h" + +/* + * QUIC Port Structure + * =================== + */ +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); + +QUIC_PORT *ossl_quic_port_new(const QUIC_PORT_ARGS *args) +{ + QUIC_PORT *port; + + 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; + + if (!port_init(port)) { + OPENSSL_free(port); + return NULL; + } + + return port; +} + +void ossl_quic_port_free(QUIC_PORT *port) +{ + if (port == NULL) + return; + + port_cleanup(port); + OPENSSL_free(port); +} + +static int port_init(QUIC_PORT *port) +{ + size_t rx_short_cid_len = 8; + + if ((port->demux = ossl_quic_demux_new(/*BIO=*/NULL, + /*Short CID Len=*/rx_short_cid_len, + get_time, port)) == NULL) + goto err; + + /* + * 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 (is_server) + //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()); + return 1; + +err: + port_cleanup(port); + return 0; +} + +static void port_cleanup(QUIC_PORT *port) +{ + ossl_quic_demux_free(port->demux); + port->demux = NULL; +} + +QUIC_REACTOR *ossl_quic_port_get0_reactor(QUIC_PORT *port) +{ + return &port->rtor; +} + +QUIC_DEMUX *ossl_quic_port_get0_demux(QUIC_PORT *port) +{ + return port->demux; +} + +CRYPTO_MUTEX *ossl_quic_port_get0_mutex(QUIC_PORT *port) +{ + return port->mutex; +} + +static OSSL_TIME get_time(void *arg) +{ + QUIC_PORT *port = arg; + + if (port->now_cb == NULL) + return ossl_time_now(); + + return port->now_cb(port->now_cb_arg); +} + +/* + * QUIC Port: Network BIO Configuration + * ==================================== + */ + +/* 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_port_get_net_rbio(QUIC_PORT *port) +{ + return port->net_rbio; +} + +BIO *ossl_quic_port_get_net_wbio(QUIC_PORT *port) +{ + return port->net_wbio; +} + +static int port_update_poll_desc(QUIC_PORT *port, 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(&port->rtor, &d); + else + ossl_quic_reactor_set_poll_r(&port->rtor, &d); + + return 1; +} + +int ossl_quic_port_update_poll_descriptors(QUIC_PORT *port) +{ + int ok = 1; + + if (!port_update_poll_desc(port, port->net_rbio, /*for_write=*/0)) + ok = 0; + + if (!port_update_poll_desc(port, port->net_wbio, /*for_write=*/1)) + ok = 0; + + return ok; +} + +/* + * QUIC_PORT does not ref any BIO it is provided with, nor is any ref + * transferred to it. The caller (e.g., 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_port_set_net_rbio(QUIC_PORT *port, BIO *net_rbio) +{ + if (port->net_rbio == net_rbio) + return 1; + + if (!port_update_poll_desc(port, net_rbio, /*for_write=*/0)) + return 0; + + ossl_quic_demux_set_bio(port->demux, net_rbio); + port->net_rbio = net_rbio; + return 1; +} + +int ossl_quic_port_set_net_wbio(QUIC_PORT *port, BIO *net_wbio) +{ + 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); + port->net_wbio = net_wbio; + return 1; +} + +/* + * QUIC Port: 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 port_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) +{ + /* TODO */ +} diff --git a/ssl/quic/quic_port_local.h b/ssl/quic/quic_port_local.h new file mode 100644 index 00000000000..0336cd7d049 --- /dev/null +++ b/ssl/quic/quic_port_local.h @@ -0,0 +1,41 @@ +#ifndef OSSL_QUIC_PORT_LOCAL_H +# define OSSL_QUIC_PORT_LOCAL_H + +# include "internal/quic_port.h" +# include "internal/quic_reactor.h" + +# ifndef OPENSSL_NO_QUIC + +/* + * QUIC Port Structure + * =================== + * + * QUIC port internals. It is intended that only the QUIC_PORT and QUIC_CHANNEL + * implementation be allowed to access this structure directly. + * + * Other components should not include this header. + */ +struct quic_port_st { + OSSL_LIB_CTX *libctx; + const char *propq; + + /* Mutex for the entire QUIC event domain. */ + 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; + + /* Network-side read and write BIOs. */ + BIO *net_rbio, *net_wbio; + + /* RX demuxer. We register incoming DCIDs with this. */ + QUIC_DEMUX *demux; +}; + +# endif + +#endif