blob: b95b4ae57e7b1a09d2838299b7555b1bb4b48ecc [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
399 * enabled.
400 */
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
439 switch (s->onerror) {
440 case HANA_ONERR_FASTINTER:
441 /* force fastinter - nothing to do here as all modes force it */
442 break;
443
444 case HANA_ONERR_SUDDTH:
445 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900446 if (s->check.health > s->check.rise)
447 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100448
Tim Duesterhus588b3142020-05-29 14:35:51 +0200449 /* fall through */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100450
451 case HANA_ONERR_FAILCHK:
452 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200453 set_server_check_status(&s->check, HCHK_STATUS_HANA,
454 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200455 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100456 break;
457
458 case HANA_ONERR_MARKDWN:
459 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900460 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200461 set_server_check_status(&s->check, HCHK_STATUS_HANA,
462 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200463 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100464 break;
465
466 default:
467 /* write a warning? */
468 break;
469 }
470
471 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100472 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100473
Simon Horman66183002013-02-23 10:16:43 +0900474 if (s->check.fastinter) {
475 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300476 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200477 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300478 /* requeue check task with new expire */
479 task_queue(s->check.task);
480 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100481 }
Willy Tarreauef781042010-01-27 11:53:01 +0100482}
483
Christopher Faulet61cc8522020-04-20 14:54:42 +0200484/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100485 * closed, keep errno intact as it is supposed to contain the valid error code.
486 * If no error is reported, check the socket's error queue using getsockopt().
487 * Warning, this must be done only once when returning from poll, and never
488 * after an I/O error was attempted, otherwise the error queue might contain
489 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
490 * socket. Returns non-zero if an error was reported, zero if everything is
491 * clean (including a properly closed socket).
492 */
493static int retrieve_errno_from_socket(struct connection *conn)
494{
495 int skerr;
496 socklen_t lskerr = sizeof(skerr);
497
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100498 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100499 return 1;
500
Willy Tarreau3c728722014-01-23 13:50:42 +0100501 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100502 return 0;
503
Willy Tarreau585744b2017-08-24 14:31:19 +0200504 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100505 errno = skerr;
506
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100507 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100508
509 if (!errno) {
510 /* we could not retrieve an error, that does not mean there is
511 * none. Just don't change anything and only report the prior
512 * error if any.
513 */
514 if (conn->flags & CO_FL_ERROR)
515 return 1;
516 else
517 return 0;
518 }
519
520 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
521 return 1;
522}
523
Christopher Faulet61cc8522020-04-20 14:54:42 +0200524/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100525 * and adjust the server status accordingly. It may make use of <errno_bck>
526 * if non-null when the caller is absolutely certain of its validity (eg:
527 * checked just after a syscall). If the caller doesn't have a valid errno,
528 * it can pass zero, and retrieve_errno_from_socket() will be called to try
529 * to extract errno from the socket. If no error is reported, it will consider
530 * the <expired> flag. This is intended to be used when a connection error was
531 * reported in conn->flags or when a timeout was reported in <expired>. The
532 * function takes care of not updating a server status which was already set.
533 * All situations where at least one of <expired> or CO_FL_ERROR are set
534 * produce a status.
535 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200536void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100537{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200538 struct conn_stream *cs = check->cs;
539 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100540 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200541 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200542 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100543
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100544 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100545 return;
546
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100547 errno = unclean_errno(errno_bck);
548 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100549 retrieve_errno_from_socket(conn);
550
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200551 if (conn && !(conn->flags & CO_FL_ERROR) &&
552 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100553 return;
554
555 /* we'll try to build a meaningful error message depending on the
556 * context of the error possibly present in conn->err_code, and the
557 * socket error possibly collected above. This is useful to know the
558 * exact step of the L6 layer (eg: SSL handshake).
559 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200560 chk = get_trash_chunk();
561
Christopher Faulet799f3a42020-04-07 12:06:14 +0200562 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200563 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200564 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200565 if (!step)
566 chunk_printf(chk, " at initial connection step of tcp-check");
567 else {
568 chunk_printf(chk, " at step %d of tcp-check", step);
569 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200570 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
571 if (check->current_step->connect.port)
572 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200573 else
574 chunk_appendf(chk, " (connect)");
575 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200576 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
577 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100578
579 switch (expect->type) {
580 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200581 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100582 break;
583 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200584 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100585 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200586 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200587 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100588 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200589 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100590 chunk_appendf(chk, " (expect binary regex)");
591 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200592 case TCPCHK_EXPECT_STRING_LF:
593 chunk_appendf(chk, " (expect log-format string)");
594 break;
595 case TCPCHK_EXPECT_BINARY_LF:
596 chunk_appendf(chk, " (expect log-format binary)");
597 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200598 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200599 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200600 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200601 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200602 chunk_appendf(chk, " (expect HTTP status regex)");
603 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200604 case TCPCHK_EXPECT_HTTP_HEADER:
605 chunk_appendf(chk, " (expect HTTP header pattern)");
606 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200607 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200608 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200609 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200610 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200611 chunk_appendf(chk, " (expect HTTP body regex)");
612 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200613 case TCPCHK_EXPECT_HTTP_BODY_LF:
614 chunk_appendf(chk, " (expect log-format HTTP body)");
615 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200616 case TCPCHK_EXPECT_CUSTOM:
617 chunk_appendf(chk, " (expect custom function)");
618 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100619 case TCPCHK_EXPECT_UNDEF:
620 chunk_appendf(chk, " (undefined expect!)");
621 break;
622 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200623 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200624 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200625 chunk_appendf(chk, " (send)");
626 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200627
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200628 if (check->current_step && check->current_step->comment)
629 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200630 }
631 }
632
Willy Tarreau00149122017-10-04 18:05:01 +0200633 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100634 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200635 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
636 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100637 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200638 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
639 chk->area);
640 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100641 }
642 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100643 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200644 chunk_printf(&trash, "%s%s", strerror(errno),
645 chk->area);
646 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100647 }
648 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200649 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100650 }
651 }
652
Willy Tarreau00149122017-10-04 18:05:01 +0200653 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200654 /* NOTE: this is reported after <fall> tries */
655 chunk_printf(chk, "No port available for the TCP connection");
656 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
657 }
658
Christopher Faulet5e293762020-10-26 11:10:49 +0100659 if (!conn || !conn->ctrl) {
660 /* error before any connection attempt (connection allocation error or no control layer) */
Willy Tarreau00149122017-10-04 18:05:01 +0200661 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
662 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100663 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100664 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200665 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100666 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
667 else if (expired)
668 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200669
670 /*
671 * might be due to a server IP change.
672 * Let's trigger a DNS resolution if none are currently running.
673 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100674 if (check->server)
675 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200676
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100677 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100678 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100679 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200680 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100681 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
682 else if (expired)
683 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
684 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200685 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100686 /* I/O error after connection was established and before we could diagnose */
687 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
688 }
689 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200690 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
691
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100692 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200693 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
694 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200695 tout = check->current_step->expect.tout_status;
696 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100697 }
698
699 return;
700}
701
Simon Horman98637e52014-06-20 12:30:16 +0900702
Christopher Faulet61cc8522020-04-20 14:54:42 +0200703/* Builds the server state header used by HTTP health-checks */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200704int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +0900705{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200706 int sv_state;
707 int ratio;
708 char addr[46];
709 char port[6];
710 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
711 "UP %d/%d", "UP",
712 "NOLB %d/%d", "NOLB",
713 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +0900714
Christopher Faulet61cc8522020-04-20 14:54:42 +0200715 if (!(s->check.state & CHK_ST_ENABLED))
716 sv_state = 6;
717 else if (s->cur_state != SRV_ST_STOPPED) {
718 if (s->check.health == s->check.rise + s->check.fall - 1)
719 sv_state = 3; /* UP */
720 else
721 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +0900722
Christopher Faulet61cc8522020-04-20 14:54:42 +0200723 if (s->cur_state == SRV_ST_STOPPING)
724 sv_state += 2;
725 } else {
726 if (s->check.health)
727 sv_state = 1; /* going up */
728 else
729 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +0900730 }
Willy Tarreaub7b24782016-06-21 15:32:29 +0200731
Christopher Faulet61cc8522020-04-20 14:54:42 +0200732 chunk_appendf(buf, srv_hlt_st[sv_state],
733 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
734 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +0200735
Christopher Faulet61cc8522020-04-20 14:54:42 +0200736 addr_to_str(&s->addr, addr, sizeof(addr));
737 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
738 snprintf(port, sizeof(port), "%u", s->svc_port);
739 else
740 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +0200741
Christopher Faulet61cc8522020-04-20 14:54:42 +0200742 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
743 addr, port, s->proxy->id, s->id,
744 global.node,
745 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
746 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
747 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
748 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +0100749
Christopher Faulet61cc8522020-04-20 14:54:42 +0200750 if ((s->cur_state == SRV_ST_STARTING) &&
751 now.tv_sec < s->last_change + s->slowstart &&
752 now.tv_sec >= s->last_change) {
753 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
754 chunk_appendf(buf, "; throttle=%d%%", ratio);
755 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200756
Christopher Faulet61cc8522020-04-20 14:54:42 +0200757 return b_data(buf);
758}
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200759
Willy Tarreau51cd5952020-06-05 12:25:38 +0200760/**************************************************************************/
Willy Tarreau51cd5952020-06-05 12:25:38 +0200761/***************** Health-checks based on connections *********************/
762/**************************************************************************/
763/* This function is used only for server health-checks. It handles connection
764 * status updates including errors. If necessary, it wakes the check task up.
765 * It returns 0 on normal cases, <0 if at least one close() has happened on the
766 * connection (eg: reconnect). It relies on tcpcheck_main().
Christopher Faulet61cc8522020-04-20 14:54:42 +0200767 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200768static int wake_srv_chk(struct conn_stream *cs)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200769{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200770 struct connection *conn = cs->conn;
771 struct check *check = cs->data;
772 struct email_alertq *q = container_of(check, typeof(*q), check);
773 int ret = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200774
Willy Tarreau51cd5952020-06-05 12:25:38 +0200775 if (check->server)
776 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
777 else
778 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200779
Willy Tarreau51cd5952020-06-05 12:25:38 +0200780 /* we may have to make progress on the TCP checks */
781 ret = tcpcheck_main(check);
Christopher Fauletaaab0832020-05-05 15:54:22 +0200782
Willy Tarreau51cd5952020-06-05 12:25:38 +0200783 cs = check->cs;
784 conn = cs->conn;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200785
Willy Tarreau51cd5952020-06-05 12:25:38 +0200786 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
787 /* We may get error reports bypassing the I/O handlers, typically
788 * the case when sending a pure TCP check which fails, then the I/O
789 * handlers above are not called. This is completely handled by the
790 * main processing task so let's simply wake it up. If we get here,
791 * we expect errno to still be valid.
792 */
793 chk_report_conn_err(check, errno, 0);
794 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200795 }
796
Christopher Faulet444b7b12021-01-18 15:47:03 +0100797 if (check->result != CHK_RES_UNKNOWN || ret == -1) {
Willy Tarreau51cd5952020-06-05 12:25:38 +0200798 /* Check complete or aborted. If connection not yet closed do it
799 * now and wake the check task up to be sure the result is
800 * handled ASAP. */
801 conn_sock_drain(conn);
802 cs_close(cs);
803 ret = -1;
Christopher Faulet444b7b12021-01-18 15:47:03 +0100804
805 if (check->wait_list.events)
806 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
807
Willy Tarreau51cd5952020-06-05 12:25:38 +0200808 /* We may have been scheduled to run, and the
809 * I/O handler expects to have a cs, so remove
810 * the tasklet
811 */
812 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
813 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200814 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200815
Willy Tarreau51cd5952020-06-05 12:25:38 +0200816 if (check->server)
817 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
818 else
819 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200820
Willy Tarreau51cd5952020-06-05 12:25:38 +0200821 /* if a connection got replaced, we must absolutely prevent the connection
822 * handler from touching its fd, and perform the FD polling updates ourselves
823 */
824 if (ret < 0)
825 conn_cond_update_polling(conn);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200826
Christopher Faulet61cc8522020-04-20 14:54:42 +0200827 return ret;
828}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200829
Willy Tarreau51cd5952020-06-05 12:25:38 +0200830/* This function checks if any I/O is wanted, and if so, attempts to do so */
831static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200832{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200833 struct check *check = ctx;
834 struct conn_stream *cs = check->cs;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200835
Willy Tarreau51cd5952020-06-05 12:25:38 +0200836 wake_srv_chk(cs);
837 return NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200838}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200839
Willy Tarreau51cd5952020-06-05 12:25:38 +0200840/* manages a server health-check that uses a connection. Returns
841 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Faulet61cc8522020-04-20 14:54:42 +0200842 *
843 * Please do NOT place any return statement in this function and only leave
Willy Tarreau51cd5952020-06-05 12:25:38 +0200844 * via the out_unlock label.
Christopher Faulet61cc8522020-04-20 14:54:42 +0200845 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200846static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200847{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200848 struct check *check = context;
849 struct proxy *proxy = check->proxy;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200850 struct conn_stream *cs = check->cs;
851 struct connection *conn = cs_conn(cs);
Willy Tarreau51cd5952020-06-05 12:25:38 +0200852 int rv;
853 int expired = tick_is_expired(t->expire, now_ms);
Willy Tarreaudeccd112018-06-14 18:38:55 +0200854
Willy Tarreau51cd5952020-06-05 12:25:38 +0200855 if (check->server)
856 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
857 if (!(check->state & CHK_ST_INPROGRESS)) {
858 /* no check currently running */
859 if (!expired) /* woke up too early */
860 goto out_unlock;
Willy Tarreauabca5b62013-12-06 14:19:25 +0100861
Willy Tarreau51cd5952020-06-05 12:25:38 +0200862 /* we don't send any health-checks when the proxy is
863 * stopped, the server should not be checked or the check
864 * is disabled.
865 */
866 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
Willy Tarreauc3914d42020-09-24 08:39:22 +0200867 proxy->disabled)
Willy Tarreau51cd5952020-06-05 12:25:38 +0200868 goto reschedule;
Christopher Faulet404f9192020-04-09 23:13:54 +0200869
Willy Tarreau51cd5952020-06-05 12:25:38 +0200870 /* we'll initiate a new check */
871 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet404f9192020-04-09 23:13:54 +0200872
Willy Tarreau51cd5952020-06-05 12:25:38 +0200873 check->state |= CHK_ST_INPROGRESS;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200874
Willy Tarreau51cd5952020-06-05 12:25:38 +0200875 task_set_affinity(t, tid_bit);
876
877 check->current_step = NULL;
878 tcpcheck_main(check);
879 goto out_unlock;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200880 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200881 else {
882 /* there was a test running.
883 * First, let's check whether there was an uncaught error,
884 * which can happen on connect timeout or error.
885 */
886 if (check->result == CHK_RES_UNKNOWN) {
887 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
888 chk_report_conn_err(check, 0, expired);
889 }
Christopher Faulet444b7b12021-01-18 15:47:03 +0100890 else {
891 if (check->state & CHK_ST_CLOSE_CONN) {
892 cs_destroy(cs);
893 cs = NULL;
894 conn = NULL;
895 check->cs = NULL;
896 check->state &= ~CHK_ST_CLOSE_CONN;
897 tcpcheck_main(check);
898 }
899 if (check->result == CHK_RES_UNKNOWN)
900 goto out_unlock; /* timeout not reached, wait again */
901 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200902 }
Christopher Faulet404f9192020-04-09 23:13:54 +0200903
Willy Tarreau51cd5952020-06-05 12:25:38 +0200904 /* check complete or aborted */
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200905
Willy Tarreau51cd5952020-06-05 12:25:38 +0200906 check->current_step = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200907
Willy Tarreau51cd5952020-06-05 12:25:38 +0200908 if (conn && conn->xprt) {
909 /* The check was aborted and the connection was not yet closed.
910 * This can happen upon timeout, or when an external event such
911 * as a failed response coupled with "observe layer7" caused the
912 * server state to be suddenly changed.
913 */
914 conn_sock_drain(conn);
915 cs_close(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200916 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200917
Willy Tarreau51cd5952020-06-05 12:25:38 +0200918 if (cs) {
919 if (check->wait_list.events)
920 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
921 /* We may have been scheduled to run, and the
922 * I/O handler expects to have a cs, so remove
923 * the tasklet
924 */
925 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
926 cs_destroy(cs);
927 cs = check->cs = NULL;
928 conn = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200929 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200930
931 if (check->sess != NULL) {
932 vars_prune(&check->vars, check->sess, NULL);
933 session_free(check->sess);
934 check->sess = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200935 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200936
937 if (check->server) {
938 if (check->result == CHK_RES_FAILED) {
939 /* a failure or timeout detected */
940 check_notify_failure(check);
941 }
942 else if (check->result == CHK_RES_CONDPASS) {
943 /* check is OK but asks for stopping mode */
944 check_notify_stopping(check);
945 }
946 else if (check->result == CHK_RES_PASSED) {
947 /* a success was detected */
948 check_notify_success(check);
949 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200950 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200951 task_set_affinity(t, MAX_THREADS_MASK);
Christopher Fauletb51e0372020-11-25 13:47:00 +0100952 check_release_buf(check, &check->bi);
953 check_release_buf(check, &check->bo);
954 check->state &= ~(CHK_ST_INPROGRESS|CHK_ST_IN_ALLOC|CHK_ST_OUT_ALLOC);
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200955
Willy Tarreau51cd5952020-06-05 12:25:38 +0200956 if (check->server) {
957 rv = 0;
958 if (global.spread_checks > 0) {
959 rv = srv_getinter(check) * global.spread_checks / 100;
960 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
Christopher Faulet61cc8522020-04-20 14:54:42 +0200961 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200962 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet61cc8522020-04-20 14:54:42 +0200963 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200964 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200965
966 reschedule:
967 while (tick_is_expired(t->expire, now_ms))
968 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
969 out_unlock:
970 if (check->server)
971 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
972 return t;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200973}
974
Willy Tarreau51cd5952020-06-05 12:25:38 +0200975
Christopher Faulet61cc8522020-04-20 14:54:42 +0200976/**************************************************************************/
977/************************** Init/deinit checks ****************************/
978/**************************************************************************/
Christopher Fauletb51e0372020-11-25 13:47:00 +0100979/*
980 * Tries to grab a buffer and to re-enables processing on check <target>. The
981 * check flags are used to figure what buffer was requested. It returns 1 if the
982 * allocation succeeds, in which case the I/O tasklet is woken up, or 0 if it's
983 * impossible to wake up and we prefer to be woken up later.
984 */
985int check_buf_available(void *target)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200986{
Christopher Fauletb51e0372020-11-25 13:47:00 +0100987 struct check *check = target;
988
989 if ((check->state & CHK_ST_IN_ALLOC) && b_alloc_margin(&check->bi, 0)) {
990 check->state &= ~CHK_ST_IN_ALLOC;
991 tasklet_wakeup(check->wait_list.tasklet);
992 return 1;
993 }
994 if ((check->state & CHK_ST_OUT_ALLOC) && b_alloc_margin(&check->bo, 0)) {
995 check->state &= ~CHK_ST_OUT_ALLOC;
996 tasklet_wakeup(check->wait_list.tasklet);
997 return 1;
998 }
999
1000 return 0;
1001}
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001002
Christopher Fauletb51e0372020-11-25 13:47:00 +01001003/*
1004 * Allocate a buffer. If if fails, it adds the check in buffer wait queue.
1005 */
1006struct buffer *check_get_buf(struct check *check, struct buffer *bptr)
1007{
1008 struct buffer *buf = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001009
Christopher Fauletb51e0372020-11-25 13:47:00 +01001010 if (likely(!MT_LIST_ADDED(&check->buf_wait.list)) &&
1011 unlikely((buf = b_alloc_margin(bptr, 0)) == NULL)) {
1012 check->buf_wait.target = check;
1013 check->buf_wait.wakeup_cb = check_buf_available;
1014 MT_LIST_ADDQ(&buffer_wq, &check->buf_wait.list);
1015 }
1016 return buf;
1017}
1018
1019/*
1020 * Release a buffer, if any, and try to wake up entities waiting in the buffer
1021 * wait queue.
1022 */
1023void check_release_buf(struct check *check, struct buffer *bptr)
1024{
1025 if (bptr->size) {
1026 b_free(bptr);
1027 offer_buffers(check->buf_wait.target, tasks_run_queue);
1028 }
1029}
1030
1031const char *init_check(struct check *check, int type)
1032{
1033 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001034
Christopher Fauletb51e0372020-11-25 13:47:00 +01001035 check->bi = BUF_NULL;
1036 check->bo = BUF_NULL;
1037 MT_LIST_INIT(&check->buf_wait.list);
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001038
Christopher Faulet61cc8522020-04-20 14:54:42 +02001039 check->wait_list.tasklet = tasklet_new();
1040 if (!check->wait_list.tasklet)
1041 return "out of memory while allocating check tasklet";
1042 check->wait_list.events = 0;
1043 check->wait_list.tasklet->process = event_srv_chk_io;
1044 check->wait_list.tasklet->context = check;
1045 return NULL;
1046}
1047
1048void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001049{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001050 task_destroy(check->task);
1051 if (check->wait_list.tasklet)
1052 tasklet_free(check->wait_list.tasklet);
1053
Christopher Fauletb51e0372020-11-25 13:47:00 +01001054 check_release_buf(check, &check->bi);
1055 check_release_buf(check, &check->bo);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001056 if (check->cs) {
1057 free(check->cs->conn);
1058 check->cs->conn = NULL;
1059 cs_free(check->cs);
1060 check->cs = NULL;
1061 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001062}
1063
Christopher Faulet61cc8522020-04-20 14:54:42 +02001064/* manages a server health-check. Returns the time the task accepts to wait, or
1065 * TIME_ETERNITY for infinity.
1066 */
Willy Tarreaucee013e2020-06-05 11:40:38 +02001067struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001068{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001069 struct check *check = context;
1070
1071 if (check->type == PR_O2_EXT_CHK)
1072 return process_chk_proc(t, context, state);
1073 return process_chk_conn(t, context, state);
1074
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001075}
1076
Christopher Faulet61cc8522020-04-20 14:54:42 +02001077
1078static int start_check_task(struct check *check, int mininter,
1079 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001080{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001081 struct task *t;
1082 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001083
Christopher Faulet61cc8522020-04-20 14:54:42 +02001084 if (check->type == PR_O2_EXT_CHK)
1085 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001086
Christopher Faulet61cc8522020-04-20 14:54:42 +02001087 /* task for the check */
1088 if ((t = task_new(thread_mask)) == NULL) {
1089 ha_alert("Starting [%s:%s] check: out of memory.\n",
1090 check->server->proxy->id, check->server->id);
1091 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001092 }
1093
Christopher Faulet61cc8522020-04-20 14:54:42 +02001094 check->task = t;
1095 t->process = process_chk;
1096 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001097
Christopher Faulet61cc8522020-04-20 14:54:42 +02001098 if (mininter < srv_getinter(check))
1099 mininter = srv_getinter(check);
1100
1101 if (global.max_spread_checks && mininter > global.max_spread_checks)
1102 mininter = global.max_spread_checks;
1103
1104 /* check this every ms */
1105 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
1106 check->start = now;
1107 task_queue(t);
1108
1109 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001110}
1111
Christopher Faulet61cc8522020-04-20 14:54:42 +02001112/* updates the server's weight during a warmup stage. Once the final weight is
1113 * reached, the task automatically stops. Note that any server status change
1114 * must have updated s->last_change accordingly.
1115 */
1116static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001117{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001118 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001119
Christopher Faulet61cc8522020-04-20 14:54:42 +02001120 /* by default, plan on stopping the task */
1121 t->expire = TICK_ETERNITY;
1122 if ((s->next_admin & SRV_ADMF_MAINT) ||
1123 (s->next_state != SRV_ST_STARTING))
1124 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02001125
Christopher Faulet61cc8522020-04-20 14:54:42 +02001126 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001127
Christopher Faulet61cc8522020-04-20 14:54:42 +02001128 /* recalculate the weights and update the state */
1129 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02001130
Christopher Faulet61cc8522020-04-20 14:54:42 +02001131 /* probably that we can refill this server with a bit more connections */
1132 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02001133
Christopher Faulet61cc8522020-04-20 14:54:42 +02001134 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02001135
Christopher Faulet61cc8522020-04-20 14:54:42 +02001136 /* get back there in 1 second or 1/20th of the slowstart interval,
1137 * whichever is greater, resulting in small 5% steps.
1138 */
1139 if (s->next_state == SRV_ST_STARTING)
1140 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
1141 return t;
1142}
1143
1144/*
1145 * Start health-check.
1146 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
1147 */
1148static int start_checks()
1149{
1150
1151 struct proxy *px;
1152 struct server *s;
1153 struct task *t;
1154 int nbcheck=0, mininter=0, srvpos=0;
1155
1156 /* 0- init the dummy frontend used to create all checks sessions */
1157 init_new_proxy(&checks_fe);
1158 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
1159 checks_fe.mode = PR_MODE_TCP;
1160 checks_fe.maxconn = 0;
1161 checks_fe.conn_retries = CONN_RETRIES;
1162 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
1163 checks_fe.timeout.client = TICK_ETERNITY;
1164
1165 /* 1- count the checkers to run simultaneously.
1166 * We also determine the minimum interval among all of those which
1167 * have an interval larger than SRV_CHK_INTER_THRES. This interval
1168 * will be used to spread their start-up date. Those which have
1169 * a shorter interval will start independently and will not dictate
1170 * too short an interval for all others.
1171 */
1172 for (px = proxies_list; px; px = px->next) {
1173 for (s = px->srv; s; s = s->next) {
1174 if (s->slowstart) {
1175 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
1176 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1177 return ERR_ALERT | ERR_FATAL;
1178 }
1179 /* We need a warmup task that will be called when the server
1180 * state switches from down to up.
1181 */
1182 s->warmup = t;
1183 t->process = server_warmup;
1184 t->context = s;
1185 /* server can be in this state only because of */
1186 if (s->next_state == SRV_ST_STARTING)
1187 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 +02001188 }
1189
Christopher Faulet61cc8522020-04-20 14:54:42 +02001190 if (s->check.state & CHK_ST_CONFIGURED) {
1191 nbcheck++;
1192 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
1193 (!mininter || mininter > srv_getinter(&s->check)))
1194 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02001195 }
1196
Christopher Faulet61cc8522020-04-20 14:54:42 +02001197 if (s->agent.state & CHK_ST_CONFIGURED) {
1198 nbcheck++;
1199 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
1200 (!mininter || mininter > srv_getinter(&s->agent)))
1201 mininter = srv_getinter(&s->agent);
1202 }
Christopher Faulet5c288742020-03-31 08:15:58 +02001203 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001204 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001205
Christopher Faulet61cc8522020-04-20 14:54:42 +02001206 if (!nbcheck)
1207 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001208
Christopher Faulet61cc8522020-04-20 14:54:42 +02001209 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02001210
Christopher Faulet61cc8522020-04-20 14:54:42 +02001211 /*
1212 * 2- start them as far as possible from each others. For this, we will
1213 * start them after their interval set to the min interval divided by
1214 * the number of servers, weighted by the server's position in the list.
1215 */
1216 for (px = proxies_list; px; px = px->next) {
1217 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
1218 if (init_pid_list()) {
1219 ha_alert("Starting [%s] check: out of memory.\n", px->id);
1220 return ERR_ALERT | ERR_FATAL;
1221 }
1222 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001223
Christopher Faulet61cc8522020-04-20 14:54:42 +02001224 for (s = px->srv; s; s = s->next) {
1225 /* A task for the main check */
1226 if (s->check.state & CHK_ST_CONFIGURED) {
1227 if (s->check.type == PR_O2_EXT_CHK) {
1228 if (!prepare_external_check(&s->check))
1229 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001230 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001231 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
1232 return ERR_ALERT | ERR_FATAL;
1233 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02001234 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001235
Christopher Faulet61cc8522020-04-20 14:54:42 +02001236 /* A task for a auxiliary agent check */
1237 if (s->agent.state & CHK_ST_CONFIGURED) {
1238 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
1239 return ERR_ALERT | ERR_FATAL;
1240 }
1241 srvpos++;
1242 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001243 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001244 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001245 return 0;
1246}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001247
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001248
Christopher Faulet61cc8522020-04-20 14:54:42 +02001249/*
1250 * Return value:
1251 * the port to be used for the health check
1252 * 0 in case no port could be found for the check
1253 */
1254static int srv_check_healthcheck_port(struct check *chk)
1255{
1256 int i = 0;
1257 struct server *srv = NULL;
1258
1259 srv = chk->server;
1260
1261 /* by default, we use the health check port ocnfigured */
1262 if (chk->port > 0)
1263 return chk->port;
1264
1265 /* try to get the port from check_core.addr if check.port not set */
1266 i = get_host_port(&chk->addr);
1267 if (i > 0)
1268 return i;
1269
1270 /* try to get the port from server address */
1271 /* prevent MAPPORTS from working at this point, since checks could
1272 * not be performed in such case (MAPPORTS impose a relative ports
1273 * based on live traffic)
1274 */
1275 if (srv->flags & SRV_F_MAPPORTS)
1276 return 0;
1277
1278 i = srv->svc_port; /* by default */
1279 if (i > 0)
1280 return i;
1281
1282 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001283}
1284
Christopher Faulet61cc8522020-04-20 14:54:42 +02001285/* Initializes an health-check attached to the server <srv>. Non-zero is returned
1286 * if an error occurred.
1287 */
1288static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001289{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001290 const char *err;
1291 struct tcpcheck_rule *r;
1292 int ret = 0;
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001293 int check_type;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001294
Christopher Faulet0a5e7132021-01-12 17:29:45 +01001295 if (!srv->do_check || !(srv->proxy->cap & PR_CAP_BE))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001296 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001297
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001298 check_type = srv->check.tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001299
Christopher Faulet61cc8522020-04-20 14:54:42 +02001300 /* If neither a port nor an addr was specified and no check transport
1301 * layer is forced, then the transport layer used by the checks is the
1302 * same as for the production traffic. Otherwise we use raw_sock by
1303 * default, unless one is specified.
1304 */
1305 if (!srv->check.port && !is_addr(&srv->check.addr)) {
1306 if (!srv->check.use_ssl && srv->use_ssl != -1) {
1307 srv->check.use_ssl = srv->use_ssl;
1308 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001309 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001310 else if (srv->check.use_ssl == 1)
1311 srv->check.xprt = xprt_get(XPRT_SSL);
1312 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001313 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02001314 else if (srv->check.use_ssl == 1)
1315 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001316
Christopher Faulet12882cf2020-04-23 15:50:18 +02001317 /* Inherit the mux protocol from the server if not already defined for
1318 * the check
1319 */
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001320 if (srv->mux_proto && !srv->check.mux_proto &&
1321 ((srv->mux_proto->mode == PROTO_MODE_HTTP && check_type == TCPCHK_RULES_HTTP_CHK) ||
1322 (srv->mux_proto->mode == PROTO_MODE_TCP && check_type != TCPCHK_RULES_HTTP_CHK))) {
Christopher Faulet12882cf2020-04-23 15:50:18 +02001323 srv->check.mux_proto = srv->mux_proto;
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001324 }
Amaury Denoyelle92c8ac12020-11-13 12:34:57 +01001325 /* test that check proto is valid if explicitly defined */
1326 else if (srv->check.mux_proto &&
1327 ((srv->check.mux_proto->mode == PROTO_MODE_HTTP && check_type != TCPCHK_RULES_HTTP_CHK) ||
1328 (srv->check.mux_proto->mode == PROTO_MODE_TCP && check_type == TCPCHK_RULES_HTTP_CHK))) {
1329 ha_alert("config: %s '%s': server '%s' uses an incompatible MUX protocol for the selected check type\n",
1330 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1331 ret |= ERR_ALERT | ERR_FATAL;
1332 goto out;
1333 }
Christopher Faulet12882cf2020-04-23 15:50:18 +02001334
Christopher Faulet61cc8522020-04-20 14:54:42 +02001335 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001336
Christopher Faulet61cc8522020-04-20 14:54:42 +02001337 /* We need at least a service port, a check port or the first tcp-check
1338 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
1339 */
1340 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
1341 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
1342 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001343
Christopher Faulet61cc8522020-04-20 14:54:42 +02001344 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
1345 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
1346 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1347 ret |= ERR_ALERT | ERR_ABORT;
1348 goto out;
1349 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001350
Christopher Faulet61cc8522020-04-20 14:54:42 +02001351 /* search the first action (connect / send / expect) in the list */
1352 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
1353 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
1354 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
1355 "nor tcp_check rule 'connect' with port information.\n",
1356 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1357 ret |= ERR_ALERT | ERR_ABORT;
1358 goto out;
1359 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001360
Christopher Faulet61cc8522020-04-20 14:54:42 +02001361 /* scan the tcp-check ruleset to ensure a port has been configured */
1362 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
1363 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
1364 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
1365 "and a tcp_check rule 'connect' with no port information.\n",
1366 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1367 ret |= ERR_ALERT | ERR_ABORT;
1368 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001369 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001370 }
1371
Christopher Faulet61cc8522020-04-20 14:54:42 +02001372 init:
1373 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
1374 struct tcpcheck_ruleset *rs = NULL;
1375 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
1376 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001377
Christopher Faulet61cc8522020-04-20 14:54:42 +02001378 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
1379 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02001380
Christopher Faulet61cc8522020-04-20 14:54:42 +02001381 rs = find_tcpcheck_ruleset("*tcp-check");
1382 if (!rs) {
1383 rs = create_tcpcheck_ruleset("*tcp-check");
1384 if (rs == NULL) {
1385 ha_alert("config: %s '%s': out of memory.\n",
1386 proxy_type_str(srv->proxy), srv->proxy->id);
1387 ret |= ERR_ALERT | ERR_FATAL;
1388 goto out;
1389 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001390 }
1391
Christopher Faulet61cc8522020-04-20 14:54:42 +02001392 free_tcpcheck_vars(&rules->preset_vars);
1393 rules->list = &rs->rules;
1394 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001395 }
1396
Christopher Faulet61cc8522020-04-20 14:54:42 +02001397 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
1398 if (err) {
1399 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
1400 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1401 ret |= ERR_ALERT | ERR_ABORT;
1402 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001403 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001404 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
1405 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02001406
Christopher Faulet61cc8522020-04-20 14:54:42 +02001407 out:
1408 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001409}
1410
Christopher Faulet61cc8522020-04-20 14:54:42 +02001411/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
1412 * if an error occurred.
1413 */
1414static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02001415{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001416 struct tcpcheck_rule *chk;
1417 const char *err;
1418 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001419
Christopher Faulet0a5e7132021-01-12 17:29:45 +01001420 if (!srv->do_agent || !(srv->proxy->cap & PR_CAP_BE))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001421 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001422
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001423 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02001424 * implicit one is inserted before all others.
1425 */
1426 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
1427 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
1428 chk = calloc(1, sizeof(*chk));
1429 if (!chk) {
1430 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
1431 " to agent-check for server '%s' (out of memory).\n",
1432 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1433 ret |= ERR_ALERT | ERR_FATAL;
1434 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001435 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001436 chk->action = TCPCHK_ACT_CONNECT;
1437 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
1438 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02001439 }
1440
Christopher Faulete5870d82020-04-15 11:32:03 +02001441
Christopher Faulet61cc8522020-04-20 14:54:42 +02001442 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
1443 if (err) {
1444 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
1445 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1446 ret |= ERR_ALERT | ERR_ABORT;
1447 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001448 }
1449
Christopher Faulet61cc8522020-04-20 14:54:42 +02001450 if (!srv->agent.inter)
1451 srv->agent.inter = srv->check.inter;
1452
1453 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
1454 global.maxsock++;
1455
1456 out:
1457 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001458}
1459
Christopher Faulet61cc8522020-04-20 14:54:42 +02001460static void deinit_srv_check(struct server *srv)
1461{
1462 if (srv->check.state & CHK_ST_CONFIGURED)
1463 free_check(&srv->check);
1464 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
1465 srv->do_check = 0;
1466}
Christopher Faulete5870d82020-04-15 11:32:03 +02001467
Christopher Faulet61cc8522020-04-20 14:54:42 +02001468
1469static void deinit_srv_agent_check(struct server *srv)
1470{
1471 if (srv->agent.tcpcheck_rules) {
1472 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
1473 free(srv->agent.tcpcheck_rules);
1474 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001475 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001476
Christopher Faulet61cc8522020-04-20 14:54:42 +02001477 if (srv->agent.state & CHK_ST_CONFIGURED)
1478 free_check(&srv->agent);
1479
1480 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
1481 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001482}
1483
Willy Tarreaucee013e2020-06-05 11:40:38 +02001484REGISTER_POST_SERVER_CHECK(init_srv_check);
1485REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
Willy Tarreaucee013e2020-06-05 11:40:38 +02001486REGISTER_POST_CHECK(start_checks);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001487
Willy Tarreaucee013e2020-06-05 11:40:38 +02001488REGISTER_SERVER_DEINIT(deinit_srv_check);
1489REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001490
Christopher Faulet61cc8522020-04-20 14:54:42 +02001491
1492/**************************************************************************/
1493/************************** Check sample fetches **************************/
1494/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001495
Christopher Faulet61cc8522020-04-20 14:54:42 +02001496static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001497 { /* END */ },
1498}};
1499
1500INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
1501
1502
1503/**************************************************************************/
1504/************************ Check's parsing functions ***********************/
1505/**************************************************************************/
Christopher Faulet51b129f2020-04-09 15:54:18 +02001506/* Parses the "http-check" proxy keyword */
1507static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
1508 struct proxy *defpx, const char *file, int line,
1509 char **errmsg)
1510{
Christopher Faulete5870d82020-04-15 11:32:03 +02001511 struct tcpcheck_ruleset *rs = NULL;
1512 struct tcpcheck_rule *chk = NULL;
1513 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001514
1515 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
1516 ret = 1;
1517
1518 cur_arg = 1;
1519 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
1520 /* enable a graceful server shutdown on an HTTP 404 response */
1521 curpx->options |= PR_O_DISABLE404;
1522 if (too_many_args(1, args, errmsg, NULL))
1523 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001524 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001525 }
1526 else if (strcmp(args[cur_arg], "send-state") == 0) {
1527 /* enable emission of the apparent state of a server in HTTP checks */
1528 curpx->options2 |= PR_O2_CHK_SNDST;
1529 if (too_many_args(1, args, errmsg, NULL))
1530 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001531 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001532 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001533
Christopher Faulete5870d82020-04-15 11:32:03 +02001534 /* Deduce the ruleset name from the proxy info */
1535 chunk_printf(&trash, "*http-check-%s_%s-%d",
1536 ((curpx == defpx) ? "defaults" : curpx->id),
1537 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001538
Christopher Faulet61cc8522020-04-20 14:54:42 +02001539 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001540 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001541 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001542 if (rs == NULL) {
1543 memprintf(errmsg, "out of memory.\n");
1544 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001545 }
1546 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001547
Christopher Faulete5870d82020-04-15 11:32:03 +02001548 index = 0;
1549 if (!LIST_ISEMPTY(&rs->rules)) {
1550 chk = LIST_PREV(&rs->rules, typeof(chk), list);
1551 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
1552 index = chk->index + 1;
1553 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001554
Christopher Faulete5870d82020-04-15 11:32:03 +02001555 if (strcmp(args[cur_arg], "connect") == 0)
1556 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1557 else if (strcmp(args[cur_arg], "send") == 0)
1558 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1559 else if (strcmp(args[cur_arg], "expect") == 0)
1560 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
1561 file, line, errmsg);
1562 else if (strcmp(args[cur_arg], "comment") == 0)
1563 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1564 else {
1565 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001566
Christopher Faulete5870d82020-04-15 11:32:03 +02001567 if (!kw) {
1568 action_kw_tcp_check_build_list(&trash);
1569 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
1570 " 'send', 'expect'%s%s. but got '%s'",
1571 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
1572 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001573 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001574 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
1575 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001576
Christopher Faulete5870d82020-04-15 11:32:03 +02001577 if (!chk) {
1578 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1579 goto error;
1580 }
1581 ret = (*errmsg != NULL); /* Handle warning */
1582
1583 chk->index = index;
1584 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
1585 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
1586 /* Use this ruleset if the proxy already has http-check enabled */
1587 curpx->tcpcheck_rules.list = &rs->rules;
1588 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
1589 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
1590 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1591 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001592 goto error;
1593 }
1594 }
1595 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02001596 /* mark this ruleset as unused for now */
1597 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
1598 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001599 }
1600
Christopher Faulete5870d82020-04-15 11:32:03 +02001601 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02001602 return ret;
1603
1604 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02001605 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001606 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001607 return -1;
1608}
1609
Christopher Faulet430e4802020-04-09 15:28:16 +02001610/* Parses the "option tcp-check" proxy keyword */
1611int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1612 const char *file, int line)
1613{
Christopher Faulet404f9192020-04-09 23:13:54 +02001614 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02001615 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1616 int err_code = 0;
1617
1618 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1619 err_code |= ERR_WARN;
1620
1621 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1622 goto out;
1623
Christopher Faulet404f9192020-04-09 23:13:54 +02001624 curpx->options2 &= ~PR_O2_CHK_ANY;
1625 curpx->options2 |= PR_O2_TCPCHK_CHK;
1626
Christopher Fauletd7e63962020-04-17 20:15:59 +02001627 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02001628 /* If a tcp-check rulesset is already set, do nothing */
1629 if (rules->list)
1630 goto out;
1631
1632 /* If a tcp-check ruleset is waiting to be used for the current proxy,
1633 * get it.
1634 */
1635 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
1636 goto curpx_ruleset;
1637
1638 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
1639 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001640 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001641 if (rs)
1642 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02001643 }
1644
Christopher Faulet404f9192020-04-09 23:13:54 +02001645 curpx_ruleset:
1646 /* Deduce the ruleset name from the proxy info */
1647 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
1648 ((curpx == defpx) ? "defaults" : curpx->id),
1649 curpx->conf.file, curpx->conf.line);
1650
Christopher Faulet61cc8522020-04-20 14:54:42 +02001651 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001652 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001653 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001654 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02001655 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1656 goto error;
1657 }
Christopher Faulet430e4802020-04-09 15:28:16 +02001658 }
1659
Christopher Faulet404f9192020-04-09 23:13:54 +02001660 ruleset_found:
1661 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02001662 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001663 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Fauletd7e63962020-04-17 20:15:59 +02001664 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02001665
1666 out:
1667 return err_code;
1668
1669 error:
1670 err_code |= ERR_ALERT | ERR_FATAL;
1671 goto out;
1672}
Christopher Faulet33f05df2020-04-01 11:08:50 +02001673
1674/* Parses the "option redis-check" proxy keyword */
1675int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1676 const char *file, int line)
1677{
1678 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
1679 static char *redis_res = "+PONG\r\n";
1680
1681 struct tcpcheck_ruleset *rs = NULL;
1682 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1683 struct tcpcheck_rule *chk;
1684 char *errmsg = NULL;
1685 int err_code = 0;
1686
1687 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1688 err_code |= ERR_WARN;
1689
1690 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1691 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001692
1693 curpx->options2 &= ~PR_O2_CHK_ANY;
1694 curpx->options2 |= PR_O2_TCPCHK_CHK;
1695
1696 free_tcpcheck_vars(&rules->preset_vars);
1697 rules->list = NULL;
1698 rules->flags = 0;
1699
Christopher Faulet61cc8522020-04-20 14:54:42 +02001700 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001701 if (rs)
1702 goto ruleset_found;
1703
Christopher Faulet61cc8522020-04-20 14:54:42 +02001704 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001705 if (rs == NULL) {
1706 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1707 goto error;
1708 }
1709
1710 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
1711 1, curpx, &rs->rules, file, line, &errmsg);
1712 if (!chk) {
1713 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1714 goto error;
1715 }
1716 chk->index = 0;
1717 LIST_ADDQ(&rs->rules, &chk->list);
1718
1719 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
1720 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001721 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02001722 "on-success", "Redis server is ok",
1723 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001724 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001725 if (!chk) {
1726 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1727 goto error;
1728 }
1729 chk->index = 1;
1730 LIST_ADDQ(&rs->rules, &chk->list);
1731
Christopher Faulet33f05df2020-04-01 11:08:50 +02001732 ruleset_found:
1733 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001734 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001735 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001736
1737 out:
1738 free(errmsg);
1739 return err_code;
1740
1741 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001742 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001743 err_code |= ERR_ALERT | ERR_FATAL;
1744 goto out;
1745}
1746
Christopher Faulet811f78c2020-04-01 11:10:27 +02001747
1748/* Parses the "option ssl-hello-chk" proxy keyword */
1749int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1750 const char *file, int line)
1751{
1752 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
1753 * ssl-hello-chk option to ensure that the remote server speaks SSL.
1754 *
1755 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
1756 */
1757 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001758 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02001759 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
1760 "0079" /* ContentLength : 0x79 bytes after this one */
1761 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
1762 "000075" /* HandshakeLength : 0x75 bytes after this one */
1763 "0300" /* Hello Version : 0x0300 = v3 */
1764 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
1765 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
1766 "00" /* Session ID length : empty (no session ID) */
1767 "004E" /* Cipher Suite Length : 78 bytes after this one */
1768 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
1769 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
1770 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
1771 "000D" "000E" "000F" "0010" /* various bit lengths, */
1772 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
1773 "0015" "0016" "0017" "0018"
1774 "0019" "001A" "001B" "002F"
1775 "0030" "0031" "0032" "0033"
1776 "0034" "0035" "0036" "0037"
1777 "0038" "0039" "003A"
1778 "01" /* Compression Length : 0x01 = 1 byte for types */
1779 "00" /* Compression Type : 0x00 = NULL compression */
1780 };
1781
1782 struct tcpcheck_ruleset *rs = NULL;
1783 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1784 struct tcpcheck_rule *chk;
1785 char *errmsg = NULL;
1786 int err_code = 0;
1787
1788 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1789 err_code |= ERR_WARN;
1790
1791 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1792 goto out;
1793
Christopher Faulet811f78c2020-04-01 11:10:27 +02001794 curpx->options2 &= ~PR_O2_CHK_ANY;
1795 curpx->options2 |= PR_O2_TCPCHK_CHK;
1796
1797 free_tcpcheck_vars(&rules->preset_vars);
1798 rules->list = NULL;
1799 rules->flags = 0;
1800
Christopher Faulet61cc8522020-04-20 14:54:42 +02001801 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001802 if (rs)
1803 goto ruleset_found;
1804
Christopher Faulet61cc8522020-04-20 14:54:42 +02001805 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001806 if (rs == NULL) {
1807 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1808 goto error;
1809 }
1810
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001811 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02001812 1, curpx, &rs->rules, file, line, &errmsg);
1813 if (!chk) {
1814 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1815 goto error;
1816 }
1817 chk->index = 0;
1818 LIST_ADDQ(&rs->rules, &chk->list);
1819
1820 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02001821 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02001822 "error-status", "L6RSP", "tout-status", "L6TOUT",
1823 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001824 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001825 if (!chk) {
1826 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1827 goto error;
1828 }
1829 chk->index = 1;
1830 LIST_ADDQ(&rs->rules, &chk->list);
1831
Christopher Faulet811f78c2020-04-01 11:10:27 +02001832 ruleset_found:
1833 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001834 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001835 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02001836
1837 out:
1838 free(errmsg);
1839 return err_code;
1840
1841 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001842 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001843 err_code |= ERR_ALERT | ERR_FATAL;
1844 goto out;
1845}
1846
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001847/* Parses the "option smtpchk" proxy keyword */
1848int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1849 const char *file, int line)
1850{
1851 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
1852
1853 struct tcpcheck_ruleset *rs = NULL;
1854 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1855 struct tcpcheck_rule *chk;
1856 struct tcpcheck_var *var = NULL;
1857 char *cmd = NULL, *errmsg = NULL;
1858 int err_code = 0;
1859
1860 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1861 err_code |= ERR_WARN;
1862
1863 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1864 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001865
1866 curpx->options2 &= ~PR_O2_CHK_ANY;
1867 curpx->options2 |= PR_O2_TCPCHK_CHK;
1868
1869 free_tcpcheck_vars(&rules->preset_vars);
1870 rules->list = NULL;
1871 rules->flags = 0;
1872
1873 cur_arg += 2;
1874 if (*args[cur_arg] && *args[cur_arg+1] &&
1875 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
Tim Duesterhus2867b402020-06-12 15:58:48 +02001876 /* <EHLO|HELO> + space (1) + <host> + null byte (1) */
1877 cmd = calloc(strlen(args[cur_arg]) + 1 + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001878 if (cmd)
1879 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
1880 }
1881 else {
1882 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
1883 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
1884 cmd = strdup("HELO localhost");
1885 }
1886
Christopher Fauletb61caf42020-04-21 10:57:42 +02001887 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001888 if (cmd == NULL || var == NULL) {
1889 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1890 goto error;
1891 }
1892 var->data.type = SMP_T_STR;
1893 var->data.u.str.area = cmd;
1894 var->data.u.str.data = strlen(cmd);
1895 LIST_INIT(&var->list);
1896 LIST_ADDQ(&rules->preset_vars, &var->list);
1897 cmd = NULL;
1898 var = NULL;
1899
Christopher Faulet61cc8522020-04-20 14:54:42 +02001900 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001901 if (rs)
1902 goto ruleset_found;
1903
Christopher Faulet61cc8522020-04-20 14:54:42 +02001904 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001905 if (rs == NULL) {
1906 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1907 goto error;
1908 }
1909
1910 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
1911 1, curpx, &rs->rules, file, line, &errmsg);
1912 if (!chk) {
1913 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1914 goto error;
1915 }
1916 chk->index = 0;
1917 LIST_ADDQ(&rs->rules, &chk->list);
1918
1919 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
1920 "min-recv", "4",
1921 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02001922 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001923 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001924 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001925 if (!chk) {
1926 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1927 goto error;
1928 }
1929 chk->index = 1;
1930 LIST_ADDQ(&rs->rules, &chk->list);
1931
1932 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
1933 "min-recv", "4",
1934 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001935 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1936 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001937 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001938 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001939 if (!chk) {
1940 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1941 goto error;
1942 }
1943 chk->index = 2;
1944 LIST_ADDQ(&rs->rules, &chk->list);
1945
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001946 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001947 1, curpx, &rs->rules, file, line, &errmsg);
1948 if (!chk) {
1949 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1950 goto error;
1951 }
1952 chk->index = 3;
1953 LIST_ADDQ(&rs->rules, &chk->list);
1954
1955 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
1956 "min-recv", "4",
1957 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001958 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1959 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1960 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001961 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001962 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001963 if (!chk) {
1964 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1965 goto error;
1966 }
1967 chk->index = 4;
1968 LIST_ADDQ(&rs->rules, &chk->list);
1969
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001970 ruleset_found:
1971 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001972 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001973 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001974
1975 out:
1976 free(errmsg);
1977 return err_code;
1978
1979 error:
1980 free(cmd);
1981 free(var);
1982 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001983 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001984 err_code |= ERR_ALERT | ERR_FATAL;
1985 goto out;
1986}
Christopher Faulet811f78c2020-04-01 11:10:27 +02001987
Christopher Fauletce355072020-04-02 11:44:39 +02001988/* Parses the "option pgsql-check" proxy keyword */
1989int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1990 const char *file, int line)
1991{
1992 static char pgsql_req[] = {
1993 "%[var(check.plen),htonl,hex]" /* The packet length*/
1994 "00030000" /* the version 3.0 */
1995 "7573657200" /* "user" key */
1996 "%[var(check.username),hex]00" /* the username */
1997 "00"
1998 };
1999
2000 struct tcpcheck_ruleset *rs = NULL;
2001 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2002 struct tcpcheck_rule *chk;
2003 struct tcpcheck_var *var = NULL;
2004 char *user = NULL, *errmsg = NULL;
2005 size_t packetlen = 0;
2006 int err_code = 0;
2007
2008 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2009 err_code |= ERR_WARN;
2010
2011 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
2012 goto out;
2013
Christopher Fauletce355072020-04-02 11:44:39 +02002014 curpx->options2 &= ~PR_O2_CHK_ANY;
2015 curpx->options2 |= PR_O2_TCPCHK_CHK;
2016
2017 free_tcpcheck_vars(&rules->preset_vars);
2018 rules->list = NULL;
2019 rules->flags = 0;
2020
2021 cur_arg += 2;
2022 if (!*args[cur_arg] || !*args[cur_arg+1]) {
2023 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
2024 file, line, args[0], args[1]);
2025 goto error;
2026 }
2027 if (strcmp(args[cur_arg], "user") == 0) {
2028 packetlen = 15 + strlen(args[cur_arg+1]);
2029 user = strdup(args[cur_arg+1]);
2030
Christopher Fauletb61caf42020-04-21 10:57:42 +02002031 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02002032 if (user == NULL || var == NULL) {
2033 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2034 goto error;
2035 }
2036 var->data.type = SMP_T_STR;
2037 var->data.u.str.area = user;
2038 var->data.u.str.data = strlen(user);
2039 LIST_INIT(&var->list);
2040 LIST_ADDQ(&rules->preset_vars, &var->list);
2041 user = NULL;
2042 var = NULL;
2043
Christopher Fauletb61caf42020-04-21 10:57:42 +02002044 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02002045 if (var == NULL) {
2046 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2047 goto error;
2048 }
2049 var->data.type = SMP_T_SINT;
2050 var->data.u.sint = packetlen;
2051 LIST_INIT(&var->list);
2052 LIST_ADDQ(&rules->preset_vars, &var->list);
2053 var = NULL;
2054 }
2055 else {
2056 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
2057 file, line, args[0], args[1]);
2058 goto error;
2059 }
2060
Christopher Faulet61cc8522020-04-20 14:54:42 +02002061 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002062 if (rs)
2063 goto ruleset_found;
2064
Christopher Faulet61cc8522020-04-20 14:54:42 +02002065 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002066 if (rs == NULL) {
2067 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2068 goto error;
2069 }
2070
2071 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2072 1, curpx, &rs->rules, file, line, &errmsg);
2073 if (!chk) {
2074 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2075 goto error;
2076 }
2077 chk->index = 0;
2078 LIST_ADDQ(&rs->rules, &chk->list);
2079
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002080 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02002081 1, curpx, &rs->rules, file, line, &errmsg);
2082 if (!chk) {
2083 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2084 goto error;
2085 }
2086 chk->index = 1;
2087 LIST_ADDQ(&rs->rules, &chk->list);
2088
2089 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
2090 "min-recv", "5",
2091 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02002092 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02002093 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002094 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002095 if (!chk) {
2096 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2097 goto error;
2098 }
2099 chk->index = 2;
2100 LIST_ADDQ(&rs->rules, &chk->list);
2101
Christopher Fauletb841c742020-04-27 18:29:49 +02002102 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 +02002103 "min-recv", "9",
2104 "error-status", "L7STS",
2105 "on-success", "PostgreSQL server is ok",
2106 "on-error", "PostgreSQL unknown error",
2107 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002108 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002109 if (!chk) {
2110 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2111 goto error;
2112 }
2113 chk->index = 3;
2114 LIST_ADDQ(&rs->rules, &chk->list);
2115
Christopher Fauletce355072020-04-02 11:44:39 +02002116 ruleset_found:
2117 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002118 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002119 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02002120
2121 out:
2122 free(errmsg);
2123 return err_code;
2124
2125 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002126 free(user);
2127 free(var);
2128 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002129 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002130 err_code |= ERR_ALERT | ERR_FATAL;
2131 goto out;
2132}
2133
2134
2135/* Parses the "option mysql-check" proxy keyword */
2136int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2137 const char *file, int line)
2138{
2139 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
2140 * const char mysql40_client_auth_pkt[] = {
2141 * "\x0e\x00\x00" // packet length
2142 * "\x01" // packet number
2143 * "\x00\x00" // client capabilities
2144 * "\x00\x00\x01" // max packet
2145 * "haproxy\x00" // username (null terminated string)
2146 * "\x00" // filler (always 0x00)
2147 * "\x01\x00\x00" // packet length
2148 * "\x00" // packet number
2149 * "\x01" // COM_QUIT command
2150 * };
2151 */
2152 static char mysql40_rsname[] = "*mysql40-check";
2153 static char mysql40_req[] = {
2154 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2155 "0080" /* client capabilities */
2156 "000001" /* max packet */
2157 "%[var(check.username),hex]00" /* the username */
2158 "00" /* filler (always 0x00) */
2159 "010000" /* packet length*/
2160 "00" /* sequence ID */
2161 "01" /* COM_QUIT command */
2162 };
2163
2164 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
2165 * const char mysql41_client_auth_pkt[] = {
2166 * "\x0e\x00\x00\" // packet length
2167 * "\x01" // packet number
2168 * "\x00\x00\x00\x00" // client capabilities
2169 * "\x00\x00\x00\x01" // max packet
2170 * "\x21" // character set (UTF-8)
2171 * char[23] // All zeroes
2172 * "haproxy\x00" // username (null terminated string)
2173 * "\x00" // filler (always 0x00)
2174 * "\x01\x00\x00" // packet length
2175 * "\x00" // packet number
2176 * "\x01" // COM_QUIT command
2177 * };
2178 */
2179 static char mysql41_rsname[] = "*mysql41-check";
2180 static char mysql41_req[] = {
2181 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2182 "00820000" /* client capabilities */
2183 "00800001" /* max packet */
2184 "21" /* character set (UTF-8) */
2185 "000000000000000000000000" /* 23 bytes, al zeroes */
2186 "0000000000000000000000"
2187 "%[var(check.username),hex]00" /* the username */
2188 "00" /* filler (always 0x00) */
2189 "010000" /* packet length*/
2190 "00" /* sequence ID */
2191 "01" /* COM_QUIT command */
2192 };
2193
2194 struct tcpcheck_ruleset *rs = NULL;
2195 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2196 struct tcpcheck_rule *chk;
2197 struct tcpcheck_var *var = NULL;
2198 char *mysql_rsname = "*mysql-check";
2199 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
2200 int index = 0, err_code = 0;
2201
2202 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2203 err_code |= ERR_WARN;
2204
2205 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2206 goto out;
2207
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002208 curpx->options2 &= ~PR_O2_CHK_ANY;
2209 curpx->options2 |= PR_O2_TCPCHK_CHK;
2210
2211 free_tcpcheck_vars(&rules->preset_vars);
2212 rules->list = NULL;
2213 rules->flags = 0;
2214
2215 cur_arg += 2;
2216 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002217 int packetlen, userlen;
2218
2219 if (strcmp(args[cur_arg], "user") != 0) {
2220 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
2221 file, line, args[0], args[1], args[cur_arg]);
2222 goto error;
2223 }
2224
2225 if (*(args[cur_arg+1]) == 0) {
2226 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
2227 file, line, args[0], args[1], args[cur_arg]);
2228 goto error;
2229 }
2230
2231 hdr = calloc(4, sizeof(*hdr));
2232 user = strdup(args[cur_arg+1]);
2233 userlen = strlen(args[cur_arg+1]);
2234
2235 if (hdr == NULL || user == NULL) {
2236 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2237 goto error;
2238 }
2239
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002240 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002241 packetlen = userlen + 7 + 27;
2242 mysql_req = mysql41_req;
2243 mysql_rsname = mysql41_rsname;
2244 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002245 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002246 packetlen = userlen + 7;
2247 mysql_req = mysql40_req;
2248 mysql_rsname = mysql40_rsname;
2249 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002250 else {
2251 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
2252 file, line, args[cur_arg], args[cur_arg+2]);
2253 goto error;
2254 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002255
2256 hdr[0] = (unsigned char)(packetlen & 0xff);
2257 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
2258 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
2259 hdr[3] = 1;
2260
Christopher Fauletb61caf42020-04-21 10:57:42 +02002261 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002262 if (var == NULL) {
2263 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2264 goto error;
2265 }
2266 var->data.type = SMP_T_STR;
2267 var->data.u.str.area = hdr;
2268 var->data.u.str.data = 4;
2269 LIST_INIT(&var->list);
2270 LIST_ADDQ(&rules->preset_vars, &var->list);
2271 hdr = NULL;
2272 var = NULL;
2273
Christopher Fauletb61caf42020-04-21 10:57:42 +02002274 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002275 if (var == NULL) {
2276 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2277 goto error;
2278 }
2279 var->data.type = SMP_T_STR;
2280 var->data.u.str.area = user;
2281 var->data.u.str.data = strlen(user);
2282 LIST_INIT(&var->list);
2283 LIST_ADDQ(&rules->preset_vars, &var->list);
2284 user = NULL;
2285 var = NULL;
2286 }
2287
Christopher Faulet61cc8522020-04-20 14:54:42 +02002288 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002289 if (rs)
2290 goto ruleset_found;
2291
Christopher Faulet61cc8522020-04-20 14:54:42 +02002292 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002293 if (rs == NULL) {
2294 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2295 goto error;
2296 }
2297
2298 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2299 1, curpx, &rs->rules, file, line, &errmsg);
2300 if (!chk) {
2301 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2302 goto error;
2303 }
2304 chk->index = index++;
2305 LIST_ADDQ(&rs->rules, &chk->list);
2306
2307 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002308 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002309 1, curpx, &rs->rules, file, line, &errmsg);
2310 if (!chk) {
2311 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2312 goto error;
2313 }
2314 chk->index = index++;
2315 LIST_ADDQ(&rs->rules, &chk->list);
2316 }
2317
2318 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002319 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002320 if (!chk) {
2321 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2322 goto error;
2323 }
2324 chk->expect.custom = tcpcheck_mysql_expect_iniths;
2325 chk->index = index++;
2326 LIST_ADDQ(&rs->rules, &chk->list);
2327
2328 if (mysql_req) {
2329 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002330 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002331 if (!chk) {
2332 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2333 goto error;
2334 }
2335 chk->expect.custom = tcpcheck_mysql_expect_ok;
2336 chk->index = index++;
2337 LIST_ADDQ(&rs->rules, &chk->list);
2338 }
2339
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002340 ruleset_found:
2341 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002342 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002343 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002344
2345 out:
2346 free(errmsg);
2347 return err_code;
2348
2349 error:
2350 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02002351 free(user);
2352 free(var);
2353 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002354 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02002355 err_code |= ERR_ALERT | ERR_FATAL;
2356 goto out;
2357}
2358
Christopher Faulet1997eca2020-04-03 23:13:50 +02002359int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2360 const char *file, int line)
2361{
2362 static char *ldap_req = "300C020101600702010304008000";
2363
2364 struct tcpcheck_ruleset *rs = NULL;
2365 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2366 struct tcpcheck_rule *chk;
2367 char *errmsg = NULL;
2368 int err_code = 0;
2369
2370 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2371 err_code |= ERR_WARN;
2372
2373 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2374 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002375
2376 curpx->options2 &= ~PR_O2_CHK_ANY;
2377 curpx->options2 |= PR_O2_TCPCHK_CHK;
2378
2379 free_tcpcheck_vars(&rules->preset_vars);
2380 rules->list = NULL;
2381 rules->flags = 0;
2382
Christopher Faulet61cc8522020-04-20 14:54:42 +02002383 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002384 if (rs)
2385 goto ruleset_found;
2386
Christopher Faulet61cc8522020-04-20 14:54:42 +02002387 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002388 if (rs == NULL) {
2389 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2390 goto error;
2391 }
2392
2393 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
2394 1, curpx, &rs->rules, file, line, &errmsg);
2395 if (!chk) {
2396 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2397 goto error;
2398 }
2399 chk->index = 0;
2400 LIST_ADDQ(&rs->rules, &chk->list);
2401
2402 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
2403 "min-recv", "14",
2404 "on-error", "Not LDAPv3 protocol",
2405 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002406 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002407 if (!chk) {
2408 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2409 goto error;
2410 }
2411 chk->index = 1;
2412 LIST_ADDQ(&rs->rules, &chk->list);
2413
2414 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002415 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002416 if (!chk) {
2417 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2418 goto error;
2419 }
2420 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
2421 chk->index = 2;
2422 LIST_ADDQ(&rs->rules, &chk->list);
2423
Christopher Faulet1997eca2020-04-03 23:13:50 +02002424 ruleset_found:
2425 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002426 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002427 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002428
2429 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02002430 free(errmsg);
2431 return err_code;
2432
2433 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002434 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002435 err_code |= ERR_ALERT | ERR_FATAL;
2436 goto out;
2437}
2438
2439int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2440 const char *file, int line)
2441{
2442 struct tcpcheck_ruleset *rs = NULL;
2443 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2444 struct tcpcheck_rule *chk;
2445 char *spop_req = NULL;
2446 char *errmsg = NULL;
2447 int spop_len = 0, err_code = 0;
2448
2449 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2450 err_code |= ERR_WARN;
2451
2452 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2453 goto out;
2454
Christopher Faulet267b01b2020-04-04 10:27:09 +02002455 curpx->options2 &= ~PR_O2_CHK_ANY;
2456 curpx->options2 |= PR_O2_TCPCHK_CHK;
2457
2458 free_tcpcheck_vars(&rules->preset_vars);
2459 rules->list = NULL;
2460 rules->flags = 0;
2461
2462
Christopher Faulet61cc8522020-04-20 14:54:42 +02002463 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002464 if (rs)
2465 goto ruleset_found;
2466
Christopher Faulet61cc8522020-04-20 14:54:42 +02002467 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002468 if (rs == NULL) {
2469 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2470 goto error;
2471 }
2472
2473 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
2474 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2475 goto error;
2476 }
2477 chunk_reset(&trash);
2478 dump_binary(&trash, spop_req, spop_len);
2479 trash.area[trash.data] = '\0';
2480
2481 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
2482 1, curpx, &rs->rules, file, line, &errmsg);
2483 if (!chk) {
2484 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2485 goto error;
2486 }
2487 chk->index = 0;
2488 LIST_ADDQ(&rs->rules, &chk->list);
2489
2490 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002491 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002492 if (!chk) {
2493 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2494 goto error;
2495 }
2496 chk->expect.custom = tcpcheck_spop_expect_agenthello;
2497 chk->index = 1;
2498 LIST_ADDQ(&rs->rules, &chk->list);
2499
Christopher Faulet267b01b2020-04-04 10:27:09 +02002500 ruleset_found:
2501 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002502 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002503 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002504
2505 out:
2506 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002507 free(errmsg);
2508 return err_code;
2509
2510 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002511 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002512 err_code |= ERR_ALERT | ERR_FATAL;
2513 goto out;
2514}
Christopher Fauletce355072020-04-02 11:44:39 +02002515
Christopher Faulete5870d82020-04-15 11:32:03 +02002516
2517struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
2518{
2519 struct tcpcheck_rule *chk = NULL;
2520 struct tcpcheck_http_hdr *hdr = NULL;
2521 char *meth = NULL, *uri = NULL, *vsn = NULL;
2522 char *hdrs, *body;
2523
2524 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
2525 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
2526 if (hdrs == body)
2527 hdrs = NULL;
2528 if (hdrs) {
2529 *hdrs = '\0';
2530 hdrs +=2;
2531 }
2532 if (body) {
2533 *body = '\0';
2534 body += 4;
2535 }
2536 if (hdrs || body) {
2537 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
2538 " Please, consider to use 'http-check send' directive instead.");
2539 }
2540
2541 chk = calloc(1, sizeof(*chk));
2542 if (!chk) {
2543 memprintf(errmsg, "out of memory");
2544 goto error;
2545 }
2546 chk->action = TCPCHK_ACT_SEND;
2547 chk->send.type = TCPCHK_SEND_HTTP;
2548 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
2549 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
2550 LIST_INIT(&chk->send.http.hdrs);
2551
2552 /* Copy the method, uri and version */
2553 if (*args[cur_arg]) {
2554 if (!*args[cur_arg+1])
2555 uri = args[cur_arg];
2556 else
2557 meth = args[cur_arg];
2558 }
2559 if (*args[cur_arg+1])
2560 uri = args[cur_arg+1];
2561 if (*args[cur_arg+2])
2562 vsn = args[cur_arg+2];
2563
2564 if (meth) {
2565 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
2566 chk->send.http.meth.str.area = strdup(meth);
2567 chk->send.http.meth.str.data = strlen(meth);
2568 if (!chk->send.http.meth.str.area) {
2569 memprintf(errmsg, "out of memory");
2570 goto error;
2571 }
2572 }
2573 if (uri) {
2574 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002575 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002576 memprintf(errmsg, "out of memory");
2577 goto error;
2578 }
2579 }
2580 if (vsn) {
2581 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002582 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002583 memprintf(errmsg, "out of memory");
2584 goto error;
2585 }
2586 }
2587
2588 /* Copy the header */
2589 if (hdrs) {
2590 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
2591 struct h1m h1m;
2592 int i, ret;
2593
2594 /* Build and parse the request */
2595 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
2596
2597 h1m.flags = H1_MF_HDRS_ONLY;
2598 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
2599 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
2600 &h1m, NULL);
2601 if (ret <= 0) {
2602 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
2603 goto error;
2604 }
2605
Christopher Fauletb61caf42020-04-21 10:57:42 +02002606 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002607 hdr = calloc(1, sizeof(*hdr));
2608 if (!hdr) {
2609 memprintf(errmsg, "out of memory");
2610 goto error;
2611 }
2612 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002613 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02002614 if (!hdr->name.ptr) {
2615 memprintf(errmsg, "out of memory");
2616 goto error;
2617 }
2618
Christopher Fauletb61caf42020-04-21 10:57:42 +02002619 ist0(tmp_hdrs[i].v);
2620 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 +02002621 goto error;
2622 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
2623 }
2624 }
2625
2626 /* Copy the body */
2627 if (body) {
2628 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002629 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002630 memprintf(errmsg, "out of memory");
2631 goto error;
2632 }
2633 }
2634
2635 return chk;
2636
2637 error:
2638 free_tcpcheck_http_hdr(hdr);
2639 free_tcpcheck(chk, 0);
2640 return NULL;
2641}
2642
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002643int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2644 const char *file, int line)
2645{
Christopher Faulete5870d82020-04-15 11:32:03 +02002646 struct tcpcheck_ruleset *rs = NULL;
2647 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2648 struct tcpcheck_rule *chk;
2649 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002650 int err_code = 0;
2651
2652 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2653 err_code |= ERR_WARN;
2654
2655 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2656 goto out;
2657
Christopher Faulete5870d82020-04-15 11:32:03 +02002658 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
2659 if (!chk) {
2660 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2661 goto error;
2662 }
2663 if (errmsg) {
2664 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
2665 err_code |= ERR_WARN;
2666 free(errmsg);
2667 errmsg = NULL;
2668 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002669
Christopher Faulete5870d82020-04-15 11:32:03 +02002670 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002671 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02002672 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002673
Christopher Faulete5870d82020-04-15 11:32:03 +02002674 free_tcpcheck_vars(&rules->preset_vars);
2675 rules->list = NULL;
2676 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002677
Christopher Faulete5870d82020-04-15 11:32:03 +02002678 /* Deduce the ruleset name from the proxy info */
2679 chunk_printf(&trash, "*http-check-%s_%s-%d",
2680 ((curpx == defpx) ? "defaults" : curpx->id),
2681 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002682
Christopher Faulet61cc8522020-04-20 14:54:42 +02002683 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002684 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002685 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002686 if (rs == NULL) {
2687 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2688 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002689 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002690 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002691
Christopher Faulete5870d82020-04-15 11:32:03 +02002692 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002693 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulete5870d82020-04-15 11:32:03 +02002694 rules->flags |= TCPCHK_RULES_HTTP_CHK;
2695 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
2696 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2697 rules->list = NULL;
2698 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002699 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002700
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002701 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02002702 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002703 return err_code;
2704
2705 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002706 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02002707 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002708 err_code |= ERR_ALERT | ERR_FATAL;
2709 goto out;
2710}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002711
Christopher Fauletce8111e2020-04-06 15:04:11 +02002712/* Parse the "addr" server keyword */
2713static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2714 char **errmsg)
2715{
2716 struct sockaddr_storage *sk;
Christopher Fauletce8111e2020-04-06 15:04:11 +02002717 int port1, port2, err_code = 0;
2718
2719
2720 if (!*args[*cur_arg+1]) {
2721 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
2722 goto error;
2723 }
2724
Willy Tarreau65ec4e32020-09-16 19:17:08 +02002725 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, NULL, errmsg, NULL, NULL,
2726 PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
Christopher Fauletce8111e2020-04-06 15:04:11 +02002727 if (!sk) {
2728 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
2729 goto error;
2730 }
2731
Christopher Fauletce8111e2020-04-06 15:04:11 +02002732 srv->check.addr = srv->agent.addr = *sk;
2733 srv->flags |= SRV_F_CHECKADDR;
2734 srv->flags |= SRV_F_AGENTADDR;
2735
2736 out:
2737 return err_code;
2738
2739 error:
2740 err_code |= ERR_ALERT | ERR_FATAL;
2741 goto out;
2742}
2743
2744
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002745/* Parse the "agent-addr" server keyword */
2746static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2747 char **errmsg)
2748{
2749 int err_code = 0;
2750
2751 if (!*(args[*cur_arg+1])) {
2752 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
2753 goto error;
2754 }
2755 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
2756 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
2757 goto error;
2758 }
2759
2760 out:
2761 return err_code;
2762
2763 error:
2764 err_code |= ERR_ALERT | ERR_FATAL;
2765 goto out;
2766}
2767
2768/* Parse the "agent-check" server keyword */
2769static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2770 char **errmsg)
2771{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002772 struct tcpcheck_ruleset *rs = NULL;
2773 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2774 struct tcpcheck_rule *chk;
2775 int err_code = 0;
2776
2777 if (srv->do_agent)
2778 goto out;
2779
Christopher Faulet0a5e7132021-01-12 17:29:45 +01002780 if (!(curpx->cap & PR_CAP_BE)) {
2781 memprintf(errmsg, "'%s' ignored because %s '%s' has no backend capability",
2782 args[*cur_arg], proxy_type_str(curpx), curpx->id);
2783 return ERR_WARN;
2784 }
2785
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002786 if (!rules) {
2787 rules = calloc(1, sizeof(*rules));
2788 if (!rules) {
2789 memprintf(errmsg, "out of memory.");
2790 goto error;
2791 }
2792 LIST_INIT(&rules->preset_vars);
2793 srv->agent.tcpcheck_rules = rules;
2794 }
2795 rules->list = NULL;
2796 rules->flags = 0;
2797
Christopher Faulet61cc8522020-04-20 14:54:42 +02002798 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002799 if (rs)
2800 goto ruleset_found;
2801
Christopher Faulet61cc8522020-04-20 14:54:42 +02002802 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002803 if (rs == NULL) {
2804 memprintf(errmsg, "out of memory.");
2805 goto error;
2806 }
2807
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002808 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002809 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
2810 if (!chk) {
2811 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2812 goto error;
2813 }
2814 chk->index = 0;
2815 LIST_ADDQ(&rs->rules, &chk->list);
2816
2817 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002818 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
2819 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002820 if (!chk) {
2821 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2822 goto error;
2823 }
2824 chk->expect.custom = tcpcheck_agent_expect_reply;
2825 chk->index = 1;
2826 LIST_ADDQ(&rs->rules, &chk->list);
2827
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002828 ruleset_found:
2829 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002830 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002831 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002832 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002833
2834 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002835 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002836
2837 error:
2838 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002839 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002840 err_code |= ERR_ALERT | ERR_FATAL;
2841 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002842}
2843
2844/* Parse the "agent-inter" server keyword */
2845static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2846 char **errmsg)
2847{
2848 const char *err = NULL;
2849 unsigned int delay;
2850 int err_code = 0;
2851
2852 if (!*(args[*cur_arg+1])) {
2853 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
2854 goto error;
2855 }
2856
2857 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
2858 if (err == PARSE_TIME_OVER) {
2859 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
2860 args[*cur_arg+1], args[*cur_arg], srv->id);
2861 goto error;
2862 }
2863 else if (err == PARSE_TIME_UNDER) {
2864 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
2865 args[*cur_arg+1], args[*cur_arg], srv->id);
2866 goto error;
2867 }
2868 else if (err) {
2869 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
2870 *err, srv->id);
2871 goto error;
2872 }
2873 if (delay <= 0) {
2874 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
2875 delay, args[*cur_arg], srv->id);
2876 goto error;
2877 }
2878 srv->agent.inter = delay;
2879
2880 out:
2881 return err_code;
2882
2883 error:
2884 err_code |= ERR_ALERT | ERR_FATAL;
2885 goto out;
2886}
2887
2888/* Parse the "agent-port" server keyword */
2889static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2890 char **errmsg)
2891{
2892 int err_code = 0;
2893
2894 if (!*(args[*cur_arg+1])) {
2895 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
2896 goto error;
2897 }
2898
2899 global.maxsock++;
2900 srv->agent.port = atol(args[*cur_arg+1]);
2901
2902 out:
2903 return err_code;
2904
2905 error:
2906 err_code |= ERR_ALERT | ERR_FATAL;
2907 goto out;
2908}
2909
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002910int set_srv_agent_send(struct server *srv, const char *send)
2911{
2912 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2913 struct tcpcheck_var *var = NULL;
2914 char *str;
2915
2916 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002917 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002918 if (str == NULL || var == NULL)
2919 goto error;
2920
2921 free_tcpcheck_vars(&rules->preset_vars);
2922
2923 var->data.type = SMP_T_STR;
2924 var->data.u.str.area = str;
2925 var->data.u.str.data = strlen(str);
2926 LIST_INIT(&var->list);
2927 LIST_ADDQ(&rules->preset_vars, &var->list);
2928
2929 return 1;
2930
2931 error:
2932 free(str);
2933 free(var);
2934 return 0;
2935}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002936
2937/* Parse the "agent-send" server keyword */
2938static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2939 char **errmsg)
2940{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002941 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002942 int err_code = 0;
2943
2944 if (!*(args[*cur_arg+1])) {
2945 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
2946 goto error;
2947 }
2948
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002949 if (!rules) {
2950 rules = calloc(1, sizeof(*rules));
2951 if (!rules) {
2952 memprintf(errmsg, "out of memory.");
2953 goto error;
2954 }
2955 LIST_INIT(&rules->preset_vars);
2956 srv->agent.tcpcheck_rules = rules;
2957 }
2958
2959 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002960 memprintf(errmsg, "out of memory.");
2961 goto error;
2962 }
2963
2964 out:
2965 return err_code;
2966
2967 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002968 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002969 err_code |= ERR_ALERT | ERR_FATAL;
2970 goto out;
2971}
2972
2973/* Parse the "no-agent-send" server keyword */
2974static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2975 char **errmsg)
2976{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002977 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002978 return 0;
2979}
2980
Christopher Fauletce8111e2020-04-06 15:04:11 +02002981/* Parse the "check" server keyword */
2982static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2983 char **errmsg)
2984{
Christopher Faulet0a5e7132021-01-12 17:29:45 +01002985 if (!(curpx->cap & PR_CAP_BE)) {
2986 memprintf(errmsg, "'%s' ignored because %s '%s' has no backend capability",
2987 args[*cur_arg], proxy_type_str(curpx), curpx->id);
2988 return ERR_WARN;
2989 }
2990
Christopher Fauletce8111e2020-04-06 15:04:11 +02002991 srv->do_check = 1;
2992 return 0;
2993}
2994
2995/* Parse the "check-send-proxy" server keyword */
2996static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2997 char **errmsg)
2998{
2999 srv->check.send_proxy = 1;
3000 return 0;
3001}
3002
3003/* Parse the "check-via-socks4" server keyword */
3004static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3005 char **errmsg)
3006{
3007 srv->check.via_socks4 = 1;
3008 return 0;
3009}
3010
3011/* Parse the "no-check" server keyword */
3012static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3013 char **errmsg)
3014{
3015 deinit_srv_check(srv);
3016 return 0;
3017}
3018
3019/* Parse the "no-check-send-proxy" server keyword */
3020static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3021 char **errmsg)
3022{
3023 srv->check.send_proxy = 0;
3024 return 0;
3025}
3026
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003027/* parse the "check-proto" server keyword */
3028static int srv_parse_check_proto(char **args, int *cur_arg,
3029 struct proxy *px, struct server *newsrv, char **err)
3030{
3031 int err_code = 0;
3032
3033 if (!*args[*cur_arg + 1]) {
3034 memprintf(err, "'%s' : missing value", args[*cur_arg]);
3035 goto error;
3036 }
3037 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
3038 if (!newsrv->check.mux_proto) {
3039 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
3040 goto error;
3041 }
3042
3043 out:
3044 return err_code;
3045
3046 error:
3047 err_code |= ERR_ALERT | ERR_FATAL;
3048 goto out;
3049}
3050
3051
Christopher Fauletce8111e2020-04-06 15:04:11 +02003052/* Parse the "rise" server keyword */
3053static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3054 char **errmsg)
3055{
3056 int err_code = 0;
3057
3058 if (!*args[*cur_arg + 1]) {
3059 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3060 goto error;
3061 }
3062
3063 srv->check.rise = atol(args[*cur_arg+1]);
3064 if (srv->check.rise <= 0) {
3065 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3066 goto error;
3067 }
3068
3069 if (srv->check.health)
3070 srv->check.health = srv->check.rise;
3071
3072 out:
3073 return err_code;
3074
3075 error:
3076 deinit_srv_agent_check(srv);
3077 err_code |= ERR_ALERT | ERR_FATAL;
3078 goto out;
3079 return 0;
3080}
3081
3082/* Parse the "fall" server keyword */
3083static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3084 char **errmsg)
3085{
3086 int err_code = 0;
3087
3088 if (!*args[*cur_arg + 1]) {
3089 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3090 goto error;
3091 }
3092
3093 srv->check.fall = atol(args[*cur_arg+1]);
3094 if (srv->check.fall <= 0) {
3095 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3096 goto error;
3097 }
3098
3099 out:
3100 return err_code;
3101
3102 error:
3103 deinit_srv_agent_check(srv);
3104 err_code |= ERR_ALERT | ERR_FATAL;
3105 goto out;
3106 return 0;
3107}
3108
3109/* Parse the "inter" server keyword */
3110static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3111 char **errmsg)
3112{
3113 const char *err = NULL;
3114 unsigned int delay;
3115 int err_code = 0;
3116
3117 if (!*(args[*cur_arg+1])) {
3118 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3119 goto error;
3120 }
3121
3122 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3123 if (err == PARSE_TIME_OVER) {
3124 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3125 args[*cur_arg+1], args[*cur_arg], srv->id);
3126 goto error;
3127 }
3128 else if (err == PARSE_TIME_UNDER) {
3129 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3130 args[*cur_arg+1], args[*cur_arg], srv->id);
3131 goto error;
3132 }
3133 else if (err) {
3134 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3135 *err, srv->id);
3136 goto error;
3137 }
3138 if (delay <= 0) {
3139 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3140 delay, args[*cur_arg], srv->id);
3141 goto error;
3142 }
3143 srv->check.inter = delay;
3144
3145 out:
3146 return err_code;
3147
3148 error:
3149 err_code |= ERR_ALERT | ERR_FATAL;
3150 goto out;
3151}
3152
3153
3154/* Parse the "fastinter" server keyword */
3155static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3156 char **errmsg)
3157{
3158 const char *err = NULL;
3159 unsigned int delay;
3160 int err_code = 0;
3161
3162 if (!*(args[*cur_arg+1])) {
3163 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3164 goto error;
3165 }
3166
3167 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3168 if (err == PARSE_TIME_OVER) {
3169 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3170 args[*cur_arg+1], args[*cur_arg], srv->id);
3171 goto error;
3172 }
3173 else if (err == PARSE_TIME_UNDER) {
3174 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3175 args[*cur_arg+1], args[*cur_arg], srv->id);
3176 goto error;
3177 }
3178 else if (err) {
3179 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3180 *err, srv->id);
3181 goto error;
3182 }
3183 if (delay <= 0) {
3184 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3185 delay, args[*cur_arg], srv->id);
3186 goto error;
3187 }
3188 srv->check.fastinter = delay;
3189
3190 out:
3191 return err_code;
3192
3193 error:
3194 err_code |= ERR_ALERT | ERR_FATAL;
3195 goto out;
3196}
3197
3198
3199/* Parse the "downinter" server keyword */
3200static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3201 char **errmsg)
3202{
3203 const char *err = NULL;
3204 unsigned int delay;
3205 int err_code = 0;
3206
3207 if (!*(args[*cur_arg+1])) {
3208 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3209 goto error;
3210 }
3211
3212 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3213 if (err == PARSE_TIME_OVER) {
3214 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3215 args[*cur_arg+1], args[*cur_arg], srv->id);
3216 goto error;
3217 }
3218 else if (err == PARSE_TIME_UNDER) {
3219 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3220 args[*cur_arg+1], args[*cur_arg], srv->id);
3221 goto error;
3222 }
3223 else if (err) {
3224 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3225 *err, srv->id);
3226 goto error;
3227 }
3228 if (delay <= 0) {
3229 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3230 delay, args[*cur_arg], srv->id);
3231 goto error;
3232 }
3233 srv->check.downinter = delay;
3234
3235 out:
3236 return err_code;
3237
3238 error:
3239 err_code |= ERR_ALERT | ERR_FATAL;
3240 goto out;
3241}
3242
3243/* Parse the "port" server keyword */
3244static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3245 char **errmsg)
3246{
3247 int err_code = 0;
3248
3249 if (!*(args[*cur_arg+1])) {
3250 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
3251 goto error;
3252 }
3253
3254 global.maxsock++;
3255 srv->check.port = atol(args[*cur_arg+1]);
3256 srv->flags |= SRV_F_CHECKPORT;
3257
3258 out:
3259 return err_code;
3260
3261 error:
3262 err_code |= ERR_ALERT | ERR_FATAL;
3263 goto out;
3264}
3265
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003266static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02003267 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003268 { 0, NULL, NULL },
3269}};
3270
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003271static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02003272 { "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 +02003273 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
3274 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
3275 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
3276 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
3277 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003278 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003279 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003280 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
3281 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003282 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003283 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
3284 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
3285 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
3286 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
3287 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
3288 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
3289 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
3290 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003291 { NULL, NULL, 0 },
3292}};
3293
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003294INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003295INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003296
Willy Tarreaubd741542010-03-16 18:46:54 +01003297/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02003298 * Local variables:
3299 * c-indent-level: 8
3300 * c-basic-offset: 8
3301 * End:
3302 */