blob: 7226defbc76e55dc36e585e759c3c069037c69c7 [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);
1032 offer_buffers(check->buf_wait.target, tasks_run_queue);
1033 }
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;
1558 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001559
Christopher Faulete5870d82020-04-15 11:32:03 +02001560 if (strcmp(args[cur_arg], "connect") == 0)
1561 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1562 else if (strcmp(args[cur_arg], "send") == 0)
1563 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1564 else if (strcmp(args[cur_arg], "expect") == 0)
1565 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
1566 file, line, errmsg);
1567 else if (strcmp(args[cur_arg], "comment") == 0)
1568 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1569 else {
1570 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001571
Christopher Faulete5870d82020-04-15 11:32:03 +02001572 if (!kw) {
1573 action_kw_tcp_check_build_list(&trash);
1574 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
1575 " 'send', 'expect'%s%s. but got '%s'",
1576 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
1577 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001578 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001579 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
1580 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001581
Christopher Faulete5870d82020-04-15 11:32:03 +02001582 if (!chk) {
1583 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1584 goto error;
1585 }
1586 ret = (*errmsg != NULL); /* Handle warning */
1587
1588 chk->index = index;
1589 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
1590 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
1591 /* Use this ruleset if the proxy already has http-check enabled */
1592 curpx->tcpcheck_rules.list = &rs->rules;
1593 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
1594 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
1595 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1596 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001597 goto error;
1598 }
1599 }
1600 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02001601 /* mark this ruleset as unused for now */
1602 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
1603 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001604 }
1605
Christopher Faulete5870d82020-04-15 11:32:03 +02001606 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02001607 return ret;
1608
1609 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02001610 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001611 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001612 return -1;
1613}
1614
Christopher Faulet430e4802020-04-09 15:28:16 +02001615/* Parses the "option tcp-check" proxy keyword */
1616int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1617 const char *file, int line)
1618{
Christopher Faulet404f9192020-04-09 23:13:54 +02001619 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02001620 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1621 int err_code = 0;
1622
1623 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1624 err_code |= ERR_WARN;
1625
1626 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1627 goto out;
1628
Christopher Faulet404f9192020-04-09 23:13:54 +02001629 curpx->options2 &= ~PR_O2_CHK_ANY;
1630 curpx->options2 |= PR_O2_TCPCHK_CHK;
1631
Christopher Fauletd7e63962020-04-17 20:15:59 +02001632 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02001633 /* If a tcp-check rulesset is already set, do nothing */
1634 if (rules->list)
1635 goto out;
1636
1637 /* If a tcp-check ruleset is waiting to be used for the current proxy,
1638 * get it.
1639 */
1640 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
1641 goto curpx_ruleset;
1642
1643 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
1644 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001645 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001646 if (rs)
1647 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02001648 }
1649
Christopher Faulet404f9192020-04-09 23:13:54 +02001650 curpx_ruleset:
1651 /* Deduce the ruleset name from the proxy info */
1652 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
1653 ((curpx == defpx) ? "defaults" : curpx->id),
1654 curpx->conf.file, curpx->conf.line);
1655
Christopher Faulet61cc8522020-04-20 14:54:42 +02001656 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001657 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001658 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001659 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02001660 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1661 goto error;
1662 }
Christopher Faulet430e4802020-04-09 15:28:16 +02001663 }
1664
Christopher Faulet404f9192020-04-09 23:13:54 +02001665 ruleset_found:
1666 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02001667 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001668 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Fauletd7e63962020-04-17 20:15:59 +02001669 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02001670
1671 out:
1672 return err_code;
1673
1674 error:
1675 err_code |= ERR_ALERT | ERR_FATAL;
1676 goto out;
1677}
Christopher Faulet33f05df2020-04-01 11:08:50 +02001678
1679/* Parses the "option redis-check" proxy keyword */
1680int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1681 const char *file, int line)
1682{
1683 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
1684 static char *redis_res = "+PONG\r\n";
1685
1686 struct tcpcheck_ruleset *rs = NULL;
1687 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1688 struct tcpcheck_rule *chk;
1689 char *errmsg = NULL;
1690 int err_code = 0;
1691
1692 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1693 err_code |= ERR_WARN;
1694
1695 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1696 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001697
1698 curpx->options2 &= ~PR_O2_CHK_ANY;
1699 curpx->options2 |= PR_O2_TCPCHK_CHK;
1700
1701 free_tcpcheck_vars(&rules->preset_vars);
1702 rules->list = NULL;
1703 rules->flags = 0;
1704
Christopher Faulet61cc8522020-04-20 14:54:42 +02001705 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001706 if (rs)
1707 goto ruleset_found;
1708
Christopher Faulet61cc8522020-04-20 14:54:42 +02001709 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001710 if (rs == NULL) {
1711 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1712 goto error;
1713 }
1714
1715 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
1716 1, curpx, &rs->rules, file, line, &errmsg);
1717 if (!chk) {
1718 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1719 goto error;
1720 }
1721 chk->index = 0;
1722 LIST_ADDQ(&rs->rules, &chk->list);
1723
1724 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
1725 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001726 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02001727 "on-success", "Redis server is ok",
1728 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001729 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001730 if (!chk) {
1731 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1732 goto error;
1733 }
1734 chk->index = 1;
1735 LIST_ADDQ(&rs->rules, &chk->list);
1736
Christopher Faulet33f05df2020-04-01 11:08:50 +02001737 ruleset_found:
1738 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001739 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001740 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001741
1742 out:
1743 free(errmsg);
1744 return err_code;
1745
1746 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001747 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001748 err_code |= ERR_ALERT | ERR_FATAL;
1749 goto out;
1750}
1751
Christopher Faulet811f78c2020-04-01 11:10:27 +02001752
1753/* Parses the "option ssl-hello-chk" proxy keyword */
1754int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1755 const char *file, int line)
1756{
1757 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
1758 * ssl-hello-chk option to ensure that the remote server speaks SSL.
1759 *
1760 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
1761 */
1762 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001763 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02001764 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
1765 "0079" /* ContentLength : 0x79 bytes after this one */
1766 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
1767 "000075" /* HandshakeLength : 0x75 bytes after this one */
1768 "0300" /* Hello Version : 0x0300 = v3 */
1769 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
1770 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
1771 "00" /* Session ID length : empty (no session ID) */
1772 "004E" /* Cipher Suite Length : 78 bytes after this one */
1773 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
1774 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
1775 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
1776 "000D" "000E" "000F" "0010" /* various bit lengths, */
1777 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
1778 "0015" "0016" "0017" "0018"
1779 "0019" "001A" "001B" "002F"
1780 "0030" "0031" "0032" "0033"
1781 "0034" "0035" "0036" "0037"
1782 "0038" "0039" "003A"
1783 "01" /* Compression Length : 0x01 = 1 byte for types */
1784 "00" /* Compression Type : 0x00 = NULL compression */
1785 };
1786
1787 struct tcpcheck_ruleset *rs = NULL;
1788 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1789 struct tcpcheck_rule *chk;
1790 char *errmsg = NULL;
1791 int err_code = 0;
1792
1793 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1794 err_code |= ERR_WARN;
1795
1796 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1797 goto out;
1798
Christopher Faulet811f78c2020-04-01 11:10:27 +02001799 curpx->options2 &= ~PR_O2_CHK_ANY;
1800 curpx->options2 |= PR_O2_TCPCHK_CHK;
1801
1802 free_tcpcheck_vars(&rules->preset_vars);
1803 rules->list = NULL;
1804 rules->flags = 0;
1805
Christopher Faulet61cc8522020-04-20 14:54:42 +02001806 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001807 if (rs)
1808 goto ruleset_found;
1809
Christopher Faulet61cc8522020-04-20 14:54:42 +02001810 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001811 if (rs == NULL) {
1812 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1813 goto error;
1814 }
1815
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001816 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02001817 1, curpx, &rs->rules, file, line, &errmsg);
1818 if (!chk) {
1819 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1820 goto error;
1821 }
1822 chk->index = 0;
1823 LIST_ADDQ(&rs->rules, &chk->list);
1824
1825 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02001826 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02001827 "error-status", "L6RSP", "tout-status", "L6TOUT",
1828 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001829 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001830 if (!chk) {
1831 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1832 goto error;
1833 }
1834 chk->index = 1;
1835 LIST_ADDQ(&rs->rules, &chk->list);
1836
Christopher Faulet811f78c2020-04-01 11:10:27 +02001837 ruleset_found:
1838 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001839 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001840 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02001841
1842 out:
1843 free(errmsg);
1844 return err_code;
1845
1846 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001847 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001848 err_code |= ERR_ALERT | ERR_FATAL;
1849 goto out;
1850}
1851
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001852/* Parses the "option smtpchk" proxy keyword */
1853int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1854 const char *file, int line)
1855{
1856 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
1857
1858 struct tcpcheck_ruleset *rs = NULL;
1859 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1860 struct tcpcheck_rule *chk;
1861 struct tcpcheck_var *var = NULL;
1862 char *cmd = NULL, *errmsg = NULL;
1863 int err_code = 0;
1864
1865 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1866 err_code |= ERR_WARN;
1867
1868 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1869 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001870
1871 curpx->options2 &= ~PR_O2_CHK_ANY;
1872 curpx->options2 |= PR_O2_TCPCHK_CHK;
1873
1874 free_tcpcheck_vars(&rules->preset_vars);
1875 rules->list = NULL;
1876 rules->flags = 0;
1877
1878 cur_arg += 2;
1879 if (*args[cur_arg] && *args[cur_arg+1] &&
1880 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
Tim Duesterhus2867b402020-06-12 15:58:48 +02001881 /* <EHLO|HELO> + space (1) + <host> + null byte (1) */
1882 cmd = calloc(strlen(args[cur_arg]) + 1 + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001883 if (cmd)
1884 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
1885 }
1886 else {
1887 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
1888 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
1889 cmd = strdup("HELO localhost");
1890 }
1891
Christopher Fauletb61caf42020-04-21 10:57:42 +02001892 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001893 if (cmd == NULL || var == NULL) {
1894 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1895 goto error;
1896 }
1897 var->data.type = SMP_T_STR;
1898 var->data.u.str.area = cmd;
1899 var->data.u.str.data = strlen(cmd);
1900 LIST_INIT(&var->list);
1901 LIST_ADDQ(&rules->preset_vars, &var->list);
1902 cmd = NULL;
1903 var = NULL;
1904
Christopher Faulet61cc8522020-04-20 14:54:42 +02001905 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001906 if (rs)
1907 goto ruleset_found;
1908
Christopher Faulet61cc8522020-04-20 14:54:42 +02001909 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001910 if (rs == NULL) {
1911 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1912 goto error;
1913 }
1914
1915 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
1916 1, curpx, &rs->rules, file, line, &errmsg);
1917 if (!chk) {
1918 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1919 goto error;
1920 }
1921 chk->index = 0;
1922 LIST_ADDQ(&rs->rules, &chk->list);
1923
1924 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
1925 "min-recv", "4",
1926 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02001927 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001928 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001929 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001930 if (!chk) {
1931 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1932 goto error;
1933 }
1934 chk->index = 1;
1935 LIST_ADDQ(&rs->rules, &chk->list);
1936
1937 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
1938 "min-recv", "4",
1939 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001940 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1941 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001942 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001943 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001944 if (!chk) {
1945 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1946 goto error;
1947 }
1948 chk->index = 2;
1949 LIST_ADDQ(&rs->rules, &chk->list);
1950
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001951 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001952 1, curpx, &rs->rules, file, line, &errmsg);
1953 if (!chk) {
1954 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1955 goto error;
1956 }
1957 chk->index = 3;
1958 LIST_ADDQ(&rs->rules, &chk->list);
1959
1960 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
1961 "min-recv", "4",
1962 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001963 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1964 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1965 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001966 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001967 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001968 if (!chk) {
1969 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1970 goto error;
1971 }
1972 chk->index = 4;
1973 LIST_ADDQ(&rs->rules, &chk->list);
1974
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001975 ruleset_found:
1976 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001977 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001978 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001979
1980 out:
1981 free(errmsg);
1982 return err_code;
1983
1984 error:
1985 free(cmd);
1986 free(var);
1987 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001988 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001989 err_code |= ERR_ALERT | ERR_FATAL;
1990 goto out;
1991}
Christopher Faulet811f78c2020-04-01 11:10:27 +02001992
Christopher Fauletce355072020-04-02 11:44:39 +02001993/* Parses the "option pgsql-check" proxy keyword */
1994int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1995 const char *file, int line)
1996{
1997 static char pgsql_req[] = {
1998 "%[var(check.plen),htonl,hex]" /* The packet length*/
1999 "00030000" /* the version 3.0 */
2000 "7573657200" /* "user" key */
2001 "%[var(check.username),hex]00" /* the username */
2002 "00"
2003 };
2004
2005 struct tcpcheck_ruleset *rs = NULL;
2006 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2007 struct tcpcheck_rule *chk;
2008 struct tcpcheck_var *var = NULL;
2009 char *user = NULL, *errmsg = NULL;
2010 size_t packetlen = 0;
2011 int err_code = 0;
2012
2013 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2014 err_code |= ERR_WARN;
2015
2016 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
2017 goto out;
2018
Christopher Fauletce355072020-04-02 11:44:39 +02002019 curpx->options2 &= ~PR_O2_CHK_ANY;
2020 curpx->options2 |= PR_O2_TCPCHK_CHK;
2021
2022 free_tcpcheck_vars(&rules->preset_vars);
2023 rules->list = NULL;
2024 rules->flags = 0;
2025
2026 cur_arg += 2;
2027 if (!*args[cur_arg] || !*args[cur_arg+1]) {
2028 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
2029 file, line, args[0], args[1]);
2030 goto error;
2031 }
2032 if (strcmp(args[cur_arg], "user") == 0) {
2033 packetlen = 15 + strlen(args[cur_arg+1]);
2034 user = strdup(args[cur_arg+1]);
2035
Christopher Fauletb61caf42020-04-21 10:57:42 +02002036 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02002037 if (user == NULL || var == NULL) {
2038 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2039 goto error;
2040 }
2041 var->data.type = SMP_T_STR;
2042 var->data.u.str.area = user;
2043 var->data.u.str.data = strlen(user);
2044 LIST_INIT(&var->list);
2045 LIST_ADDQ(&rules->preset_vars, &var->list);
2046 user = NULL;
2047 var = NULL;
2048
Christopher Fauletb61caf42020-04-21 10:57:42 +02002049 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02002050 if (var == NULL) {
2051 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2052 goto error;
2053 }
2054 var->data.type = SMP_T_SINT;
2055 var->data.u.sint = packetlen;
2056 LIST_INIT(&var->list);
2057 LIST_ADDQ(&rules->preset_vars, &var->list);
2058 var = NULL;
2059 }
2060 else {
2061 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
2062 file, line, args[0], args[1]);
2063 goto error;
2064 }
2065
Christopher Faulet61cc8522020-04-20 14:54:42 +02002066 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002067 if (rs)
2068 goto ruleset_found;
2069
Christopher Faulet61cc8522020-04-20 14:54:42 +02002070 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002071 if (rs == NULL) {
2072 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2073 goto error;
2074 }
2075
2076 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2077 1, curpx, &rs->rules, file, line, &errmsg);
2078 if (!chk) {
2079 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2080 goto error;
2081 }
2082 chk->index = 0;
2083 LIST_ADDQ(&rs->rules, &chk->list);
2084
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002085 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02002086 1, curpx, &rs->rules, file, line, &errmsg);
2087 if (!chk) {
2088 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2089 goto error;
2090 }
2091 chk->index = 1;
2092 LIST_ADDQ(&rs->rules, &chk->list);
2093
2094 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
2095 "min-recv", "5",
2096 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02002097 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02002098 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002099 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002100 if (!chk) {
2101 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2102 goto error;
2103 }
2104 chk->index = 2;
2105 LIST_ADDQ(&rs->rules, &chk->list);
2106
Christopher Fauletb841c742020-04-27 18:29:49 +02002107 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 +02002108 "min-recv", "9",
2109 "error-status", "L7STS",
2110 "on-success", "PostgreSQL server is ok",
2111 "on-error", "PostgreSQL unknown error",
2112 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002113 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002114 if (!chk) {
2115 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2116 goto error;
2117 }
2118 chk->index = 3;
2119 LIST_ADDQ(&rs->rules, &chk->list);
2120
Christopher Fauletce355072020-04-02 11:44:39 +02002121 ruleset_found:
2122 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002123 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002124 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02002125
2126 out:
2127 free(errmsg);
2128 return err_code;
2129
2130 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002131 free(user);
2132 free(var);
2133 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002134 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002135 err_code |= ERR_ALERT | ERR_FATAL;
2136 goto out;
2137}
2138
2139
2140/* Parses the "option mysql-check" proxy keyword */
2141int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2142 const char *file, int line)
2143{
2144 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
2145 * const char mysql40_client_auth_pkt[] = {
2146 * "\x0e\x00\x00" // packet length
2147 * "\x01" // packet number
2148 * "\x00\x00" // client capabilities
2149 * "\x00\x00\x01" // max packet
2150 * "haproxy\x00" // username (null terminated string)
2151 * "\x00" // filler (always 0x00)
2152 * "\x01\x00\x00" // packet length
2153 * "\x00" // packet number
2154 * "\x01" // COM_QUIT command
2155 * };
2156 */
2157 static char mysql40_rsname[] = "*mysql40-check";
2158 static char mysql40_req[] = {
2159 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2160 "0080" /* client capabilities */
2161 "000001" /* max packet */
2162 "%[var(check.username),hex]00" /* the username */
2163 "00" /* filler (always 0x00) */
2164 "010000" /* packet length*/
2165 "00" /* sequence ID */
2166 "01" /* COM_QUIT command */
2167 };
2168
2169 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
2170 * const char mysql41_client_auth_pkt[] = {
2171 * "\x0e\x00\x00\" // packet length
2172 * "\x01" // packet number
2173 * "\x00\x00\x00\x00" // client capabilities
2174 * "\x00\x00\x00\x01" // max packet
2175 * "\x21" // character set (UTF-8)
2176 * char[23] // All zeroes
2177 * "haproxy\x00" // username (null terminated string)
2178 * "\x00" // filler (always 0x00)
2179 * "\x01\x00\x00" // packet length
2180 * "\x00" // packet number
2181 * "\x01" // COM_QUIT command
2182 * };
2183 */
2184 static char mysql41_rsname[] = "*mysql41-check";
2185 static char mysql41_req[] = {
2186 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2187 "00820000" /* client capabilities */
2188 "00800001" /* max packet */
2189 "21" /* character set (UTF-8) */
2190 "000000000000000000000000" /* 23 bytes, al zeroes */
2191 "0000000000000000000000"
2192 "%[var(check.username),hex]00" /* the username */
2193 "00" /* filler (always 0x00) */
2194 "010000" /* packet length*/
2195 "00" /* sequence ID */
2196 "01" /* COM_QUIT command */
2197 };
2198
2199 struct tcpcheck_ruleset *rs = NULL;
2200 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2201 struct tcpcheck_rule *chk;
2202 struct tcpcheck_var *var = NULL;
2203 char *mysql_rsname = "*mysql-check";
2204 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
2205 int index = 0, err_code = 0;
2206
2207 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2208 err_code |= ERR_WARN;
2209
2210 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2211 goto out;
2212
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002213 curpx->options2 &= ~PR_O2_CHK_ANY;
2214 curpx->options2 |= PR_O2_TCPCHK_CHK;
2215
2216 free_tcpcheck_vars(&rules->preset_vars);
2217 rules->list = NULL;
2218 rules->flags = 0;
2219
2220 cur_arg += 2;
2221 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002222 int packetlen, userlen;
2223
2224 if (strcmp(args[cur_arg], "user") != 0) {
2225 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
2226 file, line, args[0], args[1], args[cur_arg]);
2227 goto error;
2228 }
2229
2230 if (*(args[cur_arg+1]) == 0) {
2231 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
2232 file, line, args[0], args[1], args[cur_arg]);
2233 goto error;
2234 }
2235
2236 hdr = calloc(4, sizeof(*hdr));
2237 user = strdup(args[cur_arg+1]);
2238 userlen = strlen(args[cur_arg+1]);
2239
2240 if (hdr == NULL || user == NULL) {
2241 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2242 goto error;
2243 }
2244
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002245 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002246 packetlen = userlen + 7 + 27;
2247 mysql_req = mysql41_req;
2248 mysql_rsname = mysql41_rsname;
2249 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002250 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002251 packetlen = userlen + 7;
2252 mysql_req = mysql40_req;
2253 mysql_rsname = mysql40_rsname;
2254 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002255 else {
2256 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
2257 file, line, args[cur_arg], args[cur_arg+2]);
2258 goto error;
2259 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002260
2261 hdr[0] = (unsigned char)(packetlen & 0xff);
2262 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
2263 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
2264 hdr[3] = 1;
2265
Christopher Fauletb61caf42020-04-21 10:57:42 +02002266 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002267 if (var == NULL) {
2268 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2269 goto error;
2270 }
2271 var->data.type = SMP_T_STR;
2272 var->data.u.str.area = hdr;
2273 var->data.u.str.data = 4;
2274 LIST_INIT(&var->list);
2275 LIST_ADDQ(&rules->preset_vars, &var->list);
2276 hdr = NULL;
2277 var = NULL;
2278
Christopher Fauletb61caf42020-04-21 10:57:42 +02002279 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002280 if (var == NULL) {
2281 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2282 goto error;
2283 }
2284 var->data.type = SMP_T_STR;
2285 var->data.u.str.area = user;
2286 var->data.u.str.data = strlen(user);
2287 LIST_INIT(&var->list);
2288 LIST_ADDQ(&rules->preset_vars, &var->list);
2289 user = NULL;
2290 var = NULL;
2291 }
2292
Christopher Faulet61cc8522020-04-20 14:54:42 +02002293 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002294 if (rs)
2295 goto ruleset_found;
2296
Christopher Faulet61cc8522020-04-20 14:54:42 +02002297 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002298 if (rs == NULL) {
2299 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2300 goto error;
2301 }
2302
2303 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2304 1, curpx, &rs->rules, file, line, &errmsg);
2305 if (!chk) {
2306 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2307 goto error;
2308 }
2309 chk->index = index++;
2310 LIST_ADDQ(&rs->rules, &chk->list);
2311
2312 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002313 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002314 1, curpx, &rs->rules, file, line, &errmsg);
2315 if (!chk) {
2316 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2317 goto error;
2318 }
2319 chk->index = index++;
2320 LIST_ADDQ(&rs->rules, &chk->list);
2321 }
2322
2323 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002324 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002325 if (!chk) {
2326 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2327 goto error;
2328 }
2329 chk->expect.custom = tcpcheck_mysql_expect_iniths;
2330 chk->index = index++;
2331 LIST_ADDQ(&rs->rules, &chk->list);
2332
2333 if (mysql_req) {
2334 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002335 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002336 if (!chk) {
2337 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2338 goto error;
2339 }
2340 chk->expect.custom = tcpcheck_mysql_expect_ok;
2341 chk->index = index++;
2342 LIST_ADDQ(&rs->rules, &chk->list);
2343 }
2344
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002345 ruleset_found:
2346 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002347 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002348 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002349
2350 out:
2351 free(errmsg);
2352 return err_code;
2353
2354 error:
2355 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02002356 free(user);
2357 free(var);
2358 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002359 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02002360 err_code |= ERR_ALERT | ERR_FATAL;
2361 goto out;
2362}
2363
Christopher Faulet1997eca2020-04-03 23:13:50 +02002364int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2365 const char *file, int line)
2366{
2367 static char *ldap_req = "300C020101600702010304008000";
2368
2369 struct tcpcheck_ruleset *rs = NULL;
2370 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2371 struct tcpcheck_rule *chk;
2372 char *errmsg = NULL;
2373 int err_code = 0;
2374
2375 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2376 err_code |= ERR_WARN;
2377
2378 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2379 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002380
2381 curpx->options2 &= ~PR_O2_CHK_ANY;
2382 curpx->options2 |= PR_O2_TCPCHK_CHK;
2383
2384 free_tcpcheck_vars(&rules->preset_vars);
2385 rules->list = NULL;
2386 rules->flags = 0;
2387
Christopher Faulet61cc8522020-04-20 14:54:42 +02002388 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002389 if (rs)
2390 goto ruleset_found;
2391
Christopher Faulet61cc8522020-04-20 14:54:42 +02002392 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002393 if (rs == NULL) {
2394 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2395 goto error;
2396 }
2397
2398 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
2399 1, curpx, &rs->rules, file, line, &errmsg);
2400 if (!chk) {
2401 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2402 goto error;
2403 }
2404 chk->index = 0;
2405 LIST_ADDQ(&rs->rules, &chk->list);
2406
2407 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
2408 "min-recv", "14",
2409 "on-error", "Not LDAPv3 protocol",
2410 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002411 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002412 if (!chk) {
2413 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2414 goto error;
2415 }
2416 chk->index = 1;
2417 LIST_ADDQ(&rs->rules, &chk->list);
2418
2419 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002420 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002421 if (!chk) {
2422 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2423 goto error;
2424 }
2425 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
2426 chk->index = 2;
2427 LIST_ADDQ(&rs->rules, &chk->list);
2428
Christopher Faulet1997eca2020-04-03 23:13:50 +02002429 ruleset_found:
2430 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002431 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002432 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002433
2434 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02002435 free(errmsg);
2436 return err_code;
2437
2438 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002439 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002440 err_code |= ERR_ALERT | ERR_FATAL;
2441 goto out;
2442}
2443
2444int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2445 const char *file, int line)
2446{
2447 struct tcpcheck_ruleset *rs = NULL;
2448 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2449 struct tcpcheck_rule *chk;
2450 char *spop_req = NULL;
2451 char *errmsg = NULL;
2452 int spop_len = 0, err_code = 0;
2453
2454 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2455 err_code |= ERR_WARN;
2456
2457 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2458 goto out;
2459
Christopher Faulet267b01b2020-04-04 10:27:09 +02002460 curpx->options2 &= ~PR_O2_CHK_ANY;
2461 curpx->options2 |= PR_O2_TCPCHK_CHK;
2462
2463 free_tcpcheck_vars(&rules->preset_vars);
2464 rules->list = NULL;
2465 rules->flags = 0;
2466
2467
Christopher Faulet61cc8522020-04-20 14:54:42 +02002468 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002469 if (rs)
2470 goto ruleset_found;
2471
Christopher Faulet61cc8522020-04-20 14:54:42 +02002472 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002473 if (rs == NULL) {
2474 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2475 goto error;
2476 }
2477
2478 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
2479 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2480 goto error;
2481 }
2482 chunk_reset(&trash);
2483 dump_binary(&trash, spop_req, spop_len);
2484 trash.area[trash.data] = '\0';
2485
2486 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
2487 1, curpx, &rs->rules, file, line, &errmsg);
2488 if (!chk) {
2489 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2490 goto error;
2491 }
2492 chk->index = 0;
2493 LIST_ADDQ(&rs->rules, &chk->list);
2494
2495 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002496 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002497 if (!chk) {
2498 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2499 goto error;
2500 }
2501 chk->expect.custom = tcpcheck_spop_expect_agenthello;
2502 chk->index = 1;
2503 LIST_ADDQ(&rs->rules, &chk->list);
2504
Christopher Faulet267b01b2020-04-04 10:27:09 +02002505 ruleset_found:
2506 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002507 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002508 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002509
2510 out:
2511 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002512 free(errmsg);
2513 return err_code;
2514
2515 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002516 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002517 err_code |= ERR_ALERT | ERR_FATAL;
2518 goto out;
2519}
Christopher Fauletce355072020-04-02 11:44:39 +02002520
Christopher Faulete5870d82020-04-15 11:32:03 +02002521
2522struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
2523{
2524 struct tcpcheck_rule *chk = NULL;
2525 struct tcpcheck_http_hdr *hdr = NULL;
2526 char *meth = NULL, *uri = NULL, *vsn = NULL;
2527 char *hdrs, *body;
2528
2529 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
2530 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
2531 if (hdrs == body)
2532 hdrs = NULL;
2533 if (hdrs) {
2534 *hdrs = '\0';
2535 hdrs +=2;
2536 }
2537 if (body) {
2538 *body = '\0';
2539 body += 4;
2540 }
2541 if (hdrs || body) {
2542 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
2543 " Please, consider to use 'http-check send' directive instead.");
2544 }
2545
2546 chk = calloc(1, sizeof(*chk));
2547 if (!chk) {
2548 memprintf(errmsg, "out of memory");
2549 goto error;
2550 }
2551 chk->action = TCPCHK_ACT_SEND;
2552 chk->send.type = TCPCHK_SEND_HTTP;
2553 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
2554 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
2555 LIST_INIT(&chk->send.http.hdrs);
2556
2557 /* Copy the method, uri and version */
2558 if (*args[cur_arg]) {
2559 if (!*args[cur_arg+1])
2560 uri = args[cur_arg];
2561 else
2562 meth = args[cur_arg];
2563 }
2564 if (*args[cur_arg+1])
2565 uri = args[cur_arg+1];
2566 if (*args[cur_arg+2])
2567 vsn = args[cur_arg+2];
2568
2569 if (meth) {
2570 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
2571 chk->send.http.meth.str.area = strdup(meth);
2572 chk->send.http.meth.str.data = strlen(meth);
2573 if (!chk->send.http.meth.str.area) {
2574 memprintf(errmsg, "out of memory");
2575 goto error;
2576 }
2577 }
2578 if (uri) {
2579 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002580 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002581 memprintf(errmsg, "out of memory");
2582 goto error;
2583 }
2584 }
2585 if (vsn) {
2586 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002587 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002588 memprintf(errmsg, "out of memory");
2589 goto error;
2590 }
2591 }
2592
2593 /* Copy the header */
2594 if (hdrs) {
2595 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
2596 struct h1m h1m;
2597 int i, ret;
2598
2599 /* Build and parse the request */
2600 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
2601
2602 h1m.flags = H1_MF_HDRS_ONLY;
2603 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
2604 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
2605 &h1m, NULL);
2606 if (ret <= 0) {
2607 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
2608 goto error;
2609 }
2610
Christopher Fauletb61caf42020-04-21 10:57:42 +02002611 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002612 hdr = calloc(1, sizeof(*hdr));
2613 if (!hdr) {
2614 memprintf(errmsg, "out of memory");
2615 goto error;
2616 }
2617 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002618 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02002619 if (!hdr->name.ptr) {
2620 memprintf(errmsg, "out of memory");
2621 goto error;
2622 }
2623
Christopher Fauletb61caf42020-04-21 10:57:42 +02002624 ist0(tmp_hdrs[i].v);
2625 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 +02002626 goto error;
2627 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
2628 }
2629 }
2630
2631 /* Copy the body */
2632 if (body) {
2633 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002634 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002635 memprintf(errmsg, "out of memory");
2636 goto error;
2637 }
2638 }
2639
2640 return chk;
2641
2642 error:
2643 free_tcpcheck_http_hdr(hdr);
2644 free_tcpcheck(chk, 0);
2645 return NULL;
2646}
2647
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002648int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2649 const char *file, int line)
2650{
Christopher Faulete5870d82020-04-15 11:32:03 +02002651 struct tcpcheck_ruleset *rs = NULL;
2652 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2653 struct tcpcheck_rule *chk;
2654 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002655 int err_code = 0;
2656
2657 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2658 err_code |= ERR_WARN;
2659
2660 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2661 goto out;
2662
Christopher Faulete5870d82020-04-15 11:32:03 +02002663 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
2664 if (!chk) {
2665 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2666 goto error;
2667 }
2668 if (errmsg) {
2669 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
2670 err_code |= ERR_WARN;
2671 free(errmsg);
2672 errmsg = NULL;
2673 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002674
Christopher Faulete5870d82020-04-15 11:32:03 +02002675 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002676 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02002677 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002678
Christopher Faulete5870d82020-04-15 11:32:03 +02002679 free_tcpcheck_vars(&rules->preset_vars);
2680 rules->list = NULL;
2681 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002682
Christopher Faulete5870d82020-04-15 11:32:03 +02002683 /* Deduce the ruleset name from the proxy info */
2684 chunk_printf(&trash, "*http-check-%s_%s-%d",
2685 ((curpx == defpx) ? "defaults" : curpx->id),
2686 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002687
Christopher Faulet61cc8522020-04-20 14:54:42 +02002688 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002689 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002690 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002691 if (rs == NULL) {
2692 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2693 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002694 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002695 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002696
Christopher Faulete5870d82020-04-15 11:32:03 +02002697 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002698 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulete5870d82020-04-15 11:32:03 +02002699 rules->flags |= TCPCHK_RULES_HTTP_CHK;
2700 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
2701 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2702 rules->list = NULL;
2703 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002704 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002705
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002706 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02002707 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002708 return err_code;
2709
2710 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002711 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02002712 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002713 err_code |= ERR_ALERT | ERR_FATAL;
2714 goto out;
2715}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002716
Christopher Fauletce8111e2020-04-06 15:04:11 +02002717/* Parse the "addr" server keyword */
2718static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2719 char **errmsg)
2720{
2721 struct sockaddr_storage *sk;
Christopher Fauletce8111e2020-04-06 15:04:11 +02002722 int port1, port2, err_code = 0;
2723
2724
2725 if (!*args[*cur_arg+1]) {
2726 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
2727 goto error;
2728 }
2729
Willy Tarreau65ec4e32020-09-16 19:17:08 +02002730 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, NULL, errmsg, NULL, NULL,
2731 PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
Christopher Fauletce8111e2020-04-06 15:04:11 +02002732 if (!sk) {
2733 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
2734 goto error;
2735 }
2736
Christopher Fauletce8111e2020-04-06 15:04:11 +02002737 srv->check.addr = srv->agent.addr = *sk;
2738 srv->flags |= SRV_F_CHECKADDR;
2739 srv->flags |= SRV_F_AGENTADDR;
2740
2741 out:
2742 return err_code;
2743
2744 error:
2745 err_code |= ERR_ALERT | ERR_FATAL;
2746 goto out;
2747}
2748
2749
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002750/* Parse the "agent-addr" server keyword */
2751static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2752 char **errmsg)
2753{
2754 int err_code = 0;
2755
2756 if (!*(args[*cur_arg+1])) {
2757 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
2758 goto error;
2759 }
2760 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
2761 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
2762 goto error;
2763 }
2764
2765 out:
2766 return err_code;
2767
2768 error:
2769 err_code |= ERR_ALERT | ERR_FATAL;
2770 goto out;
2771}
2772
2773/* Parse the "agent-check" server keyword */
2774static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2775 char **errmsg)
2776{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002777 struct tcpcheck_ruleset *rs = NULL;
2778 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2779 struct tcpcheck_rule *chk;
2780 int err_code = 0;
2781
2782 if (srv->do_agent)
2783 goto out;
2784
Christopher Faulet0a5e7132021-01-12 17:29:45 +01002785 if (!(curpx->cap & PR_CAP_BE)) {
2786 memprintf(errmsg, "'%s' ignored because %s '%s' has no backend capability",
2787 args[*cur_arg], proxy_type_str(curpx), curpx->id);
2788 return ERR_WARN;
2789 }
2790
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002791 if (!rules) {
2792 rules = calloc(1, sizeof(*rules));
2793 if (!rules) {
2794 memprintf(errmsg, "out of memory.");
2795 goto error;
2796 }
2797 LIST_INIT(&rules->preset_vars);
2798 srv->agent.tcpcheck_rules = rules;
2799 }
2800 rules->list = NULL;
2801 rules->flags = 0;
2802
Christopher Faulet61cc8522020-04-20 14:54:42 +02002803 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002804 if (rs)
2805 goto ruleset_found;
2806
Christopher Faulet61cc8522020-04-20 14:54:42 +02002807 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002808 if (rs == NULL) {
2809 memprintf(errmsg, "out of memory.");
2810 goto error;
2811 }
2812
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002813 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002814 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
2815 if (!chk) {
2816 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2817 goto error;
2818 }
2819 chk->index = 0;
2820 LIST_ADDQ(&rs->rules, &chk->list);
2821
2822 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002823 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
2824 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002825 if (!chk) {
2826 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2827 goto error;
2828 }
2829 chk->expect.custom = tcpcheck_agent_expect_reply;
2830 chk->index = 1;
2831 LIST_ADDQ(&rs->rules, &chk->list);
2832
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002833 ruleset_found:
2834 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002835 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002836 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002837 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002838
2839 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002840 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002841
2842 error:
2843 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002844 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002845 err_code |= ERR_ALERT | ERR_FATAL;
2846 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002847}
2848
2849/* Parse the "agent-inter" server keyword */
2850static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2851 char **errmsg)
2852{
2853 const char *err = NULL;
2854 unsigned int delay;
2855 int err_code = 0;
2856
2857 if (!*(args[*cur_arg+1])) {
2858 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
2859 goto error;
2860 }
2861
2862 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
2863 if (err == PARSE_TIME_OVER) {
2864 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
2865 args[*cur_arg+1], args[*cur_arg], srv->id);
2866 goto error;
2867 }
2868 else if (err == PARSE_TIME_UNDER) {
2869 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
2870 args[*cur_arg+1], args[*cur_arg], srv->id);
2871 goto error;
2872 }
2873 else if (err) {
2874 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
2875 *err, srv->id);
2876 goto error;
2877 }
2878 if (delay <= 0) {
2879 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
2880 delay, args[*cur_arg], srv->id);
2881 goto error;
2882 }
2883 srv->agent.inter = delay;
2884
2885 out:
2886 return err_code;
2887
2888 error:
2889 err_code |= ERR_ALERT | ERR_FATAL;
2890 goto out;
2891}
2892
2893/* Parse the "agent-port" server keyword */
2894static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2895 char **errmsg)
2896{
2897 int err_code = 0;
2898
2899 if (!*(args[*cur_arg+1])) {
2900 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
2901 goto error;
2902 }
2903
2904 global.maxsock++;
2905 srv->agent.port = atol(args[*cur_arg+1]);
2906
2907 out:
2908 return err_code;
2909
2910 error:
2911 err_code |= ERR_ALERT | ERR_FATAL;
2912 goto out;
2913}
2914
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002915int set_srv_agent_send(struct server *srv, const char *send)
2916{
2917 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2918 struct tcpcheck_var *var = NULL;
2919 char *str;
2920
2921 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002922 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002923 if (str == NULL || var == NULL)
2924 goto error;
2925
2926 free_tcpcheck_vars(&rules->preset_vars);
2927
2928 var->data.type = SMP_T_STR;
2929 var->data.u.str.area = str;
2930 var->data.u.str.data = strlen(str);
2931 LIST_INIT(&var->list);
2932 LIST_ADDQ(&rules->preset_vars, &var->list);
2933
2934 return 1;
2935
2936 error:
2937 free(str);
2938 free(var);
2939 return 0;
2940}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002941
2942/* Parse the "agent-send" server keyword */
2943static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2944 char **errmsg)
2945{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002946 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002947 int err_code = 0;
2948
2949 if (!*(args[*cur_arg+1])) {
2950 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
2951 goto error;
2952 }
2953
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002954 if (!rules) {
2955 rules = calloc(1, sizeof(*rules));
2956 if (!rules) {
2957 memprintf(errmsg, "out of memory.");
2958 goto error;
2959 }
2960 LIST_INIT(&rules->preset_vars);
2961 srv->agent.tcpcheck_rules = rules;
2962 }
2963
2964 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002965 memprintf(errmsg, "out of memory.");
2966 goto error;
2967 }
2968
2969 out:
2970 return err_code;
2971
2972 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002973 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002974 err_code |= ERR_ALERT | ERR_FATAL;
2975 goto out;
2976}
2977
2978/* Parse the "no-agent-send" server keyword */
2979static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2980 char **errmsg)
2981{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002982 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002983 return 0;
2984}
2985
Christopher Fauletce8111e2020-04-06 15:04:11 +02002986/* Parse the "check" server keyword */
2987static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2988 char **errmsg)
2989{
Christopher Faulet0a5e7132021-01-12 17:29:45 +01002990 if (!(curpx->cap & PR_CAP_BE)) {
2991 memprintf(errmsg, "'%s' ignored because %s '%s' has no backend capability",
2992 args[*cur_arg], proxy_type_str(curpx), curpx->id);
2993 return ERR_WARN;
2994 }
2995
Christopher Fauletce8111e2020-04-06 15:04:11 +02002996 srv->do_check = 1;
2997 return 0;
2998}
2999
3000/* Parse the "check-send-proxy" server keyword */
3001static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3002 char **errmsg)
3003{
3004 srv->check.send_proxy = 1;
3005 return 0;
3006}
3007
3008/* Parse the "check-via-socks4" server keyword */
3009static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3010 char **errmsg)
3011{
3012 srv->check.via_socks4 = 1;
3013 return 0;
3014}
3015
3016/* Parse the "no-check" server keyword */
3017static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3018 char **errmsg)
3019{
3020 deinit_srv_check(srv);
3021 return 0;
3022}
3023
3024/* Parse the "no-check-send-proxy" server keyword */
3025static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3026 char **errmsg)
3027{
3028 srv->check.send_proxy = 0;
3029 return 0;
3030}
3031
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003032/* parse the "check-proto" server keyword */
3033static int srv_parse_check_proto(char **args, int *cur_arg,
3034 struct proxy *px, struct server *newsrv, char **err)
3035{
3036 int err_code = 0;
3037
3038 if (!*args[*cur_arg + 1]) {
3039 memprintf(err, "'%s' : missing value", args[*cur_arg]);
3040 goto error;
3041 }
3042 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
3043 if (!newsrv->check.mux_proto) {
3044 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
3045 goto error;
3046 }
3047
3048 out:
3049 return err_code;
3050
3051 error:
3052 err_code |= ERR_ALERT | ERR_FATAL;
3053 goto out;
3054}
3055
3056
Christopher Fauletce8111e2020-04-06 15:04:11 +02003057/* Parse the "rise" server keyword */
3058static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3059 char **errmsg)
3060{
3061 int err_code = 0;
3062
3063 if (!*args[*cur_arg + 1]) {
3064 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3065 goto error;
3066 }
3067
3068 srv->check.rise = atol(args[*cur_arg+1]);
3069 if (srv->check.rise <= 0) {
3070 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3071 goto error;
3072 }
3073
3074 if (srv->check.health)
3075 srv->check.health = srv->check.rise;
3076
3077 out:
3078 return err_code;
3079
3080 error:
3081 deinit_srv_agent_check(srv);
3082 err_code |= ERR_ALERT | ERR_FATAL;
3083 goto out;
3084 return 0;
3085}
3086
3087/* Parse the "fall" server keyword */
3088static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3089 char **errmsg)
3090{
3091 int err_code = 0;
3092
3093 if (!*args[*cur_arg + 1]) {
3094 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3095 goto error;
3096 }
3097
3098 srv->check.fall = atol(args[*cur_arg+1]);
3099 if (srv->check.fall <= 0) {
3100 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3101 goto error;
3102 }
3103
3104 out:
3105 return err_code;
3106
3107 error:
3108 deinit_srv_agent_check(srv);
3109 err_code |= ERR_ALERT | ERR_FATAL;
3110 goto out;
3111 return 0;
3112}
3113
3114/* Parse the "inter" server keyword */
3115static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3116 char **errmsg)
3117{
3118 const char *err = NULL;
3119 unsigned int delay;
3120 int err_code = 0;
3121
3122 if (!*(args[*cur_arg+1])) {
3123 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3124 goto error;
3125 }
3126
3127 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3128 if (err == PARSE_TIME_OVER) {
3129 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3130 args[*cur_arg+1], args[*cur_arg], srv->id);
3131 goto error;
3132 }
3133 else if (err == PARSE_TIME_UNDER) {
3134 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3135 args[*cur_arg+1], args[*cur_arg], srv->id);
3136 goto error;
3137 }
3138 else if (err) {
3139 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3140 *err, srv->id);
3141 goto error;
3142 }
3143 if (delay <= 0) {
3144 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3145 delay, args[*cur_arg], srv->id);
3146 goto error;
3147 }
3148 srv->check.inter = delay;
3149
3150 out:
3151 return err_code;
3152
3153 error:
3154 err_code |= ERR_ALERT | ERR_FATAL;
3155 goto out;
3156}
3157
3158
3159/* Parse the "fastinter" server keyword */
3160static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3161 char **errmsg)
3162{
3163 const char *err = NULL;
3164 unsigned int delay;
3165 int err_code = 0;
3166
3167 if (!*(args[*cur_arg+1])) {
3168 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3169 goto error;
3170 }
3171
3172 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3173 if (err == PARSE_TIME_OVER) {
3174 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3175 args[*cur_arg+1], args[*cur_arg], srv->id);
3176 goto error;
3177 }
3178 else if (err == PARSE_TIME_UNDER) {
3179 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3180 args[*cur_arg+1], args[*cur_arg], srv->id);
3181 goto error;
3182 }
3183 else if (err) {
3184 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3185 *err, srv->id);
3186 goto error;
3187 }
3188 if (delay <= 0) {
3189 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3190 delay, args[*cur_arg], srv->id);
3191 goto error;
3192 }
3193 srv->check.fastinter = delay;
3194
3195 out:
3196 return err_code;
3197
3198 error:
3199 err_code |= ERR_ALERT | ERR_FATAL;
3200 goto out;
3201}
3202
3203
3204/* Parse the "downinter" server keyword */
3205static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3206 char **errmsg)
3207{
3208 const char *err = NULL;
3209 unsigned int delay;
3210 int err_code = 0;
3211
3212 if (!*(args[*cur_arg+1])) {
3213 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3214 goto error;
3215 }
3216
3217 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3218 if (err == PARSE_TIME_OVER) {
3219 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3220 args[*cur_arg+1], args[*cur_arg], srv->id);
3221 goto error;
3222 }
3223 else if (err == PARSE_TIME_UNDER) {
3224 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3225 args[*cur_arg+1], args[*cur_arg], srv->id);
3226 goto error;
3227 }
3228 else if (err) {
3229 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3230 *err, srv->id);
3231 goto error;
3232 }
3233 if (delay <= 0) {
3234 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3235 delay, args[*cur_arg], srv->id);
3236 goto error;
3237 }
3238 srv->check.downinter = delay;
3239
3240 out:
3241 return err_code;
3242
3243 error:
3244 err_code |= ERR_ALERT | ERR_FATAL;
3245 goto out;
3246}
3247
3248/* Parse the "port" server keyword */
3249static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3250 char **errmsg)
3251{
3252 int err_code = 0;
3253
3254 if (!*(args[*cur_arg+1])) {
3255 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
3256 goto error;
3257 }
3258
3259 global.maxsock++;
3260 srv->check.port = atol(args[*cur_arg+1]);
3261 srv->flags |= SRV_F_CHECKPORT;
3262
3263 out:
3264 return err_code;
3265
3266 error:
3267 err_code |= ERR_ALERT | ERR_FATAL;
3268 goto out;
3269}
3270
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003271static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02003272 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003273 { 0, NULL, NULL },
3274}};
3275
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003276static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02003277 { "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 +02003278 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
3279 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
3280 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
3281 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
3282 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003283 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003284 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003285 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
3286 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003287 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003288 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
3289 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
3290 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
3291 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
3292 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
3293 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
3294 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
3295 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003296 { NULL, NULL, 0 },
3297}};
3298
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003299INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003300INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003301
Willy Tarreaubd741542010-03-16 18:46:54 +01003302/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02003303 * Local variables:
3304 * c-indent-level: 8
3305 * c-basic-offset: 8
3306 * End:
3307 */