Blame SOURCES/haproxy-tcp-user-timeout.patch

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