MEDIUM: tcp: implement tcp-ut bind option to set TCP_USER_TIMEOUT

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.
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 66d8b9d..b890d4b 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -859,6 +859,15 @@
 		}
 	}
 #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 @@
 }
 #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 @@
 #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