]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: tcp: add the "tfo" option to support TCP fastopen on the server
authorWilly Tarreau <w@1wt.eu>
Mon, 23 Jan 2017 22:36:45 +0000 (23:36 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 6 May 2019 20:29:39 +0000 (22:29 +0200)
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>
doc/configuration.txt
include/common/compat.h
include/proto/stream_interface.h
include/types/protocol.h
include/types/server.h
src/cfgparse.c
src/proto_tcp.c
src/raw_sock.c
src/server.c

index 95f345e7e9c6685d490218457d85d0bbf5a36f16..2993e6e3d4518636b1dc97e4e8c943182563c48c 100644 (file)
@@ -12506,6 +12506,14 @@ tcp-ut <delay>
   argument is a delay expressed in milliseconds by default. This only works for
   regular TCP connections, and is ignored for other protocols.
 
+tfo
+  This option enables using TCP fast open when connecting to servers, on
+  systems that support it (currently only the Linux kernel >= 4.11).
+  See the "tfo" bind option for more information about TCP fast open.
+  Please note that when using tfo, you should also use the "conn-failure",
+  "empty-response" and "response-timeout" keywords for "retry-on", or haproxy
+  won't be able to retry the connection on failure.
+
 track [<proxy>/]<server>
   This option enables ability to set the current state of the server by tracking
   another one. It is possible to track a server which itself tracks another
index 8a7bbd5bf304b4ea8a3cf559e7030c506a6c6646..1401f91d4c8a8d520851c7adf00a53a0b2318a4e 100644 (file)
 #ifndef TCP_FASTOPEN
 #define TCP_FASTOPEN 23
 #endif
+
+#ifndef TCP_FASTOPEN_CONNECT
+#define TCP_FASTOPEN_CONNECT 30
+#endif
 #endif
 
 /* FreeBSD doesn't define SOL_IP and prefers IPPROTO_IP */
index 8a4b77afe00943c4ef77dd1a6dc320d6852334af..2f6a7a5bea7c3f17f0a1eb0245567205b51998d2 100644 (file)
@@ -484,13 +484,17 @@ static inline void si_chk_snd(struct stream_interface *si)
 static inline int si_connect(struct stream_interface *si, struct connection *conn)
 {
        int ret = SF_ERR_NONE;
+       int conn_flags = 0;
 
        if (unlikely(!conn || !conn->ctrl || !conn->ctrl->connect))
                return SF_ERR_INTERNAL;
 
+       if (!channel_is_empty(si_oc(si)))
+               conn_flags |= CONNECT_HAS_DATA;
+       if (si->conn_retries == si_strm(si)->be->conn_retries)
+               conn_flags |= CONNECT_CAN_USE_TFO;
        if (!conn_ctrl_ready(conn) || !conn_xprt_ready(conn)) {
-               ret = conn->ctrl->connect(conn, channel_is_empty(si_oc(si)) ?
-                                         CONNECT_HAS_DATA : 0);
+               ret = conn->ctrl->connect(conn, conn_flags);
                if (ret != SF_ERR_NONE)
                        return ret;
 
index a33d12922bad91c231a0f34da502dcec518f3f3f..1d3404b91dc903aec9570234871a8e21f611a507 100644 (file)
@@ -88,6 +88,7 @@ struct protocol {
 #define CONNECT_HAS_DATA                        0x00000001 /* There's data available to be sent */
 #define CONNECT_DELACK_SMART_CONNECT            0x00000002 /* Use a delayed ACK if the backend has tcp-smart-connect */
 #define CONNECT_DELACK_ALWAYS                   0x00000004 /* Use a delayed ACK */
+#define CONNECT_CAN_USE_TFO                     0x00000008 /* We can use TFO for this connection */
 #endif /* _TYPES_PROTOCOL_H */
 
 /*
index 7835f11c05af06c639919962e1d5cfbd218fe2c5..1a7109a2fdf36e673e9a09d33bd4b96965d48034 100644 (file)
@@ -143,6 +143,7 @@ enum srv_initaddr {
 #define SRV_F_CHECKPORT    0x0040        /* this server has a check port configured */
 #define SRV_F_AGENTADDR    0x0080        /* this server has a agent addr configured */
 #define SRV_F_COOKIESET    0x0100        /* this server has a cookie configured, so don't generate dynamic cookies */
+#define SRV_F_FASTOPEN     0x0100        /* Use TCP Fast Open to connect to server */
 
 /* configured server options for send-proxy (server->pp_opts) */
 #define SRV_PP_V1               0x0001   /* proxy protocol version 1 */
index dd99bbbf13ad7f8faf3117a2d29f975f13ff3811..24b5714d2b01c243c337ecb2f4bee1cfc44115f3 100644 (file)
@@ -3121,6 +3121,14 @@ out_uri_auth_compat:
                                        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;
 
index e668a85f10f7cce345f9b3620ecd91133dec5d91..95068ee6c4e333a92c2fb99ca12124d07246f415 100644 (file)
@@ -293,6 +293,7 @@ int tcp_connect_server(struct connection *conn, int flags)
        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 @@ int tcp_connect_server(struct connection *conn, int flags)
        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 @@ int tcp_connect_server(struct connection *conn, int flags)
        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));
 
index 6c5a634a9b31e402523dd316467801647ca22496..6dde3f8755b46a5bdfb14673c228093fe81570f1 100644 (file)
@@ -402,7 +402,7 @@ static size_t raw_sock_from_buf(struct connection *conn, void *xprt_ctx, const s
                        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;
index 9916361d978bc7bcca75f9a64bd081e82fc0e6df..a9e7a424ebb22852005c50f4a861c6968022fa32 100644 (file)
@@ -889,6 +889,13 @@ static int srv_parse_track(char **args, int *cur_arg,
 }
 
 
+/* 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 @@ static struct srv_kw_list srv_kws = { "ALL", { }, {
        { "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 },
 }};