017d18e
From efc843e57ea96ea198c63398c454430bc9e6cbcc Mon Sep 17 00:00:00 2001
0d70219
From: Willy Tarreau <w@1wt.eu>
0d70219
Date: Wed, 4 Feb 2015 00:45:58 +0100
017d18e
Subject: [PATCH] MEDIUM: tcp: implement tcp-ut bind option to set
0d70219
 TCP_USER_TIMEOUT
0d70219
0d70219
On Linux since 2.6.37, it's possible to set the socket timeout for
0d70219
pending outgoing data, with an accuracy of 1 millisecond. This is
0d70219
pretty handy to deal with dead connections to clients and or servers.
0d70219
0d70219
For now we only implement it on the frontend side (bind line) so
0d70219
that when a client disappears from the net, we're able to quickly
0d70219
get rid of its connection and possibly release a server connection.
0d70219
This can be useful with long-lived connections where an application
0d70219
level timeout is not suited because long pauses are expected (remote
0d70219
terminals, connection pools, etc).
0d70219
0d70219
Thanks to Thijs Houtenbos and John Eckersberg for the suggestion.
0d70219
---
0d70219
 doc/configuration.txt    | 13 +++++++++++++
0d70219
 include/types/listener.h |  1 +
0d70219
 src/proto_tcp.c          | 42 +++++++++++++++++++++++++++++++++++++++++-
0d70219
 3 files changed, 55 insertions(+), 1 deletion(-)
0d70219
0d70219
diff --git a/doc/configuration.txt b/doc/configuration.txt
017d18e
index d5ecf6c..b4b7701 100644
0d70219
--- a/doc/configuration.txt
0d70219
+++ b/doc/configuration.txt
017d18e
@@ -8637,6 +8637,19 @@ strict-sni
0d70219
   a certificate. The default certificate is not used.
0d70219
   See the "crt" option for more information.
0d70219
 
0d70219
+tcp-ut <delay>
0d70219
+  Sets the TCP User Timeout for all incoming connections instanciated from this
0d70219
+  listening socket. This option is available on Linux since version 2.6.37. It
0d70219
+  allows haproxy to configure a timeout for sockets which contain data not
0d70219
+  receiving an acknoledgement for the configured delay. This is especially
0d70219
+  useful on long-lived connections experiencing long idle periods such as
0d70219
+  remote terminals or database connection pools, where the client and server
0d70219
+  timeouts must remain high to allow a long period of idle, but where it is
0d70219
+  important to detect that the client has disappeared in order to release all
0d70219
+  resources associated with its connection (and the server's session). The
0d70219
+  argument is a delay expressed in milliseconds by default. This only works
0d70219
+  for regular TCP connections, and is ignored for other protocols.
0d70219
+
0d70219
 tfo
0d70219
   Is an optional keyword which is supported only on Linux kernels >= 3.7. It
0d70219
   enables TCP Fast Open on the listening socket, which means that clients which
0d70219
diff --git a/include/types/listener.h b/include/types/listener.h
0d70219
index 83b63af..2d71df6 100644
0d70219
--- a/include/types/listener.h
0d70219
+++ b/include/types/listener.h
0d70219
@@ -175,6 +175,7 @@ struct listener {
0d70219
 	struct list wait_queue;		/* link element to make the listener wait for something (LI_LIMITED)  */
0d70219
 	unsigned int analysers;		/* bitmap of required protocol analysers */
0d70219
 	int maxseg;			/* for TCP, advertised MSS */
0d70219
+	int tcp_ut;                     /* for TCP, user timeout */
0d70219
 	char *interface;		/* interface name or NULL */
0d70219
 
0d70219
 	struct list by_fe;              /* chaining in frontend's list of listeners */
0d70219
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
0d70219
index cfa62f7..e98a9fb 100644
0d70219
--- a/src/proto_tcp.c
0d70219
+++ b/src/proto_tcp.c
0d70219
@@ -838,6 +838,15 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
0d70219
 		}
0d70219
 	}
0d70219
 #endif
0d70219
+#if defined(TCP_USER_TIMEOUT)
0d70219
+	if (listener->tcp_ut) {
0d70219
+		if (setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT,
0d70219
+			       &listener->tcp_ut, sizeof(listener->tcp_ut)) == -1) {
0d70219
+			msg = "cannot set TCP User Timeout";
0d70219
+			err |= ERR_WARN;
0d70219
+		}
0d70219
+	}
0d70219
+#endif
0d70219
 #if defined(TCP_DEFER_ACCEPT)
0d70219
 	if (listener->options & LI_O_DEF_ACCEPT) {
0d70219
 		/* defer accept by up to one second */
0d70219
@@ -1986,8 +1995,36 @@ static int bind_parse_mss(char **args, int cur_arg, struct proxy *px, struct bin
0d70219
 }
0d70219
 #endif
0d70219
 
0d70219
+#ifdef TCP_USER_TIMEOUT
0d70219
+/* parse the "tcp-ut" bind keyword */
0d70219
+static int bind_parse_tcp_ut(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
0d70219
+{
0d70219
+	const char *ptr = NULL;
0d70219
+	struct listener *l;
0d70219
+	unsigned int timeout;
0d70219
+
0d70219
+	if (!*args[cur_arg + 1]) {
0d70219
+		memprintf(err, "'%s' : missing TCP User Timeout value", args[cur_arg]);
0d70219
+		return ERR_ALERT | ERR_FATAL;
0d70219
+	}
0d70219
+
0d70219
+	ptr = parse_time_err(args[cur_arg + 1], &timeout, TIME_UNIT_MS);
0d70219
+	if (ptr) {
0d70219
+		memprintf(err, "'%s' : expects a positive delay in milliseconds", args[cur_arg]);
0d70219
+		return ERR_ALERT | ERR_FATAL;
0d70219
+	}
0d70219
+
0d70219
+	list_for_each_entry(l, &conf->listeners, by_bind) {
0d70219
+		if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6)
0d70219
+			l->tcp_ut = timeout;
0d70219
+	}
0d70219
+
0d70219
+	return 0;
0d70219
+}
0d70219
+#endif
0d70219
+
0d70219
 #ifdef SO_BINDTODEVICE
0d70219
-/* parse the "mss" bind keyword */
0d70219
+/* parse the "interface" bind keyword */
0d70219
 static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
0d70219
 {
0d70219
 	struct listener *l;
0d70219
@@ -2056,6 +2093,9 @@ static struct bind_kw_list bind_kws = { "TCP", { }, {
0d70219
 #ifdef TCP_MAXSEG
0d70219
 	{ "mss",           bind_parse_mss,          1 }, /* set MSS of listening socket */
0d70219
 #endif
0d70219
+#ifdef TCP_USER_TIMEOUT
0d70219
+	{ "tcp-ut",        bind_parse_tcp_ut,       1 }, /* set User Timeout on listening socket */
0d70219
+#endif
0d70219
 #ifdef TCP_FASTOPEN
0d70219
 	{ "tfo",           bind_parse_tfo,          0 }, /* enable TCP_FASTOPEN of listening socket */
0d70219
 #endif
0d70219
-- 
0d70219
1.9.3
0d70219