ede1e2
From 7b952299ea7dbe17289d5f34d5c2867a8077fb19 Mon Sep 17 00:00:00 2001
ede1e2
From: Ryan O'Hara <rohara@redhat.com>
ede1e2
Date: Thu, 28 Feb 2019 08:22:45 -0600
ede1e2
Subject: [PATCH] BUG/MEDIUM: tcp-check: single connect rule can't detect DOWN
ede1e2
 servers
ede1e2
ede1e2
When tcpcheck is used to do TCP port monitoring only and the script is
ede1e2
composed by a single "tcp-check connect" rule (whatever port and ssl
ede1e2
options enabled), then the server can't be seen as DOWN.
ede1e2
Simple configuration to reproduce:
ede1e2
ede1e2
  backend b
ede1e2
    [...]
ede1e2
    option tcp-check
ede1e2
    tcp-check connect
ede1e2
    server s1 127.0.0.1:22 check
ede1e2
ede1e2
The main reason for this issue is that the piece of code which validates
ede1e2
that we're not at the end of the chained list (of rules) prevents
ede1e2
executing the validation of the establishment of the TCP connection.
ede1e2
Since validation is not executed, the rule is terminated and the report
ede1e2
says no errors were encountered, hence the server is UP all the time.
ede1e2
ede1e2
The workaround is simple: move the connection validation outsied the
ede1e2
CONNECT rule processing loop, into the main function.
ede1e2
That way, if the connection status is not CONNECTED, then HAProxy will
ede1e2
now add more time to wait for it. If the time is expired, an error is
ede1e2
now well reported.
ede1e2
---
ede1e2
 src/checks.c | 19 +++++++++++++++++++
ede1e2
 1 file changed, 19 insertions(+)
ede1e2
ede1e2
diff --git a/src/checks.c b/src/checks.c
ede1e2
index 27a23b21..f280367b 100644
ede1e2
--- a/src/checks.c
ede1e2
+++ b/src/checks.c
ede1e2
@@ -2246,6 +2246,25 @@ static void tcpcheck_main(struct connection *conn)
ede1e2
 		} /* end expect */
ede1e2
 	} /* end loop over double chained step list */
ede1e2
 
ede1e2
+	/* don't do anything until the connection is established */
ede1e2
+	if (!(conn->flags & CO_FL_CONNECTED)) {
ede1e2
+		/* update expire time, should be done by process_chk */
ede1e2
+		/* we allow up to min(inter, timeout.connect) for a connection
ede1e2
+		 * to establish but only when timeout.check is set
ede1e2
+		 * as it may be to short for a full check otherwise
ede1e2
+		 */
ede1e2
+                while (tick_is_expired(t->expire, now_ms)) {
ede1e2
+                        int t_con;
ede1e2
+
ede1e2
+                        t_con = tick_add(t->expire, s->proxy->timeout.connect);
ede1e2
+                        t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
ede1e2
+
ede1e2
+                        if (s->proxy->timeout.check)
ede1e2
+                                t->expire = tick_first(t->expire, t_con);
ede1e2
+                }
ede1e2
+		return;
ede1e2
+        }
ede1e2
+
ede1e2
 	/* We're waiting for some I/O to complete, we've reached the end of the
ede1e2
 	 * rules, or both. Do what we have to do, otherwise we're done.
ede1e2
 	 */
ede1e2
-- 
ede1e2
2.20.1
ede1e2