From 5c28874a6900bba948da7ed7e115188b8d3b0b58 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Tue, 31 Mar 2020 08:15:58 +0200 Subject: [PATCH] MINOR: checks: Add the addr option for tcp-check connect rule With this option, it is now possible to use a specific address to open the connection for a tcp-check connect rule. If the port option is also specified, it is used in priority. --- doc/configuration.txt | 11 ++++--- include/types/checks.h | 11 ++++--- src/checks.c | 69 +++++++++++++++++++++++++++++++++--------- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index bc5f88ad2..ecd218bf0 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -9821,9 +9821,12 @@ tcp-check connect [params*] default Use default options of the server line to do the health checks. This parameter is exclusive with all other options. - port if not set, check port or server port is used. - It tells HAProxy where to open the connection to. - must be a valid TCP port source integer, from 1 to 65535. + port if not set, check port or server port is used. + It tells HAProxy where to open the connection to. + must be a valid TCP port source integer, from 1 to + 65535. + + addr defines the IP address to do the health check. send-proxy send a PROXY protocol string @@ -9838,7 +9841,7 @@ tcp-check connect [params*] for instance: "http/1.1,http/1.0" (without quotes). If it is not set, the server ALPN is used. - linger cleanly close the connection instead of using a single RST. + linger cleanly close the connection instead of using a single RST. Examples: # check HTTP and HTTPs services on a server. diff --git a/include/types/checks.h b/include/types/checks.h index 0edc726d9..29d0ebd11 100644 --- a/include/types/checks.h +++ b/include/types/checks.h @@ -220,11 +220,12 @@ struct analyze_status { #define TCPCHK_OPT_SOCKS4 0x0010 /* check the connection via socks4 proxy */ struct tcpcheck_connect { - uint16_t port; /* port to connect to */ - uint16_t options; /* options when setting up a new connection */ - char *sni; /* server name to use for SSL connections */ - char *alpn; /* ALPN to use for the SSL connection */ - int alpn_len; /* ALPN string length */ + char *sni; /* server name to use for SSL connections */ + char *alpn; /* ALPN to use for the SSL connection */ + int alpn_len; /* ALPN string length */ + uint16_t options; /* options when setting up a new connection */ + uint16_t port; /* port to connect to */ + struct sockaddr_storage addr; /* the address to the connect */ }; enum tcpcheck_send_type { diff --git a/src/checks.c b/src/checks.c index 7ac661149..cf6d0d3e3 100644 --- a/src/checks.c +++ b/src/checks.c @@ -2807,7 +2807,7 @@ static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct struct protocol *proto; struct xprt_ops *xprt; char *comment; - int status; + int status, port; /* For a connect action we'll create a new connection. We may also have * to kill a previous one. But we don't want to leave *without* a @@ -2859,20 +2859,26 @@ static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct goto fail_check; } - /* connect to the check addr if specified on the server. otherwise, use - * the server addr + /* connect to the connect rule addr if specified, otherwise the check + * addr if specified on the server. otherwise, use the server addr */ - *conn->dst = (is_addr(&check->addr) ? check->addr : s->addr); + *conn->dst = (is_addr(&connect->addr) + ? connect->addr + : (is_addr(&check->addr) ? check->addr : s->addr)); proto = protocol_by_family(conn->dst->ss_family); - if (connect->port) - set_host_port(conn->dst, connect->port); - else if (check->port) - set_host_port(conn->dst, check->port); - else { - int i = get_host_port(&check->addr); - set_host_port(conn->dst, ((i > 0) ? i : s->svc_port)); - } + port = 0; + if (!port && connect->port) + port = connect->port; + if (!port && is_inet_addr(&connect->addr)) + port = get_host_port(&connect->addr); + if (!port && check->port) + port = check->port; + if (!port && is_inet_addr(&check->addr)) + port = get_host_port(&check->addr); + if (!port) + port = s->svc_port; + set_host_port(conn->dst, port); xprt = ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt @@ -3929,7 +3935,7 @@ static int init_srv_check(struct server *srv) /* search the first action (connect / send / expect) in the list */ r = get_first_tcpcheck_rule(srv->proxy->tcpcheck_rules); - if (!r || (r->action != TCPCHK_ACT_CONNECT) || !r->connect.port) { + if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) { ha_alert("config: %s '%s': server '%s' has neither service port nor check port " "nor tcp_check rule 'connect' with port information.\n", proxy_type_str(srv->proxy), srv->proxy->id, srv->id); @@ -3939,7 +3945,7 @@ static int init_srv_check(struct server *srv) /* scan the tcp-check ruleset to ensure a port has been configured */ list_for_each_entry(r, srv->proxy->tcpcheck_rules, list) { - if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port)) { + if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) { ha_alert("config: %s '%s': server '%s' has neither service port nor check port, " "and a tcp_check rule 'connect' with no port information.\n", proxy_type_str(srv->proxy), srv->proxy->id, srv->id); @@ -4081,6 +4087,7 @@ static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, st char **errmsg) { struct tcpcheck_rule *chk = NULL; + struct sockaddr_storage *sk = NULL; char *comment = NULL, *sni = NULL, *alpn = NULL; unsigned short conn_opts = 0; long port = 0; @@ -4106,6 +4113,36 @@ static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, st } conn_opts = TCPCHK_OPT_DEFAULT_CONNECT; } + else if (strcmp(args[cur_arg], "addr") == 0) { + int port1, port2; + struct protocol *proto; + + if (!*(args[cur_arg+1])) { + memprintf(errmsg, "'%s' expects as argument.", args[cur_arg]); + goto error; + } + + sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1); + if (!sk) { + memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg); + goto error; + } + + proto = protocol_by_family(sk->ss_family); + if (!proto || !proto->connect) { + memprintf(errmsg, "'%s' : connect() not supported for this address family.\n", + args[cur_arg]); + goto error; + } + + if (port1 != port2) { + memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n", + args[cur_arg], args[cur_arg+1]); + goto error; + } + + cur_arg++; + } else if (strcmp(args[cur_arg], "port") == 0) { if (!*(args[cur_arg+1])) { memprintf(errmsg, "'%s' expects a port number as argument.", args[cur_arg]); @@ -4171,7 +4208,7 @@ static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, st #endif /* USE_OPENSSL */ else { - memprintf(errmsg, "expects 'comment', 'port', 'send-proxy'" + memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'" #ifdef USE_OPENSSL ", 'ssl', 'sni', 'alpn'" #endif /* USE_OPENSSL */ @@ -4194,6 +4231,8 @@ static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, st chk->connect.sni = sni; chk->connect.alpn = alpn; chk->connect.alpn_len= alpn_len; + if (sk) + chk->connect.addr = *sk; return chk; error: