]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: tcp: implement tcp-ut bind option to set TCP_USER_TIMEOUT
authorWilly Tarreau <w@1wt.eu>
Tue, 3 Feb 2015 23:45:58 +0000 (00:45 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 3 Feb 2015 23:54:40 +0000 (00:54 +0100)
On Linux since 2.6.37, it's possible to set the socket timeout for
pending outgoing data, with an accuracy of 1 millisecond. This is
pretty handy to deal with dead connections to clients and or servers.

For now we only implement it on the frontend side (bind line) so
that when a client disappears from the net, we're able to quickly
get rid of its connection and possibly release a server connection.
This can be useful with long-lived connections where an application
level timeout is not suited because long pauses are expected (remote
terminals, connection pools, etc).

Thanks to Thijs Houtenbos and John Eckersberg for the suggestion.

doc/configuration.txt
include/types/listener.h
src/proto_tcp.c

index aa3f30f5cd517b5ce9ba491b63811393c93b0322..bd2de33f4ba1674987da8999e40e6dd63c1bde97 100644 (file)
@@ -8919,6 +8919,19 @@ strict-sni
   a certificate. The default certificate is not used.
   See the "crt" option for more information.
 
+tcp-ut <delay>
+  Sets the TCP User Timeout for all incoming connections instanciated from this
+  listening socket. This option is available on Linux since version 2.6.37. It
+  allows haproxy to configure a timeout for sockets which contain data not
+  receiving an acknoledgement for the configured delay. This is especially
+  useful on long-lived connections experiencing long idle periods such as
+  remote terminals or database connection pools, where the client and server
+  timeouts must remain high to allow a long period of idle, but where it is
+  important to detect that the client has disappeared in order to release all
+  resources associated with its connection (and the server's session). The
+  argument is a delay expressed in milliseconds by default. This only works
+  for regular TCP connections, and is ignored for other protocols.
+
 tfo
   Is an optional keyword which is supported only on Linux kernels >= 3.7. It
   enables TCP Fast Open on the listening socket, which means that clients which
index 725808b347d2d87c3632390bccb868d9ead48ca1..6dcaacc14e3239b47c39f95d20f1131e15273b1f 100644 (file)
@@ -175,6 +175,7 @@ struct listener {
        struct list wait_queue;         /* link element to make the listener wait for something (LI_LIMITED)  */
        unsigned int analysers;         /* bitmap of required protocol analysers */
        int maxseg;                     /* for TCP, advertised MSS */
+       int tcp_ut;                     /* for TCP, user timeout */
        char *interface;                /* interface name or NULL */
 
        const struct netns_entry *netns; /* network namespace of the listener*/
index 66d8b9db27932e772d3a82cefb4369ce7eff8d8f..b890d4b5d56907166b4e06cf4169d3807a895178 100644 (file)
@@ -859,6 +859,15 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
                }
        }
 #endif
+#if defined(TCP_USER_TIMEOUT)
+       if (listener->tcp_ut) {
+               if (setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT,
+                              &listener->tcp_ut, sizeof(listener->tcp_ut)) == -1) {
+                       msg = "cannot set TCP User Timeout";
+                       err |= ERR_WARN;
+               }
+       }
+#endif
 #if defined(TCP_DEFER_ACCEPT)
        if (listener->options & LI_O_DEF_ACCEPT) {
                /* defer accept by up to one second */
@@ -2007,8 +2016,36 @@ static int bind_parse_mss(char **args, int cur_arg, struct proxy *px, struct bin
 }
 #endif
 
+#ifdef TCP_USER_TIMEOUT
+/* parse the "tcp-ut" bind keyword */
+static int bind_parse_tcp_ut(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       const char *ptr = NULL;
+       struct listener *l;
+       unsigned int timeout;
+
+       if (!*args[cur_arg + 1]) {
+               memprintf(err, "'%s' : missing TCP User Timeout value", args[cur_arg]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       ptr = parse_time_err(args[cur_arg + 1], &timeout, TIME_UNIT_MS);
+       if (ptr) {
+               memprintf(err, "'%s' : expects a positive delay in milliseconds", args[cur_arg]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       list_for_each_entry(l, &conf->listeners, by_bind) {
+               if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6)
+                       l->tcp_ut = timeout;
+       }
+
+       return 0;
+}
+#endif
+
 #ifdef SO_BINDTODEVICE
-/* parse the "mss" bind keyword */
+/* parse the "interface" bind keyword */
 static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
        struct listener *l;
@@ -2105,6 +2142,9 @@ static struct bind_kw_list bind_kws = { "TCP", { }, {
 #ifdef TCP_MAXSEG
        { "mss",           bind_parse_mss,          1 }, /* set MSS of listening socket */
 #endif
+#ifdef TCP_USER_TIMEOUT
+       { "tcp-ut",        bind_parse_tcp_ut,       1 }, /* set User Timeout on listening socket */
+#endif
 #ifdef TCP_FASTOPEN
        { "tfo",           bind_parse_tfo,          0 }, /* enable TCP_FASTOPEN of listening socket */
 #endif