From b9809fe0d0dd447a8452324b49a28271ca4e8d59 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Wed, 5 Nov 2025 11:52:12 +0100 Subject: [PATCH] MINOR: quic: remove field This patch removes 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 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 | 13 +------------ include/haproxy/quic_conn.h | 4 ++++ src/quic_cli.c | 6 +++--- src/quic_conn.c | 12 ++++++------ src/quic_rx.c | 15 +++++++-------- src/quic_ssl.c | 1 - src/quic_tx.c | 1 - src/xprt_quic.c | 33 +++++++++++++++++++-------------- 8 files changed, 40 insertions(+), 45 deletions(-) diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h index 9ac624a8f..9fe7b69f7 100644 --- a/include/haproxy/quic_conn-t.h +++ b/include/haproxy/quic_conn-t.h @@ -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) */ diff --git a/include/haproxy/quic_conn.h b/include/haproxy/quic_conn.h index b48b53f87..2fcb81dcd 100644 --- a/include/haproxy/quic_conn.h +++ b/include/haproxy/quic_conn.h @@ -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 */ diff --git a/src/quic_cli.c b/src/quic_cli.c index 2173d22d8..bfc68dc7f 100644 --- a/src/quic_cli.c +++ b/src/quic_cli.c @@ -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"); diff --git a/src/quic_conn.c b/src/quic_conn.c index e6d59cfe8..0cc9b6ed9 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -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. */ diff --git a/src/quic_rx.c b/src/quic_rx.c index ee31d54a4..a05214857 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -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; } diff --git a/src/quic_ssl.c b/src/quic_ssl.c index bf756f929..e467136f2 100644 --- a/src/quic_ssl.c +++ b/src/quic_ssl.c @@ -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); diff --git a/src/quic_tx.c b/src/quic_tx.c index 22caf5b20..f0cd143fe 100644 --- a/src/quic_tx.c +++ b/src/quic_tx.c @@ -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; diff --git a/src/xprt_quic.c b/src/xprt_quic.c index 12b838c03..c256f4427 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -19,6 +19,19 @@ #include #include +/* Returns true if conn layer above 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 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 -- 2.47.3