MINOR: listener: add the "cc" bind keyword to set the TCP congestion controller

It is possible on at least Linux and FreeBSD to set the congestion control
algorithm to be used with incoming connections, among the list of supported
and permitted ones. Let's expose this setting with "cc". Permission issues
might be reported (as warnings).
This commit is contained in:
Willy Tarreau 2025-09-17 17:03:42 +02:00
parent 31d0695a6a
commit 4ed3cf295d
6 changed files with 61 additions and 0 deletions

View File

@ -16382,6 +16382,25 @@ ca-verify-file <cafile>
be defined with intermediate certificates, and "ca-verify-file" with be defined with intermediate certificates, and "ca-verify-file" with
certificates to ending the chain, like root CA. certificates to ending the chain, like root CA.
cc <algo>
This setting is only available on systems which define TCP_CONGESTION, and
was validated on Linux and FreeBSD. It takes the name of a TCP congestion
control algorithm and configures the listener to use this algorithm on all
connections that are accepted from this listener. Typical names include
"reno", "cubic" and will depend on the operating system. On some systems,
special permissions may be required to configure certain algorithms. On
Linux, the list of available algorithms may be found in the sysctl
"net.ipv4.tcp_available_congestion_control", and the list of those permitted
without privileges is in "net.ipv4.tcp_allowed_congestion_control". In order
to access algorithms requiring extra permissions, the "cap_net_admin"
capability might be required (see "setcap" in the global section). In case of
failure to configure a specific congestion control algorithm, the default one
will remain unchanged and a warning will be emitted to report the problem.
Example:
frontend public
bind :443 cc bbr # use the BBR algorithm for high bandwidths
ciphers <ciphers> ciphers <ciphers>
This setting is only available when support for OpenSSL was built in. It sets This setting is only available when support for OpenSSL was built in. It sets
the string describing the list of cipher algorithms ("cipher suite") that are the string describing the list of cipher algorithms ("cipher suite") that are

View File

@ -195,6 +195,7 @@ struct bind_conf {
int maxseg; /* for TCP, advertised MSS */ int maxseg; /* for TCP, advertised MSS */
int tcp_ut; /* for TCP, user timeout */ int tcp_ut; /* for TCP, user timeout */
char *tcp_md5sig; /* TCP MD5 signature password (RFC2385) */ char *tcp_md5sig; /* TCP MD5 signature password (RFC2385) */
char *cc_algo; /* TCP congestion control algorithm ("cc" parameter) */
int idle_ping; /* MUX idle-ping interval in ms */ int idle_ping; /* MUX idle-ping interval in ms */
int maxaccept; /* if set, max number of connections accepted at once (-1 when disabled) */ int maxaccept; /* if set, max number of connections accepted at once (-1 when disabled) */
unsigned int backlog; /* if set, listen backlog */ unsigned int backlog; /* if set, listen backlog */

View File

@ -61,6 +61,26 @@ static int bind_parse_transparent(char **args, int cur_arg, struct proxy *px, st
} }
#endif #endif
#if defined(TCP_CONGESTION)
/* parse the "cc" bind keyword */
static int bind_parse_cc(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
{
if (!*args[cur_arg + 1]) {
memprintf(err, "'%s' : missing TCP congestion control algorithm", args[cur_arg]);
return ERR_ALERT | ERR_FATAL;
}
ha_free(&conf->cc_algo);
conf->cc_algo = strdup(args[cur_arg + 1]);
if (!conf->cc_algo) {
memprintf(err, "'%s %s' : out of memory", args[cur_arg], args[cur_arg + 1]);
return ERR_ALERT | ERR_FATAL;
}
return 0;
}
#endif
#if defined(TCP_DEFER_ACCEPT) || defined(SO_ACCEPTFILTER) #if defined(TCP_DEFER_ACCEPT) || defined(SO_ACCEPTFILTER)
/* parse the "defer-accept" bind keyword */ /* parse the "defer-accept" bind keyword */
static int bind_parse_defer_accept(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) static int bind_parse_defer_accept(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
@ -278,6 +298,9 @@ static int srv_parse_tcp_ut(char **args, int *cur_arg, struct proxy *px, struct
* not enabled. * not enabled.
*/ */
static struct bind_kw_list bind_kws = { "TCP", { }, { static struct bind_kw_list bind_kws = { "TCP", { }, {
#if defined(TCP_CONGESTION)
{ "cc", bind_parse_cc, 1 }, /* set TCP congestion control algorithm */
#endif
#if defined(TCP_DEFER_ACCEPT) || defined(SO_ACCEPTFILTER) #if defined(TCP_DEFER_ACCEPT) || defined(SO_ACCEPTFILTER)
{ "defer-accept", bind_parse_defer_accept, 0 }, /* wait for some data for 1 second max before doing accept */ { "defer-accept", bind_parse_defer_accept, 0 }, /* wait for some data for 1 second max before doing accept */
#endif #endif

View File

@ -2091,6 +2091,7 @@ struct bind_conf *bind_conf_alloc(struct proxy *fe, const char *file,
bind_conf->rhttp_srvname = NULL; bind_conf->rhttp_srvname = NULL;
bind_conf->tcp_md5sig = NULL; bind_conf->tcp_md5sig = NULL;
bind_conf->cc_algo = NULL;
return bind_conf; return bind_conf;

View File

@ -732,6 +732,22 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
} }
} }
#endif #endif
#if defined(TCP_CONGESTION)
if (listener->bind_conf->cc_algo) {
/* Changing congestion control might fail due to loaded
* algorithms or permission. In this case the default algorithm
* remains active, but we can emit a warning about it to give a
* chance to the user to fix it.
*/
if (setsockopt(fd, IPPROTO_TCP, TCP_CONGESTION, listener->bind_conf->cc_algo, strlen(listener->bind_conf->cc_algo)) < 0) {
chunk_appendf(msg, "%scannot set TCP congestion control algorithm, (%s)", msg->data ? ", " : "",
strerror(errno));
err |= ERR_WARN;
}
}
#endif
#if defined(__linux__) && defined(TCP_MD5SIG) #if defined(__linux__) && defined(TCP_MD5SIG)
if (listener->bind_conf->tcp_md5sig) { if (listener->bind_conf->tcp_md5sig) {
struct tcp_md5sig md5; struct tcp_md5sig md5;

View File

@ -411,6 +411,7 @@ void deinit_proxy(struct proxy *p)
free(bind_conf->guid_prefix); free(bind_conf->guid_prefix);
free(bind_conf->rhttp_srvname); free(bind_conf->rhttp_srvname);
free(bind_conf->tcp_md5sig); free(bind_conf->tcp_md5sig);
free(bind_conf->cc_algo);
#ifdef USE_QUIC #ifdef USE_QUIC
free(bind_conf->quic_cc_algo); free(bind_conf->quic_cc_algo);
#endif #endif