]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic: remove <mux_state> field quic-interop
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 5 Nov 2025 10:52:12 +0000 (11:52 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 5 Nov 2025 13:03:34 +0000 (14:03 +0100)
This patch removes <mux_state> field from quic_conn structure. The
purpose of this field was to indicate if MUX layer above quic_conn is
not yet initialized, active, or already released.

It became tedious to properly set it as initialization order of the
various quic_conn/conn/MUX layers now differ between the frontend and
backend sides, and also depending if 0-RTT is used or not. Recently, a
new change introduced in connect_server() will allow to initialize QUIC
MUX earlier if ALPN is cached on the server structure. This had another
level of complexity.

Thus, this patch removes <mux_state> field completely. Instead, a new
flag QUIC_FL_CONN_XPRT_CLOSED is defined. It is set at a single place
only on close XPRT callback invokation. It can be mixed with the new
utility functions qc_wait_for_conn()/qc_is_conn_ready() to determine the
status of conn/MUX layers now without an extra quic_conn field.

include/haproxy/quic_conn-t.h
include/haproxy/quic_conn.h
src/quic_cli.c
src/quic_conn.c
src/quic_rx.c
src/quic_ssl.c
src/quic_tx.c
src/xprt_quic.c

index 9ac624a8ff59170128f61e70eca706dd3f69d1fe..9fe7b69f720a88bfa0d16ad353f5d979036d04d7 100644 (file)
@@ -243,17 +243,6 @@ extern const struct quic_version *quic_version_2;
 /* The maximum number of bytes of CRYPTO data in flight during handshakes. */
 #define QUIC_CRYPTO_IN_FLIGHT_MAX 4096
 
-/* Status of the MUX layer. This defines how to handle app data.
- *
- * During a standard quic_conn lifetime it transitions like this :
- * QC_MUX_NULL -> QC_MUX_READY -> QC_MUX_RELEASED
- */
-enum qc_mux_state {
-       QC_MUX_NULL,     /* not allocated, data should be buffered */
-       QC_MUX_READY,    /* allocated, ready to handle data */
-       QC_MUX_RELEASED, /* released, data can be dropped */
-};
-
 /* Counters at QUIC connection level */
 struct quic_conn_cntrs {
        long long dropped_pkt;           /* total number of dropped packets */
@@ -333,7 +322,6 @@ struct quic_conn {
        /* QUIC transport parameters TLS extension */
        int tps_tls_ext;
        int state;
-       enum qc_mux_state mux_state; /* status of the connection/mux layer */
 #ifdef HAVE_OPENSSL_QUIC
        uint32_t prot_level;
 #endif
@@ -457,6 +445,7 @@ struct quic_conn_closed {
 #define QUIC_FL_CONN_PEER_VALIDATED_ADDR         (1U << 17) /* Peer address is considered as validated for this connection. */
 #define QUIC_FL_CONN_NO_TOKEN_RCVD               (1U << 18) /* Client dit not send any token */
 #define QUIC_FL_CONN_SCID_RECEIVED               (1U << 19) /* (client only: first Initial received. */
+#define QUIC_FL_CONN_XPRT_CLOSED                 (1U << 20) /* close callback of xprt layer already called */
 /* gap here */
 #define QUIC_FL_CONN_TO_KILL                     (1U << 24) /* Unusable connection, to be killed */
 #define QUIC_FL_CONN_TX_TP_RECEIVED              (1U << 25) /* Peer transport parameters have been received (used for the transmitting part) */
index b48b53f87a081e0bcea42a8ca449484c0a598ae3..2fcb81dcd5c44af33bf05abfaaf3ea4cb9078690 100644 (file)
@@ -218,5 +218,9 @@ extern uint64_t (*quic_hash64_from_cid)(const unsigned char *cid, int size, cons
 /* Function pointer that can be used to derive a new CID from the previously computed hash */
 extern void (*quic_newcid_from_hash64)(unsigned char *cid, int size, uint64_t hash, const unsigned char *secret, size_t secretlen);
 
+/* QUIC xprt layer functions */
+int qc_wait_for_conn(const struct quic_conn *qc);
+int qc_is_conn_ready(const struct quic_conn *qc);
+
 #endif /* USE_QUIC */
 #endif /* _HAPROXY_QUIC_CONN_H */
index 2173d22d857aec8bacaecaf5153d2e45f6e1afff..bfc68dc7fb15d4775bdb1953f07adb84d98bb3a8 100644 (file)
@@ -311,9 +311,9 @@ static void dump_quic_full(struct show_quic_ctx *ctx, struct quic_conn *qc)
        else
                chunk_appendf(&trash, "  st=opened           ");
 
-       if (qc->mux_state == QC_MUX_NULL)
+       if (qc_wait_for_conn(qc))
                chunk_appendf(&trash, "mux=null                                      ");
-       else if (qc->mux_state == QC_MUX_READY)
+       else if (qc_is_conn_ready(qc))
                chunk_appendf(&trash, "mux=ready                                     ");
        else
                chunk_appendf(&trash, "mux=released                                  ");
@@ -428,7 +428,7 @@ static void dump_quic_full(struct show_quic_ctx *ctx, struct quic_conn *qc)
        if (addnl)
                chunk_appendf(&trash, "\n");
 
-       if (ctx->fields & QUIC_DUMP_FLD_MUX && qc->mux_state == QC_MUX_READY)
+       if ((ctx->fields & QUIC_DUMP_FLD_MUX) && qc_is_conn_ready(qc))
                qcc_show_quic(qc->qcc);
 
        chunk_appendf(&trash, "\n");
index e6d59cfe8f45294f0fe01d4c695fd5653a54ae76..0cc9b6ed9ffe8a622dd8ec321314a7f8bc55aba6 100644 (file)
@@ -294,7 +294,7 @@ void qc_check_close_on_released_mux(struct quic_conn *qc)
 {
        TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
 
-       if (qc->mux_state == QC_MUX_RELEASED && eb_is_empty(&qc->streams_by_id)) {
+       if ((qc->flags & QUIC_FL_CONN_XPRT_CLOSED) && eb_is_empty(&qc->streams_by_id)) {
                /* Reuse errcode which should have been previously set by the MUX on release. */
                quic_set_connection_close(qc, qc->err);
                tasklet_wakeup(qc->wait_event.tasklet);
@@ -337,10 +337,11 @@ void quic_conn_closed_err_count_inc(struct quic_conn *qc, struct quic_frame *frm
 {
        TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
 
-       if (frm->type == QUIC_FT_CONNECTION_CLOSE)
+       if (frm->type == QUIC_FT_CONNECTION_CLOSE) {
                quic_stats_transp_err_count_inc(qc->prx_counters, frm->connection_close.error_code);
+       }
        else if (frm->type == QUIC_FT_CONNECTION_CLOSE_APP) {
-               if (qc->mux_state != QC_MUX_READY || !qc->qcc->app_ops->inc_err_cnt)
+               if (!qc_is_conn_ready(qc) || !qc->qcc->app_ops->inc_err_cnt)
                        goto out;
 
                qc->qcc->app_ops->inc_err_cnt(qc->qcc->ctx, frm->connection_close_app.error_code);
@@ -1227,7 +1228,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
 
                qc->next_cid_seq_num = 1;
        }
-       qc->mux_state = QC_MUX_NULL;
        qc->err = quic_err_transport(QC_ERR_NO_ERROR);
 
        /* Listener only: if connection is instantiated due to an INITIAL packet with an
@@ -1916,7 +1916,7 @@ int qc_notify_send(struct quic_conn *qc)
        /* Wake up streams layer waiting for buffer. Useful after congestion
         * window increase.
         */
-       if (qc->mux_state == QC_MUX_READY && (qc->qcc->flags & QC_CF_CONN_FULL))
+       if (qc_is_conn_ready(qc) && (qc->qcc->flags & QC_CF_CONN_FULL))
                qcc_notify_buf(qc->qcc, 0);
 
        return 0;
@@ -1927,7 +1927,7 @@ void qc_notify_err(struct quic_conn *qc)
 {
        TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
 
-       if (qc->mux_state == QC_MUX_READY) {
+       if (qc_is_conn_ready(qc)) {
                TRACE_STATE("error notified to mux", QUIC_EV_CONN_CLOSE, qc);
 
                /* Mark socket as closed. */
index ee31d54a460234b9dfee8dd42545d66ea45f8c41..a05214857cfdeb04ebc3ecc05455fb3bc08ecca5 100644 (file)
@@ -871,7 +871,7 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
                        break;
                }
                case QUIC_FT_RESET_STREAM:
-                       if (qc->mux_state == QC_MUX_READY) {
+                       if (qc_is_conn_ready(qc)) {
                                struct qf_reset_stream *rs_frm = &frm->reset_stream;
                                qcc_recv_reset_stream(qc->qcc, rs_frm->id, rs_frm->app_error_code, rs_frm->final_size);
                        }
@@ -879,7 +879,7 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
                case QUIC_FT_STOP_SENDING:
                {
                        struct qf_stop_sending *ss_frm = &frm->stop_sending;
-                       if (qc->mux_state == QC_MUX_READY) {
+                       if (qc_is_conn_ready(qc)) {
                                if (qcc_recv_stop_sending(qc->qcc, ss_frm->id,
                                                          ss_frm->app_error_code)) {
                                        TRACE_ERROR("qcc_recv_stop_sending() failed", QUIC_EV_CONN_PRSHPKT, qc);
@@ -921,7 +921,7 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
                          qc->rx.stream_max_uni : qc->rx.stream_max_bidi;
 
                        /* The upper layer may not be allocated. */
-                       if (qc->mux_state != QC_MUX_READY) {
+                       if (!qc_is_conn_ready(qc)) {
                                if (strm_frm->id < max) {
                                        TRACE_DATA("Already closed stream", QUIC_EV_CONN_PRSHPKT, qc);
                                }
@@ -951,13 +951,13 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
                        break;
                }
                case QUIC_FT_MAX_DATA:
-                       if (qc->mux_state == QC_MUX_READY) {
+                       if (qc_is_conn_ready(qc)) {
                                struct qf_max_data *md_frm = &frm->max_data;
                                qcc_recv_max_data(qc->qcc, md_frm->max_data);
                        }
                        break;
                case QUIC_FT_MAX_STREAM_DATA:
-                       if (qc->mux_state == QC_MUX_READY) {
+                       if (qc_is_conn_ready(qc)) {
                                struct qf_max_stream_data *msd_frm = &frm->max_stream_data;
                                if (qcc_recv_max_stream_data(qc->qcc, msd_frm->id,
                                                              msd_frm->max_stream_data)) {
@@ -968,7 +968,7 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
                        break;
                case QUIC_FT_MAX_STREAMS_BIDI:
                case QUIC_FT_MAX_STREAMS_UNI:
-                       if (qc->mux_state == QC_MUX_READY) {
+                       if (qc_is_conn_ready(qc)) {
                                int bidi;
                                struct qf_max_streams *ms_frm;
 
@@ -1207,8 +1207,7 @@ static int qc_qel_may_rm_hp(struct quic_conn *qc, struct quic_enc_level *qel)
        }
 
        /* check if the connection layer is ready before using app level */
-       if ((qel == qc->ael || qel == qc->eel) &&
-           qc->mux_state == QC_MUX_NULL) {
+       if ((qel == qc->ael || qel == qc->eel) && qc_wait_for_conn(qc)) {
                TRACE_PROTO("connection layer not ready", QUIC_EV_CONN_TRMHP, qc);
                goto cant_rm_hp;
        }
index bf756f92943959c2e84901f84e9b97a33a109164..e467136f27d57c5af28d3b672280cc4fe56d0f6d 100644 (file)
@@ -1001,7 +1001,6 @@ int qc_ssl_do_hanshake(struct quic_conn *qc, struct ssl_sock_ctx *ctx)
 
                        /* Wake up MUX after its creation. Operation similar to TLS+ALPN on TCP stack. */
                        qc->conn->mux->wake(qc->conn);
-                       qc->mux_state = QC_MUX_READY;
                }
                else {
                        TRACE_PROTO("could not start the mux", QUIC_EV_CONN_IO_CB, qc);
index 22caf5b2038f31a69f3413013cf977b8d8e3e9f0..f0cd143fe93648a01e3178b6a79c27362298b844 100644 (file)
@@ -518,7 +518,6 @@ enum quic_tx_err qc_send_mux(struct quic_conn *qc, struct list *frms,
        int max_dgram = 0, sent;
 
        TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
-       BUG_ON(qc->mux_state != QC_MUX_READY); /* Only MUX can uses this function so it must be ready. */
 
        if (qc->conn->flags & CO_FL_SOCK_WR_SH) {
                qc->conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH;
index 12b838c038a1386be7fa01fc374a927cdda46619..c256f442709202fe10d7cd8e62eed89080f6a3a1 100644 (file)
 #include <haproxy/quic_trace.h>
 #include <haproxy/trace.h>
 
+/* Returns true if conn layer above <qc> has not been yet fully initialized, i.e. MUX is not yet opened. */
+int qc_wait_for_conn(const struct quic_conn *qc)
+{
+       return (!qc->conn || !qc->conn->mux) &&
+         !(qc->flags & QUIC_FL_CONN_XPRT_CLOSED);
+}
+
+/* Returns true if conn layer above <qc> is fully initialized and available. */
+int qc_is_conn_ready(const struct quic_conn *qc)
+{
+       return qc->conn && qc->conn->mux;
+}
+
 static void quic_close(struct connection *conn, void *xprt_ctx)
 {
        struct ssl_sock_ctx *conn_ctx = xprt_ctx;
@@ -26,11 +39,9 @@ static void quic_close(struct connection *conn, void *xprt_ctx)
 
        TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
 
+       qc->flags |= QUIC_FL_CONN_XPRT_CLOSED;
        qc->conn = NULL;
 
-       /* Next application data can be dropped. */
-       qc->mux_state = QC_MUX_RELEASED;
-
        /* If the quic-conn timer has already expired or if already in "connection close"
         * state, free the quic-conn.
         */
@@ -161,17 +172,11 @@ static int qc_xprt_start(struct connection *conn, void *ctx)
        qc = conn->handle.qc;
        TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
 
-       if (objt_listener(conn->target)) {
-               /* mux-quic can now be considered ready. */
-               qc->mux_state = QC_MUX_READY;
-       }
-       else {
-               /* This has as side effet to create a SSL_SESSION object attached to
-                * the SSL object.
-                */
-               if (!qc_ssl_do_hanshake(qc, ctx))
-                       goto out;
-       }
+       /* This has as side effet to create a SSL_SESSION object attached to
+        * the SSL object.
+        */
+       if (qc_is_back(qc) && !qc_ssl_do_hanshake(qc, ctx))
+               goto out;
 
        /* Schedule quic-conn to ensure post handshake frames are emitted. This
         * is not done for 0-RTT as xprt->start happens before handshake