[MINOR] tcp: add support for the defer_accept bind option

This can ensure that data is readily available on a socket when
we accept it, but a bug in the kernel ignores the timeout so the
socket can remain pending as long as the client does not talk.
Use with care.
This commit is contained in:
Willy Tarreau 2009-10-13 07:34:14 +02:00
parent f2d2b1d128
commit cb6cd43725
4 changed files with 45 additions and 1 deletions

View File

@ -1132,6 +1132,7 @@ bind [<address>]:<port> [, ...] mss <maxseg>
bind [<address>]:<port> [, ...] transparent bind [<address>]:<port> [, ...] transparent
bind [<address>]:<port> [, ...] id <id> bind [<address>]:<port> [, ...] id <id>
bind [<address>]:<port> [, ...] name <name> bind [<address>]:<port> [, ...] name <name>
bind [<address>]:<port> [, ...] defer_accept
Define one or several listening addresses and/or ports in a frontend. Define one or several listening addresses and/or ports in a frontend.
May be used in sections : defaults | frontend | listen | backend May be used in sections : defaults | frontend | listen | backend
no | yes | yes | no no | yes | yes | no
@ -1182,6 +1183,20 @@ bind [<address>]:<port> [, ...] name <name>
the specified port. This keyword is available only when the specified port. This keyword is available only when
HAProxy is built with USE_LINUX_TPROXY=1. HAProxy is built with USE_LINUX_TPROXY=1.
defer_accept is an optional keyword which is supported only on certain
Linux kernels. It states that a connection will only be
accepted once some data arrive on it, or at worst after the
first retransmit. This should be used only on protocols for
which the client talks first (eg: HTTP). It can slightly
improve performance by ensuring that most of the request is
already available when the connection is accepted. On the
other hand, it will not be able to detect connections which
don't talk. It is important to note that this option is
broken in all kernels up to 2.6.31, as the connection is
never accepted until the client talks. This can cause issues
with front firewalls which would see an established
connection while the proxy will only see it in SYN_RECV.
It is possible to specify a list of address:port combinations delimited by It is possible to specify a list of address:port combinations delimited by
commas. The frontend will then listen on all of these addresses. There is no commas. The frontend will then listen on all of these addresses. There is no
fixed limit to the number of addresses and ports which can be listened on in fixed limit to the number of addresses and ports which can be listened on in

View File

@ -71,6 +71,7 @@
#define LI_O_NOLINGER 0x0001 /* disable linger on this socket */ #define LI_O_NOLINGER 0x0001 /* disable linger on this socket */
#define LI_O_FOREIGN 0x0002 /* permit listening on foreing addresses */ #define LI_O_FOREIGN 0x0002 /* permit listening on foreing addresses */
#define LI_O_NOQUICKACK 0x0004 /* disable quick ack of immediate data (linux) */ #define LI_O_NOQUICKACK 0x0004 /* disable quick ack of immediate data (linux) */
#define LI_O_DEF_ACCEPT 0x0008 /* wait up to 1 second for data before accepting */
/* The listener will be directly referenced by the fdtab[] which holds its /* The listener will be directly referenced by the fdtab[] which holds its
* socket. The listener provides the protocol-specific accept() function to * socket. The listener provides the protocol-specific accept() function to

View File

@ -1143,6 +1143,24 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
goto out; goto out;
#endif #endif
} }
if (!strcmp(args[cur_arg], "defer-accept")) { /* wait for some data for 1 second max before doing accept */
#ifdef TCP_DEFER_ACCEPT
struct listener *l;
for (l = curproxy->listen; l != last_listen; l = l->next)
l->options |= LI_O_DEF_ACCEPT;
cur_arg ++;
continue;
#else
Alert("parsing [%s:%d] : '%s' : '%s' option not implemented.\n",
file, linenum, args[0], args[cur_arg]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
#endif
}
if (!strcmp(args[cur_arg], "transparent")) { /* transparently bind to these addresses */ if (!strcmp(args[cur_arg], "transparent")) { /* transparently bind to these addresses */
#ifdef CONFIG_HAP_LINUX_TPROXY #ifdef CONFIG_HAP_LINUX_TPROXY
struct listener *l; struct listener *l;
@ -1212,7 +1230,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
continue; continue;
} }
Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'name', 'id', 'mss' and 'interface' options.\n", Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n",
file, linenum, args[0]); file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;

View File

@ -519,6 +519,16 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
err |= ERR_WARN; err |= ERR_WARN;
} }
} }
#endif
#if defined(TCP_DEFER_ACCEPT)
if (listener->options & LI_O_DEF_ACCEPT) {
/* defer accept by up to one second */
int accept_delay = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &accept_delay, sizeof(accept_delay)) == -1) {
msg = "cannot enable DEFER_ACCEPT";
err |= ERR_WARN;
}
}
#endif #endif
if (bind(fd, (struct sockaddr *)&listener->addr, listener->proto->sock_addrlen) == -1) { if (bind(fd, (struct sockaddr *)&listener->addr, listener->proto->sock_addrlen) == -1) {
err |= ERR_RETRYABLE | ERR_ALERT; err |= ERR_RETRYABLE | ERR_ALERT;