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:
MIZUTA Takeshi 2020-07-09 11:13:20 +09:00 committed by Willy Tarreau
parent 3b8f9b7b88
commit b24bc0dfb6
6 changed files with 297 additions and 2 deletions

View File

@ -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

View File

@ -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) */

View File

@ -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) {

View File

@ -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

View File

@ -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 },
}};

View File

@ -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;