]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC APL: Unify blocking mode handling for all object types
authorHugo Landau <hlandau@openssl.org>
Thu, 4 Apr 2024 10:30:15 +0000 (11:30 +0100)
committerNeil Horman <nhorman@openssl.org>
Mon, 17 Feb 2025 16:27:32 +0000 (11:27 -0500)
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/24037)

include/internal/quic_engine.h
include/internal/quic_port.h
ssl/quic/quic_engine.c
ssl/quic/quic_impl.c
ssl/quic/quic_local.h
ssl/quic/quic_obj.c
ssl/quic/quic_obj_local.h
ssl/quic/quic_port.c
ssl/quic/quic_port_local.h

index e19ee5fb73c3bf1916432cda1a037ba29e3b6285..454c4bfaaaacf5cd2a454572de0dc76afe8fa55a 100644 (file)
@@ -82,6 +82,14 @@ QUIC_REACTOR *ossl_quic_engine_get0_reactor(QUIC_ENGINE *qeng);
 OSSL_LIB_CTX *ossl_quic_engine_get0_libctx(QUIC_ENGINE *qeng);
 const char *ossl_quic_engine_get0_propq(QUIC_ENGINE *qeng);
 
+/*
+ * Look through all the engine's ports and determine if any of them have had a
+ * BIO changed. If so, update the blocking support detection data in the
+ * QUIC_REACTOR. If force is 1, always do the update even if nothing seems
+ * to have changed.
+ */
+void ossl_quic_engine_update_poll_descriptors(QUIC_ENGINE *qeng, int force);
+
 # endif
 
 #endif
index 9c74390ab298ae950ab4beb8681bef99caabbb43..a150eff90d304676a005d7ee291d38b591421c0d 100644 (file)
@@ -99,10 +99,11 @@ 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.
+ * Re-poll the network BIOs already set to determine if their support for
+ * polling has changed. If force is 0, only check again if the BIOs have been
+ * changed.
  */
-int ossl_quic_port_update_poll_descriptors(QUIC_PORT *port);
+int ossl_quic_port_update_poll_descriptors(QUIC_PORT *port, int force);
 
 /* Gets the engine which this port is a child of. */
 QUIC_ENGINE *ossl_quic_port_get0_engine(QUIC_PORT *port);
index bae5e626040954cc94b15ec557868bfcbc39be8f..25f8fa04255926416875c7385aba77b3c66f48a3 100644 (file)
@@ -97,6 +97,24 @@ const char *ossl_quic_engine_get0_propq(QUIC_ENGINE *qeng)
     return qeng->propq;
 }
 
+void ossl_quic_engine_update_poll_descriptors(QUIC_ENGINE *qeng, int force)
+{
+    QUIC_PORT *port;
+
+    /*
+     * TODO(QUIC MULTIPORT): The implementation of
+     * ossl_quic_port_update_poll_descriptors assumes an engine only ever has a
+     * single port for now due to reactor limitations. This limitation will be
+     * removed in future.
+     *
+     * TODO(QUIC MULTIPORT): Consider only iterating the port list when dirty at
+     * the engine level in future when we can have multiple ports. This is not
+     * important currently as the port list has a single entry.
+     */
+    OSSL_LIST_FOREACH(port, port, &qeng->port_list)
+        ossl_quic_port_update_poll_descriptors(port, force);
+}
+
 /*
  * QUIC Engine: Child Object Lifecycle Management
  * ==============================================
index fab118301d65398037e9e85f9f0ac17e9f6780ef..505920f707b7ee4aab59b53b05595b2f8e581ff1 100644 (file)
@@ -41,8 +41,6 @@ static void qc_set_default_xso_keep_ref(QUIC_CONNECTION *qc, QUIC_XSO *xso,
 static SSL *quic_conn_stream_new(QCTX *ctx, uint64_t flags, int need_lock);
 static int quic_validate_for_write(QUIC_XSO *xso, int *err);
 static int quic_mutation_allowed(QUIC_CONNECTION *qc, int req_active);
-static int qc_blocking_mode(const QUIC_CONNECTION *qc);
-static int xso_blocking_mode(const QUIC_XSO *xso);
 static void qctx_maybe_autotick(QCTX *ctx);
 static int qctx_should_autotick(QCTX *ctx);
 
@@ -480,6 +478,16 @@ static int quic_mutation_allowed(QUIC_CONNECTION *qc, int req_active)
     return 1;
 }
 
+static int qctx_is_top_level(QCTX *ctx)
+{
+    return ctx->obj->parent_obj == NULL;
+}
+
+static int qctx_blocking(QCTX *ctx)
+{
+    return ossl_quic_obj_blocking(ctx->obj);
+}
+
 /*
  * Block until a predicate is met.
  *
@@ -585,11 +593,8 @@ SSL *ossl_quic_new(SSL_CTX *ctx)
     qc->default_stream_mode     = SSL_DEFAULT_STREAM_MODE_AUTO_BIDI;
     qc->default_ssl_mode        = qc->obj.ssl.ctx->mode;
     qc->default_ssl_options     = qc->obj.ssl.ctx->options & OSSL_QUIC_PERMITTED_OPTIONS;
-    qc->desires_blocking        = 1;
-    qc->blocking                = 0;
     qc->incoming_stream_policy  = SSL_INCOMING_STREAM_POLICY_AUTO;
     qc->last_error              = SSL_ERROR_NONE;
-    qc->last_net_bio_epoch      = UINT64_MAX;
 
     qc_update_reject_policy(qc);
 
@@ -1051,24 +1056,6 @@ static int csm_analyse_init_peer_addr(BIO *net_wbio, BIO_ADDR *peer)
     return 1;
 }
 
-static int qc_can_support_blocking_cached(QUIC_CONNECTION *qc)
-{
-    QUIC_REACTOR *rtor = ossl_quic_channel_get_reactor(qc->ch);
-
-    return ossl_quic_reactor_can_poll_r(rtor)
-        && ossl_quic_reactor_can_poll_w(rtor);
-}
-
-static void qc_update_can_support_blocking(QUIC_CONNECTION *qc)
-{
-    ossl_quic_port_update_poll_descriptors(qc->port); /* best effort */
-}
-
-static void qc_update_blocking_mode(QUIC_CONNECTION *qc)
-{
-    qc->blocking = qc->desires_blocking && qc_can_support_blocking_cached(qc);
-}
-
 static int
 quic_set0_net_rbio(QUIC_OBJ *obj, BIO *net_rbio)
 {
@@ -1165,22 +1152,20 @@ int ossl_quic_conn_get_blocking_mode(const SSL *s)
 {
     QCTX ctx;
 
-    if (!expect_quic_cs(s, &ctx))
+    if (!expect_quic_csl(s, &ctx))
         return 0;
 
-    if (ctx.is_stream)
-        return xso_blocking_mode(ctx.xso);
-
-    return qc_blocking_mode(ctx.qc);
+    return qctx_blocking(&ctx);
 }
 
 QUIC_TAKES_LOCK
 int ossl_quic_conn_set_blocking_mode(SSL *s, int blocking)
 {
     int ret = 0;
+    unsigned int mode;
     QCTX ctx;
 
-    if (!expect_quic_cs(s, &ctx))
+    if (!expect_quic_csl(s, &ctx))
         return 0;
 
     qctx_lock(&ctx);
@@ -1188,38 +1173,27 @@ int ossl_quic_conn_set_blocking_mode(SSL *s, int blocking)
     /* Sanity check - can we support the request given the current network BIO? */
     if (blocking) {
         /*
-         * If called directly on a QCSO, update our information on network BIO
-         * capabilities.
+         * If called directly on a top-level object (QCSO or QLSO), update our
+         * information on network BIO capabilities.
          */
-        if (!ctx.is_stream)
-            qc_update_can_support_blocking(ctx.qc);
+        if (qctx_is_top_level(&ctx))
+            ossl_quic_engine_update_poll_descriptors(ctx.obj->engine, /*force=*/1);
 
         /* Cannot enable blocking mode if we do not have pollable FDs. */
-        if (!qc_can_support_blocking_cached(ctx.qc)) {
+        if (!ossl_quic_obj_can_support_blocking(ctx.obj)) {
             ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_UNSUPPORTED, NULL);
             goto out;
         }
     }
 
-    if (!ctx.is_stream)
-        /*
-         * If called directly on a QCSO, update default and connection-level
-         * blocking modes.
-         */
-        ctx.qc->desires_blocking = (blocking != 0);
+    mode = (blocking != 0)
+        ? QUIC_BLOCKING_MODE_BLOCKING
+        : QUIC_BLOCKING_MODE_NONBLOCKING;
 
-    if (ctx.xso != NULL) {
-        /*
-         * If called on a QSSO or a QCSO with a default XSO, update the blocking
-         * mode.
-         */
-        ctx.xso->desires_blocking       = (blocking != 0);
-        ctx.xso->desires_blocking_set   = 1;
-    }
+    ossl_quic_obj_set_blocking_mode(ctx.obj, mode);
 
     ret = 1;
 out:
-    qc_update_blocking_mode(ctx.qc);
     qctx_unlock(&ctx);
     return ret;
 }
@@ -1254,34 +1228,6 @@ int ossl_quic_conn_set_initial_peer_addr(SSL *s,
  *   (BIO/)SSL_get_poll_fd          => ossl_quic_get_poll_fd
  *
  */
-static void qc_try_update_blocking(QUIC_CONNECTION *qc)
-{
-    uint64_t cur_epoch;
-
-    cur_epoch = ossl_quic_port_get_net_bio_epoch(qc->port);
-    if (qc->last_net_bio_epoch == cur_epoch)
-        return;
-
-    qc_update_can_support_blocking(qc);
-    qc_update_blocking_mode(qc);
-    qc->last_net_bio_epoch = cur_epoch;
-}
-
-/* Returns 1 if the connection is being used in blocking mode. */
-static int qc_blocking_mode(const QUIC_CONNECTION *qc)
-{
-    qc_try_update_blocking((QUIC_CONNECTION *)qc);
-    return qc->blocking;
-}
-
-static int xso_blocking_mode(const QUIC_XSO *xso)
-{
-    if (xso->desires_blocking_set)
-        return xso->desires_blocking && qc_can_support_blocking_cached(xso->conn);
-    else
-        /* Only ever set if we can support blocking. */
-        return xso->conn->blocking;
-}
 
 /* SSL_handle_events; performs QUIC I/O and timeout processing. */
 QUIC_TAKES_LOCK
@@ -1503,7 +1449,7 @@ int ossl_quic_conn_shutdown(SSL *s, uint64_t flags,
         qc_shutdown_flush_init(ctx.qc);
 
         if (!qc_shutdown_flush_finished(ctx.qc)) {
-            if (!no_block && qc_blocking_mode(ctx.qc)) {
+            if (!no_block && qctx_blocking(&ctx)) {
                 ret = block_until_pred(&ctx, quic_shutdown_flush_wait, ctx.qc, 0);
                 if (ret < 1) {
                     ret = 0;
@@ -1522,7 +1468,7 @@ int ossl_quic_conn_shutdown(SSL *s, uint64_t flags,
 
     /* Phase 2: Connection Closure */
     if (wait_peer && !ossl_quic_channel_is_term_any(ctx.qc->ch)) {
-        if (!no_block && qc_blocking_mode(ctx.qc)) {
+        if (!no_block && qctx_blocking(&ctx)) {
             ret = block_until_pred(&ctx, quic_shutdown_peer_wait, ctx.qc, 0);
             if (ret < 1) {
                 ret = 0;
@@ -1562,7 +1508,7 @@ int ossl_quic_conn_shutdown(SSL *s, uint64_t flags,
     }
 
     /* Phase 3: Terminating Wait Time */
-    if (!no_block && qc_blocking_mode(ctx.qc)
+    if (!no_block && qctx_blocking(&ctx)
         && (flags & SSL_SHUTDOWN_FLAG_RAPID) == 0) {
         ret = block_until_pred(&ctx, quic_shutdown_wait, ctx.qc, 0);
         if (ret < 1) {
@@ -1891,7 +1837,7 @@ static int quic_do_handshake(QCTX *ctx)
         /* The handshake is now done. */
         return 1;
 
-    if (!qc_blocking_mode(qc)) {
+    if (!qctx_blocking(ctx)) {
         /* Try to advance the reactor. */
         qctx_maybe_autotick(ctx);
 
@@ -1902,7 +1848,7 @@ static int quic_do_handshake(QCTX *ctx)
         if (ossl_quic_channel_is_term_any(qc->ch)) {
             QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
             return 0;
-        } else if (qc->desires_blocking) {
+        } else if (ossl_quic_obj_desires_blocking(&qc->obj)) {
             /*
              * As a special case when doing a handshake when blocking mode is
              * desired yet not available, see if the network BIOs have become
@@ -1910,16 +1856,14 @@ static int quic_do_handshake(QCTX *ctx)
              * which do late creation of socket FDs and therefore cannot expose
              * a poll descriptor until after a network BIO is set on the QCSO.
              */
-            assert(!qc->blocking);
-            qc_update_can_support_blocking(qc);
-            qc_update_blocking_mode(qc);
+            ossl_quic_engine_update_poll_descriptors(qc->obj.engine, /*force=*/1);
         }
     }
 
     /*
      * We are either in blocking mode or just entered it due to the code above.
      */
-    if (qc_blocking_mode(qc)) {
+    if (qctx_blocking(ctx)) {
         /* In blocking mode, wait for the handshake to complete. */
         struct quic_handshake_wait_args args;
 
@@ -2109,7 +2053,7 @@ static int qc_wait_for_default_xso_for_read(QCTX *ctx, int peek)
         if (peek)
             return 0;
 
-        if (!qc_blocking_mode(qc))
+        if (!qctx_blocking(ctx))
             /* Non-blocking mode, so just bail immediately. */
             return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_WANT_READ);
 
@@ -2233,7 +2177,7 @@ static SSL *quic_conn_stream_new(QCTX *ctx, uint64_t flags, int need_lock)
          * Stream count flow control currently doesn't permit this stream to be
          * opened.
          */
-        if (no_blocking || !qc_blocking_mode(qc)) {
+        if (no_blocking || !qctx_blocking(ctx)) {
             QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_STREAM_COUNT_LIMITED, NULL);
             goto err;
         }
@@ -2807,7 +2751,7 @@ int ossl_quic_write_flags(SSL *s, const void *buf, size_t len,
         goto out;
     }
 
-    if (xso_blocking_mode(ctx.xso))
+    if (qctx_blocking(&ctx))
         ret = quic_write_blocking(&ctx, buf, len, flags, written);
     else if (partial_write)
         ret = quic_write_nonblocking_epw(&ctx, buf, len, flags, written);
@@ -3017,7 +2961,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
          */
         qctx_maybe_autotick(&ctx);
         ret = 1;
-    } else if (xso_blocking_mode(ctx.xso)) {
+    } else if (qctx_blocking(&ctx)) {
         /*
          * We were not able to read anything immediately, so our stream
          * buffer is empty. This means we need to block until we get
@@ -3820,7 +3764,7 @@ SSL *ossl_quic_accept_stream(SSL *s, uint64_t flags)
 
     qs = ossl_quic_stream_map_peek_accept_queue(qsm);
     if (qs == NULL) {
-        if (qc_blocking_mode(ctx.qc)
+        if (qctx_blocking(&ctx)
             && (flags & SSL_ACCEPT_STREAM_NO_BLOCK) == 0) {
             struct wait_for_incoming_stream_args args;
 
@@ -4351,11 +4295,9 @@ static QUIC_CONNECTION *create_qc_from_incoming_conn(QUIC_LISTENER *ql, QUIC_CHA
     qc->mutex                   = ql->mutex;
 #endif
     qc->tls                     = ossl_quic_channel_get0_tls(ch);
-    qc->last_net_bio_epoch      = UINT64_MAX;
     qc->started                 = 1;
     qc->as_server               = 1;
     qc->as_server_state         = 1;
-    qc->desires_blocking        = 1;
     qc->default_stream_mode     = SSL_DEFAULT_STREAM_MODE_AUTO_BIDI;
     qc->default_ssl_options     = ql->obj.ssl.ctx->options & OSSL_QUIC_PERMITTED_OPTIONS;
     qc->incoming_stream_policy  = SSL_INCOMING_STREAM_POLICY_AUTO;
index 85c73fe6c713ee225b240f7fd9dcf36d476f2804..f75d128362843081a406ab1ec237e01e9f9c96d6 100644 (file)
@@ -42,19 +42,6 @@ struct quic_xso_st {
     /* The stream object. Always non-NULL for as long as the XSO exists. */
     QUIC_STREAM                     *stream;
 
-    /*
-     * Has this stream been logically configured into blocking mode? Only
-     * meaningful if desires_blocking_set is 1. Ignored if blocking is not
-     * currently possible given QUIC_CONNECTION configuration.
-     */
-    unsigned int                    desires_blocking        : 1;
-
-    /*
-     * Has SSL_set_blocking_mode been called on this stream? If not set, we
-     * inherit from the QUIC_CONNECTION blocking state.
-     */
-    unsigned int                    desires_blocking_set    : 1;
-
     /* The application has retired a FIN (i.e. SSL_ERROR_ZERO_RETURN). */
     unsigned int                    retired_fin             : 1;
 
@@ -205,12 +192,6 @@ struct quic_conn_st {
     /* Are we using thread assisted mode? Never changes after init. */
     unsigned int                    is_thread_assisted      : 1;
 
-    /* Do connection-level operations (e.g. handshakes) run in blocking mode? */
-    unsigned int                    blocking                : 1;
-
-    /* Does the application want blocking mode? */
-    unsigned int                    desires_blocking        : 1;
-
     /* Have we created a default XSO yet? */
     unsigned int                    default_xso_created     : 1;
 
@@ -244,11 +225,6 @@ struct quic_conn_st {
     int                             incoming_stream_policy;
     uint64_t                        incoming_stream_aec;
 
-    /*
-     * Last network BIO epoch at which blocking mode compatibility was checked.
-     */
-    uint64_t                        last_net_bio_epoch;
-
     /*
      * Last 'normal' error during an app-level I/O operation, used by
      * SSL_get_error(); used to track data-path errors like SSL_ERROR_WANT_READ
index ffe6b1bcda1116bc0132afff61d9f8deb1801db1..827b0c38b04571275d31ad776c63b43cbe38193c 100644 (file)
@@ -39,6 +39,7 @@ int ossl_quic_obj_init(QUIC_OBJ *obj,
     obj->is_port_leader     = is_port_leader;
     obj->engine             = engine;
     obj->port               = port;
+    obj->req_blocking_mode  = QUIC_BLOCKING_MODE_INHERIT;
     if (!obj_update_cache(obj))
         goto err;
 
@@ -87,3 +88,38 @@ SSL_CONNECTION *ossl_quic_obj_get0_handshake_layer(QUIC_OBJ *obj)
 
     return SSL_CONNECTION_FROM_SSL_ONLY(((QUIC_CONNECTION *)obj)->tls);
 }
+
+/* (Returns a cached result.) */
+int ossl_quic_obj_can_support_blocking(const QUIC_OBJ *obj)
+{
+    QUIC_REACTOR *rtor = ossl_quic_obj_get0_reactor(obj);
+
+    return ossl_quic_reactor_can_poll_r(rtor)
+        || ossl_quic_reactor_can_poll_w(rtor);
+}
+
+int ossl_quic_obj_desires_blocking(const QUIC_OBJ *obj)
+{
+    unsigned int req_blocking_mode;
+
+    for (; (req_blocking_mode = obj->req_blocking_mode)
+            == QUIC_BLOCKING_MODE_INHERIT && obj->parent_obj != NULL;
+         obj = obj->parent_obj);
+
+    return req_blocking_mode != QUIC_BLOCKING_MODE_NONBLOCKING;
+}
+
+int ossl_quic_obj_blocking(const QUIC_OBJ *obj)
+{
+    if (!ossl_quic_obj_desires_blocking(obj))
+        return 0;
+
+    ossl_quic_engine_update_poll_descriptors(ossl_quic_obj_get0_engine(obj),
+                                             /*force=*/0);
+    return ossl_quic_obj_can_support_blocking(obj);
+}
+
+void ossl_quic_obj_set_blocking_mode(QUIC_OBJ *obj, unsigned int mode)
+{
+    obj->req_blocking_mode = mode;
+}
index fa5b33f2656cc5b60b4df6ec99ca4d6372e8009c..efd11bac2589ea9922ed17a60806b063ddf3e678 100644 (file)
@@ -103,6 +103,18 @@ struct quic_obj_st {
     unsigned int            init_done       : 1;
     unsigned int            is_event_leader : 1;
     unsigned int            is_port_leader  : 1;
+
+    /*
+     * Blocking mode configuration is handled generically through QUIC_OBJ as it
+     * by default inherits from the parent SSL object.
+     */
+    unsigned int            req_blocking_mode       : 2; /* QUIC_BLOCKING_MODE */
+};
+
+enum {
+    QUIC_BLOCKING_MODE_INHERIT,
+    QUIC_BLOCKING_MODE_NONBLOCKING,
+    QUIC_BLOCKING_MODE_BLOCKING
 };
 
 /*
@@ -219,6 +231,32 @@ ossl_quic_obj_get0_port_local(const QUIC_OBJ *obj)
         ? ossl_quic_obj_get0_port(obj) : NULL;
 }
 
+/*
+ * Return 1 if we are currently capable of supporting blocking mode (regardless
+ * of whether it is actually turned on).
+ */
+int ossl_quic_obj_can_support_blocking(const QUIC_OBJ *obj);
+
+/*
+ * Returns 1 if we *desire* to do blocking I/O, regardless of whether it will
+ * actually be used (e.g. because it cannot currently be supported).
+ */
+int ossl_quic_obj_desires_blocking(const QUIC_OBJ *obj);
+
+/*
+ * Return 1 if an API call directly to the given object should use blocking mode
+ * and 0 otherwise.
+ */
+int ossl_quic_obj_blocking(const QUIC_OBJ *obj);
+
+/*
+ * Set the (requested) blocking mode, which might or might not be honoured
+ * depending on whether the BIO configuration can support it. Argument is a
+ * QUIC_BLOCKING_MODE value. If the top-level object in a QSO hierarchy is set
+ * to QUIC_BLOCKING_MODE_INHERIT, defaults to blocking mode.
+ */
+void ossl_quic_obj_set_blocking_mode(QUIC_OBJ *obj, unsigned int mode);
+
 /*
  * Convenience Inlines
  * ===================
index a33a7a337991025c04c183a5afed3287526b255e..8cba36f627459a07b4d2b4ff614e7d8ac69858b6 100644 (file)
@@ -95,6 +95,7 @@ static int port_init(QUIC_PORT *port)
 
     ossl_list_port_insert_tail(&port->engine->port_list, port);
     port->on_engine_list    = 1;
+    port->bio_changed       = 1;
     return 1;
 
 err:
@@ -238,16 +239,20 @@ static int port_update_poll_desc(QUIC_PORT *port, BIO *net_bio, int for_write)
     return 1;
 }
 
-int ossl_quic_port_update_poll_descriptors(QUIC_PORT *port)
+int ossl_quic_port_update_poll_descriptors(QUIC_PORT *port, int force)
 {
     int ok = 1;
 
+    if (!force && !port->bio_changed)
+        return 0;
+
     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;
 
+    port->bio_changed = 0;
     return ok;
 }
 
@@ -292,7 +297,7 @@ static void port_update_addressing_mode(QUIC_PORT *port)
 
     port->addressed_mode_r = ((rcaps & BIO_DGRAM_CAP_PROVIDES_SRC_ADDR) != 0);
     port->addressed_mode_w = ((wcaps & BIO_DGRAM_CAP_HANDLES_DST_ADDR) != 0);
-    ++port->net_bio_epoch;
+    port->bio_changed = 1;
 }
 
 int ossl_quic_port_is_addressed_r(const QUIC_PORT *port)
@@ -348,11 +353,6 @@ int ossl_quic_port_set_net_wbio(QUIC_PORT *port, BIO *net_wbio)
     return 1;
 }
 
-uint64_t ossl_quic_port_get_net_bio_epoch(const QUIC_PORT *port)
-{
-    return port->net_bio_epoch;
-}
-
 /*
  * QUIC Port: Channel Lifecycle
  * ============================
index 79336ebef5fde94af7e0db30a23b5b2292ec33a5..0b954d6d1cc995790ac7c3d3b0451cfce39cfa30 100644 (file)
@@ -81,9 +81,6 @@ struct quic_port_st {
     /* Port-level permanent errors (causing failure state) are stored here. */
     ERR_STATE                       *err_state;
 
-    /* Network BIO epoch. Increments whenever network BIO config changes. */
-    uint64_t                        net_bio_epoch;
-
     /* DCID length used for incoming short header packets. */
     unsigned char                   rx_short_dcid_len;
     /* For clients, CID length used for outgoing Initial packets. */
@@ -107,6 +104,9 @@ struct quic_port_st {
     /* Are we using addressed mode (BIO_sendmmsg with non-NULL peer)? */
     unsigned int                    addressed_mode_w                : 1;
     unsigned int                    addressed_mode_r                : 1;
+
+    /* Has the BIO been changed since we last updated reactor pollability? */
+    unsigned int                    bio_changed                     : 1;
 };
 
 # endif