diff --git a/doc/configuration.txt b/doc/configuration.txt index e98f571f36..cd15e93aa2 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3470,10 +3470,10 @@ tune.quic.retry-threshold See https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1.2 for more information about QUIC retry. -tune.quic.socket-owner { listener | connection } - Specifies how QUIC connections will use socket for receive/send operations. - Connections can share listener socket or each connection can allocate its - own socket. +tune.quic.socket-owner { connection | listener } + Specifies globally how QUIC connections will use socket for receive/send + operations. Connections can share listener socket or each connection can + allocate its own socket. When default "connection" value is set, a dedicated socket will be allocated by every QUIC connections. This option is the preferred one to achieve the @@ -3493,6 +3493,12 @@ tune.quic.socket-owner { listener | connection } a higher CPU usage if listeners are shared across a lot of threads or a large number of QUIC connections can be used simultaneously. + This setting is applied in conjunction with each "quic-socket" bind options. + If "connection" mode is used on global tuning, it will be activated for each + listener, unless its bind option is set to "listener". However, if "listener" + is used globally, it will be forced on every listener instance, regardless of + their individual configuration. + tune.rcvbuf.client tune.rcvbuf.server Forces the kernel socket receive buffer size on the client or the server side @@ -15520,6 +15526,11 @@ quic-force-retry See https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1.2 for more information about QUIC retry. +quic-socket [ connection | listener ] + This QUIC specific setting allows to define the socket allocation mode for + the specific listeners. See "tune.quic.socket-owner" for a full description + of its usage. + shards | by-thread | by-group In multi-threaded mode, on operating systems supporting multiple listeners on the same IP:port, this will automatically create this number of multiple diff --git a/include/haproxy/listener-t.h b/include/haproxy/listener-t.h index 8e02799a41..8c66dd00c1 100644 --- a/include/haproxy/listener-t.h +++ b/include/haproxy/listener-t.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -183,6 +184,7 @@ struct bind_conf { #ifdef USE_QUIC struct quic_transport_params quic_params; /* QUIC transport parameters. */ struct quic_cc_algo *quic_cc_algo; /* QUIC control congestion algorithm */ + enum quic_sock_mode quic_mode; /* QUIC socket allocation strategy */ #endif struct proxy *frontend; /* the frontend all these listeners belong to, or NULL */ const struct mux_proto_list *mux_proto; /* the mux to use for all incoming connections (specified by the "proto" keyword) */ diff --git a/include/haproxy/quic_sock-t.h b/include/haproxy/quic_sock-t.h index 5a79b69b0d..364fdd1bf5 100644 --- a/include/haproxy/quic_sock-t.h +++ b/include/haproxy/quic_sock-t.h @@ -2,6 +2,12 @@ #define _HAPROXY_QUIC_SOCK_T_H #ifdef USE_QUIC +/* QUIC socket allocation strategy. */ +enum quic_sock_mode { + QUIC_SOCK_MODE_CONN, /* Use a dedicated socket per connection. */ + QUIC_SOCK_MODE_LSTNR, /* Multiplex connections over listener socket. */ +}; + /* QUIC connection accept queue. One per thread. */ struct quic_accept_queue { struct mt_list listeners; /* QUIC listeners with at least one connection ready to be accepted on this queue */ diff --git a/src/cfgparse-quic.c b/src/cfgparse-quic.c index c8cb7d9630..a49a468236 100644 --- a/src/cfgparse-quic.c +++ b/src/cfgparse-quic.c @@ -50,9 +50,34 @@ static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px, return 0; } +static int bind_parse_quic_socket(char **args, int cur_arg, struct proxy *px, + struct bind_conf *conf, char **err) +{ + char *arg; + if (!*args[cur_arg + 1]) { + memprintf(err, "'%s' : missing argument, use either connection or listener.", args[cur_arg]); + return ERR_ALERT | ERR_FATAL; + } + + arg = args[cur_arg + 1]; + if (strcmp(arg, "connection") == 0) { + conf->quic_mode = QUIC_SOCK_MODE_CONN; + } + else if (strcmp(arg, "listener") == 0) { + conf->quic_mode = QUIC_SOCK_MODE_LSTNR; + } + else { + memprintf(err, "'%s' : unknown argument, use either connection or listener.", args[cur_arg]); + return ERR_ALERT | ERR_FATAL; + } + + return 0; +} + static struct bind_kw_list bind_kws = { "QUIC", { }, { { "quic-force-retry", bind_parse_quic_force_retry, 0 }, { "quic-cc-algo", bind_parse_quic_cc_algo, 1 }, + { "quic-socket", bind_parse_quic_socket, 1 }, { NULL, NULL, 0 }, }}; diff --git a/src/listener.c b/src/listener.c index 434291476b..da483702ed 100644 --- a/src/listener.c +++ b/src/listener.c @@ -1947,6 +1947,10 @@ struct bind_conf *bind_conf_alloc(struct proxy *fe, const char *file, HA_RWLOCK_INIT(&bind_conf->sni_lock); bind_conf->sni_ctx = EB_ROOT; bind_conf->sni_w_ctx = EB_ROOT; +#endif +#ifdef USE_QUIC + /* Use connection socket for QUIC by default. */ + bind_conf->quic_mode = QUIC_SOCK_MODE_CONN; #endif LIST_INIT(&bind_conf->listeners); diff --git a/src/quic_conn.c b/src/quic_conn.c index 051c9da579..a2d28c7b04 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -1264,7 +1264,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4, conn_id->qc = qc; - if ((global.tune.options & GTUNE_QUIC_SOCK_PER_CONN) && + if (l->bind_conf->quic_mode == QUIC_SOCK_MODE_CONN && + (global.tune.options & GTUNE_QUIC_SOCK_PER_CONN) && is_addr(local_addr)) { TRACE_USER("Allocate a socket for QUIC connection", QUIC_EV_CONN_INIT, qc); qc_alloc_fd(qc, local_addr, peer_addr);