From: Amaury Denoyelle Date: Tue, 15 Oct 2024 15:37:00 +0000 (+0200) Subject: BUG/MEDIUM: quic: support wait-for-handshake X-Git-Tag: v3.0.7~51 X-Git-Url: http://git.haproxy.org/?a=commitdiff_plain;h=05658956ae53e2d027ade7f237c64d79cffc1c4a;p=haproxy-3.0.git BUG/MEDIUM: quic: support wait-for-handshake wait-for-handshake http-request action was completely ineffective with QUIC protocol. This commit implements its support for QUIC. QUIC MUX layer is extended to support wait-for-handshake. A new function qcc_handle_wait_for_hs() is executed during qcc_io_process(). It detects if MUX processing occurs after underlying QUIC handshake completion. If this is the case, it indicates that early data may be received. As such, connection is flagged with CO_FL_EARLY_SSL_HS, which is necessary to block stream processing on wait-for-handshake action. After this, qcc subscribs on quic_conn layer for RECV notification. This is used to detect QUIC handshake completion. Thus, qcc_handle_wait_for_hs() can be reexecuted one last time, to remove CO_FL_EARLY_SSL_HS and notify every streams flagged as SE_FL_WAIT_FOR_HS. This patch must be backported up to 2.6, after a mandatory period of observation. Note that it relies on the backport of the two previous patches : - MINOR: quic: notify connection layer on handshake completion - BUG/MINOR: stream: unblock stream on wait-for-handshake completion (cherry picked from commit 0918c41ef63964a986c627d20b8a1324de639cc2) Signed-off-by: Christopher Faulet --- diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h index 02f8a72..a8251bb 100644 --- a/include/haproxy/mux_quic-t.h +++ b/include/haproxy/mux_quic-t.h @@ -35,6 +35,7 @@ enum qcs_type { #define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */ #define QC_CF_APP_SHUT 0x00000010 /* Application layer shutdown done. */ #define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */ +#define QC_CF_WAIT_FOR_HS 0x00000040 /* QUIC handshake has been completed */ struct qcc { struct connection *conn; diff --git a/src/mux_quic.c b/src/mux_quic.c index b3ea3f7..d8dcd50 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -2464,6 +2464,44 @@ static int qcc_wake_some_streams(struct qcc *qcc) return 0; } +/* Checks whether QUIC handshake is still active or not. This is necessary to + * mark that connection may convey early data to delay stream processing if + * wait-for-handshake is active. On handshake completion, any SE_FL_WAIT_FOR_HS + * streams are woken up to restart their processing. + */ +static void qcc_wait_for_hs(struct qcc *qcc) +{ + struct connection *conn = qcc->conn; + struct quic_conn *qc = conn->handle.qc; + struct eb64_node *node; + struct qcs *qcs; + + if (qc->state < QUIC_HS_ST_COMPLETE) { + if (!(conn->flags & CO_FL_EARLY_SSL_HS)) { + TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn); + conn->flags |= CO_FL_EARLY_SSL_HS; + /* subscribe for handshake completion */ + conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV, + &qcc->wait_event); + } + } + else { + if (conn->flags & CO_FL_EARLY_SSL_HS) { + TRACE_STATE("mark early data as ready", QMUX_EV_QCC_WAKE, conn); + conn->flags &= ~CO_FL_EARLY_SSL_HS; + } + qcc->flags |= QC_CF_WAIT_FOR_HS; + + node = eb64_first(&qcc->streams_by_id); + while (node) { + qcs = container_of(node, struct qcs, by_id); + if (se_fl_test(qcs->sd, SE_FL_WAIT_FOR_HS)) + qcs_notify_recv(qcs); + node = eb64_next(node); + } + } +} + /* Conduct operations which should be made for connection after * input/output. Most notably, closed streams are purged which may leave the * connection has ready to be released. @@ -2474,6 +2512,9 @@ static int qcc_io_process(struct qcc *qcc) { qcc_purge_streams(qcc); + if (!(qcc->flags & QC_CF_WAIT_FOR_HS)) + qcc_wait_for_hs(qcc); + /* Check if a soft-stop is in progress. * * TODO this is relevant for frontend connections only.