mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-18 19:56:59 +00:00
MINOR: tcp: Support TCP keepalive parameters customization
It is now possible to customize TCP keepalive parameters. These correspond to the socket options TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL and are valid for the defaults, listen, frontend and backend sections. This patch fixes GitHub issue #670.
This commit is contained in:
parent
3b8f9b7b88
commit
b24bc0dfb6
@ -2816,6 +2816,9 @@ bind-process X X X X
|
||||
capture cookie - X X -
|
||||
capture request header - X X -
|
||||
capture response header - X X -
|
||||
clitcpka-cnt X X X -
|
||||
clitcpka-idle X X X -
|
||||
clitcpka-intvl X X X -
|
||||
compression X X X X
|
||||
cookie X - X X
|
||||
declare capture - X X -
|
||||
@ -2934,6 +2937,9 @@ server - - X X
|
||||
server-state-file-name X - X X
|
||||
server-template - - X X
|
||||
source X - X X
|
||||
srvtcpka-cnt X - X X
|
||||
srvtcpka-idle X - X X
|
||||
srvtcpka-intvl X - X X
|
||||
stats admin - X X X
|
||||
stats auth X X X X
|
||||
stats enable X X X X
|
||||
@ -3572,6 +3578,54 @@ capture response header <name> len <length>
|
||||
about logging.
|
||||
|
||||
|
||||
clitcpka-cnt <count>
|
||||
Sets the maximum number of keepalive probes TCP should send before dropping
|
||||
the connection on the client side.
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | yes | yes | no
|
||||
Arguments :
|
||||
<count> is the maximum number of keepalive probes.
|
||||
|
||||
This keyword corresponds to the socket option TCP_KEEPCNT. If this keyword
|
||||
is not specified, system-wide TCP parameter (tcp_keepalive_probes) is used.
|
||||
|
||||
See also : "option clitcpka", "clitcpka-idle", "clitcpka-intvl".
|
||||
|
||||
|
||||
clitcpka-idle <timeout>
|
||||
Sets the time the connection needs to remain idle before TCP starts sending
|
||||
keepalive probes, if enabled the sending of TCP keepalive packets on the
|
||||
client side.
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | yes | yes | no
|
||||
Arguments :
|
||||
<timeout> is the time the connection needs to remain idle before TCP starts
|
||||
sending keepalive probes. It is specified in seconds by default,
|
||||
but can be in any other unit if the number is suffixed by the
|
||||
unit, as explained at the top of this document.
|
||||
|
||||
This keyword corresponds to the socket option TCP_KEEPIDLE. If this keyword
|
||||
is not specified, system-wide TCP parameter (tcp_keepalive_time) is used.
|
||||
|
||||
See also : "option clitcpka", "clitcpka-cnt", "clitcpka-intvl".
|
||||
|
||||
|
||||
clitcpka-intvl <timeout>
|
||||
Sets the time between individual keepalive probes on the client side.
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | yes | yes | no
|
||||
Arguments :
|
||||
<timeout> is the time between individual keepalive probes. It is specified
|
||||
in seconds by default, but can be in any other unit if the number
|
||||
is suffixed by the unit, as explained at the top of this
|
||||
document.
|
||||
|
||||
This keyword corresponds to the socket option TCP_KEEPINTVL. If this keyword
|
||||
is not specified, system-wide TCP parameter (tcp_keepalive_intvl) is used.
|
||||
|
||||
See also : "option clitcpka", "clitcpka-cnt", "clitcpka-idle".
|
||||
|
||||
|
||||
compression algo <algorithm> ...
|
||||
compression type <mime type> ...
|
||||
compression offload
|
||||
@ -9407,6 +9461,54 @@ source <addr>[:<port>] [interface <name>]
|
||||
the Linux kernel on www.balabit.com, the "bind" keyword.
|
||||
|
||||
|
||||
srvtcpka-cnt <count>
|
||||
Sets the maximum number of keepalive probes TCP should send before dropping
|
||||
the connection on the server side.
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
Arguments :
|
||||
<count> is the maximum number of keepalive probes.
|
||||
|
||||
This keyword corresponds to the socket option TCP_KEEPCNT. If this keyword
|
||||
is not specified, system-wide TCP parameter (tcp_keepalive_probes) is used.
|
||||
|
||||
See also : "option srvtcpka", "srvtcpka-idle", "srvtcpka-intvl".
|
||||
|
||||
|
||||
srvtcpka-idle <timeout>
|
||||
Sets the time the connection needs to remain idle before TCP starts sending
|
||||
keepalive probes, if enabled the sending of TCP keepalive packets on the
|
||||
server side.
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
Arguments :
|
||||
<timeout> is the time the connection needs to remain idle before TCP starts
|
||||
sending keepalive probes. It is specified in seconds by default,
|
||||
but can be in any other unit if the number is suffixed by the
|
||||
unit, as explained at the top of this document.
|
||||
|
||||
This keyword corresponds to the socket option TCP_KEEPIDLE. If this keyword
|
||||
is not specified, system-wide TCP parameter (tcp_keepalive_time) is used.
|
||||
|
||||
See also : "option srvtcpka", "srvtcpka-cnt", "srvtcpka-intvl".
|
||||
|
||||
|
||||
srvtcpka-intvl <timeout>
|
||||
Sets the time between individual keepalive probes on the server side.
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
Arguments :
|
||||
<timeout> is the time between individual keepalive probes. It is specified
|
||||
in seconds by default, but can be in any other unit if the number
|
||||
is suffixed by the unit, as explained at the top of this
|
||||
document.
|
||||
|
||||
This keyword corresponds to the socket option TCP_KEEPINTVL. If this keyword
|
||||
is not specified, system-wide TCP parameter (tcp_keepalive_intvl) is used.
|
||||
|
||||
See also : "option srvtcpka", "srvtcpka-cnt", "srvtcpka-idle".
|
||||
|
||||
|
||||
stats admin { if | unless } <cond>
|
||||
Enable statistics admin level if/unless a condition is matched
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
|
@ -306,6 +306,12 @@ struct proxy {
|
||||
int capture_len; /* length of the string to be captured */
|
||||
struct uri_auth *uri_auth; /* if non-NULL, the (list of) per-URI authentications */
|
||||
int max_ka_queue; /* 1+maximum requests in queue accepted for reusing a K-A conn (0=none) */
|
||||
int clitcpka_cnt; /* The maximum number of keepalive probes TCP should send before dropping the connection. (client side) */
|
||||
int clitcpka_idle; /* The time (in seconds) the connection needs to remain idle before TCP starts sending keepalive probes. (client side) */
|
||||
int clitcpka_intvl; /* The time (in seconds) between individual keepalive probes. (client side) */
|
||||
int srvtcpka_cnt; /* The maximum number of keepalive probes TCP should send before dropping the connection. (server side) */
|
||||
int srvtcpka_idle; /* The time (in seconds) the connection needs to remain idle before TCP starts sending keepalive probes. (server side) */
|
||||
int srvtcpka_intvl; /* The time (in seconds) between individual keepalive probes. (server side) */
|
||||
int monitor_uri_len; /* length of the string above. 0 if unused */
|
||||
char *monitor_uri; /* a special URI to which we respond with HTTP/200 OK */
|
||||
struct list mon_fail_cond; /* list of conditions to fail monitoring requests (chained) */
|
||||
|
@ -277,6 +277,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
|
||||
curproxy->to_log = defproxy.to_log & ~LW_COOKIE & ~LW_REQHDR & ~ LW_RSPHDR;
|
||||
curproxy->max_out_conns = defproxy.max_out_conns;
|
||||
|
||||
curproxy->clitcpka_cnt = defproxy.clitcpka_cnt;
|
||||
curproxy->clitcpka_idle = defproxy.clitcpka_idle;
|
||||
curproxy->clitcpka_intvl = defproxy.clitcpka_intvl;
|
||||
}
|
||||
|
||||
if (curproxy->cap & PR_CAP_BE) {
|
||||
@ -337,6 +341,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
curproxy->conn_src.tproxy_addr = defproxy.conn_src.tproxy_addr;
|
||||
#endif
|
||||
curproxy->load_server_state_from_file = defproxy.load_server_state_from_file;
|
||||
|
||||
curproxy->srvtcpka_cnt = defproxy.srvtcpka_cnt;
|
||||
curproxy->srvtcpka_idle = defproxy.srvtcpka_idle;
|
||||
curproxy->srvtcpka_intvl = defproxy.srvtcpka_intvl;
|
||||
}
|
||||
|
||||
if (curproxy->cap & PR_CAP_FE) {
|
||||
|
@ -379,9 +379,19 @@ int tcp_connect_server(struct connection *conn, int flags)
|
||||
return SF_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
if (be->options & PR_O_TCP_SRV_KA)
|
||||
if (be->options & PR_O_TCP_SRV_KA) {
|
||||
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
|
||||
|
||||
if (be->srvtcpka_cnt)
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &be->srvtcpka_cnt, sizeof(be->srvtcpka_cnt));
|
||||
|
||||
if (be->srvtcpka_idle)
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &be->srvtcpka_idle, sizeof(be->srvtcpka_idle));
|
||||
|
||||
if (be->srvtcpka_intvl)
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &be->srvtcpka_intvl, sizeof(be->srvtcpka_intvl));
|
||||
}
|
||||
|
||||
/* allow specific binding :
|
||||
* - server-specific at first
|
||||
* - proxy-specific next
|
||||
|
159
src/proxy.c
159
src/proxy.c
@ -570,6 +570,159 @@ proxy_parse_retry_on(char **args, int section, struct proxy *curpx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function parses "{cli|srv}tcpka-cnt" statements */
|
||||
static int proxy_parse_tcpka_cnt(char **args, int section, struct proxy *proxy,
|
||||
struct proxy *defpx, const char *file, int line,
|
||||
char **err)
|
||||
{
|
||||
int retval;
|
||||
char *res;
|
||||
unsigned int tcpka_cnt;
|
||||
|
||||
retval = 0;
|
||||
|
||||
if (*args[1] == 0) {
|
||||
memprintf(err, "'%s' expects an integer value", args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tcpka_cnt = strtol(args[1], &res, 0);
|
||||
if (*res) {
|
||||
memprintf(err, "'%s' : unexpected character '%c' in integer value '%s'", args[0], *res, args[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(args[0], "clitcpka-cnt")) {
|
||||
if (!(proxy->cap & PR_CAP_FE)) {
|
||||
memprintf(err, "%s will be ignored because %s '%s' has no frontend capability",
|
||||
args[0], proxy_type_str(proxy), proxy->id);
|
||||
retval = 1;
|
||||
}
|
||||
proxy->clitcpka_cnt = tcpka_cnt;
|
||||
} else if (!strcmp(args[0], "srvtcpka-cnt")) {
|
||||
if (!(proxy->cap & PR_CAP_BE)) {
|
||||
memprintf(err, "%s will be ignored because %s '%s' has no backend capability",
|
||||
args[0], proxy_type_str(proxy), proxy->id);
|
||||
retval = 1;
|
||||
}
|
||||
proxy->srvtcpka_cnt = tcpka_cnt;
|
||||
} else {
|
||||
/* unreachable */
|
||||
memprintf(err, "'%s': unknown keyword", args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* This function parses "{cli|srv}tcpka-idle" statements */
|
||||
static int proxy_parse_tcpka_idle(char **args, int section, struct proxy *proxy,
|
||||
struct proxy *defpx, const char *file, int line,
|
||||
char **err)
|
||||
{
|
||||
int retval;
|
||||
const char *res;
|
||||
unsigned int tcpka_idle;
|
||||
|
||||
retval = 0;
|
||||
|
||||
if (*args[1] == 0) {
|
||||
memprintf(err, "'%s' expects an integer value", args[0]);
|
||||
return -1;
|
||||
}
|
||||
res = parse_time_err(args[1], &tcpka_idle, TIME_UNIT_S);
|
||||
if (res == PARSE_TIME_OVER) {
|
||||
memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)",
|
||||
args[1], args[0]);
|
||||
return -1;
|
||||
}
|
||||
else if (res == PARSE_TIME_UNDER) {
|
||||
memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)",
|
||||
args[1], args[0]);
|
||||
return -1;
|
||||
}
|
||||
else if (res) {
|
||||
memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(args[0], "clitcpka-idle")) {
|
||||
if (!(proxy->cap & PR_CAP_FE)) {
|
||||
memprintf(err, "%s will be ignored because %s '%s' has no frontend capability",
|
||||
args[0], proxy_type_str(proxy), proxy->id);
|
||||
retval = 1;
|
||||
}
|
||||
proxy->clitcpka_idle = tcpka_idle;
|
||||
} else if (!strcmp(args[0], "srvtcpka-idle")) {
|
||||
if (!(proxy->cap & PR_CAP_BE)) {
|
||||
memprintf(err, "%s will be ignored because %s '%s' has no backend capability",
|
||||
args[0], proxy_type_str(proxy), proxy->id);
|
||||
retval = 1;
|
||||
}
|
||||
proxy->srvtcpka_idle = tcpka_idle;
|
||||
} else {
|
||||
/* unreachable */
|
||||
memprintf(err, "'%s': unknown keyword", args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* This function parses "{cli|srv}tcpka-intvl" statements */
|
||||
static int proxy_parse_tcpka_intvl(char **args, int section, struct proxy *proxy,
|
||||
struct proxy *defpx, const char *file, int line,
|
||||
char **err)
|
||||
{
|
||||
int retval;
|
||||
const char *res;
|
||||
unsigned int tcpka_intvl;
|
||||
|
||||
retval = 0;
|
||||
|
||||
if (*args[1] == 0) {
|
||||
memprintf(err, "'%s' expects an integer value", args[0]);
|
||||
return -1;
|
||||
}
|
||||
res = parse_time_err(args[1], &tcpka_intvl, TIME_UNIT_S);
|
||||
if (res == PARSE_TIME_OVER) {
|
||||
memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)",
|
||||
args[1], args[0]);
|
||||
return -1;
|
||||
}
|
||||
else if (res == PARSE_TIME_UNDER) {
|
||||
memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)",
|
||||
args[1], args[0]);
|
||||
return -1;
|
||||
}
|
||||
else if (res) {
|
||||
memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(args[0], "clitcpka-intvl")) {
|
||||
if (!(proxy->cap & PR_CAP_FE)) {
|
||||
memprintf(err, "%s will be ignored because %s '%s' has no frontend capability",
|
||||
args[0], proxy_type_str(proxy), proxy->id);
|
||||
retval = 1;
|
||||
}
|
||||
proxy->clitcpka_intvl = tcpka_intvl;
|
||||
} else if (!strcmp(args[0], "srvtcpka-intvl")) {
|
||||
if (!(proxy->cap & PR_CAP_BE)) {
|
||||
memprintf(err, "%s will be ignored because %s '%s' has no backend capability",
|
||||
args[0], proxy_type_str(proxy), proxy->id);
|
||||
retval = 1;
|
||||
}
|
||||
proxy->srvtcpka_intvl = tcpka_intvl;
|
||||
} else {
|
||||
/* unreachable */
|
||||
memprintf(err, "'%s': unknown keyword", args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* This function inserts proxy <px> into the tree of known proxies. The proxy's
|
||||
* name is used as the storing key so it must already have been initialized.
|
||||
*/
|
||||
@ -1675,6 +1828,12 @@ static struct cfg_kw_list cfg_kws = {ILH, {
|
||||
{ CFG_LISTEN, "max-keep-alive-queue", proxy_parse_max_ka_queue },
|
||||
{ CFG_LISTEN, "declare", proxy_parse_declare },
|
||||
{ CFG_LISTEN, "retry-on", proxy_parse_retry_on },
|
||||
{ CFG_LISTEN, "clitcpka-cnt", proxy_parse_tcpka_cnt },
|
||||
{ CFG_LISTEN, "clitcpka-idle", proxy_parse_tcpka_idle },
|
||||
{ CFG_LISTEN, "clitcpka-intvl", proxy_parse_tcpka_intvl },
|
||||
{ CFG_LISTEN, "srvtcpka-cnt", proxy_parse_tcpka_cnt },
|
||||
{ CFG_LISTEN, "srvtcpka-idle", proxy_parse_tcpka_idle },
|
||||
{ CFG_LISTEN, "srvtcpka-intvl", proxy_parse_tcpka_intvl },
|
||||
{ 0, NULL, NULL },
|
||||
}};
|
||||
|
||||
|
@ -224,9 +224,19 @@ int session_accept_fd(struct listener *l, int cfd, struct sockaddr_storage *addr
|
||||
if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6) {
|
||||
setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one));
|
||||
|
||||
if (p->options & PR_O_TCP_CLI_KA)
|
||||
if (p->options & PR_O_TCP_CLI_KA) {
|
||||
setsockopt(cfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
|
||||
|
||||
if (p->clitcpka_cnt)
|
||||
setsockopt(cfd, IPPROTO_TCP, TCP_KEEPCNT, &p->clitcpka_cnt, sizeof(p->clitcpka_cnt));
|
||||
|
||||
if (p->clitcpka_idle)
|
||||
setsockopt(cfd, IPPROTO_TCP, TCP_KEEPIDLE, &p->clitcpka_idle, sizeof(p->clitcpka_idle));
|
||||
|
||||
if (p->clitcpka_intvl)
|
||||
setsockopt(cfd, IPPROTO_TCP, TCP_KEEPINTVL, &p->clitcpka_intvl, sizeof(p->clitcpka_intvl));
|
||||
}
|
||||
|
||||
if (p->options & PR_O_TCP_NOLING)
|
||||
fdtab[cfd].linger_risk = 1;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user