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.
This commit is contained in:
Maxime Henrion 2026-04-21 15:25:58 -04:00 committed by Amaury Denoyelle
parent df0614b177
commit cc231f3468
8 changed files with 41 additions and 26 deletions

View File

@ -7,6 +7,7 @@ extern struct pool_head *pool_head_quic_rx_packet;
#include <import/eb64tree.h>
#include <haproxy/api-t.h>
#include <haproxy/quic_cid-t.h>
#include <haproxy/quic_sock-t.h>
#include <inttypes.h>
#include <sys/socket.h>
@ -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;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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