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