MINOR: quic: remove <mux_state> field

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.
This commit is contained in:
Amaury Denoyelle 2025-11-05 11:52:12 +01:00
parent 99a2454e9d
commit b9809fe0d0
8 changed files with 40 additions and 45 deletions

View 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) */

View 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 */

View 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");

View 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. */

View 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;
}

View 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);

View 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;

View File

@ -19,6 +19,19 @@
#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