From cc231f3468d4654aaecf77afeadd616abd7c73dd Mon Sep 17 00:00:00 2001 From: Maxime Henrion Date: Tue, 21 Apr 2026 15:25:58 -0400 Subject: [PATCH] OPTIM: quic: reduce the size of struct quic_dgram The QUIC code can only handle IPv4 or IPv6 addresses, so using two sockaddr_storage structs wastes a lot of space in the quic_dgram struct. This is a very large overhead since this structure is written in the MPSC ring buffers before every datagram, while many of those datagrams are only 50 bytes or less. Using an union instead saves 200 bytes per datagram, increasing the capacity of the buffers significantly. --- include/haproxy/quic_rx-t.h | 3 ++- include/haproxy/quic_sock-t.h | 9 +++++++-- src/quic_retry.c | 3 ++- src/quic_rules.c | 4 ++-- src/quic_rx.c | 23 +++++++++++++---------- src/quic_sock.c | 9 ++++++--- src/quic_token.c | 2 +- src/quic_trace.c | 14 ++++++++------ 8 files changed, 41 insertions(+), 26 deletions(-) diff --git a/include/haproxy/quic_rx-t.h b/include/haproxy/quic_rx-t.h index 686e6fff1..9bfe6fbb1 100644 --- a/include/haproxy/quic_rx-t.h +++ b/include/haproxy/quic_rx-t.h @@ -7,6 +7,7 @@ extern struct pool_head *pool_head_quic_rx_packet; #include #include #include +#include #include #include @@ -52,7 +53,7 @@ struct quic_rx_packet { struct eb64_node pn_node; volatile unsigned int refcnt; /* Source address of this packet. */ - struct sockaddr_storage saddr; + union sockaddr_in46 saddr; unsigned int flags; unsigned int time_received; }; diff --git a/include/haproxy/quic_sock-t.h b/include/haproxy/quic_sock-t.h index 735801757..19bf8d51e 100644 --- a/include/haproxy/quic_sock-t.h +++ b/include/haproxy/quic_sock-t.h @@ -21,6 +21,11 @@ struct quic_accept_queue { #define QUIC_DGRAM_FL_REJECT 0x00000001 #define QUIC_DGRAM_FL_SEND_RETRY 0x00000002 +union sockaddr_in46 { + struct sockaddr_in in4; + struct sockaddr_in6 in6; +}; + /* QUIC datagram */ struct quic_dgram { enum obj_type obj_type; @@ -29,8 +34,8 @@ struct quic_dgram { size_t len; size_t dcid_off; size_t dcid_len; - struct sockaddr_storage saddr; - struct sockaddr_storage daddr; + union sockaddr_in46 saddr; + union sockaddr_in46 daddr; struct quic_conn *qc; int flags; /* QUIC_DGRAM_FL_* values */ diff --git a/src/quic_retry.c b/src/quic_retry.c index 2ac981034..23b74d8fc 100644 --- a/src/quic_retry.c +++ b/src/quic_retry.c @@ -283,7 +283,8 @@ int quic_retry_token_check(struct quic_rx_packet *pkt, goto err; } - aadlen = quic_generate_retry_token_aad(aad, qv->num, &pkt->scid, &dgram->saddr); + aadlen = quic_generate_retry_token_aad(aad, qv->num, &pkt->scid, + (struct sockaddr_storage *)&dgram->saddr); salt = token + tokenlen - QUIC_RETRY_TOKEN_SALTLEN; if (!quic_tls_derive_retry_token_secret(EVP_sha256(), key, sizeof key, iv, sizeof iv, salt, QUIC_RETRY_TOKEN_SALTLEN, sec, seclen)) { diff --git a/src/quic_rules.c b/src/quic_rules.c index d73a4a7e4..55ccb899c 100644 --- a/src/quic_rules.c +++ b/src/quic_rules.c @@ -28,8 +28,8 @@ int quic_init_exec_rules(struct listener *li, struct quic_dgram *dgram) */ rule_sess.fe = px; rule_sess.listener = li; - rule_sess.src = &dgram->saddr; - rule_sess.dst = &dgram->daddr; + rule_sess.src = (struct sockaddr_storage *)&dgram->saddr; + rule_sess.dst = (struct sockaddr_storage *)&dgram->daddr; rule_sess.origin = &dgram->obj_type; list_for_each_entry(rule, &px->quic_init_rules, list) { diff --git a/src/quic_rx.c b/src/quic_rx.c index 4f2cb14a9..6c1c2733b 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -1753,7 +1753,7 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt, prx = l->bind_conf->frontend; prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module); - qc = retrieve_qc_conn_from_cid(pkt, &dgram->saddr, new_tid); + qc = retrieve_qc_conn_from_cid(pkt, (struct sockaddr_storage *)&dgram->saddr, new_tid); /* quic_conn must be set to NULL if bind on another thread. */ BUG_ON_HOT(qc && *new_tid != -1); @@ -1788,7 +1788,7 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt, /* Validate the token, retry or not only when connection is unknown. */ if (!quic_token_validate(pkt, dgram, l, qc, &token_odcid)) { if (dgram->flags & QUIC_DGRAM_FL_SEND_RETRY) { - if (send_retry(l->rx.fd, &dgram->saddr, pkt, pkt->version)) { + if (send_retry(l->rx.fd, (struct sockaddr_storage *)&dgram->saddr, pkt, pkt->version)) { TRACE_ERROR("Error during Retry generation", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version); } @@ -1818,7 +1818,7 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt, TRACE_PROTO("Initial without token, sending retry", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version); - if (send_retry(l->rx.fd, &dgram->saddr, pkt, pkt->version)) { + if (send_retry(l->rx.fd, (struct sockaddr_storage *)&dgram->saddr, pkt, pkt->version)) { TRACE_ERROR("Error during Retry generation", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version); goto out; @@ -1850,7 +1850,7 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt, goto err; } - if (quic_cid_derive_from_odcid(conn_id, &pkt->dcid, &pkt->saddr)) { + if (quic_cid_derive_from_odcid(conn_id, &pkt->dcid, (struct sockaddr_storage *)&pkt->saddr)) { TRACE_ERROR("error on CID generation", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version); pool_free(pool_head_quic_connection_id, conn_id); @@ -1868,8 +1868,9 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt, pool_free(pool_head_quic_connection_id, conn_id); } else { - qc = qc_new_conn(l, pkt, &token_odcid, - NULL, conn_id, &dgram->daddr, &pkt->saddr); + qc = qc_new_conn(l, pkt, &token_odcid, NULL, conn_id, + (struct sockaddr_storage *)&dgram->daddr, + (struct sockaddr_storage *)&pkt->saddr); if (qc == NULL) { quic_cid_delete(conn_id); /* Removes CID from global tree as it points to a NULL qc. */ pool_free(pool_head_quic_connection_id, conn_id); @@ -1896,7 +1897,7 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt, * emits stateless_reset_token in its TPs. */ TRACE_PROTO("RX non Initial pkt without connection", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version); - if (!send_stateless_reset(l, &dgram->saddr, pkt)) + if (!send_stateless_reset(l, (struct sockaddr_storage *)&dgram->saddr, pkt)) TRACE_ERROR("stateless reset not sent", QUIC_EV_CONN_LPKT, qc); goto err; } @@ -1993,7 +1994,7 @@ static int quic_rx_pkt_parse(struct quic_conn *qc, struct quic_rx_packet *pkt, */ if (l && !pkt->version) { /* unsupported version, send Negotiation packet */ - if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) { + if (send_version_negotiation(l->rx.fd, (struct sockaddr_storage *)&dgram->saddr, pkt)) { TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT); goto drop_silent; } @@ -2489,8 +2490,10 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc, } /* Detect QUIC connection migration. */ - if (li && ipcmp(&qc->peer_addr, &dgram->saddr, 1)) { - if (qc_handle_conn_migration(qc, &dgram->saddr, &dgram->daddr)) { + if (li && ipcmp(&qc->peer_addr, (struct sockaddr_storage *)&dgram->saddr, 1)) { + if (qc_handle_conn_migration(qc, + (struct sockaddr_storage *)&dgram->saddr, + (struct sockaddr_storage *)&dgram->daddr)) { /* Skip the entire datagram. */ TRACE_ERROR("error during connection migration, datagram dropped", QUIC_EV_CONN_LPKT, qc); pkt->len = end - pos; diff --git a/src/quic_sock.c b/src/quic_sock.c index ea9a8e0e3..4620e612f 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -267,14 +267,16 @@ static void quic_dgram_init(struct quic_dgram *dgram, struct sockaddr_storage *saddr, struct sockaddr_storage *daddr) { + BUG_ON_HOT(!is_inet_addr(saddr) || !is_inet_addr(daddr)); + dgram->obj_type = OBJ_TYPE_DGRAM; dgram->owner = owner; dgram->buf = pos; dgram->len = len; dgram->dcid_off = dcid_off; dgram->dcid_len = dcid_len; - dgram->saddr = *saddr; - dgram->daddr = *daddr; + memcpy(&dgram->saddr, saddr, sizeof(dgram->saddr)); + memcpy(&dgram->daddr, daddr, sizeof(dgram->daddr)); dgram->qc = NULL; dgram->flags = 0; } @@ -432,7 +434,8 @@ int quic_dgram_requeue(struct quic_dgram *dgram, int cid_tid) { return quic_dgram_write(dgram->buf, dgram->len, dgram->owner, dgram->dcid_off, dgram->dcid_len, - &dgram->saddr, &dgram->daddr, cid_tid); + (struct sockaddr_storage *)&dgram->saddr, + (struct sockaddr_storage *)&dgram->daddr, cid_tid); } /* Attempt to push a datagram to its handler thread. diff --git a/src/quic_token.c b/src/quic_token.c index 9c1d69cd1..5200225ef 100644 --- a/src/quic_token.c +++ b/src/quic_token.c @@ -135,7 +135,7 @@ int quic_token_check(struct quic_rx_packet *pkt, } /* Generate the AAD. */ - aadlen = ipaddrcpy(aad, &dgram->saddr); + aadlen = ipaddrcpy(aad, (struct sockaddr_storage *)&dgram->saddr); rand = token + tokenlen - QUIC_TOKEN_RAND_DLEN; if (!quic_tls_derive_token_secret(EVP_sha256(), key, sizeof key, iv, sizeof iv, rand, QUIC_TOKEN_RAND_DLEN, sec, seclen)) { diff --git a/src/quic_trace.c b/src/quic_trace.c index 2dd12b805..21d8bc72c 100644 --- a/src/quic_trace.c +++ b/src/quic_trace.c @@ -606,6 +606,7 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace if (mask & QUIC_EV_CONN_RCV) { int i; const struct quic_dgram *dgram = a2; + const struct sockaddr_storage *saddr, *daddr; char bufaddr[INET6_ADDRSTRLEN], bufport[6]; if (qc) { @@ -617,14 +618,15 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace if (dgram) { chunk_appendf(&trace_buf, " dgram.len=%zu", dgram->len); /* Socket */ - if (dgram->saddr.ss_family == AF_INET || - dgram->saddr.ss_family == AF_INET6) { - addr_to_str(&dgram->saddr, bufaddr, sizeof(bufaddr)); - port_to_str(&dgram->saddr, bufport, sizeof(bufport)); + saddr = (struct sockaddr_storage *)&dgram->saddr; + daddr = (struct sockaddr_storage *)&dgram->daddr; + if (saddr->ss_family == AF_INET || saddr->ss_family == AF_INET6) { + addr_to_str(saddr, bufaddr, sizeof(bufaddr)); + port_to_str(saddr, bufport, sizeof(bufport)); chunk_appendf(&trace_buf, "saddr=%s:%s ", bufaddr, bufport); - addr_to_str(&dgram->daddr, bufaddr, sizeof(bufaddr)); - port_to_str(&dgram->daddr, bufport, sizeof(bufport)); + addr_to_str(daddr, bufaddr, sizeof(bufaddr)); + port_to_str(daddr, bufport, sizeof(bufport)); chunk_appendf(&trace_buf, "daddr=%s:%s ", bufaddr, bufport); } /* DCID */