MINOR: quic-be: get rid of ->li quic_conn member

Replace ->li quic_conn pointer to struct listener member by  ->target which is
an object type enum and adapt the code.
Use __objt_(listener|server)() where the object type is known. Typically
this is were the code which is specific to one connection type (frontend/backend).
Remove <server> parameter passed to qc_new_conn(). It is redundant with the
<target> parameter.
GSO is not supported at this time for QUIC backend. qc_prep_pkts() is modified
to prevent it from building more than an MTU. This has as consequence to prevent
qc_send_ppkts() to use GSO.
ssl_clienthello.c code is run only by listeners. This is why __objt_listener()
is used in place of ->li.
This commit is contained in:
Frederic Lecaille 2025-06-06 15:20:00 +02:00 committed by Amaury Denoyelle
parent f6ef3bbc8a
commit b9703cf711
14 changed files with 81 additions and 74 deletions

View File

@ -319,7 +319,7 @@ struct qcc_app_ops;
* with a connection \
*/ \
struct eb_root *cids; \
struct listener *li; /* only valid for frontend connections */ \
enum obj_type *target; \
/* Idle timer task */ \
struct task *idle_timer_task; \
unsigned int idle_expire; \

View File

@ -69,7 +69,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
struct quic_connection_id *conn_id,
struct sockaddr_storage *local_addr,
struct sockaddr_storage *peer_addr,
int server, int token, void *owner,
int token, void *owner,
struct connection *conn);
int quic_build_post_handshake_frames(struct quic_conn *qc);
const struct quic_version *qc_supported_version(uint32_t version);
@ -164,13 +164,6 @@ static inline void quic_free_ncbuf(struct ncbuf *ncbuf)
*ncbuf = NCBUF_NULL;
}
/* Return the address of the connection owner object type. */
static inline enum obj_type *qc_owner_obj_type(struct quic_conn *qc)
{
return qc_is_listener(qc) ? &qc->li->obj_type :
&objt_server(qc->conn->target)->obj_type;
}
/* Return the address of the QUIC counters attached to the proxy of
* the owner of the connection whose object type address is <o> for
* listener and servers, or NULL for others object type.

View File

@ -33,6 +33,7 @@
#include <haproxy/connection-t.h>
#include <haproxy/fd-t.h>
#include <haproxy/listener-t.h>
#include <haproxy/obj_type.h>
#include <haproxy/quic_conn-t.h>
#include <haproxy/quic_sock-t.h>
@ -78,7 +79,8 @@ static inline char qc_test_fd(struct quic_conn *qc)
*/
static inline int qc_fd(struct quic_conn *qc)
{
return qc_test_fd(qc) ? qc->fd : qc->li->rx.fd;
/* TODO: check this: For backends, qc->fd is always initialized */
return qc_test_fd(qc) ? qc->fd : __objt_listener(qc->target)->rx.fd;
}
/* Try to increment <l> handshake current counter. If listener limit is

View File

@ -1423,7 +1423,8 @@ static int cli_io_handler_show_fd(struct appctx *appctx)
#if defined(USE_QUIC)
else if (fdt.iocb == quic_conn_sock_fd_iocb) {
qc = fdtab[fd].owner;
li = qc ? qc->li : NULL;
li = qc ? objt_listener(qc->target) : NULL;
sv = qc ? objt_server(qc->target) : NULL;
xprt_ctx = qc ? qc->xprt_ctx : NULL;
conn = qc ? qc->conn : NULL;
xprt = conn ? conn->xprt : NULL; // in fact it's &ssl_quic

View File

@ -4,7 +4,7 @@
#include <haproxy/cli.h>
#include <haproxy/list.h>
#include <haproxy/mux_quic.h>
#include <haproxy/quic_conn-t.h>
#include <haproxy/quic_conn.h>
#include <haproxy/quic_tp.h>
#include <haproxy/quic_utils.h>
#include <haproxy/tools.h>
@ -181,9 +181,11 @@ static void dump_quic_oneline(struct show_quic_ctx *ctx, struct quic_conn *qc)
char bufaddr[INET6_ADDRSTRLEN], bufport[6];
int ret;
unsigned char cid_len;
struct listener *l = objt_listener(qc->target);
ret = chunk_appendf(&trash, "%p[%02u]/%-.12s ", qc, ctx->thr,
qc->li->bind_conf->frontend->id);
l ? l->bind_conf->frontend->id : __objt_server(qc->target)->id);
chunk_appendf(&trash, "%*s", 36 - ret, " "); /* align output */
/* State */

View File

@ -749,7 +749,7 @@ static struct quic_conn_closed *qc_new_cc_conn(struct quic_conn *qc)
cc_qc->dcid = qc->dcid;
cc_qc->scid = qc->scid;
cc_qc->li = qc->li;
cc_qc->target = qc->target;
cc_qc->cids = qc->cids;
cc_qc->idle_timer_task = qc->idle_timer_task;
@ -1067,27 +1067,18 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
struct quic_connection_id *conn_id,
struct sockaddr_storage *local_addr,
struct sockaddr_storage *peer_addr,
int server, int token, void *owner,
int token, void *target,
struct connection *conn)
{
struct quic_conn *qc = NULL;
struct listener *l = NULL;
struct server *srv = NULL;
struct proxy *prx = NULL;
struct listener *l = objt_listener(target);
struct server *srv = objt_server(target);
struct proxy *prx = l ? l->bind_conf->frontend : __objt_server(target)->proxy;
struct quic_cc_algo *cc_algo = NULL;
unsigned int next_actconn = 0, next_sslconn = 0, next_handshake = 0;
TRACE_ENTER(QUIC_EV_CONN_INIT);
if (server) {
l = owner;
prx = l->bind_conf->frontend;
}
else {
srv = owner;
prx = srv->proxy;
}
next_actconn = increment_actconn();
if (!next_actconn) {
_HA_ATOMIC_INC(&maxconn_reached);
@ -1101,7 +1092,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
goto err;
}
if (server) {
if (l) {
next_handshake = quic_increment_curr_handshake(l);
if (!next_handshake) {
TRACE_STATE("max handshake reached", QUIC_EV_CONN_INIT);
@ -1121,6 +1112,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
goto err;
}
qc->target = target;
*qc->cids = EB_ROOT;
/* Now that quic_conn instance is allocated, quic_conn_release() will
* ensure global accounting is decremented.
@ -1176,7 +1168,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
/* QUIC Server (or listener). */
if (server) {
if (l) {
cc_algo = l->bind_conf->quic_cc_algo;
qc->flags = QUIC_FL_CONN_LISTENER;
@ -1189,7 +1181,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
/* Copy the packet SCID to reuse it as DCID for sending */
qc->dcid = *scid;
qc->tx.buf = BUF_NULL;
qc->li = l;
conn_id->qc = qc;
}
/* QUIC Client (outgoing connection to servers) */
@ -1218,7 +1209,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
conn_id = conn_cid;
qc->tx.buf = BUF_NULL;
qc->li = NULL;
qc->next_cid_seq_num = 1;
conn->handle.qc = qc;
}
@ -1228,7 +1218,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
/* Listener only: if connection is instantiated due to an INITIAL packet with an
* already checked token, consider the peer address as validated.
*/
if (server) {
if (l) {
if (token_odcid->len) {
TRACE_STATE("validate peer address due to initial token",
QUIC_EV_CONN_INIT, qc);
@ -1301,7 +1291,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
qc->max_ack_delay = 0;
/* Only one path at this time (multipath not supported) */
qc->path = &qc->paths[0];
quic_cc_path_init(qc->path, ipv4, server ? l->bind_conf->max_cwnd : 0,
quic_cc_path_init(qc->path, ipv4, l ? l->bind_conf->max_cwnd : 0,
cc_algo ? cc_algo : default_quic_cc_algo, qc);
if (local_addr)
@ -1310,7 +1300,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
memset(&qc->local_addr, 0, sizeof(qc->local_addr));
memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);
if (server) {
if (l) {
qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
conn_id->stateless_reset_token,
qc->dcid.data, qc->dcid.len,
@ -1344,7 +1334,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
!quic_conn_init_idle_timer_task(qc, prx))
goto err;
if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, server))
if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, !!l))
goto err;
/* Counters initialization */
@ -1395,7 +1385,7 @@ int qc_handle_conn_migration(struct quic_conn *qc,
* used during the handshake, unless the endpoint has acted on a
* preferred_address transport parameter from the peer.
*/
if (qc->li->bind_conf->quic_params.disable_active_migration) {
if (__objt_listener(qc->target)->bind_conf->quic_params.disable_active_migration) {
TRACE_ERROR("Active migration was disabled, datagram dropped", QUIC_EV_CONN_LPKT, qc);
goto err;
}
@ -1525,8 +1515,8 @@ int quic_conn_release(struct quic_conn *qc)
*/
if (MT_LIST_INLIST(&qc->accept_list)) {
MT_LIST_DELETE(&qc->accept_list);
BUG_ON(qc->li->rx.quic_curr_accept == 0);
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_accept);
BUG_ON(__objt_listener(qc->target)->rx.quic_curr_accept == 0);
HA_ATOMIC_DEC(&__objt_listener(qc->target)->rx.quic_curr_accept);
}
/* Substract last congestion window from global memory counter. */
@ -1596,8 +1586,8 @@ int quic_conn_release(struct quic_conn *qc)
/* Connection released before handshake completion. */
if (unlikely(qc->state < QUIC_HS_ST_COMPLETE)) {
if (qc_is_listener(qc)) {
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
BUG_ON(__objt_listener(qc->target)->rx.quic_curr_handshake == 0);
HA_ATOMIC_DEC(&__objt_listener(qc->target)->rx.quic_curr_handshake);
}
}
@ -2005,8 +1995,8 @@ void qc_bind_tid_commit(struct quic_conn *qc, struct listener *new_li)
/* At this point no connection was accounted for yet on this
* listener so it's OK to just swap the pointer.
*/
if (new_li && new_li != qc->li)
qc->li = new_li;
if (new_li && new_li != __objt_listener(qc->target))
qc->target = &new_li->obj_type;
/* Rebind the connection FD. */
if (qc_test_fd(qc)) {

View File

@ -1819,7 +1819,7 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
goto err;
qc = qc_new_conn(pkt->version, ipv4, &pkt->dcid, &pkt->scid, &token_odcid,
conn_id, &dgram->daddr, &pkt->saddr, 1,
conn_id, &dgram->daddr, &pkt->saddr,
!!pkt->token_len, l, NULL);
if (qc == NULL) {
pool_free(pool_head_quic_connection_id, conn_id);

View File

@ -87,13 +87,13 @@ int quic_sock_get_dst(struct connection *conn, struct sockaddr *addr, socklen_t
memcpy(addr, &qc->peer_addr, len);
} else {
struct sockaddr_storage *from;
struct listener *l = objt_listener(qc->target);
/* Return listener address if IP_PKTINFO or friends are not
* supported by the socket.
*/
BUG_ON(!qc->li);
from = is_addr(&qc->local_addr) ? &qc->local_addr :
&qc->li->rx.addr;
BUG_ON(!l);
from = is_addr(&qc->local_addr) ? &qc->local_addr : &l->rx.addr;
if (len > sizeof(*from))
len = sizeof(*from);
memcpy(addr, from, len);
@ -819,6 +819,7 @@ int qc_rcv_buf(struct quic_conn *qc)
struct buffer buf = BUF_NULL;
unsigned char *dgram_buf;
ssize_t ret = 0;
struct listener *l = objt_listener(qc->target);
/* Do not call this if quic-conn FD is uninitialized. */
BUG_ON(qc->fd < 0);
@ -869,7 +870,7 @@ int qc_rcv_buf(struct quic_conn *qc)
continue;
}
if (qc_is_listener(qc) && !qc_check_dcid(qc, new_dgram->dcid, new_dgram->dcid_len)) {
if (l && !qc_check_dcid(qc, new_dgram->dcid, new_dgram->dcid_len)) {
/* Datagram received by error on the connection FD, dispatch it
* to its associated quic-conn.
*
@ -879,7 +880,6 @@ int qc_rcv_buf(struct quic_conn *qc)
struct quic_dgram *tmp_dgram;
unsigned char *rxbuf_tail;
size_t cspace;
struct listener *l = qc->li;
TRACE_STATE("datagram for other connection on quic-conn socket, requeue it", QUIC_EV_CONN_RCV, qc);
@ -928,7 +928,7 @@ int qc_rcv_buf(struct quic_conn *qc)
continue;
}
quic_dgram_parse(new_dgram, qc, qc_owner_obj_type(qc));
quic_dgram_parse(new_dgram, qc, qc->target);
/* A datagram must always be consumed after quic_parse_dgram(). */
BUG_ON(new_dgram->buf);
} while (ret > 0);
@ -950,11 +950,13 @@ int qc_rcv_buf(struct quic_conn *qc)
*
* Return the socket FD or a negative error code. On error, socket is marked as
* uninitialized.
* Note: This function must not be run for backends connection.
*/
void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
const struct sockaddr_storage *dst)
{
struct bind_conf *bc = qc->li->bind_conf;
struct listener *l = __objt_listener(qc->target);
struct bind_conf *bc = l->bind_conf;
struct proxy *p = bc->frontend;
int fd = -1;
int ret;
@ -1007,7 +1009,7 @@ void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
}
/* Fallback to listener socket for this receiver instance. */
HA_ATOMIC_STORE(&qc->li->rx.quic_mode, QUIC_SOCK_MODE_LSTNR);
HA_ATOMIC_STORE(&l->rx.quic_mode, QUIC_SOCK_MODE_LSTNR);
}
goto err;
}
@ -1061,13 +1063,14 @@ struct quic_accept_queue *quic_accept_queues;
void quic_accept_push_qc(struct quic_conn *qc)
{
struct quic_accept_queue *queue = &quic_accept_queues[tid];
struct li_per_thread *lthr = &qc->li->per_thr[ti->ltid];
struct listener *l = __objt_listener(qc->target);
struct li_per_thread *lthr = &l->per_thr[ti->ltid];
/* A connection must only be accepted once per instance. */
BUG_ON(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED);
BUG_ON(MT_LIST_INLIST(&qc->accept_list));
HA_ATOMIC_INC(&qc->li->rx.quic_curr_accept);
HA_ATOMIC_INC(&l->rx.quic_curr_accept);
qc->flags |= QUIC_FL_CONN_ACCEPT_REGISTERED;
/* 1. insert the listener in the accept queue

View File

@ -937,14 +937,16 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
* provided by the stack. This happens after having received the peer
* handshake level CRYPTO data which are validated by the TLS stack.
*/
if (qc->li->bind_conf->ssl_conf.early_data &&
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
TRACE_PROTO("SSL handshake in progress",
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
goto out;
}
else {
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
if (qc_is_listener(qc)) {
if (__objt_listener(qc->target)->bind_conf->ssl_conf.early_data &&
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
TRACE_PROTO("SSL handshake in progress",
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
goto out;
}
else {
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
}
}
#endif
@ -957,6 +959,7 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
if (qc_is_listener(ctx->qc)) {
struct listener *l = __objt_listener(qc->target);
/* I/O callback switch */
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
qc->state = QUIC_HS_ST_CONFIRMED;
@ -972,8 +975,8 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
tasklet_wakeup(qc->wait_event.tasklet);
}
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
BUG_ON(l->rx.quic_curr_handshake == 0);
HA_ATOMIC_DEC(&l->rx.quic_curr_handshake);
}
else {
qc->state = QUIC_HS_ST_COMPLETE;
@ -1201,7 +1204,7 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, struct connection *conn)
ctx->qc = qc;
if (qc_is_listener(qc)) {
struct bind_conf *bc = qc->li->bind_conf;
struct bind_conf *bc = __objt_listener(qc->target)->bind_conf;
if (qc_ssl_sess_init(qc, bc->initial_ctx, &ctx->ssl, NULL, 1) == -1)
goto err;

View File

@ -288,8 +288,10 @@ 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;
@ -305,7 +307,11 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
/* If datagram bigger than MTU, several ones were encoded for GSO usage. */
if (dglen > qc->path->mtu) {
if (likely(!(HA_ATOMIC_LOAD(&qc->li->flags) & LI_F_UDP_GSO_NOTSUPP))) {
/* 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 (dglen <= qc->path->mtu). So, here l is not NULL.
*/
if (likely(!(HA_ATOMIC_LOAD(&l->flags) & LI_F_UDP_GSO_NOTSUPP))) {
TRACE_PROTO("send multiple datagrams with GSO", QUIC_EV_CONN_SPPKTS, qc);
gso = qc->path->mtu;
}
@ -327,11 +333,15 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, gso);
if (ret < 0) {
if (gso && ret == -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(&qc->li->flags, LI_F_UDP_GSO_NOTSUPP);
HA_ATOMIC_OR(&l->flags, LI_F_UDP_GSO_NOTSUPP);
continue;
}
@ -576,6 +586,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
int dgram_cnt = 0;
/* Restrict GSO emission to comply with sendmsg limitation. See QUIC_MAX_GSO_DGRAMS for more details. */
uchar gso_dgram_cnt = 0;
struct listener *l = objt_listener(qc->target);
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
/* Currently qc_prep_pkts() does not handle buffer wrapping so the
@ -765,11 +776,13 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
prv_pkt = cur_pkt;
}
else if (!(quic_tune.options & QUIC_TUNE_NO_UDP_GSO) &&
!(HA_ATOMIC_LOAD(&qc->li->flags) & LI_F_UDP_GSO_NOTSUPP) &&
dglen == qc->path->mtu &&
(char *)end < b_wrap(buf) &&
++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS) {
++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS &&
l && !(HA_ATOMIC_LOAD(&l->flags) & LI_F_UDP_GSO_NOTSUPP)) {
/* 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.

View File

@ -177,7 +177,7 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg)
s = __objt_listener(conn->target)->bind_conf;
#ifdef USE_QUIC
else if (qc)
s = qc->li->bind_conf;
s = __objt_listener(qc->target)->bind_conf;
#endif /* USE_QUIC */
if (!s) {

View File

@ -127,7 +127,7 @@ int ssl_sock_ocsp_stapling_cbk(SSL *ssl, void *arg)
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
/* null if not a listener */
li = qc->li;
li = objt_listener(qc->target);
}
#endif

View File

@ -926,7 +926,7 @@ static int ssl_tlsext_ticket_key_cb(SSL *s, unsigned char key_name[16], unsigned
ref = __objt_listener(conn->target)->bind_conf->keys_ref;
#ifdef USE_QUIC
else if (qc)
ref = qc->li->bind_conf->keys_ref;
ref = __objt_listener(qc->target)->bind_conf->keys_ref;
#endif
if (!ref) {
@ -1482,7 +1482,7 @@ int ssl_sock_bind_verifycbk(int ok, X509_STORE_CTX *x_store)
else {
qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
BUG_ON(!qc); /* Must never happen */
bind_conf = qc->li->bind_conf;
bind_conf = __objt_listener(qc->target)->bind_conf;
ctx = qc->xprt_ctx;
}
#endif

View File

@ -123,7 +123,7 @@ static int qc_conn_init(struct connection *conn, void **xprt_ctx)
int ipv4 = conn->dst->ss_family == AF_INET;
struct server *srv = objt_server(conn->target);
qc = qc_new_conn(quic_version_1, ipv4, NULL, NULL, NULL,
NULL, NULL, &srv->addr, 0, 0, srv, conn);
NULL, NULL, &srv->addr, 0, srv, conn);
}
if (!qc)