MEDIUM: tcp: add the "tfo" option to support TCP fastopen on the server

This implements support for the new API which relies on a call to
setsockopt().
On systems that support it (currently, only Linux >= 4.11), this enables
using TCP fast open when connecting to server.
Please note that you should use the retry-on "conn-failure", "empty-response"
and "response-timeout" keywords, or the request won't be able to be retried
on failure.

Co-authored-by: Olivier Houchard <ohouchard@haproxy.com>
diff --git a/src/cfgparse.c b/src/cfgparse.c
index dd99bbb..24b5714 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -3121,6 +3121,14 @@
 					cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv);
 			}
 
+			if ((newsrv->flags & SRV_F_FASTOPEN) &&
+			    ((curproxy->retry_type & (PR_RE_DISCONNECTED | PR_RE_TIMEOUT)) !=
+			     (PR_RE_DISCONNECTED | PR_RE_TIMEOUT)))
+				ha_warning("parsing [%s:%d] : %s '%s': server '%s' has tfo activated, the backend should be configured with at least 'conn-failure', 'empty-response' and 'response-timeout' or we wouldn't be able to retry the connection on failure.\n",
+				    newsrv->conf.file, newsrv->conf.line,
+				    proxy_type_str(curproxy), curproxy->id,
+				    newsrv->id);
+
 			/* set the check type on the server */
 			newsrv->check.type = curproxy->options2 & PR_O2_CHK_ANY;
 
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index e668a85..95068ee 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -293,6 +293,7 @@
 	struct server *srv;
 	struct proxy *be;
 	struct conn_src *src;
+	int use_fastopen = 0;
 
 	conn->flags |= CO_FL_WAIT_L4_CONN; /* connection in progress */
 
@@ -304,6 +305,14 @@
 	case OBJ_TYPE_SERVER:
 		srv = objt_server(conn->target);
 		be = srv->proxy;
+		/* Make sure we check that we have data before activating
+		 * TFO, or we could trigger a kernel issue whereby after
+		 * a successful connect() == 0, any subsequent connect()
+		 * will return EINPROGRESS instead of EISCONN.
+		 */
+		use_fastopen = (srv->flags & SRV_F_FASTOPEN) &&
+		               ((flags & (CONNECT_CAN_USE_TFO | CONNECT_HAS_DATA)) ==
+				(CONNECT_CAN_USE_TFO | CONNECT_HAS_DATA));
 		break;
 	default:
 		conn->flags |= CO_FL_ERROR;
@@ -493,6 +502,12 @@
 	if (srv && srv->tcp_ut)
 		setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &srv->tcp_ut, sizeof(srv->tcp_ut));
 #endif
+
+	if (use_fastopen) {
+#if defined(TCP_FASTOPEN_CONNECT)
+                setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &one, sizeof(one));
+#endif
+	}
 	if (global.tune.server_sndbuf)
                 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &global.tune.server_sndbuf, sizeof(global.tune.server_sndbuf));
 
diff --git a/src/raw_sock.c b/src/raw_sock.c
index 6c5a634..6dde3f8 100644
--- a/src/raw_sock.c
+++ b/src/raw_sock.c
@@ -402,7 +402,7 @@
 			if (ret < try)
 				break;
 		}
-		else if (ret == 0 || errno == EAGAIN || errno == ENOTCONN) {
+		else if (ret == 0 || errno == EAGAIN || errno == ENOTCONN || errno == EINPROGRESS) {
 			/* nothing written, we need to poll for write first */
 			fd_cant_send(conn->handle.fd);
 			break;
diff --git a/src/server.c b/src/server.c
index 9916361..a9e7a42 100644
--- a/src/server.c
+++ b/src/server.c
@@ -889,6 +889,13 @@
 }
 
 
+/* parse the "tfo" server keyword */
+static int srv_parse_tfo(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
+{
+	newsrv->flags |= SRV_F_FASTOPEN;
+	return 0;
+}
+
 /* Shutdown all connections of a server. The caller must pass a termination
  * code in <why>, which must be one of SF_ERR_* indicating the reason for the
  * shutdown.
@@ -1277,6 +1284,7 @@
 	{ "send-proxy-v2",       srv_parse_send_proxy_v2,       0,  1 }, /* Enforce use of PROXY V2 protocol */
 	{ "source",              srv_parse_source,             -1,  1 }, /* Set the source address to be used to connect to the server */
 	{ "stick",               srv_parse_stick,               0,  1 }, /* Enable stick-table persistence */
+	{ "tfo",                 srv_parse_tfo,                 0,  0 }, /* enable TCP Fast Open of server */
 	{ "track",               srv_parse_track,               1,  1 }, /* Set the current state of the server, tracking another one */
 	{ NULL, NULL, 0 },
 }};