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 <qc.conn.target>. This is enough to guarantee
that server is not deleted.

This does not need to be backported.
This commit is contained in:
Amaury Denoyelle 2025-08-05 10:26:29 +02:00
parent e653dc304e
commit 0b6908385e
4 changed files with 19 additions and 14 deletions

View File

@ -47,6 +47,7 @@
#include <haproxy/quic_loss.h>
#include <haproxy/quic_pacing.h>
#include <haproxy/quic_rx.h>
#include <haproxy/quic_tune.h>
#include <haproxy/mux_quic.h>
#include <openssl/rand.h>

View File

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

View File

@ -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

View File

@ -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.