blob: 72ee221a3282c661f849b70807434c4215ba1497 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
Willy Tarreau26c25062009-03-08 09:38:41 +01004 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02005 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
Willy Tarreaubaaee002006-06-26 02:48:02 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
Willy Tarreaub8816082008-01-18 12:18:15 +010014#include <assert.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020015#include <ctype.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020016#include <errno.h>
17#include <fcntl.h>
Simon Horman0ba0e4a2015-01-30 11:23:00 +090018#include <stdarg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020019#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020020#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020021#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020022#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020023#include <unistd.h>
Willy Tarreau9f6dc722019-03-01 11:15:10 +010024#include <sys/resource.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020025#include <sys/socket.h>
Dmitry Sivachenkocaf58982009-08-24 15:11:06 +040026#include <sys/types.h>
Simon Horman98637e52014-06-20 12:30:16 +090027#include <sys/wait.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020028#include <netinet/in.h>
Willy Tarreau1274bc42009-07-15 07:16:31 +020029#include <netinet/tcp.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020030#include <arpa/inet.h>
31
Willy Tarreau122eba92020-06-04 10:15:32 +020032#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020033#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020034#include <haproxy/arg.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020035#include <haproxy/cfgparse.h>
Willy Tarreau4aa573d2020-06-04 18:21:56 +020036#include <haproxy/check.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020037#include <haproxy/chunk.h>
Willy Tarreau7c18b542020-06-11 09:23:02 +020038#include <haproxy/dgram.h>
Willy Tarreaueb92deb2020-06-04 10:53:16 +020039#include <haproxy/dns.h>
Christopher Fauletb51e0372020-11-25 13:47:00 +010040#include <haproxy/dynbuf-t.h>
Willy Tarreaubcc67332020-06-05 15:31:31 +020041#include <haproxy/extcheck.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020042#include <haproxy/fd.h>
43#include <haproxy/global.h>
44#include <haproxy/h1.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020045#include <haproxy/http.h>
Willy Tarreau87735332020-06-04 09:08:41 +020046#include <haproxy/http_htx.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020047#include <haproxy/htx.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020048#include <haproxy/istbuf.h>
49#include <haproxy/list.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020050#include <haproxy/log.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020051#include <haproxy/mailers.h>
52#include <haproxy/port_range.h>
53#include <haproxy/proto_tcp.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020054#include <haproxy/protocol.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020055#include <haproxy/proxy.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020056#include <haproxy/queue.h>
57#include <haproxy/regex.h>
58#include <haproxy/sample.h>
Willy Tarreau1e56f922020-06-04 23:20:13 +020059#include <haproxy/server.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020060#include <haproxy/ssl_sock.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020061#include <haproxy/stats-t.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020062#include <haproxy/stream_interface.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020063#include <haproxy/task.h>
Willy Tarreau51cd5952020-06-05 12:25:38 +020064#include <haproxy/tcpcheck.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020065#include <haproxy/thread.h>
66#include <haproxy/time.h>
67#include <haproxy/tools.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020068#include <haproxy/vars.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020069
Olivier Houchard9130a962017-10-17 17:33:43 +020070
Christopher Faulet61cc8522020-04-20 14:54:42 +020071static int wake_srv_chk(struct conn_stream *cs);
72struct data_cb check_conn_cb = {
73 .wake = wake_srv_chk,
74 .name = "CHCK",
75};
Christopher Fauletd7e63962020-04-17 20:15:59 +020076
Christopher Faulet5d503fc2020-03-30 20:34:34 +020077
Gaetan Rivet05d692d2020-02-14 17:42:54 +010078/* Dummy frontend used to create all checks sessions. */
Willy Tarreau51cd5952020-06-05 12:25:38 +020079struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020080
Christopher Faulet61cc8522020-04-20 14:54:42 +020081/**************************************************************************/
82/************************ Handle check results ****************************/
83/**************************************************************************/
84struct check_status {
85 short result; /* one of SRV_CHK_* */
86 char *info; /* human readable short info */
87 char *desc; /* long description */
88};
89
90struct analyze_status {
91 char *desc; /* description */
92 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
93};
94
Simon Horman63a4a822012-03-19 07:24:41 +090095static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +010096 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
97 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +020098 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020099
Willy Tarreau23964182014-05-20 20:56:30 +0200100 /* Below we have finished checks */
101 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100102 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100103
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100104 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200105
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100106 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
107 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
108 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200109
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100110 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
111 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
112 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200113
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100114 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
115 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200116
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200117 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200118
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100119 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
120 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
121 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900122
123 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
124 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200125 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200126};
127
Simon Horman63a4a822012-03-19 07:24:41 +0900128static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100129 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
130
131 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
132 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
133
134 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
135 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
136 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
137 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
138
139 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
140 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
141 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
142};
143
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100144/* checks if <err> is a real error for errno or one that can be ignored, and
145 * return 0 for these ones or <err> for real ones.
146 */
147static inline int unclean_errno(int err)
148{
149 if (err == EAGAIN || err == EINPROGRESS ||
150 err == EISCONN || err == EALREADY)
151 return 0;
152 return err;
153}
154
Christopher Faulet61cc8522020-04-20 14:54:42 +0200155/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200156const char *get_check_status_description(short check_status) {
157
158 const char *desc;
159
160 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200161 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200162 else
163 desc = NULL;
164
165 if (desc && *desc)
166 return desc;
167 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200168 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200169}
170
Christopher Faulet61cc8522020-04-20 14:54:42 +0200171/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200172const char *get_check_status_info(short check_status) {
173
174 const char *info;
175
176 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200177 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200178 else
179 info = NULL;
180
181 if (info && *info)
182 return info;
183 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200184 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200185}
186
Christopher Faulet61cc8522020-04-20 14:54:42 +0200187/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100188const char *get_analyze_status(short analyze_status) {
189
190 const char *desc;
191
192 if (analyze_status < HANA_STATUS_SIZE)
193 desc = analyze_statuses[analyze_status].desc;
194 else
195 desc = NULL;
196
197 if (desc && *desc)
198 return desc;
199 else
200 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
201}
202
Christopher Faulet61cc8522020-04-20 14:54:42 +0200203/* Sets check->status, update check->duration and fill check->result with an
204 * adequate CHK_RES_* value. The new check->health is computed based on the
205 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200206 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200207 * Shows information in logs about failed health check if server is UP or
208 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200209 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200210void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100211{
Simon Horman4a741432013-02-23 15:35:38 +0900212 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200213 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200214 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900215
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200216 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100217 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900218 check->desc[0] = '\0';
219 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200220 return;
221 }
222
Simon Horman4a741432013-02-23 15:35:38 +0900223 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200224 return;
225
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200226 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900227 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
228 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200229 } else
Simon Horman4a741432013-02-23 15:35:38 +0900230 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200231
Simon Horman4a741432013-02-23 15:35:38 +0900232 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200233 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900234 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200235
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100236 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900237 check->duration = -1;
238 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200239 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900240 check->duration = tv_ms_elapsed(&check->start, &now);
241 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200242 }
243
Willy Tarreau23964182014-05-20 20:56:30 +0200244 /* no change is expected if no state change occurred */
245 if (check->result == CHK_RES_NEUTRAL)
246 return;
247
Olivier Houchard0923fa42019-01-11 18:43:04 +0100248 /* If the check was really just sending a mail, it won't have an
249 * associated server, so we're done now.
250 */
251 if (!s)
252 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200253 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200254
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200255 switch (check->result) {
256 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200257 /* Failure to connect to the agent as a secondary check should not
258 * cause the server to be marked down.
259 */
260 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900261 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200262 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100263 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200264 report = 1;
265 check->health--;
266 if (check->health < check->rise)
267 check->health = 0;
268 }
269 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200270
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200271 case CHK_RES_PASSED:
272 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
273 if ((check->health < check->rise + check->fall - 1) &&
274 (check->result == CHK_RES_PASSED || check->health > 0)) {
275 report = 1;
276 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200277
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200278 if (check->health >= check->rise)
279 check->health = check->rise + check->fall - 1; /* OK now */
280 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200281
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200282 /* clear consecutive_errors if observing is enabled */
283 if (s->onerror)
284 s->consecutive_errors = 0;
285 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100286
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200287 default:
288 break;
289 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200290
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200291 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
292 (status != prev_status || report)) {
293 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200294 "%s check for %sserver %s/%s %s%s",
295 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200296 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100297 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100298 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200299 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200300
Emeric Brun5a133512017-10-19 14:42:30 +0200301 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200302
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100303 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200304 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
305 (check->health >= check->rise) ? check->fall : check->rise,
306 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200307
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200308 ha_warning("%s.\n", trash.area);
309 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
310 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200311 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200312}
313
Willy Tarreau4eec5472014-05-20 22:32:27 +0200314/* Marks the check <check>'s server down if the current check is already failed
315 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200316 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200317void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200318{
Simon Horman4a741432013-02-23 15:35:38 +0900319 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900320
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200321 /* The agent secondary check should only cause a server to be marked
322 * as down if check->status is HCHK_STATUS_L7STS, which indicates
323 * that the agent returned "fail", "stopped" or "down".
324 * The implication here is that failure to connect to the agent
325 * as a secondary check should not cause the server to be marked
326 * down. */
327 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
328 return;
329
Willy Tarreau4eec5472014-05-20 22:32:27 +0200330 if (check->health > 0)
331 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100332
Willy Tarreau4eec5472014-05-20 22:32:27 +0200333 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200334 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200335}
336
Willy Tarreauaf549582014-05-16 17:37:50 +0200337/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200338 * it isn't in maintenance, it is not tracking a down server and other checks
339 * comply. The rule is simple : by default, a server is up, unless any of the
340 * following conditions is true :
341 * - health check failed (check->health < rise)
342 * - agent check failed (agent->health < rise)
343 * - the server tracks a down server (track && track->state == STOPPED)
344 * Note that if the server has a slowstart, it will switch to STARTING instead
345 * of RUNNING. Also, only the health checks support the nolb mode, so the
346 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200347 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200348void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200349{
Simon Horman4a741432013-02-23 15:35:38 +0900350 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100351
Emeric Brun52a91d32017-08-31 14:41:55 +0200352 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200353 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100354
Emeric Brun52a91d32017-08-31 14:41:55 +0200355 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200356 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100357
Willy Tarreau3e048382014-05-21 10:30:54 +0200358 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
359 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100360
Willy Tarreau3e048382014-05-21 10:30:54 +0200361 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
362 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200363
Emeric Brun52a91d32017-08-31 14:41:55 +0200364 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200365 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100366
Emeric Brun5a133512017-10-19 14:42:30 +0200367 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100368}
369
Willy Tarreaudb58b792014-05-21 13:57:23 +0200370/* Marks the check <check> as valid and tries to set its server into stopping mode
371 * if it was running or starting, and provided it isn't in maintenance and other
372 * checks comply. The conditions for the server to be marked in stopping mode are
373 * the same as for it to be turned up. Also, only the health checks support the
374 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200375 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200376void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200377{
Simon Horman4a741432013-02-23 15:35:38 +0900378 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100379
Emeric Brun52a91d32017-08-31 14:41:55 +0200380 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200381 return;
382
Willy Tarreaudb58b792014-05-21 13:57:23 +0200383 if (check->state & CHK_ST_AGENT)
384 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100385
Emeric Brun52a91d32017-08-31 14:41:55 +0200386 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200387 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100388
Willy Tarreaudb58b792014-05-21 13:57:23 +0200389 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
390 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100391
Willy Tarreaudb58b792014-05-21 13:57:23 +0200392 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
393 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100394
Willy Tarreaub26881a2017-12-23 11:16:49 +0100395 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100396}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200397
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100398/* note: use health_adjust() only, which first checks that the observe mode is
Willy Tarreau93da4dc2021-02-17 15:20:19 +0100399 * enabled. This will take the server lock if needed.
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100400 */
401void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100402{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100403 int failed;
404 int expire;
405
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100406 if (s->observe >= HANA_OBS_SIZE)
407 return;
408
Willy Tarreaubb956662013-01-24 00:37:39 +0100409 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100410 return;
411
412 switch (analyze_statuses[status].lr[s->observe - 1]) {
413 case 1:
414 failed = 1;
415 break;
416
417 case 2:
418 failed = 0;
419 break;
420
421 default:
422 return;
423 }
424
425 if (!failed) {
426 /* good: clear consecutive_errors */
427 s->consecutive_errors = 0;
428 return;
429 }
430
Olivier Houchard7059c552019-03-08 18:49:32 +0100431 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100432
433 if (s->consecutive_errors < s->consecutive_errors_limit)
434 return;
435
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100436 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
437 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100438
Willy Tarreau93da4dc2021-02-17 15:20:19 +0100439 if (s->check.fastinter)
440 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
441 else
442 expire = TICK_ETERNITY;
443
444 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
445
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100446 switch (s->onerror) {
447 case HANA_ONERR_FASTINTER:
448 /* force fastinter - nothing to do here as all modes force it */
449 break;
450
451 case HANA_ONERR_SUDDTH:
452 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900453 if (s->check.health > s->check.rise)
454 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100455
Tim Duesterhus588b3142020-05-29 14:35:51 +0200456 /* fall through */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100457
458 case HANA_ONERR_FAILCHK:
459 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200460 set_server_check_status(&s->check, HCHK_STATUS_HANA,
461 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200462 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100463 break;
464
465 case HANA_ONERR_MARKDWN:
466 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900467 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200468 set_server_check_status(&s->check, HCHK_STATUS_HANA,
469 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200470 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100471 break;
472
473 default:
474 /* write a warning? */
475 break;
476 }
477
Willy Tarreau93da4dc2021-02-17 15:20:19 +0100478 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
479
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100480 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100481 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100482
Willy Tarreau93da4dc2021-02-17 15:20:19 +0100483 if (tick_is_lt(expire, s->check.task->expire)) {
484 /* requeue check task with new expire */
485 task_schedule(s->check.task, expire);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100486 }
Willy Tarreauef781042010-01-27 11:53:01 +0100487}
488
Christopher Faulet61cc8522020-04-20 14:54:42 +0200489/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100490 * closed, keep errno intact as it is supposed to contain the valid error code.
491 * If no error is reported, check the socket's error queue using getsockopt().
492 * Warning, this must be done only once when returning from poll, and never
493 * after an I/O error was attempted, otherwise the error queue might contain
494 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
495 * socket. Returns non-zero if an error was reported, zero if everything is
496 * clean (including a properly closed socket).
497 */
498static int retrieve_errno_from_socket(struct connection *conn)
499{
500 int skerr;
501 socklen_t lskerr = sizeof(skerr);
502
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100503 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100504 return 1;
505
Willy Tarreau3c728722014-01-23 13:50:42 +0100506 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100507 return 0;
508
Willy Tarreau585744b2017-08-24 14:31:19 +0200509 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100510 errno = skerr;
511
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100512 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100513
514 if (!errno) {
515 /* we could not retrieve an error, that does not mean there is
516 * none. Just don't change anything and only report the prior
517 * error if any.
518 */
519 if (conn->flags & CO_FL_ERROR)
520 return 1;
521 else
522 return 0;
523 }
524
525 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
526 return 1;
527}
528
Christopher Faulet61cc8522020-04-20 14:54:42 +0200529/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100530 * and adjust the server status accordingly. It may make use of <errno_bck>
531 * if non-null when the caller is absolutely certain of its validity (eg:
532 * checked just after a syscall). If the caller doesn't have a valid errno,
533 * it can pass zero, and retrieve_errno_from_socket() will be called to try
534 * to extract errno from the socket. If no error is reported, it will consider
535 * the <expired> flag. This is intended to be used when a connection error was
536 * reported in conn->flags or when a timeout was reported in <expired>. The
537 * function takes care of not updating a server status which was already set.
538 * All situations where at least one of <expired> or CO_FL_ERROR are set
539 * produce a status.
540 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200541void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100542{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200543 struct conn_stream *cs = check->cs;
544 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100545 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200546 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200547 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100548
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100549 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100550 return;
551
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100552 errno = unclean_errno(errno_bck);
553 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100554 retrieve_errno_from_socket(conn);
555
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200556 if (conn && !(conn->flags & CO_FL_ERROR) &&
557 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100558 return;
559
560 /* we'll try to build a meaningful error message depending on the
561 * context of the error possibly present in conn->err_code, and the
562 * socket error possibly collected above. This is useful to know the
563 * exact step of the L6 layer (eg: SSL handshake).
564 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200565 chk = get_trash_chunk();
566
Christopher Faulet799f3a42020-04-07 12:06:14 +0200567 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200568 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200569 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200570 if (!step)
571 chunk_printf(chk, " at initial connection step of tcp-check");
572 else {
573 chunk_printf(chk, " at step %d of tcp-check", step);
574 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200575 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
576 if (check->current_step->connect.port)
577 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200578 else
579 chunk_appendf(chk, " (connect)");
580 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200581 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
582 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100583
584 switch (expect->type) {
585 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200586 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100587 break;
588 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200589 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100590 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200591 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200592 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100593 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200594 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100595 chunk_appendf(chk, " (expect binary regex)");
596 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200597 case TCPCHK_EXPECT_STRING_LF:
598 chunk_appendf(chk, " (expect log-format string)");
599 break;
600 case TCPCHK_EXPECT_BINARY_LF:
601 chunk_appendf(chk, " (expect log-format binary)");
602 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200603 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200604 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200605 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200606 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200607 chunk_appendf(chk, " (expect HTTP status regex)");
608 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200609 case TCPCHK_EXPECT_HTTP_HEADER:
610 chunk_appendf(chk, " (expect HTTP header pattern)");
611 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200612 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200613 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200614 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200615 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200616 chunk_appendf(chk, " (expect HTTP body regex)");
617 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200618 case TCPCHK_EXPECT_HTTP_BODY_LF:
619 chunk_appendf(chk, " (expect log-format HTTP body)");
620 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200621 case TCPCHK_EXPECT_CUSTOM:
622 chunk_appendf(chk, " (expect custom function)");
623 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100624 case TCPCHK_EXPECT_UNDEF:
625 chunk_appendf(chk, " (undefined expect!)");
626 break;
627 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200628 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200629 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200630 chunk_appendf(chk, " (send)");
631 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200632
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200633 if (check->current_step && check->current_step->comment)
634 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200635 }
636 }
637
Willy Tarreau00149122017-10-04 18:05:01 +0200638 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100639 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200640 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
641 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100642 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200643 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
644 chk->area);
645 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100646 }
647 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100648 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200649 chunk_printf(&trash, "%s%s", strerror(errno),
650 chk->area);
651 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100652 }
653 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200654 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100655 }
656 }
657
Willy Tarreau00149122017-10-04 18:05:01 +0200658 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200659 /* NOTE: this is reported after <fall> tries */
660 chunk_printf(chk, "No port available for the TCP connection");
661 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
662 }
663
Christopher Faulet5e293762020-10-26 11:10:49 +0100664 if (!conn || !conn->ctrl) {
665 /* error before any connection attempt (connection allocation error or no control layer) */
Willy Tarreau00149122017-10-04 18:05:01 +0200666 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
667 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100668 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100669 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200670 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100671 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
672 else if (expired)
673 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200674
675 /*
676 * might be due to a server IP change.
677 * Let's trigger a DNS resolution if none are currently running.
678 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100679 if (check->server)
680 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200681
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100682 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100683 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100684 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200685 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100686 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
687 else if (expired)
688 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
689 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200690 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100691 /* I/O error after connection was established and before we could diagnose */
692 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
693 }
694 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200695 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
696
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100697 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200698 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
699 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200700 tout = check->current_step->expect.tout_status;
701 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100702 }
703
704 return;
705}
706
Simon Horman98637e52014-06-20 12:30:16 +0900707
Christopher Faulet61cc8522020-04-20 14:54:42 +0200708/* Builds the server state header used by HTTP health-checks */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200709int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +0900710{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200711 int sv_state;
712 int ratio;
713 char addr[46];
714 char port[6];
715 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
716 "UP %d/%d", "UP",
717 "NOLB %d/%d", "NOLB",
718 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +0900719
Christopher Faulet61cc8522020-04-20 14:54:42 +0200720 if (!(s->check.state & CHK_ST_ENABLED))
721 sv_state = 6;
722 else if (s->cur_state != SRV_ST_STOPPED) {
723 if (s->check.health == s->check.rise + s->check.fall - 1)
724 sv_state = 3; /* UP */
725 else
726 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +0900727
Christopher Faulet61cc8522020-04-20 14:54:42 +0200728 if (s->cur_state == SRV_ST_STOPPING)
729 sv_state += 2;
730 } else {
731 if (s->check.health)
732 sv_state = 1; /* going up */
733 else
734 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +0900735 }
Willy Tarreaub7b24782016-06-21 15:32:29 +0200736
Christopher Faulet61cc8522020-04-20 14:54:42 +0200737 chunk_appendf(buf, srv_hlt_st[sv_state],
738 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
739 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +0200740
Christopher Faulet61cc8522020-04-20 14:54:42 +0200741 addr_to_str(&s->addr, addr, sizeof(addr));
742 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
743 snprintf(port, sizeof(port), "%u", s->svc_port);
744 else
745 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +0200746
Christopher Faulet61cc8522020-04-20 14:54:42 +0200747 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
748 addr, port, s->proxy->id, s->id,
749 global.node,
750 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
751 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
752 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
753 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +0100754
Christopher Faulet61cc8522020-04-20 14:54:42 +0200755 if ((s->cur_state == SRV_ST_STARTING) &&
756 now.tv_sec < s->last_change + s->slowstart &&
757 now.tv_sec >= s->last_change) {
758 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
759 chunk_appendf(buf, "; throttle=%d%%", ratio);
760 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200761
Christopher Faulet61cc8522020-04-20 14:54:42 +0200762 return b_data(buf);
763}
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200764
Willy Tarreau51cd5952020-06-05 12:25:38 +0200765/**************************************************************************/
Willy Tarreau51cd5952020-06-05 12:25:38 +0200766/***************** Health-checks based on connections *********************/
767/**************************************************************************/
768/* This function is used only for server health-checks. It handles connection
769 * status updates including errors. If necessary, it wakes the check task up.
770 * It returns 0 on normal cases, <0 if at least one close() has happened on the
771 * connection (eg: reconnect). It relies on tcpcheck_main().
Christopher Faulet61cc8522020-04-20 14:54:42 +0200772 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200773static int wake_srv_chk(struct conn_stream *cs)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200774{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200775 struct connection *conn = cs->conn;
776 struct check *check = cs->data;
777 struct email_alertq *q = container_of(check, typeof(*q), check);
778 int ret = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200779
Willy Tarreau51cd5952020-06-05 12:25:38 +0200780 if (check->server)
781 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
782 else
783 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200784
Willy Tarreau51cd5952020-06-05 12:25:38 +0200785 /* we may have to make progress on the TCP checks */
786 ret = tcpcheck_main(check);
Christopher Fauletaaab0832020-05-05 15:54:22 +0200787
Willy Tarreau51cd5952020-06-05 12:25:38 +0200788 cs = check->cs;
789 conn = cs->conn;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200790
Willy Tarreau51cd5952020-06-05 12:25:38 +0200791 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
792 /* We may get error reports bypassing the I/O handlers, typically
793 * the case when sending a pure TCP check which fails, then the I/O
794 * handlers above are not called. This is completely handled by the
795 * main processing task so let's simply wake it up. If we get here,
796 * we expect errno to still be valid.
797 */
798 chk_report_conn_err(check, errno, 0);
799 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200800 }
801
Christopher Faulet444b7b12021-01-18 15:47:03 +0100802 if (check->result != CHK_RES_UNKNOWN || ret == -1) {
Willy Tarreau51cd5952020-06-05 12:25:38 +0200803 /* Check complete or aborted. If connection not yet closed do it
804 * now and wake the check task up to be sure the result is
805 * handled ASAP. */
806 conn_sock_drain(conn);
807 cs_close(cs);
808 ret = -1;
Christopher Faulet444b7b12021-01-18 15:47:03 +0100809
810 if (check->wait_list.events)
811 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
812
Willy Tarreau51cd5952020-06-05 12:25:38 +0200813 /* We may have been scheduled to run, and the
814 * I/O handler expects to have a cs, so remove
815 * the tasklet
816 */
817 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
818 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200819 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200820
Willy Tarreau51cd5952020-06-05 12:25:38 +0200821 if (check->server)
822 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
823 else
824 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200825
Willy Tarreau51cd5952020-06-05 12:25:38 +0200826 /* if a connection got replaced, we must absolutely prevent the connection
827 * handler from touching its fd, and perform the FD polling updates ourselves
828 */
829 if (ret < 0)
830 conn_cond_update_polling(conn);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200831
Christopher Faulet61cc8522020-04-20 14:54:42 +0200832 return ret;
833}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200834
Willy Tarreau51cd5952020-06-05 12:25:38 +0200835/* This function checks if any I/O is wanted, and if so, attempts to do so */
836static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200837{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200838 struct check *check = ctx;
839 struct conn_stream *cs = check->cs;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200840
Willy Tarreau51cd5952020-06-05 12:25:38 +0200841 wake_srv_chk(cs);
842 return NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200843}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200844
Willy Tarreau51cd5952020-06-05 12:25:38 +0200845/* manages a server health-check that uses a connection. Returns
846 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Faulet61cc8522020-04-20 14:54:42 +0200847 *
848 * Please do NOT place any return statement in this function and only leave
Willy Tarreau51cd5952020-06-05 12:25:38 +0200849 * via the out_unlock label.
Christopher Faulet61cc8522020-04-20 14:54:42 +0200850 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200851static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200852{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200853 struct check *check = context;
854 struct proxy *proxy = check->proxy;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200855 struct conn_stream *cs = check->cs;
856 struct connection *conn = cs_conn(cs);
Willy Tarreau51cd5952020-06-05 12:25:38 +0200857 int rv;
858 int expired = tick_is_expired(t->expire, now_ms);
Willy Tarreaudeccd112018-06-14 18:38:55 +0200859
Willy Tarreau51cd5952020-06-05 12:25:38 +0200860 if (check->server)
861 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
862 if (!(check->state & CHK_ST_INPROGRESS)) {
863 /* no check currently running */
864 if (!expired) /* woke up too early */
865 goto out_unlock;
Willy Tarreauabca5b62013-12-06 14:19:25 +0100866
Willy Tarreau51cd5952020-06-05 12:25:38 +0200867 /* we don't send any health-checks when the proxy is
868 * stopped, the server should not be checked or the check
869 * is disabled.
870 */
871 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
Willy Tarreauc3914d42020-09-24 08:39:22 +0200872 proxy->disabled)
Willy Tarreau51cd5952020-06-05 12:25:38 +0200873 goto reschedule;
Christopher Faulet404f9192020-04-09 23:13:54 +0200874
Willy Tarreau51cd5952020-06-05 12:25:38 +0200875 /* we'll initiate a new check */
876 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet404f9192020-04-09 23:13:54 +0200877
Willy Tarreau51cd5952020-06-05 12:25:38 +0200878 check->state |= CHK_ST_INPROGRESS;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200879
Willy Tarreau51cd5952020-06-05 12:25:38 +0200880 task_set_affinity(t, tid_bit);
881
882 check->current_step = NULL;
883 tcpcheck_main(check);
884 goto out_unlock;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200885 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200886 else {
887 /* there was a test running.
888 * First, let's check whether there was an uncaught error,
889 * which can happen on connect timeout or error.
890 */
891 if (check->result == CHK_RES_UNKNOWN) {
892 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
893 chk_report_conn_err(check, 0, expired);
894 }
Christopher Faulet444b7b12021-01-18 15:47:03 +0100895 else {
896 if (check->state & CHK_ST_CLOSE_CONN) {
897 cs_destroy(cs);
898 cs = NULL;
899 conn = NULL;
900 check->cs = NULL;
901 check->state &= ~CHK_ST_CLOSE_CONN;
902 tcpcheck_main(check);
903 }
904 if (check->result == CHK_RES_UNKNOWN)
905 goto out_unlock; /* timeout not reached, wait again */
906 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200907 }
Christopher Faulet404f9192020-04-09 23:13:54 +0200908
Willy Tarreau51cd5952020-06-05 12:25:38 +0200909 /* check complete or aborted */
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200910
Willy Tarreau51cd5952020-06-05 12:25:38 +0200911 check->current_step = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200912
Willy Tarreau51cd5952020-06-05 12:25:38 +0200913 if (conn && conn->xprt) {
914 /* The check was aborted and the connection was not yet closed.
915 * This can happen upon timeout, or when an external event such
916 * as a failed response coupled with "observe layer7" caused the
917 * server state to be suddenly changed.
918 */
919 conn_sock_drain(conn);
920 cs_close(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200921 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200922
Willy Tarreau51cd5952020-06-05 12:25:38 +0200923 if (cs) {
924 if (check->wait_list.events)
925 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
926 /* We may have been scheduled to run, and the
927 * I/O handler expects to have a cs, so remove
928 * the tasklet
929 */
930 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
931 cs_destroy(cs);
932 cs = check->cs = NULL;
933 conn = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200934 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200935
936 if (check->sess != NULL) {
937 vars_prune(&check->vars, check->sess, NULL);
938 session_free(check->sess);
939 check->sess = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200940 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200941
942 if (check->server) {
943 if (check->result == CHK_RES_FAILED) {
944 /* a failure or timeout detected */
945 check_notify_failure(check);
946 }
947 else if (check->result == CHK_RES_CONDPASS) {
948 /* check is OK but asks for stopping mode */
949 check_notify_stopping(check);
950 }
951 else if (check->result == CHK_RES_PASSED) {
952 /* a success was detected */
953 check_notify_success(check);
954 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200955 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200956 task_set_affinity(t, MAX_THREADS_MASK);
Christopher Fauletb51e0372020-11-25 13:47:00 +0100957 check_release_buf(check, &check->bi);
958 check_release_buf(check, &check->bo);
959 check->state &= ~(CHK_ST_INPROGRESS|CHK_ST_IN_ALLOC|CHK_ST_OUT_ALLOC);
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200960
Willy Tarreau51cd5952020-06-05 12:25:38 +0200961 if (check->server) {
962 rv = 0;
963 if (global.spread_checks > 0) {
964 rv = srv_getinter(check) * global.spread_checks / 100;
965 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
Christopher Faulet61cc8522020-04-20 14:54:42 +0200966 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200967 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet61cc8522020-04-20 14:54:42 +0200968 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200969 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200970
971 reschedule:
972 while (tick_is_expired(t->expire, now_ms))
973 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
974 out_unlock:
975 if (check->server)
976 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
977 return t;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200978}
979
Willy Tarreau51cd5952020-06-05 12:25:38 +0200980
Christopher Faulet61cc8522020-04-20 14:54:42 +0200981/**************************************************************************/
982/************************** Init/deinit checks ****************************/
983/**************************************************************************/
Christopher Fauletb51e0372020-11-25 13:47:00 +0100984/*
985 * Tries to grab a buffer and to re-enables processing on check <target>. The
986 * check flags are used to figure what buffer was requested. It returns 1 if the
987 * allocation succeeds, in which case the I/O tasklet is woken up, or 0 if it's
988 * impossible to wake up and we prefer to be woken up later.
989 */
990int check_buf_available(void *target)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200991{
Christopher Fauletb51e0372020-11-25 13:47:00 +0100992 struct check *check = target;
993
994 if ((check->state & CHK_ST_IN_ALLOC) && b_alloc_margin(&check->bi, 0)) {
995 check->state &= ~CHK_ST_IN_ALLOC;
996 tasklet_wakeup(check->wait_list.tasklet);
997 return 1;
998 }
999 if ((check->state & CHK_ST_OUT_ALLOC) && b_alloc_margin(&check->bo, 0)) {
1000 check->state &= ~CHK_ST_OUT_ALLOC;
1001 tasklet_wakeup(check->wait_list.tasklet);
1002 return 1;
1003 }
1004
1005 return 0;
1006}
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001007
Christopher Fauletb51e0372020-11-25 13:47:00 +01001008/*
1009 * Allocate a buffer. If if fails, it adds the check in buffer wait queue.
1010 */
1011struct buffer *check_get_buf(struct check *check, struct buffer *bptr)
1012{
1013 struct buffer *buf = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001014
Willy Tarreau954827a2021-02-20 11:49:49 +01001015 if (likely(!LIST_ADDED(&check->buf_wait.list)) &&
Christopher Fauletb51e0372020-11-25 13:47:00 +01001016 unlikely((buf = b_alloc_margin(bptr, 0)) == NULL)) {
1017 check->buf_wait.target = check;
1018 check->buf_wait.wakeup_cb = check_buf_available;
Willy Tarreau954827a2021-02-20 11:49:49 +01001019 LIST_ADDQ(&ti->buffer_wq, &check->buf_wait.list);
Christopher Fauletb51e0372020-11-25 13:47:00 +01001020 }
1021 return buf;
1022}
1023
1024/*
1025 * Release a buffer, if any, and try to wake up entities waiting in the buffer
1026 * wait queue.
1027 */
1028void check_release_buf(struct check *check, struct buffer *bptr)
1029{
1030 if (bptr->size) {
1031 b_free(bptr);
Willy Tarreau132b3a42021-02-20 12:02:46 +01001032 offer_buffers(check->buf_wait.target, 1);
Christopher Fauletb51e0372020-11-25 13:47:00 +01001033 }
1034}
1035
1036const char *init_check(struct check *check, int type)
1037{
1038 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001039
Christopher Fauletb51e0372020-11-25 13:47:00 +01001040 check->bi = BUF_NULL;
1041 check->bo = BUF_NULL;
Willy Tarreau954827a2021-02-20 11:49:49 +01001042 LIST_INIT(&check->buf_wait.list);
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001043
Christopher Faulet61cc8522020-04-20 14:54:42 +02001044 check->wait_list.tasklet = tasklet_new();
1045 if (!check->wait_list.tasklet)
1046 return "out of memory while allocating check tasklet";
1047 check->wait_list.events = 0;
1048 check->wait_list.tasklet->process = event_srv_chk_io;
1049 check->wait_list.tasklet->context = check;
1050 return NULL;
1051}
1052
1053void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001054{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001055 task_destroy(check->task);
1056 if (check->wait_list.tasklet)
1057 tasklet_free(check->wait_list.tasklet);
1058
Christopher Fauletb51e0372020-11-25 13:47:00 +01001059 check_release_buf(check, &check->bi);
1060 check_release_buf(check, &check->bo);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001061 if (check->cs) {
1062 free(check->cs->conn);
1063 check->cs->conn = NULL;
1064 cs_free(check->cs);
1065 check->cs = NULL;
1066 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001067}
1068
Christopher Faulet61cc8522020-04-20 14:54:42 +02001069/* manages a server health-check. Returns the time the task accepts to wait, or
1070 * TIME_ETERNITY for infinity.
1071 */
Willy Tarreaucee013e2020-06-05 11:40:38 +02001072struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001073{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001074 struct check *check = context;
1075
1076 if (check->type == PR_O2_EXT_CHK)
1077 return process_chk_proc(t, context, state);
1078 return process_chk_conn(t, context, state);
1079
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001080}
1081
Christopher Faulet61cc8522020-04-20 14:54:42 +02001082
1083static int start_check_task(struct check *check, int mininter,
1084 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001085{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001086 struct task *t;
1087 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001088
Christopher Faulet61cc8522020-04-20 14:54:42 +02001089 if (check->type == PR_O2_EXT_CHK)
1090 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001091
Christopher Faulet61cc8522020-04-20 14:54:42 +02001092 /* task for the check */
1093 if ((t = task_new(thread_mask)) == NULL) {
1094 ha_alert("Starting [%s:%s] check: out of memory.\n",
1095 check->server->proxy->id, check->server->id);
1096 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001097 }
1098
Christopher Faulet61cc8522020-04-20 14:54:42 +02001099 check->task = t;
1100 t->process = process_chk;
1101 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001102
Christopher Faulet61cc8522020-04-20 14:54:42 +02001103 if (mininter < srv_getinter(check))
1104 mininter = srv_getinter(check);
1105
1106 if (global.max_spread_checks && mininter > global.max_spread_checks)
1107 mininter = global.max_spread_checks;
1108
1109 /* check this every ms */
1110 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
1111 check->start = now;
1112 task_queue(t);
1113
1114 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001115}
1116
Christopher Faulet61cc8522020-04-20 14:54:42 +02001117/* updates the server's weight during a warmup stage. Once the final weight is
1118 * reached, the task automatically stops. Note that any server status change
1119 * must have updated s->last_change accordingly.
1120 */
1121static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001122{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001123 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001124
Christopher Faulet61cc8522020-04-20 14:54:42 +02001125 /* by default, plan on stopping the task */
1126 t->expire = TICK_ETERNITY;
1127 if ((s->next_admin & SRV_ADMF_MAINT) ||
1128 (s->next_state != SRV_ST_STARTING))
1129 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02001130
Christopher Faulet61cc8522020-04-20 14:54:42 +02001131 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001132
Christopher Faulet61cc8522020-04-20 14:54:42 +02001133 /* recalculate the weights and update the state */
1134 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02001135
Christopher Faulet61cc8522020-04-20 14:54:42 +02001136 /* probably that we can refill this server with a bit more connections */
1137 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02001138
Christopher Faulet61cc8522020-04-20 14:54:42 +02001139 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02001140
Christopher Faulet61cc8522020-04-20 14:54:42 +02001141 /* get back there in 1 second or 1/20th of the slowstart interval,
1142 * whichever is greater, resulting in small 5% steps.
1143 */
1144 if (s->next_state == SRV_ST_STARTING)
1145 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
1146 return t;
1147}
1148
1149/*
1150 * Start health-check.
1151 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
1152 */
1153static int start_checks()
1154{
1155
1156 struct proxy *px;
1157 struct server *s;
1158 struct task *t;
1159 int nbcheck=0, mininter=0, srvpos=0;
1160
1161 /* 0- init the dummy frontend used to create all checks sessions */
1162 init_new_proxy(&checks_fe);
1163 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
1164 checks_fe.mode = PR_MODE_TCP;
1165 checks_fe.maxconn = 0;
1166 checks_fe.conn_retries = CONN_RETRIES;
1167 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
1168 checks_fe.timeout.client = TICK_ETERNITY;
1169
1170 /* 1- count the checkers to run simultaneously.
1171 * We also determine the minimum interval among all of those which
1172 * have an interval larger than SRV_CHK_INTER_THRES. This interval
1173 * will be used to spread their start-up date. Those which have
1174 * a shorter interval will start independently and will not dictate
1175 * too short an interval for all others.
1176 */
1177 for (px = proxies_list; px; px = px->next) {
1178 for (s = px->srv; s; s = s->next) {
1179 if (s->slowstart) {
1180 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
1181 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1182 return ERR_ALERT | ERR_FATAL;
1183 }
1184 /* We need a warmup task that will be called when the server
1185 * state switches from down to up.
1186 */
1187 s->warmup = t;
1188 t->process = server_warmup;
1189 t->context = s;
1190 /* server can be in this state only because of */
1191 if (s->next_state == SRV_ST_STARTING)
1192 task_schedule(s->warmup, tick_add(now_ms, MS_TO_TICKS(MAX(1000, (now.tv_sec - s->last_change)) / 20)));
Christopher Faulet5c288742020-03-31 08:15:58 +02001193 }
1194
Christopher Faulet61cc8522020-04-20 14:54:42 +02001195 if (s->check.state & CHK_ST_CONFIGURED) {
1196 nbcheck++;
1197 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
1198 (!mininter || mininter > srv_getinter(&s->check)))
1199 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02001200 }
1201
Christopher Faulet61cc8522020-04-20 14:54:42 +02001202 if (s->agent.state & CHK_ST_CONFIGURED) {
1203 nbcheck++;
1204 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
1205 (!mininter || mininter > srv_getinter(&s->agent)))
1206 mininter = srv_getinter(&s->agent);
1207 }
Christopher Faulet5c288742020-03-31 08:15:58 +02001208 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001209 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001210
Christopher Faulet61cc8522020-04-20 14:54:42 +02001211 if (!nbcheck)
1212 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001213
Christopher Faulet61cc8522020-04-20 14:54:42 +02001214 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02001215
Christopher Faulet61cc8522020-04-20 14:54:42 +02001216 /*
1217 * 2- start them as far as possible from each others. For this, we will
1218 * start them after their interval set to the min interval divided by
1219 * the number of servers, weighted by the server's position in the list.
1220 */
1221 for (px = proxies_list; px; px = px->next) {
1222 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
1223 if (init_pid_list()) {
1224 ha_alert("Starting [%s] check: out of memory.\n", px->id);
1225 return ERR_ALERT | ERR_FATAL;
1226 }
1227 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001228
Christopher Faulet61cc8522020-04-20 14:54:42 +02001229 for (s = px->srv; s; s = s->next) {
1230 /* A task for the main check */
1231 if (s->check.state & CHK_ST_CONFIGURED) {
1232 if (s->check.type == PR_O2_EXT_CHK) {
1233 if (!prepare_external_check(&s->check))
1234 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001235 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001236 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
1237 return ERR_ALERT | ERR_FATAL;
1238 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02001239 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001240
Christopher Faulet61cc8522020-04-20 14:54:42 +02001241 /* A task for a auxiliary agent check */
1242 if (s->agent.state & CHK_ST_CONFIGURED) {
1243 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
1244 return ERR_ALERT | ERR_FATAL;
1245 }
1246 srvpos++;
1247 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001248 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001249 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001250 return 0;
1251}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001252
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001253
Christopher Faulet61cc8522020-04-20 14:54:42 +02001254/*
1255 * Return value:
1256 * the port to be used for the health check
1257 * 0 in case no port could be found for the check
1258 */
1259static int srv_check_healthcheck_port(struct check *chk)
1260{
1261 int i = 0;
1262 struct server *srv = NULL;
1263
1264 srv = chk->server;
1265
1266 /* by default, we use the health check port ocnfigured */
1267 if (chk->port > 0)
1268 return chk->port;
1269
1270 /* try to get the port from check_core.addr if check.port not set */
1271 i = get_host_port(&chk->addr);
1272 if (i > 0)
1273 return i;
1274
1275 /* try to get the port from server address */
1276 /* prevent MAPPORTS from working at this point, since checks could
1277 * not be performed in such case (MAPPORTS impose a relative ports
1278 * based on live traffic)
1279 */
1280 if (srv->flags & SRV_F_MAPPORTS)
1281 return 0;
1282
1283 i = srv->svc_port; /* by default */
1284 if (i > 0)
1285 return i;
1286
1287 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001288}
1289
Christopher Faulet61cc8522020-04-20 14:54:42 +02001290/* Initializes an health-check attached to the server <srv>. Non-zero is returned
1291 * if an error occurred.
1292 */
1293static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001294{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001295 const char *err;
1296 struct tcpcheck_rule *r;
1297 int ret = 0;
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001298 int check_type;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001299
Christopher Faulet0a5e7132021-01-12 17:29:45 +01001300 if (!srv->do_check || !(srv->proxy->cap & PR_CAP_BE))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001301 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001302
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001303 check_type = srv->check.tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001304
Christopher Faulet61cc8522020-04-20 14:54:42 +02001305 /* If neither a port nor an addr was specified and no check transport
1306 * layer is forced, then the transport layer used by the checks is the
1307 * same as for the production traffic. Otherwise we use raw_sock by
1308 * default, unless one is specified.
1309 */
1310 if (!srv->check.port && !is_addr(&srv->check.addr)) {
1311 if (!srv->check.use_ssl && srv->use_ssl != -1) {
1312 srv->check.use_ssl = srv->use_ssl;
1313 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001314 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001315 else if (srv->check.use_ssl == 1)
1316 srv->check.xprt = xprt_get(XPRT_SSL);
1317 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001318 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02001319 else if (srv->check.use_ssl == 1)
1320 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001321
Christopher Faulet12882cf2020-04-23 15:50:18 +02001322 /* Inherit the mux protocol from the server if not already defined for
1323 * the check
1324 */
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001325 if (srv->mux_proto && !srv->check.mux_proto &&
1326 ((srv->mux_proto->mode == PROTO_MODE_HTTP && check_type == TCPCHK_RULES_HTTP_CHK) ||
1327 (srv->mux_proto->mode == PROTO_MODE_TCP && check_type != TCPCHK_RULES_HTTP_CHK))) {
Christopher Faulet12882cf2020-04-23 15:50:18 +02001328 srv->check.mux_proto = srv->mux_proto;
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001329 }
Amaury Denoyelle92c8ac12020-11-13 12:34:57 +01001330 /* test that check proto is valid if explicitly defined */
1331 else if (srv->check.mux_proto &&
1332 ((srv->check.mux_proto->mode == PROTO_MODE_HTTP && check_type != TCPCHK_RULES_HTTP_CHK) ||
1333 (srv->check.mux_proto->mode == PROTO_MODE_TCP && check_type == TCPCHK_RULES_HTTP_CHK))) {
1334 ha_alert("config: %s '%s': server '%s' uses an incompatible MUX protocol for the selected check type\n",
1335 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1336 ret |= ERR_ALERT | ERR_FATAL;
1337 goto out;
1338 }
Christopher Faulet12882cf2020-04-23 15:50:18 +02001339
Christopher Faulet61cc8522020-04-20 14:54:42 +02001340 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001341
Christopher Faulet61cc8522020-04-20 14:54:42 +02001342 /* We need at least a service port, a check port or the first tcp-check
1343 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
1344 */
1345 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
1346 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
1347 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001348
Christopher Faulet61cc8522020-04-20 14:54:42 +02001349 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
1350 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
1351 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1352 ret |= ERR_ALERT | ERR_ABORT;
1353 goto out;
1354 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001355
Christopher Faulet61cc8522020-04-20 14:54:42 +02001356 /* search the first action (connect / send / expect) in the list */
1357 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
1358 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
1359 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
1360 "nor tcp_check rule 'connect' with port information.\n",
1361 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1362 ret |= ERR_ALERT | ERR_ABORT;
1363 goto out;
1364 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001365
Christopher Faulet61cc8522020-04-20 14:54:42 +02001366 /* scan the tcp-check ruleset to ensure a port has been configured */
1367 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
1368 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
1369 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
1370 "and a tcp_check rule 'connect' with no port information.\n",
1371 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1372 ret |= ERR_ALERT | ERR_ABORT;
1373 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001374 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001375 }
1376
Christopher Faulet61cc8522020-04-20 14:54:42 +02001377 init:
1378 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
1379 struct tcpcheck_ruleset *rs = NULL;
1380 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
1381 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001382
Christopher Faulet61cc8522020-04-20 14:54:42 +02001383 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
1384 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02001385
Christopher Faulet61cc8522020-04-20 14:54:42 +02001386 rs = find_tcpcheck_ruleset("*tcp-check");
1387 if (!rs) {
1388 rs = create_tcpcheck_ruleset("*tcp-check");
1389 if (rs == NULL) {
1390 ha_alert("config: %s '%s': out of memory.\n",
1391 proxy_type_str(srv->proxy), srv->proxy->id);
1392 ret |= ERR_ALERT | ERR_FATAL;
1393 goto out;
1394 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001395 }
1396
Christopher Faulet61cc8522020-04-20 14:54:42 +02001397 free_tcpcheck_vars(&rules->preset_vars);
1398 rules->list = &rs->rules;
1399 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001400 }
1401
Christopher Faulet61cc8522020-04-20 14:54:42 +02001402 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
1403 if (err) {
1404 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
1405 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1406 ret |= ERR_ALERT | ERR_ABORT;
1407 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001408 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001409 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
1410 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02001411
Christopher Faulet61cc8522020-04-20 14:54:42 +02001412 out:
1413 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001414}
1415
Christopher Faulet61cc8522020-04-20 14:54:42 +02001416/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
1417 * if an error occurred.
1418 */
1419static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02001420{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001421 struct tcpcheck_rule *chk;
1422 const char *err;
1423 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001424
Christopher Faulet0a5e7132021-01-12 17:29:45 +01001425 if (!srv->do_agent || !(srv->proxy->cap & PR_CAP_BE))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001426 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001427
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001428 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02001429 * implicit one is inserted before all others.
1430 */
1431 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
1432 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
1433 chk = calloc(1, sizeof(*chk));
1434 if (!chk) {
1435 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
1436 " to agent-check for server '%s' (out of memory).\n",
1437 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1438 ret |= ERR_ALERT | ERR_FATAL;
1439 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001440 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001441 chk->action = TCPCHK_ACT_CONNECT;
1442 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
1443 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02001444 }
1445
Christopher Faulete5870d82020-04-15 11:32:03 +02001446
Christopher Faulet61cc8522020-04-20 14:54:42 +02001447 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
1448 if (err) {
1449 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
1450 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1451 ret |= ERR_ALERT | ERR_ABORT;
1452 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001453 }
1454
Christopher Faulet61cc8522020-04-20 14:54:42 +02001455 if (!srv->agent.inter)
1456 srv->agent.inter = srv->check.inter;
1457
1458 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
1459 global.maxsock++;
1460
1461 out:
1462 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001463}
1464
Christopher Faulet61cc8522020-04-20 14:54:42 +02001465static void deinit_srv_check(struct server *srv)
1466{
1467 if (srv->check.state & CHK_ST_CONFIGURED)
1468 free_check(&srv->check);
1469 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
1470 srv->do_check = 0;
1471}
Christopher Faulete5870d82020-04-15 11:32:03 +02001472
Christopher Faulet61cc8522020-04-20 14:54:42 +02001473
1474static void deinit_srv_agent_check(struct server *srv)
1475{
1476 if (srv->agent.tcpcheck_rules) {
1477 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
1478 free(srv->agent.tcpcheck_rules);
1479 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001480 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001481
Christopher Faulet61cc8522020-04-20 14:54:42 +02001482 if (srv->agent.state & CHK_ST_CONFIGURED)
1483 free_check(&srv->agent);
1484
1485 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
1486 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001487}
1488
Willy Tarreaucee013e2020-06-05 11:40:38 +02001489REGISTER_POST_SERVER_CHECK(init_srv_check);
1490REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
Willy Tarreaucee013e2020-06-05 11:40:38 +02001491REGISTER_POST_CHECK(start_checks);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001492
Willy Tarreaucee013e2020-06-05 11:40:38 +02001493REGISTER_SERVER_DEINIT(deinit_srv_check);
1494REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001495
Christopher Faulet61cc8522020-04-20 14:54:42 +02001496
1497/**************************************************************************/
1498/************************** Check sample fetches **************************/
1499/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001500
Christopher Faulet61cc8522020-04-20 14:54:42 +02001501static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001502 { /* END */ },
1503}};
1504
1505INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
1506
1507
1508/**************************************************************************/
1509/************************ Check's parsing functions ***********************/
1510/**************************************************************************/
Christopher Faulet51b129f2020-04-09 15:54:18 +02001511/* Parses the "http-check" proxy keyword */
1512static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
1513 struct proxy *defpx, const char *file, int line,
1514 char **errmsg)
1515{
Christopher Faulete5870d82020-04-15 11:32:03 +02001516 struct tcpcheck_ruleset *rs = NULL;
1517 struct tcpcheck_rule *chk = NULL;
1518 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001519
1520 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
1521 ret = 1;
1522
1523 cur_arg = 1;
1524 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
1525 /* enable a graceful server shutdown on an HTTP 404 response */
1526 curpx->options |= PR_O_DISABLE404;
1527 if (too_many_args(1, args, errmsg, NULL))
1528 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001529 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001530 }
1531 else if (strcmp(args[cur_arg], "send-state") == 0) {
1532 /* enable emission of the apparent state of a server in HTTP checks */
1533 curpx->options2 |= PR_O2_CHK_SNDST;
1534 if (too_many_args(1, args, errmsg, NULL))
1535 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001536 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001537 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001538
Christopher Faulete5870d82020-04-15 11:32:03 +02001539 /* Deduce the ruleset name from the proxy info */
1540 chunk_printf(&trash, "*http-check-%s_%s-%d",
1541 ((curpx == defpx) ? "defaults" : curpx->id),
1542 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001543
Christopher Faulet61cc8522020-04-20 14:54:42 +02001544 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001545 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001546 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001547 if (rs == NULL) {
1548 memprintf(errmsg, "out of memory.\n");
1549 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001550 }
1551 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001552
Christopher Faulete5870d82020-04-15 11:32:03 +02001553 index = 0;
1554 if (!LIST_ISEMPTY(&rs->rules)) {
1555 chk = LIST_PREV(&rs->rules, typeof(chk), list);
1556 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
1557 index = chk->index + 1;
Christopher Faulet37e583d2021-03-12 12:00:14 +01001558 chk = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001559 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001560
Christopher Faulete5870d82020-04-15 11:32:03 +02001561 if (strcmp(args[cur_arg], "connect") == 0)
1562 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1563 else if (strcmp(args[cur_arg], "send") == 0)
1564 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1565 else if (strcmp(args[cur_arg], "expect") == 0)
1566 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
1567 file, line, errmsg);
1568 else if (strcmp(args[cur_arg], "comment") == 0)
1569 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1570 else {
1571 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001572
Christopher Faulete5870d82020-04-15 11:32:03 +02001573 if (!kw) {
1574 action_kw_tcp_check_build_list(&trash);
1575 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
1576 " 'send', 'expect'%s%s. but got '%s'",
1577 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
1578 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001579 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001580 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
1581 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001582
Christopher Faulete5870d82020-04-15 11:32:03 +02001583 if (!chk) {
1584 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1585 goto error;
1586 }
1587 ret = (*errmsg != NULL); /* Handle warning */
1588
1589 chk->index = index;
1590 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
1591 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
1592 /* Use this ruleset if the proxy already has http-check enabled */
1593 curpx->tcpcheck_rules.list = &rs->rules;
1594 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
1595 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
1596 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1597 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001598 goto error;
1599 }
1600 }
1601 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02001602 /* mark this ruleset as unused for now */
1603 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
1604 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001605 }
1606
Christopher Faulete5870d82020-04-15 11:32:03 +02001607 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02001608 return ret;
1609
1610 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02001611 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001612 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001613 return -1;
1614}
1615
Christopher Faulet430e4802020-04-09 15:28:16 +02001616/* Parses the "option tcp-check" proxy keyword */
1617int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1618 const char *file, int line)
1619{
Christopher Faulet404f9192020-04-09 23:13:54 +02001620 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02001621 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1622 int err_code = 0;
1623
1624 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1625 err_code |= ERR_WARN;
1626
1627 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1628 goto out;
1629
Christopher Faulet404f9192020-04-09 23:13:54 +02001630 curpx->options2 &= ~PR_O2_CHK_ANY;
1631 curpx->options2 |= PR_O2_TCPCHK_CHK;
1632
Christopher Fauletd7e63962020-04-17 20:15:59 +02001633 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02001634 /* If a tcp-check rulesset is already set, do nothing */
1635 if (rules->list)
1636 goto out;
1637
1638 /* If a tcp-check ruleset is waiting to be used for the current proxy,
1639 * get it.
1640 */
1641 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
1642 goto curpx_ruleset;
1643
1644 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
1645 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001646 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001647 if (rs)
1648 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02001649 }
1650
Christopher Faulet404f9192020-04-09 23:13:54 +02001651 curpx_ruleset:
1652 /* Deduce the ruleset name from the proxy info */
1653 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
1654 ((curpx == defpx) ? "defaults" : curpx->id),
1655 curpx->conf.file, curpx->conf.line);
1656
Christopher Faulet61cc8522020-04-20 14:54:42 +02001657 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001658 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001659 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001660 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02001661 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1662 goto error;
1663 }
Christopher Faulet430e4802020-04-09 15:28:16 +02001664 }
1665
Christopher Faulet404f9192020-04-09 23:13:54 +02001666 ruleset_found:
1667 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02001668 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001669 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Fauletd7e63962020-04-17 20:15:59 +02001670 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02001671
1672 out:
1673 return err_code;
1674
1675 error:
1676 err_code |= ERR_ALERT | ERR_FATAL;
1677 goto out;
1678}
Christopher Faulet33f05df2020-04-01 11:08:50 +02001679
1680/* Parses the "option redis-check" proxy keyword */
1681int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1682 const char *file, int line)
1683{
1684 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
1685 static char *redis_res = "+PONG\r\n";
1686
1687 struct tcpcheck_ruleset *rs = NULL;
1688 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1689 struct tcpcheck_rule *chk;
1690 char *errmsg = NULL;
1691 int err_code = 0;
1692
1693 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1694 err_code |= ERR_WARN;
1695
1696 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1697 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001698
1699 curpx->options2 &= ~PR_O2_CHK_ANY;
1700 curpx->options2 |= PR_O2_TCPCHK_CHK;
1701
1702 free_tcpcheck_vars(&rules->preset_vars);
1703 rules->list = NULL;
1704 rules->flags = 0;
1705
Christopher Faulet61cc8522020-04-20 14:54:42 +02001706 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001707 if (rs)
1708 goto ruleset_found;
1709
Christopher Faulet61cc8522020-04-20 14:54:42 +02001710 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001711 if (rs == NULL) {
1712 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1713 goto error;
1714 }
1715
1716 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
1717 1, curpx, &rs->rules, file, line, &errmsg);
1718 if (!chk) {
1719 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1720 goto error;
1721 }
1722 chk->index = 0;
1723 LIST_ADDQ(&rs->rules, &chk->list);
1724
1725 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
1726 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001727 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02001728 "on-success", "Redis server is ok",
1729 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001730 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001731 if (!chk) {
1732 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1733 goto error;
1734 }
1735 chk->index = 1;
1736 LIST_ADDQ(&rs->rules, &chk->list);
1737
Christopher Faulet33f05df2020-04-01 11:08:50 +02001738 ruleset_found:
1739 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001740 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001741 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001742
1743 out:
1744 free(errmsg);
1745 return err_code;
1746
1747 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001748 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001749 err_code |= ERR_ALERT | ERR_FATAL;
1750 goto out;
1751}
1752
Christopher Faulet811f78c2020-04-01 11:10:27 +02001753
1754/* Parses the "option ssl-hello-chk" proxy keyword */
1755int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1756 const char *file, int line)
1757{
1758 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
1759 * ssl-hello-chk option to ensure that the remote server speaks SSL.
1760 *
1761 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
1762 */
1763 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001764 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02001765 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
1766 "0079" /* ContentLength : 0x79 bytes after this one */
1767 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
1768 "000075" /* HandshakeLength : 0x75 bytes after this one */
1769 "0300" /* Hello Version : 0x0300 = v3 */
1770 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
1771 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
1772 "00" /* Session ID length : empty (no session ID) */
1773 "004E" /* Cipher Suite Length : 78 bytes after this one */
1774 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
1775 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
1776 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
1777 "000D" "000E" "000F" "0010" /* various bit lengths, */
1778 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
1779 "0015" "0016" "0017" "0018"
1780 "0019" "001A" "001B" "002F"
1781 "0030" "0031" "0032" "0033"
1782 "0034" "0035" "0036" "0037"
1783 "0038" "0039" "003A"
1784 "01" /* Compression Length : 0x01 = 1 byte for types */
1785 "00" /* Compression Type : 0x00 = NULL compression */
1786 };
1787
1788 struct tcpcheck_ruleset *rs = NULL;
1789 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1790 struct tcpcheck_rule *chk;
1791 char *errmsg = NULL;
1792 int err_code = 0;
1793
1794 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1795 err_code |= ERR_WARN;
1796
1797 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1798 goto out;
1799
Christopher Faulet811f78c2020-04-01 11:10:27 +02001800 curpx->options2 &= ~PR_O2_CHK_ANY;
1801 curpx->options2 |= PR_O2_TCPCHK_CHK;
1802
1803 free_tcpcheck_vars(&rules->preset_vars);
1804 rules->list = NULL;
1805 rules->flags = 0;
1806
Christopher Faulet61cc8522020-04-20 14:54:42 +02001807 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001808 if (rs)
1809 goto ruleset_found;
1810
Christopher Faulet61cc8522020-04-20 14:54:42 +02001811 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001812 if (rs == NULL) {
1813 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1814 goto error;
1815 }
1816
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001817 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02001818 1, curpx, &rs->rules, file, line, &errmsg);
1819 if (!chk) {
1820 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1821 goto error;
1822 }
1823 chk->index = 0;
1824 LIST_ADDQ(&rs->rules, &chk->list);
1825
1826 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02001827 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02001828 "error-status", "L6RSP", "tout-status", "L6TOUT",
1829 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001830 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001831 if (!chk) {
1832 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1833 goto error;
1834 }
1835 chk->index = 1;
1836 LIST_ADDQ(&rs->rules, &chk->list);
1837
Christopher Faulet811f78c2020-04-01 11:10:27 +02001838 ruleset_found:
1839 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001840 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001841 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02001842
1843 out:
1844 free(errmsg);
1845 return err_code;
1846
1847 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001848 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001849 err_code |= ERR_ALERT | ERR_FATAL;
1850 goto out;
1851}
1852
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001853/* Parses the "option smtpchk" proxy keyword */
1854int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1855 const char *file, int line)
1856{
1857 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
1858
1859 struct tcpcheck_ruleset *rs = NULL;
1860 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1861 struct tcpcheck_rule *chk;
1862 struct tcpcheck_var *var = NULL;
1863 char *cmd = NULL, *errmsg = NULL;
1864 int err_code = 0;
1865
1866 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1867 err_code |= ERR_WARN;
1868
1869 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1870 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001871
1872 curpx->options2 &= ~PR_O2_CHK_ANY;
1873 curpx->options2 |= PR_O2_TCPCHK_CHK;
1874
1875 free_tcpcheck_vars(&rules->preset_vars);
1876 rules->list = NULL;
1877 rules->flags = 0;
1878
1879 cur_arg += 2;
1880 if (*args[cur_arg] && *args[cur_arg+1] &&
1881 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
Tim Duesterhus2867b402020-06-12 15:58:48 +02001882 /* <EHLO|HELO> + space (1) + <host> + null byte (1) */
1883 cmd = calloc(strlen(args[cur_arg]) + 1 + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001884 if (cmd)
1885 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
1886 }
1887 else {
1888 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
1889 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
1890 cmd = strdup("HELO localhost");
1891 }
1892
Christopher Fauletb61caf42020-04-21 10:57:42 +02001893 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001894 if (cmd == NULL || var == NULL) {
1895 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1896 goto error;
1897 }
1898 var->data.type = SMP_T_STR;
1899 var->data.u.str.area = cmd;
1900 var->data.u.str.data = strlen(cmd);
1901 LIST_INIT(&var->list);
1902 LIST_ADDQ(&rules->preset_vars, &var->list);
1903 cmd = NULL;
1904 var = NULL;
1905
Christopher Faulet61cc8522020-04-20 14:54:42 +02001906 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001907 if (rs)
1908 goto ruleset_found;
1909
Christopher Faulet61cc8522020-04-20 14:54:42 +02001910 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001911 if (rs == NULL) {
1912 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1913 goto error;
1914 }
1915
1916 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
1917 1, curpx, &rs->rules, file, line, &errmsg);
1918 if (!chk) {
1919 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1920 goto error;
1921 }
1922 chk->index = 0;
1923 LIST_ADDQ(&rs->rules, &chk->list);
1924
1925 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
1926 "min-recv", "4",
1927 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02001928 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001929 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001930 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001931 if (!chk) {
1932 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1933 goto error;
1934 }
1935 chk->index = 1;
1936 LIST_ADDQ(&rs->rules, &chk->list);
1937
1938 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
1939 "min-recv", "4",
1940 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001941 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1942 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001943 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001944 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001945 if (!chk) {
1946 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1947 goto error;
1948 }
1949 chk->index = 2;
1950 LIST_ADDQ(&rs->rules, &chk->list);
1951
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001952 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001953 1, curpx, &rs->rules, file, line, &errmsg);
1954 if (!chk) {
1955 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1956 goto error;
1957 }
1958 chk->index = 3;
1959 LIST_ADDQ(&rs->rules, &chk->list);
1960
1961 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
1962 "min-recv", "4",
1963 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001964 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1965 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1966 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001967 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001968 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001969 if (!chk) {
1970 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1971 goto error;
1972 }
1973 chk->index = 4;
1974 LIST_ADDQ(&rs->rules, &chk->list);
1975
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001976 ruleset_found:
1977 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001978 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001979 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001980
1981 out:
1982 free(errmsg);
1983 return err_code;
1984
1985 error:
1986 free(cmd);
1987 free(var);
1988 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001989 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001990 err_code |= ERR_ALERT | ERR_FATAL;
1991 goto out;
1992}
Christopher Faulet811f78c2020-04-01 11:10:27 +02001993
Christopher Fauletce355072020-04-02 11:44:39 +02001994/* Parses the "option pgsql-check" proxy keyword */
1995int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1996 const char *file, int line)
1997{
1998 static char pgsql_req[] = {
1999 "%[var(check.plen),htonl,hex]" /* The packet length*/
2000 "00030000" /* the version 3.0 */
2001 "7573657200" /* "user" key */
2002 "%[var(check.username),hex]00" /* the username */
2003 "00"
2004 };
2005
2006 struct tcpcheck_ruleset *rs = NULL;
2007 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2008 struct tcpcheck_rule *chk;
2009 struct tcpcheck_var *var = NULL;
2010 char *user = NULL, *errmsg = NULL;
2011 size_t packetlen = 0;
2012 int err_code = 0;
2013
2014 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2015 err_code |= ERR_WARN;
2016
2017 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
2018 goto out;
2019
Christopher Fauletce355072020-04-02 11:44:39 +02002020 curpx->options2 &= ~PR_O2_CHK_ANY;
2021 curpx->options2 |= PR_O2_TCPCHK_CHK;
2022
2023 free_tcpcheck_vars(&rules->preset_vars);
2024 rules->list = NULL;
2025 rules->flags = 0;
2026
2027 cur_arg += 2;
2028 if (!*args[cur_arg] || !*args[cur_arg+1]) {
2029 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
2030 file, line, args[0], args[1]);
2031 goto error;
2032 }
2033 if (strcmp(args[cur_arg], "user") == 0) {
2034 packetlen = 15 + strlen(args[cur_arg+1]);
2035 user = strdup(args[cur_arg+1]);
2036
Christopher Fauletb61caf42020-04-21 10:57:42 +02002037 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02002038 if (user == NULL || var == NULL) {
2039 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2040 goto error;
2041 }
2042 var->data.type = SMP_T_STR;
2043 var->data.u.str.area = user;
2044 var->data.u.str.data = strlen(user);
2045 LIST_INIT(&var->list);
2046 LIST_ADDQ(&rules->preset_vars, &var->list);
2047 user = NULL;
2048 var = NULL;
2049
Christopher Fauletb61caf42020-04-21 10:57:42 +02002050 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02002051 if (var == NULL) {
2052 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2053 goto error;
2054 }
2055 var->data.type = SMP_T_SINT;
2056 var->data.u.sint = packetlen;
2057 LIST_INIT(&var->list);
2058 LIST_ADDQ(&rules->preset_vars, &var->list);
2059 var = NULL;
2060 }
2061 else {
2062 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
2063 file, line, args[0], args[1]);
2064 goto error;
2065 }
2066
Christopher Faulet61cc8522020-04-20 14:54:42 +02002067 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002068 if (rs)
2069 goto ruleset_found;
2070
Christopher Faulet61cc8522020-04-20 14:54:42 +02002071 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002072 if (rs == NULL) {
2073 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2074 goto error;
2075 }
2076
2077 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2078 1, curpx, &rs->rules, file, line, &errmsg);
2079 if (!chk) {
2080 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2081 goto error;
2082 }
2083 chk->index = 0;
2084 LIST_ADDQ(&rs->rules, &chk->list);
2085
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002086 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02002087 1, curpx, &rs->rules, file, line, &errmsg);
2088 if (!chk) {
2089 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2090 goto error;
2091 }
2092 chk->index = 1;
2093 LIST_ADDQ(&rs->rules, &chk->list);
2094
2095 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
2096 "min-recv", "5",
2097 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02002098 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02002099 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002100 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002101 if (!chk) {
2102 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2103 goto error;
2104 }
2105 chk->index = 2;
2106 LIST_ADDQ(&rs->rules, &chk->list);
2107
Christopher Fauletb841c742020-04-27 18:29:49 +02002108 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^52000000(08|0A|0C)000000(00|02|03|04|05|06)",
Christopher Fauletce355072020-04-02 11:44:39 +02002109 "min-recv", "9",
2110 "error-status", "L7STS",
2111 "on-success", "PostgreSQL server is ok",
2112 "on-error", "PostgreSQL unknown error",
2113 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002114 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002115 if (!chk) {
2116 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2117 goto error;
2118 }
2119 chk->index = 3;
2120 LIST_ADDQ(&rs->rules, &chk->list);
2121
Christopher Fauletce355072020-04-02 11:44:39 +02002122 ruleset_found:
2123 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002124 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002125 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02002126
2127 out:
2128 free(errmsg);
2129 return err_code;
2130
2131 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002132 free(user);
2133 free(var);
2134 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002135 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002136 err_code |= ERR_ALERT | ERR_FATAL;
2137 goto out;
2138}
2139
2140
2141/* Parses the "option mysql-check" proxy keyword */
2142int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2143 const char *file, int line)
2144{
2145 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
2146 * const char mysql40_client_auth_pkt[] = {
2147 * "\x0e\x00\x00" // packet length
2148 * "\x01" // packet number
2149 * "\x00\x00" // client capabilities
2150 * "\x00\x00\x01" // max packet
2151 * "haproxy\x00" // username (null terminated string)
2152 * "\x00" // filler (always 0x00)
2153 * "\x01\x00\x00" // packet length
2154 * "\x00" // packet number
2155 * "\x01" // COM_QUIT command
2156 * };
2157 */
2158 static char mysql40_rsname[] = "*mysql40-check";
2159 static char mysql40_req[] = {
2160 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2161 "0080" /* client capabilities */
2162 "000001" /* max packet */
2163 "%[var(check.username),hex]00" /* the username */
2164 "00" /* filler (always 0x00) */
2165 "010000" /* packet length*/
2166 "00" /* sequence ID */
2167 "01" /* COM_QUIT command */
2168 };
2169
2170 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
2171 * const char mysql41_client_auth_pkt[] = {
2172 * "\x0e\x00\x00\" // packet length
2173 * "\x01" // packet number
2174 * "\x00\x00\x00\x00" // client capabilities
2175 * "\x00\x00\x00\x01" // max packet
2176 * "\x21" // character set (UTF-8)
2177 * char[23] // All zeroes
2178 * "haproxy\x00" // username (null terminated string)
2179 * "\x00" // filler (always 0x00)
2180 * "\x01\x00\x00" // packet length
2181 * "\x00" // packet number
2182 * "\x01" // COM_QUIT command
2183 * };
2184 */
2185 static char mysql41_rsname[] = "*mysql41-check";
2186 static char mysql41_req[] = {
2187 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2188 "00820000" /* client capabilities */
2189 "00800001" /* max packet */
2190 "21" /* character set (UTF-8) */
2191 "000000000000000000000000" /* 23 bytes, al zeroes */
2192 "0000000000000000000000"
2193 "%[var(check.username),hex]00" /* the username */
2194 "00" /* filler (always 0x00) */
2195 "010000" /* packet length*/
2196 "00" /* sequence ID */
2197 "01" /* COM_QUIT command */
2198 };
2199
2200 struct tcpcheck_ruleset *rs = NULL;
2201 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2202 struct tcpcheck_rule *chk;
2203 struct tcpcheck_var *var = NULL;
2204 char *mysql_rsname = "*mysql-check";
2205 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
2206 int index = 0, err_code = 0;
2207
2208 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2209 err_code |= ERR_WARN;
2210
2211 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2212 goto out;
2213
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002214 curpx->options2 &= ~PR_O2_CHK_ANY;
2215 curpx->options2 |= PR_O2_TCPCHK_CHK;
2216
2217 free_tcpcheck_vars(&rules->preset_vars);
2218 rules->list = NULL;
2219 rules->flags = 0;
2220
2221 cur_arg += 2;
2222 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002223 int packetlen, userlen;
2224
2225 if (strcmp(args[cur_arg], "user") != 0) {
2226 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
2227 file, line, args[0], args[1], args[cur_arg]);
2228 goto error;
2229 }
2230
2231 if (*(args[cur_arg+1]) == 0) {
2232 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
2233 file, line, args[0], args[1], args[cur_arg]);
2234 goto error;
2235 }
2236
2237 hdr = calloc(4, sizeof(*hdr));
2238 user = strdup(args[cur_arg+1]);
2239 userlen = strlen(args[cur_arg+1]);
2240
2241 if (hdr == NULL || user == NULL) {
2242 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2243 goto error;
2244 }
2245
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002246 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002247 packetlen = userlen + 7 + 27;
2248 mysql_req = mysql41_req;
2249 mysql_rsname = mysql41_rsname;
2250 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002251 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002252 packetlen = userlen + 7;
2253 mysql_req = mysql40_req;
2254 mysql_rsname = mysql40_rsname;
2255 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002256 else {
2257 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
2258 file, line, args[cur_arg], args[cur_arg+2]);
2259 goto error;
2260 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002261
2262 hdr[0] = (unsigned char)(packetlen & 0xff);
2263 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
2264 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
2265 hdr[3] = 1;
2266
Christopher Fauletb61caf42020-04-21 10:57:42 +02002267 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002268 if (var == NULL) {
2269 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2270 goto error;
2271 }
2272 var->data.type = SMP_T_STR;
2273 var->data.u.str.area = hdr;
2274 var->data.u.str.data = 4;
2275 LIST_INIT(&var->list);
2276 LIST_ADDQ(&rules->preset_vars, &var->list);
2277 hdr = NULL;
2278 var = NULL;
2279
Christopher Fauletb61caf42020-04-21 10:57:42 +02002280 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002281 if (var == NULL) {
2282 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2283 goto error;
2284 }
2285 var->data.type = SMP_T_STR;
2286 var->data.u.str.area = user;
2287 var->data.u.str.data = strlen(user);
2288 LIST_INIT(&var->list);
2289 LIST_ADDQ(&rules->preset_vars, &var->list);
2290 user = NULL;
2291 var = NULL;
2292 }
2293
Christopher Faulet61cc8522020-04-20 14:54:42 +02002294 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002295 if (rs)
2296 goto ruleset_found;
2297
Christopher Faulet61cc8522020-04-20 14:54:42 +02002298 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002299 if (rs == NULL) {
2300 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2301 goto error;
2302 }
2303
2304 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2305 1, curpx, &rs->rules, file, line, &errmsg);
2306 if (!chk) {
2307 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2308 goto error;
2309 }
2310 chk->index = index++;
2311 LIST_ADDQ(&rs->rules, &chk->list);
2312
2313 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002314 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002315 1, curpx, &rs->rules, file, line, &errmsg);
2316 if (!chk) {
2317 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2318 goto error;
2319 }
2320 chk->index = index++;
2321 LIST_ADDQ(&rs->rules, &chk->list);
2322 }
2323
2324 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002325 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002326 if (!chk) {
2327 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2328 goto error;
2329 }
2330 chk->expect.custom = tcpcheck_mysql_expect_iniths;
2331 chk->index = index++;
2332 LIST_ADDQ(&rs->rules, &chk->list);
2333
2334 if (mysql_req) {
2335 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002336 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002337 if (!chk) {
2338 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2339 goto error;
2340 }
2341 chk->expect.custom = tcpcheck_mysql_expect_ok;
2342 chk->index = index++;
2343 LIST_ADDQ(&rs->rules, &chk->list);
2344 }
2345
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002346 ruleset_found:
2347 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002348 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002349 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002350
2351 out:
2352 free(errmsg);
2353 return err_code;
2354
2355 error:
2356 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02002357 free(user);
2358 free(var);
2359 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002360 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02002361 err_code |= ERR_ALERT | ERR_FATAL;
2362 goto out;
2363}
2364
Christopher Faulet1997eca2020-04-03 23:13:50 +02002365int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2366 const char *file, int line)
2367{
2368 static char *ldap_req = "300C020101600702010304008000";
2369
2370 struct tcpcheck_ruleset *rs = NULL;
2371 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2372 struct tcpcheck_rule *chk;
2373 char *errmsg = NULL;
2374 int err_code = 0;
2375
2376 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2377 err_code |= ERR_WARN;
2378
2379 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2380 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002381
2382 curpx->options2 &= ~PR_O2_CHK_ANY;
2383 curpx->options2 |= PR_O2_TCPCHK_CHK;
2384
2385 free_tcpcheck_vars(&rules->preset_vars);
2386 rules->list = NULL;
2387 rules->flags = 0;
2388
Christopher Faulet61cc8522020-04-20 14:54:42 +02002389 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002390 if (rs)
2391 goto ruleset_found;
2392
Christopher Faulet61cc8522020-04-20 14:54:42 +02002393 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002394 if (rs == NULL) {
2395 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2396 goto error;
2397 }
2398
2399 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
2400 1, curpx, &rs->rules, file, line, &errmsg);
2401 if (!chk) {
2402 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2403 goto error;
2404 }
2405 chk->index = 0;
2406 LIST_ADDQ(&rs->rules, &chk->list);
2407
2408 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
2409 "min-recv", "14",
2410 "on-error", "Not LDAPv3 protocol",
2411 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002412 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002413 if (!chk) {
2414 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2415 goto error;
2416 }
2417 chk->index = 1;
2418 LIST_ADDQ(&rs->rules, &chk->list);
2419
2420 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002421 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002422 if (!chk) {
2423 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2424 goto error;
2425 }
2426 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
2427 chk->index = 2;
2428 LIST_ADDQ(&rs->rules, &chk->list);
2429
Christopher Faulet1997eca2020-04-03 23:13:50 +02002430 ruleset_found:
2431 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002432 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002433 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002434
2435 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02002436 free(errmsg);
2437 return err_code;
2438
2439 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002440 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002441 err_code |= ERR_ALERT | ERR_FATAL;
2442 goto out;
2443}
2444
2445int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2446 const char *file, int line)
2447{
2448 struct tcpcheck_ruleset *rs = NULL;
2449 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2450 struct tcpcheck_rule *chk;
2451 char *spop_req = NULL;
2452 char *errmsg = NULL;
2453 int spop_len = 0, err_code = 0;
2454
2455 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2456 err_code |= ERR_WARN;
2457
2458 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2459 goto out;
2460
Christopher Faulet267b01b2020-04-04 10:27:09 +02002461 curpx->options2 &= ~PR_O2_CHK_ANY;
2462 curpx->options2 |= PR_O2_TCPCHK_CHK;
2463
2464 free_tcpcheck_vars(&rules->preset_vars);
2465 rules->list = NULL;
2466 rules->flags = 0;
2467
2468
Christopher Faulet61cc8522020-04-20 14:54:42 +02002469 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002470 if (rs)
2471 goto ruleset_found;
2472
Christopher Faulet61cc8522020-04-20 14:54:42 +02002473 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002474 if (rs == NULL) {
2475 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2476 goto error;
2477 }
2478
2479 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
2480 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2481 goto error;
2482 }
2483 chunk_reset(&trash);
2484 dump_binary(&trash, spop_req, spop_len);
2485 trash.area[trash.data] = '\0';
2486
2487 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
2488 1, curpx, &rs->rules, file, line, &errmsg);
2489 if (!chk) {
2490 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2491 goto error;
2492 }
2493 chk->index = 0;
2494 LIST_ADDQ(&rs->rules, &chk->list);
2495
2496 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002497 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002498 if (!chk) {
2499 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2500 goto error;
2501 }
2502 chk->expect.custom = tcpcheck_spop_expect_agenthello;
2503 chk->index = 1;
2504 LIST_ADDQ(&rs->rules, &chk->list);
2505
Christopher Faulet267b01b2020-04-04 10:27:09 +02002506 ruleset_found:
2507 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002508 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002509 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002510
2511 out:
2512 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002513 free(errmsg);
2514 return err_code;
2515
2516 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002517 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002518 err_code |= ERR_ALERT | ERR_FATAL;
2519 goto out;
2520}
Christopher Fauletce355072020-04-02 11:44:39 +02002521
Christopher Faulete5870d82020-04-15 11:32:03 +02002522
2523struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
2524{
2525 struct tcpcheck_rule *chk = NULL;
2526 struct tcpcheck_http_hdr *hdr = NULL;
2527 char *meth = NULL, *uri = NULL, *vsn = NULL;
2528 char *hdrs, *body;
2529
2530 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
2531 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
2532 if (hdrs == body)
2533 hdrs = NULL;
2534 if (hdrs) {
2535 *hdrs = '\0';
2536 hdrs +=2;
2537 }
2538 if (body) {
2539 *body = '\0';
2540 body += 4;
2541 }
2542 if (hdrs || body) {
2543 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
2544 " Please, consider to use 'http-check send' directive instead.");
2545 }
2546
2547 chk = calloc(1, sizeof(*chk));
2548 if (!chk) {
2549 memprintf(errmsg, "out of memory");
2550 goto error;
2551 }
2552 chk->action = TCPCHK_ACT_SEND;
2553 chk->send.type = TCPCHK_SEND_HTTP;
2554 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
2555 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
2556 LIST_INIT(&chk->send.http.hdrs);
2557
2558 /* Copy the method, uri and version */
2559 if (*args[cur_arg]) {
2560 if (!*args[cur_arg+1])
2561 uri = args[cur_arg];
2562 else
2563 meth = args[cur_arg];
2564 }
2565 if (*args[cur_arg+1])
2566 uri = args[cur_arg+1];
2567 if (*args[cur_arg+2])
2568 vsn = args[cur_arg+2];
2569
2570 if (meth) {
2571 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
2572 chk->send.http.meth.str.area = strdup(meth);
2573 chk->send.http.meth.str.data = strlen(meth);
2574 if (!chk->send.http.meth.str.area) {
2575 memprintf(errmsg, "out of memory");
2576 goto error;
2577 }
2578 }
2579 if (uri) {
2580 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002581 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002582 memprintf(errmsg, "out of memory");
2583 goto error;
2584 }
2585 }
2586 if (vsn) {
2587 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002588 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002589 memprintf(errmsg, "out of memory");
2590 goto error;
2591 }
2592 }
2593
2594 /* Copy the header */
2595 if (hdrs) {
2596 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
2597 struct h1m h1m;
2598 int i, ret;
2599
2600 /* Build and parse the request */
2601 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
2602
2603 h1m.flags = H1_MF_HDRS_ONLY;
2604 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
2605 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
2606 &h1m, NULL);
2607 if (ret <= 0) {
2608 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
2609 goto error;
2610 }
2611
Christopher Fauletb61caf42020-04-21 10:57:42 +02002612 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002613 hdr = calloc(1, sizeof(*hdr));
2614 if (!hdr) {
2615 memprintf(errmsg, "out of memory");
2616 goto error;
2617 }
2618 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002619 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02002620 if (!hdr->name.ptr) {
2621 memprintf(errmsg, "out of memory");
2622 goto error;
2623 }
2624
Christopher Fauletb61caf42020-04-21 10:57:42 +02002625 ist0(tmp_hdrs[i].v);
2626 if (!parse_logformat_string(istptr(tmp_hdrs[i].v), px, &hdr->value, 0, SMP_VAL_BE_CHK_RUL, errmsg))
Christopher Faulete5870d82020-04-15 11:32:03 +02002627 goto error;
2628 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
2629 }
2630 }
2631
2632 /* Copy the body */
2633 if (body) {
2634 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002635 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002636 memprintf(errmsg, "out of memory");
2637 goto error;
2638 }
2639 }
2640
2641 return chk;
2642
2643 error:
2644 free_tcpcheck_http_hdr(hdr);
2645 free_tcpcheck(chk, 0);
2646 return NULL;
2647}
2648
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002649int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2650 const char *file, int line)
2651{
Christopher Faulete5870d82020-04-15 11:32:03 +02002652 struct tcpcheck_ruleset *rs = NULL;
2653 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2654 struct tcpcheck_rule *chk;
2655 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002656 int err_code = 0;
2657
2658 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2659 err_code |= ERR_WARN;
2660
2661 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2662 goto out;
2663
Christopher Faulete5870d82020-04-15 11:32:03 +02002664 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
2665 if (!chk) {
2666 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2667 goto error;
2668 }
2669 if (errmsg) {
2670 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
2671 err_code |= ERR_WARN;
2672 free(errmsg);
2673 errmsg = NULL;
2674 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002675
Christopher Faulete5870d82020-04-15 11:32:03 +02002676 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002677 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02002678 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002679
Christopher Faulete5870d82020-04-15 11:32:03 +02002680 free_tcpcheck_vars(&rules->preset_vars);
2681 rules->list = NULL;
2682 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002683
Christopher Faulete5870d82020-04-15 11:32:03 +02002684 /* Deduce the ruleset name from the proxy info */
2685 chunk_printf(&trash, "*http-check-%s_%s-%d",
2686 ((curpx == defpx) ? "defaults" : curpx->id),
2687 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002688
Christopher Faulet61cc8522020-04-20 14:54:42 +02002689 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002690 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002691 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002692 if (rs == NULL) {
2693 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2694 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002695 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002696 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002697
Christopher Faulete5870d82020-04-15 11:32:03 +02002698 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002699 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulete5870d82020-04-15 11:32:03 +02002700 rules->flags |= TCPCHK_RULES_HTTP_CHK;
2701 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
2702 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2703 rules->list = NULL;
2704 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002705 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002706
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002707 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02002708 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002709 return err_code;
2710
2711 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002712 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02002713 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002714 err_code |= ERR_ALERT | ERR_FATAL;
2715 goto out;
2716}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002717
Christopher Fauletce8111e2020-04-06 15:04:11 +02002718/* Parse the "addr" server keyword */
2719static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2720 char **errmsg)
2721{
2722 struct sockaddr_storage *sk;
Christopher Fauletce8111e2020-04-06 15:04:11 +02002723 int port1, port2, err_code = 0;
2724
2725
2726 if (!*args[*cur_arg+1]) {
2727 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
2728 goto error;
2729 }
2730
Willy Tarreau65ec4e32020-09-16 19:17:08 +02002731 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, NULL, errmsg, NULL, NULL,
2732 PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
Christopher Fauletce8111e2020-04-06 15:04:11 +02002733 if (!sk) {
2734 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
2735 goto error;
2736 }
2737
Christopher Fauletce8111e2020-04-06 15:04:11 +02002738 srv->check.addr = srv->agent.addr = *sk;
2739 srv->flags |= SRV_F_CHECKADDR;
2740 srv->flags |= SRV_F_AGENTADDR;
2741
2742 out:
2743 return err_code;
2744
2745 error:
2746 err_code |= ERR_ALERT | ERR_FATAL;
2747 goto out;
2748}
2749
2750
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002751/* Parse the "agent-addr" server keyword */
2752static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2753 char **errmsg)
2754{
2755 int err_code = 0;
2756
2757 if (!*(args[*cur_arg+1])) {
2758 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
2759 goto error;
2760 }
2761 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
2762 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
2763 goto error;
2764 }
2765
2766 out:
2767 return err_code;
2768
2769 error:
2770 err_code |= ERR_ALERT | ERR_FATAL;
2771 goto out;
2772}
2773
2774/* Parse the "agent-check" server keyword */
2775static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2776 char **errmsg)
2777{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002778 struct tcpcheck_ruleset *rs = NULL;
2779 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2780 struct tcpcheck_rule *chk;
2781 int err_code = 0;
2782
2783 if (srv->do_agent)
2784 goto out;
2785
Christopher Faulet0a5e7132021-01-12 17:29:45 +01002786 if (!(curpx->cap & PR_CAP_BE)) {
2787 memprintf(errmsg, "'%s' ignored because %s '%s' has no backend capability",
2788 args[*cur_arg], proxy_type_str(curpx), curpx->id);
2789 return ERR_WARN;
2790 }
2791
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002792 if (!rules) {
2793 rules = calloc(1, sizeof(*rules));
2794 if (!rules) {
2795 memprintf(errmsg, "out of memory.");
2796 goto error;
2797 }
2798 LIST_INIT(&rules->preset_vars);
2799 srv->agent.tcpcheck_rules = rules;
2800 }
2801 rules->list = NULL;
2802 rules->flags = 0;
2803
Christopher Faulet61cc8522020-04-20 14:54:42 +02002804 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002805 if (rs)
2806 goto ruleset_found;
2807
Christopher Faulet61cc8522020-04-20 14:54:42 +02002808 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002809 if (rs == NULL) {
2810 memprintf(errmsg, "out of memory.");
2811 goto error;
2812 }
2813
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002814 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002815 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
2816 if (!chk) {
2817 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2818 goto error;
2819 }
2820 chk->index = 0;
2821 LIST_ADDQ(&rs->rules, &chk->list);
2822
2823 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002824 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
2825 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002826 if (!chk) {
2827 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2828 goto error;
2829 }
2830 chk->expect.custom = tcpcheck_agent_expect_reply;
2831 chk->index = 1;
2832 LIST_ADDQ(&rs->rules, &chk->list);
2833
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002834 ruleset_found:
2835 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002836 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002837 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002838 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002839
2840 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002841 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002842
2843 error:
2844 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002845 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002846 err_code |= ERR_ALERT | ERR_FATAL;
2847 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002848}
2849
2850/* Parse the "agent-inter" server keyword */
2851static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2852 char **errmsg)
2853{
2854 const char *err = NULL;
2855 unsigned int delay;
2856 int err_code = 0;
2857
2858 if (!*(args[*cur_arg+1])) {
2859 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
2860 goto error;
2861 }
2862
2863 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
2864 if (err == PARSE_TIME_OVER) {
2865 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
2866 args[*cur_arg+1], args[*cur_arg], srv->id);
2867 goto error;
2868 }
2869 else if (err == PARSE_TIME_UNDER) {
2870 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
2871 args[*cur_arg+1], args[*cur_arg], srv->id);
2872 goto error;
2873 }
2874 else if (err) {
2875 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
2876 *err, srv->id);
2877 goto error;
2878 }
2879 if (delay <= 0) {
2880 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
2881 delay, args[*cur_arg], srv->id);
2882 goto error;
2883 }
2884 srv->agent.inter = delay;
2885
2886 out:
2887 return err_code;
2888
2889 error:
2890 err_code |= ERR_ALERT | ERR_FATAL;
2891 goto out;
2892}
2893
2894/* Parse the "agent-port" server keyword */
2895static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2896 char **errmsg)
2897{
2898 int err_code = 0;
2899
2900 if (!*(args[*cur_arg+1])) {
2901 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
2902 goto error;
2903 }
2904
2905 global.maxsock++;
2906 srv->agent.port = atol(args[*cur_arg+1]);
2907
2908 out:
2909 return err_code;
2910
2911 error:
2912 err_code |= ERR_ALERT | ERR_FATAL;
2913 goto out;
2914}
2915
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002916int set_srv_agent_send(struct server *srv, const char *send)
2917{
2918 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2919 struct tcpcheck_var *var = NULL;
2920 char *str;
2921
2922 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002923 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002924 if (str == NULL || var == NULL)
2925 goto error;
2926
2927 free_tcpcheck_vars(&rules->preset_vars);
2928
2929 var->data.type = SMP_T_STR;
2930 var->data.u.str.area = str;
2931 var->data.u.str.data = strlen(str);
2932 LIST_INIT(&var->list);
2933 LIST_ADDQ(&rules->preset_vars, &var->list);
2934
2935 return 1;
2936
2937 error:
2938 free(str);
2939 free(var);
2940 return 0;
2941}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002942
2943/* Parse the "agent-send" server keyword */
2944static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2945 char **errmsg)
2946{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002947 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002948 int err_code = 0;
2949
2950 if (!*(args[*cur_arg+1])) {
2951 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
2952 goto error;
2953 }
2954
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002955 if (!rules) {
2956 rules = calloc(1, sizeof(*rules));
2957 if (!rules) {
2958 memprintf(errmsg, "out of memory.");
2959 goto error;
2960 }
2961 LIST_INIT(&rules->preset_vars);
2962 srv->agent.tcpcheck_rules = rules;
2963 }
2964
2965 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002966 memprintf(errmsg, "out of memory.");
2967 goto error;
2968 }
2969
2970 out:
2971 return err_code;
2972
2973 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002974 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002975 err_code |= ERR_ALERT | ERR_FATAL;
2976 goto out;
2977}
2978
2979/* Parse the "no-agent-send" server keyword */
2980static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2981 char **errmsg)
2982{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002983 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002984 return 0;
2985}
2986
Christopher Fauletce8111e2020-04-06 15:04:11 +02002987/* Parse the "check" server keyword */
2988static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2989 char **errmsg)
2990{
Christopher Faulet0a5e7132021-01-12 17:29:45 +01002991 if (!(curpx->cap & PR_CAP_BE)) {
2992 memprintf(errmsg, "'%s' ignored because %s '%s' has no backend capability",
2993 args[*cur_arg], proxy_type_str(curpx), curpx->id);
2994 return ERR_WARN;
2995 }
2996
Christopher Fauletce8111e2020-04-06 15:04:11 +02002997 srv->do_check = 1;
2998 return 0;
2999}
3000
3001/* Parse the "check-send-proxy" server keyword */
3002static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3003 char **errmsg)
3004{
3005 srv->check.send_proxy = 1;
3006 return 0;
3007}
3008
3009/* Parse the "check-via-socks4" server keyword */
3010static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3011 char **errmsg)
3012{
3013 srv->check.via_socks4 = 1;
3014 return 0;
3015}
3016
3017/* Parse the "no-check" server keyword */
3018static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3019 char **errmsg)
3020{
3021 deinit_srv_check(srv);
3022 return 0;
3023}
3024
3025/* Parse the "no-check-send-proxy" server keyword */
3026static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3027 char **errmsg)
3028{
3029 srv->check.send_proxy = 0;
3030 return 0;
3031}
3032
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003033/* parse the "check-proto" server keyword */
3034static int srv_parse_check_proto(char **args, int *cur_arg,
3035 struct proxy *px, struct server *newsrv, char **err)
3036{
3037 int err_code = 0;
3038
3039 if (!*args[*cur_arg + 1]) {
3040 memprintf(err, "'%s' : missing value", args[*cur_arg]);
3041 goto error;
3042 }
3043 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
3044 if (!newsrv->check.mux_proto) {
3045 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
3046 goto error;
3047 }
3048
3049 out:
3050 return err_code;
3051
3052 error:
3053 err_code |= ERR_ALERT | ERR_FATAL;
3054 goto out;
3055}
3056
3057
Christopher Fauletce8111e2020-04-06 15:04:11 +02003058/* Parse the "rise" server keyword */
3059static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3060 char **errmsg)
3061{
3062 int err_code = 0;
3063
3064 if (!*args[*cur_arg + 1]) {
3065 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3066 goto error;
3067 }
3068
3069 srv->check.rise = atol(args[*cur_arg+1]);
3070 if (srv->check.rise <= 0) {
3071 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3072 goto error;
3073 }
3074
3075 if (srv->check.health)
3076 srv->check.health = srv->check.rise;
3077
3078 out:
3079 return err_code;
3080
3081 error:
3082 deinit_srv_agent_check(srv);
3083 err_code |= ERR_ALERT | ERR_FATAL;
3084 goto out;
3085 return 0;
3086}
3087
3088/* Parse the "fall" server keyword */
3089static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3090 char **errmsg)
3091{
3092 int err_code = 0;
3093
3094 if (!*args[*cur_arg + 1]) {
3095 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3096 goto error;
3097 }
3098
3099 srv->check.fall = atol(args[*cur_arg+1]);
3100 if (srv->check.fall <= 0) {
3101 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3102 goto error;
3103 }
3104
3105 out:
3106 return err_code;
3107
3108 error:
3109 deinit_srv_agent_check(srv);
3110 err_code |= ERR_ALERT | ERR_FATAL;
3111 goto out;
3112 return 0;
3113}
3114
3115/* Parse the "inter" server keyword */
3116static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3117 char **errmsg)
3118{
3119 const char *err = NULL;
3120 unsigned int delay;
3121 int err_code = 0;
3122
3123 if (!*(args[*cur_arg+1])) {
3124 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3125 goto error;
3126 }
3127
3128 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3129 if (err == PARSE_TIME_OVER) {
3130 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3131 args[*cur_arg+1], args[*cur_arg], srv->id);
3132 goto error;
3133 }
3134 else if (err == PARSE_TIME_UNDER) {
3135 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3136 args[*cur_arg+1], args[*cur_arg], srv->id);
3137 goto error;
3138 }
3139 else if (err) {
3140 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3141 *err, srv->id);
3142 goto error;
3143 }
3144 if (delay <= 0) {
3145 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3146 delay, args[*cur_arg], srv->id);
3147 goto error;
3148 }
3149 srv->check.inter = delay;
3150
3151 out:
3152 return err_code;
3153
3154 error:
3155 err_code |= ERR_ALERT | ERR_FATAL;
3156 goto out;
3157}
3158
3159
3160/* Parse the "fastinter" server keyword */
3161static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3162 char **errmsg)
3163{
3164 const char *err = NULL;
3165 unsigned int delay;
3166 int err_code = 0;
3167
3168 if (!*(args[*cur_arg+1])) {
3169 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3170 goto error;
3171 }
3172
3173 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3174 if (err == PARSE_TIME_OVER) {
3175 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3176 args[*cur_arg+1], args[*cur_arg], srv->id);
3177 goto error;
3178 }
3179 else if (err == PARSE_TIME_UNDER) {
3180 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3181 args[*cur_arg+1], args[*cur_arg], srv->id);
3182 goto error;
3183 }
3184 else if (err) {
3185 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3186 *err, srv->id);
3187 goto error;
3188 }
3189 if (delay <= 0) {
3190 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3191 delay, args[*cur_arg], srv->id);
3192 goto error;
3193 }
3194 srv->check.fastinter = delay;
3195
3196 out:
3197 return err_code;
3198
3199 error:
3200 err_code |= ERR_ALERT | ERR_FATAL;
3201 goto out;
3202}
3203
3204
3205/* Parse the "downinter" server keyword */
3206static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3207 char **errmsg)
3208{
3209 const char *err = NULL;
3210 unsigned int delay;
3211 int err_code = 0;
3212
3213 if (!*(args[*cur_arg+1])) {
3214 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3215 goto error;
3216 }
3217
3218 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3219 if (err == PARSE_TIME_OVER) {
3220 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3221 args[*cur_arg+1], args[*cur_arg], srv->id);
3222 goto error;
3223 }
3224 else if (err == PARSE_TIME_UNDER) {
3225 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3226 args[*cur_arg+1], args[*cur_arg], srv->id);
3227 goto error;
3228 }
3229 else if (err) {
3230 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3231 *err, srv->id);
3232 goto error;
3233 }
3234 if (delay <= 0) {
3235 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3236 delay, args[*cur_arg], srv->id);
3237 goto error;
3238 }
3239 srv->check.downinter = delay;
3240
3241 out:
3242 return err_code;
3243
3244 error:
3245 err_code |= ERR_ALERT | ERR_FATAL;
3246 goto out;
3247}
3248
3249/* Parse the "port" server keyword */
3250static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3251 char **errmsg)
3252{
3253 int err_code = 0;
3254
3255 if (!*(args[*cur_arg+1])) {
3256 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
3257 goto error;
3258 }
3259
3260 global.maxsock++;
3261 srv->check.port = atol(args[*cur_arg+1]);
3262 srv->flags |= SRV_F_CHECKPORT;
3263
3264 out:
3265 return err_code;
3266
3267 error:
3268 err_code |= ERR_ALERT | ERR_FATAL;
3269 goto out;
3270}
3271
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003272static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02003273 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003274 { 0, NULL, NULL },
3275}};
3276
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003277static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02003278 { "addr", srv_parse_addr, 1, 1 }, /* IP address to send health to or to probe from agent-check */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003279 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
3280 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
3281 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
3282 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
3283 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003284 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003285 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003286 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
3287 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003288 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003289 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
3290 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
3291 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
3292 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
3293 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
3294 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
3295 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
3296 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003297 { NULL, NULL, 0 },
3298}};
3299
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003300INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003301INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003302
Willy Tarreaubd741542010-03-16 18:46:54 +01003303/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02003304 * Local variables:
3305 * c-indent-level: 8
3306 * c-basic-offset: 8
3307 * End:
3308 */