|
|
a1cde2 |
From 6248d4bad6a79ca6a39d5a60ac038bf22eb28dae 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
|
|
|
a1cde2 |
index 19df5ae..12b9236 100644
|
|
|
a1cde2 |
--- a/doc/configuration.txt
|
|
|
a1cde2 |
+++ b/doc/configuration.txt
|
|
|
a1cde2 |
@@ -8547,6 +8547,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
|
|
|
a1cde2 |
index 72dc92b..f833861 100644
|
|
|
a1cde2 |
--- a/src/proto_tcp.c
|
|
|
a1cde2 |
+++ b/src/proto_tcp.c
|
|
|
a1cde2 |
@@ -829,6 +829,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 */
|
|
|
a1cde2 |
@@ -1973,8 +1982,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;
|
|
|
a1cde2 |
@@ -2043,6 +2080,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 |
--
|
|
|
a1cde2 |
1.8.1.4
|
|
|
a1cde2 |
|