MEDIUM: tcp: enable TCP Fast Open on systems which support it

If TCP_FASTOPEN is defined, then the "tfo" option is supported on
"bind" lines to enable TCP Fast Open (linux >= 3.6).
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 8c3132b..3d8bbfd 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -6937,6 +6937,17 @@
   appear in clear text, so that ACLs and HTTP processing will only have access
   to deciphered contents.
 
+tfo
+  Is an optional keyword which is supported only on Linux kernels >= 3.6. It
+  enables TCP Fast Open on the listening socket, which means that clients which
+  support this feature will be able to send a request and receive a response
+  during the 3-way handshake starting from second connection, thus saving one
+  round-trip after the first connection. This only makes sense with protocols
+  that use high connection rates and where each round trip matters. This can
+  possibly cause issues with many firewalls which do not accept data on SYN
+  packets, so this option should only be enabled once well tested. This option
+  is only supported on TCPv4/TCPv6 sockets and ignored by other ones.
+
 transparent
   Is an optional keyword which is supported only on certain Linux kernels. It
   indicates that the addresses will be bound even if they do not belong to the
diff --git a/include/types/listener.h b/include/types/listener.h
index 935a003..6fe21ab 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -88,6 +88,7 @@
 #define LI_O_CHK_MONNET 0x0020  /* check the source against a monitor-net rule */
 #define LI_O_ACC_PROXY  0x0040  /* find the proxied address in the first request line */
 #define LI_O_UNLIMITED  0x0080  /* listener not subject to global limits (peers & stats socket) */
+#define LI_O_TCP_FO     0x0100  /* enable TCP Fast Open (linux >= 3.6) */
 
 /* Note: if a listener uses LI_O_UNLIMITED, it is highly recommended that it adds its own
  * maxconn setting to the global.maxsock value so that its resources are reserved.
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index ea6f943..8b0792b 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -675,6 +675,16 @@
 		}
 	}
 #endif
+#if defined(TCP_FASTOPEN)
+	if (listener->options & LI_O_TCP_FO) {
+		/* TFO needs a queue length, let's use the configured backlog */
+		int qlen = listener->backlog ? listener->backlog : listener->maxconn;
+		if (setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) == -1) {
+			msg = "cannot enable TCP_FASTOPEN";
+			err |= ERR_WARN;
+		}
+	}
+#endif
 	if (bind(fd, (struct sockaddr *)&listener->addr, listener->proto->sock_addrlen) == -1) {
 		err |= ERR_RETRYABLE | ERR_ALERT;
 		msg = "cannot bind socket";
@@ -1726,6 +1736,21 @@
 }
 #endif
 
+#ifdef TCP_FASTOPEN
+/* parse the "defer-accept" bind keyword */
+static int bind_parse_tfo(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+	struct listener *l;
+
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6)
+			l->options |= LI_O_TCP_FO;
+	}
+
+	return 0;
+}
+#endif
+
 #ifdef TCP_MAXSEG
 /* parse the "mss" bind keyword */
 static int bind_parse_mss(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
@@ -1832,6 +1857,9 @@
 #ifdef TCP_MAXSEG
 	{ "mss",           bind_parse_mss,          1 }, /* set MSS of listening socket */
 #endif
+#ifdef TCP_FASTOPEN
+	{ "tfo",           bind_parse_tfo,          0 }, /* enable TCP_FASTOPEN of listening socket */
+#endif
 #ifdef CONFIG_HAP_LINUX_TPROXY
 	{ "transparent",   bind_parse_transparent,  0 }, /* transparently bind to the specified addresses */
 #endif