diff --git a/include/proto/checks.h b/include/proto/checks.h index 9ab3e509b..ecd4a5ce4 100644 --- a/include/proto/checks.h +++ b/include/proto/checks.h @@ -50,6 +50,7 @@ void free_check(struct check *check); void send_email_alert(struct server *s, int priority, const char *format, ...) __attribute__ ((format(printf, 3, 4))); +int srv_check_healthcheck_port(struct check *chk); #endif /* _PROTO_CHECKS_H */ /* diff --git a/include/types/checks.h b/include/types/checks.h index dd2018400..283ff3dbe 100644 --- a/include/types/checks.h +++ b/include/types/checks.h @@ -41,6 +41,7 @@ enum chk_result { #define CHK_ST_ENABLED 0x0004 /* this check is currently administratively enabled */ #define CHK_ST_PAUSED 0x0008 /* checks are paused because of maintenance (health only) */ #define CHK_ST_AGENT 0x0010 /* check is an agent check (otherwise it's a health check) */ +#define CHK_ST_PORT_MISS 0x0020 /* check can't be send because no port is configured to run it */ /* check status */ enum { diff --git a/include/types/stream.h b/include/types/stream.h index 17e74b8ea..0d8c5002a 100644 --- a/include/types/stream.h +++ b/include/types/stream.h @@ -73,6 +73,7 @@ #define SF_ERR_DOWN 0x00009000 /* the proxy killed a stream because the backend became unavailable */ #define SF_ERR_KILLED 0x0000a000 /* the proxy killed a stream because it was asked to do so */ #define SF_ERR_UP 0x0000b000 /* the proxy killed a stream because a preferred backend became available */ +#define SF_ERR_CHK_PORT 0x0000c000 /* no port could be found for a health check. TODO: check SF_ERR_SHIFT */ #define SF_ERR_MASK 0x0000f000 /* mask to get only stream error flags */ #define SF_ERR_SHIFT 12 /* bit shift */ diff --git a/src/checks.c b/src/checks.c index 5d026420b..65d003743 100644 --- a/src/checks.c +++ b/src/checks.c @@ -679,6 +679,12 @@ static void chk_report_conn_err(struct connection *conn, int errno_bck, int expi } } + if (check->state & CHK_ST_PORT_MISS) { + /* NOTE: this is reported after tries */ + chunk_printf(chk, "No port available for the TCP connection"); + set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg); + } + if ((conn->flags & (CO_FL_CONNECTED|CO_FL_WAIT_L4_CONN)) == CO_FL_WAIT_L4_CONN) { /* L4 not established (yet) */ if (conn->flags & CO_FL_ERROR) @@ -1430,6 +1436,7 @@ static struct task *server_warmup(struct task *t) * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn) * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...) * - SF_ERR_INTERNAL for any other purely internal errors + * - SF_ERR_CHK_PORT if no port could be found to run a health check on an AF_INET* socket * Additionnally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted. * Note that we try to prevent the network stack from sending the ACK during the * connect() when a pure TCP check is used (without PROXY protocol). @@ -1491,8 +1498,16 @@ static int connect_conn_chk(struct task *t) conn->addr.to = s->addr; } - if (check->port) { - set_host_port(&conn->addr.to, check->port); + if ((conn->addr.to.ss_family == AF_INET) || (conn->addr.to.ss_family == AF_INET6)) { + int i = 0; + + i = srv_check_healthcheck_port(check); + if (i == 0) { + conn->owner = check; + return SF_ERR_CHK_PORT; + } + + set_host_port(&conn->addr.to, i); } proto = protocol_by_family(conn->addr.to.ss_family); @@ -2072,6 +2087,9 @@ static struct task *process_chk_conn(struct task *t) conn->flags |= CO_FL_ERROR; chk_report_conn_err(conn, errno, 0); break; + /* should share same code than cases below */ + case SF_ERR_CHK_PORT: + check->state |= CHK_ST_PORT_MISS; case SF_ERR_PRXCOND: case SF_ERR_RESOURCE: case SF_ERR_INTERNAL: @@ -3368,6 +3386,53 @@ void send_email_alert(struct server *s, int level, const char *format, ...) enqueue_email_alert(p, buf); } +/* + * Return value: + * the port to be used for the health check + * 0 in case no port could be found for the check + */ +int srv_check_healthcheck_port(struct check *chk) +{ + int i = 0; + struct server *srv = NULL; + + srv = chk->server; + + /* If neither a port nor an addr was specified and no check transport + * layer is forced, then the transport layer used by the checks is the + * same as for the production traffic. Otherwise we use raw_sock by + * default, unless one is specified. + */ + if (!chk->port && !is_addr(&chk->addr)) { +#ifdef USE_OPENSSL + chk->use_ssl |= (srv->use_ssl || (srv->proxy->options & PR_O_TCPCHK_SSL)); +#endif + chk->send_proxy |= (srv->pp_opts); + } + + /* by default, we use the health check port ocnfigured */ + if (chk->port > 0) + return chk->port; + + /* try to get the port from check_core.addr if check.port not set */ + i = get_host_port(&chk->addr); + if (i > 0) + return i; + + /* try to get the port from server address */ + /* prevent MAPPORTS from working at this point, since checks could + * not be performed in such case (MAPPORTS impose a relative ports + * based on live traffic) + */ + if (srv->flags & SRV_F_MAPPORTS) + return 0; + i = get_host_port(&srv->addr); /* by default */ + if (i > 0) + return i; + + return 0; +} + /* * Local variables: diff --git a/src/server.c b/src/server.c index 62c08b056..e41afc744 100644 --- a/src/server.c +++ b/src/server.c @@ -869,7 +869,6 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) { /* server address */ int cur_arg; - short realport = 0; int do_agent = 0, do_check = 0, defsrv = (*args[0] == 'd'); if (!defsrv && curproxy == defproxy) { @@ -961,10 +960,6 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr err_code |= ERR_ALERT | ERR_FATAL; goto out; } - else { - /* used by checks */ - realport = port1; - } /* save hostname and create associated name resolution */ newsrv->hostname = fqdn; @@ -1749,29 +1744,11 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr goto out; } - /* If neither a port nor an addr was specified and no check transport - * layer is forced, then the transport layer used by the checks is the - * same as for the production traffic. Otherwise we use raw_sock by - * default, unless one is specified. - */ - if (!newsrv->check.port && !is_addr(&newsrv->check.addr)) { -#ifdef USE_OPENSSL - newsrv->check.use_ssl |= (newsrv->use_ssl || (newsrv->proxy->options & PR_O_TCPCHK_SSL)); -#endif - newsrv->check.send_proxy |= (newsrv->pp_opts); - } - /* try to get the port from check_core.addr if check.port not set */ - if (!newsrv->check.port) - newsrv->check.port = get_host_port(&newsrv->check.addr); - - if (!newsrv->check.port) - newsrv->check.port = realport; /* by default */ - /* * We need at least a service port, a check port or the first tcp-check rule must * be a 'connect' one when checking an IPv4/IPv6 server. */ - if (!newsrv->check.port && + if ((srv_check_healthcheck_port(&newsrv->check) == 0) && (is_inet_addr(&newsrv->check.addr) || (!is_addr(&newsrv->check.addr) && is_inet_addr(&newsrv->addr)))) { struct tcpcheck_rule *r = NULL;