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.
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index 34cf346..df4021d 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -277,6 +277,10 @@
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 @@
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) {
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 9f03096..0fdb944 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -379,9 +379,19 @@
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
diff --git a/src/proxy.c b/src/proxy.c
index a475095..a03cbd0 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -570,6 +570,159 @@
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 @@
{ 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 },
}};
diff --git a/src/session.c b/src/session.c
index 02b0212..51380d6 100644
--- a/src/session.c
+++ b/src/session.c
@@ -224,9 +224,19 @@
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;