]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: listener: add the "cc" bind keyword to set the TCP congestion controller
authorWilly Tarreau <w@1wt.eu>
Wed, 17 Sep 2025 15:03:42 +0000 (17:03 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 17 Sep 2025 15:03:42 +0000 (17:03 +0200)
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).

doc/configuration.txt
include/haproxy/listener-t.h
src/cfgparse-tcp.c
src/listener.c
src/proto_tcp.c
src/proxy.c

index 0041a5a9c3b3447cf851bf655c6b2fd8786ed3e6..ccaafbba914c40871986a930e94b6f92ec25dc2f 100644 (file)
@@ -16382,6 +16382,25 @@ ca-verify-file <cafile>
   be defined with intermediate certificates, and "ca-verify-file" with
   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>
   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
index c36108713bbdec45bb726d37d8c0223a647fdd6a..0fbed6c7f40119d12fc15d62855d590d31d9ee58 100644 (file)
@@ -195,6 +195,7 @@ struct bind_conf {
        int maxseg;                /* for TCP, advertised MSS */
        int tcp_ut;                /* for TCP, user timeout */
        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 maxaccept;             /* if set, max number of connections accepted at once (-1 when disabled) */
        unsigned int backlog;      /* if set, listen backlog */
index 40419cc51deb8d55a35f5620267c571a92773437..15b8818f5351df6e11231f899fd245782ed0edd1 100644 (file)
@@ -61,6 +61,26 @@ static int bind_parse_transparent(char **args, int cur_arg, struct proxy *px, st
 }
 #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)
 /* 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)
@@ -278,6 +298,9 @@ static int srv_parse_tcp_ut(char **args, int *cur_arg, struct proxy *px, struct
  * not enabled.
  */
 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)
        { "defer-accept",  bind_parse_defer_accept, 0 }, /* wait for some data for 1 second max before doing accept */
 #endif
index 089137b665f8636e913985a8e63f1fc1b0a3aa25..03c5ba52c866515de288c47b14cb44cf98ba55ce 100644 (file)
@@ -2091,6 +2091,7 @@ struct bind_conf *bind_conf_alloc(struct proxy *fe, const char *file,
        bind_conf->rhttp_srvname = NULL;
 
        bind_conf->tcp_md5sig = NULL;
+       bind_conf->cc_algo = NULL;
 
        return bind_conf;
 
index ea9f4c9fec6eff887e00e6ec71399172139d31e9..b2b9b2121660f0e22562015c63f2f5b94bede4ff 100644 (file)
@@ -732,6 +732,22 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
                }
        }
 #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 (listener->bind_conf->tcp_md5sig) {
                struct tcp_md5sig md5;
index 527ae09c452b58338120372339954fe850c17f97..03894d24e1ee196f8419d7fb82d0d19d53e2a15b 100644 (file)
@@ -411,6 +411,7 @@ void deinit_proxy(struct proxy *p)
                free(bind_conf->guid_prefix);
                free(bind_conf->rhttp_srvname);
                free(bind_conf->tcp_md5sig);
+               free(bind_conf->cc_algo);
 #ifdef USE_QUIC
                free(bind_conf->quic_cc_algo);
 #endif