From 0b6908385e329441bf8fced097adfa276be5e8da Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Tue, 5 Aug 2025 10:26:29 +0200 Subject: [PATCH] BUG/MINOR: quic: properly support GSO on backend side Previously, GSO emission was explicitely disabled on backend side. This is not true since the following patch, thus GSO can be used, for example when transfering large POST requests to a HTTP/3 backend. commit e064e5d46171d32097a84b8f84ccc510a5c211db MINOR: quic: duplicate GSO unsupp status from listener to conn However, GSO on the backend side may cause crash when handling EIO. In this case, GSO must be completely disabled. Previously, this was performed by flagging listener instance. In backend side, this would cause a crash as listener is NULL. This patch fixes it by supporting GSO disable flag for servers. Thus, in qc_send_ppkts(), EIO can be converted either to a listener or server flag depending on the quic_conn proxy side. On backend side, server instance is retrieved via . This is enough to guarantee that server is not deleted. This does not need to be backported. --- include/haproxy/quic_conn.h | 1 + include/haproxy/server-t.h | 1 + src/quic_conn.c | 4 ++++ src/quic_tx.c | 27 +++++++++++++-------------- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/include/haproxy/quic_conn.h b/include/haproxy/quic_conn.h index 56367f1b4..37050f865 100644 --- a/include/haproxy/quic_conn.h +++ b/include/haproxy/quic_conn.h @@ -47,6 +47,7 @@ #include #include #include +#include #include #include diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h index 1829b9763..bed933226 100644 --- a/include/haproxy/server-t.h +++ b/include/haproxy/server-t.h @@ -172,6 +172,7 @@ enum srv_init_state { #define SRV_F_DELETED 0x8000 /* srv is deleted but not yet purged */ #define SRV_F_STRICT_MAXCONN 0x10000 /* maxconn is to be strictly enforced, as a limit of outbound connections */ #define SRV_F_CHK_NO_AUTO_SNI 0x20000 /* disable automatic SNI selection for healthcheck */ +#define SRV_F_UDP_GSO_NOTSUPP 0x40000 /* UDP GSO is disabled due to a previous error encountered */ /* configured server options for send-proxy (server->pp_opts) */ #define SRV_PP_V1 0x0001 /* proxy protocol version 1 */ diff --git a/src/quic_conn.c b/src/quic_conn.c index 7c0fcb7da..d55be2b41 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -1197,6 +1197,10 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4, struct quic_connection_id *conn_cid = NULL; qc->flags = QUIC_FL_CONN_IS_BACK|QUIC_FL_CONN_PEER_VALIDATED_ADDR; + /* Duplicate GSO status on server to connection */ + if (HA_ATOMIC_LOAD(&srv->flags) & SRV_F_UDP_GSO_NOTSUPP) + qc->flags |= QUIC_FL_CONN_UDP_GSO_EIO; + qc->state = QUIC_HS_ST_CLIENT_INITIAL; /* This is the original connection ID from the peer server diff --git a/src/quic_tx.c b/src/quic_tx.c index 10a6c5e65..6c5631651 100644 --- a/src/quic_tx.c +++ b/src/quic_tx.c @@ -289,10 +289,8 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx) int ret = 0; struct quic_conn *qc; char skip_sendto = 0; - struct listener *l; qc = ctx->qc; - l = objt_listener(qc->target); TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc); while (b_contig_data(buf, 0)) { unsigned char *pos; @@ -333,15 +331,19 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx) /* GSO must not be used if already disabled. */ BUG_ON(qc->flags & QUIC_FL_CONN_UDP_GSO_EIO); - /* TODO: note that at this time for connection to backends this - * part is not run because no more than an MTU has been - * prepared for such connections (l is not NULL). - */ - /* Disable permanently UDP GSO for this listener. - * Retry standard emission. - */ - TRACE_ERROR("mark listener UDP GSO as unsupported", QUIC_EV_CONN_SPPKTS, qc, first_pkt); - HA_ATOMIC_OR(&l->flags, LI_F_UDP_GSO_NOTSUPP); + /* Permanently disable UDP GSO for future conns which use current listener/server instance. */ + if (!qc_is_back(qc)) { + struct listener *l = __objt_listener(qc->target); + TRACE_ERROR("mark listener UDP GSO as unsupported", QUIC_EV_CONN_SPPKTS, qc, first_pkt); + HA_ATOMIC_OR(&l->flags, LI_F_UDP_GSO_NOTSUPP); + } + else if (qc->conn) { + struct server *srv = __objt_server(qc->conn->target); + TRACE_ERROR("mark server UDP GSO as unsupported", QUIC_EV_CONN_SPPKTS, qc, first_pkt); + HA_ATOMIC_OR(&srv->flags, SRV_F_UDP_GSO_NOTSUPP); + } + + /* Retry emission without GSO. */ qc->flags |= QUIC_FL_CONN_UDP_GSO_EIO; continue; } @@ -813,9 +815,6 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf, dglen == qc->path->mtu && (char *)end < b_wrap(buf) && ++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS) { - /* TODO: note that for backends GSO is not used. No more than - * an MTU is prepared. - */ /* A datagram covering the full MTU has been * built, use GSO to built next entry. Do not * reserve extra space for datagram header.