blob: 337030fbd15b286c0a50e54092e3498f69096e6c [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
Willy Tarreau26c25062009-03-08 09:38:41 +01004 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02005 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
Willy Tarreaubaaee002006-06-26 02:48:02 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
Willy Tarreaub8816082008-01-18 12:18:15 +010014#include <assert.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020015#include <ctype.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020016#include <errno.h>
17#include <fcntl.h>
Simon Horman0ba0e4a2015-01-30 11:23:00 +090018#include <stdarg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020019#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020020#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020021#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020022#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020023#include <unistd.h>
Willy Tarreau9f6dc722019-03-01 11:15:10 +010024#include <sys/resource.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020025#include <sys/socket.h>
Dmitry Sivachenkocaf58982009-08-24 15:11:06 +040026#include <sys/types.h>
Simon Horman98637e52014-06-20 12:30:16 +090027#include <sys/wait.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020028#include <netinet/in.h>
Willy Tarreau1274bc42009-07-15 07:16:31 +020029#include <netinet/tcp.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020030#include <arpa/inet.h>
31
Willy Tarreau122eba92020-06-04 10:15:32 +020032#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020033#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020034#include <haproxy/arg.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020035#include <haproxy/cfgparse.h>
Willy Tarreau4aa573d2020-06-04 18:21:56 +020036#include <haproxy/check.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020037#include <haproxy/chunk.h>
Willy Tarreau7c18b542020-06-11 09:23:02 +020038#include <haproxy/dgram.h>
Willy Tarreaueb92deb2020-06-04 10:53:16 +020039#include <haproxy/dns.h>
Christopher Fauletb51e0372020-11-25 13:47:00 +010040#include <haproxy/dynbuf-t.h>
Willy Tarreaubcc67332020-06-05 15:31:31 +020041#include <haproxy/extcheck.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020042#include <haproxy/fd.h>
43#include <haproxy/global.h>
44#include <haproxy/h1.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020045#include <haproxy/http.h>
Willy Tarreau87735332020-06-04 09:08:41 +020046#include <haproxy/http_htx.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020047#include <haproxy/htx.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020048#include <haproxy/istbuf.h>
49#include <haproxy/list.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020050#include <haproxy/log.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020051#include <haproxy/mailers.h>
52#include <haproxy/port_range.h>
53#include <haproxy/proto_tcp.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020054#include <haproxy/protocol.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020055#include <haproxy/proxy.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020056#include <haproxy/queue.h>
57#include <haproxy/regex.h>
58#include <haproxy/sample.h>
Willy Tarreau1e56f922020-06-04 23:20:13 +020059#include <haproxy/server.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020060#include <haproxy/ssl_sock.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020061#include <haproxy/stats-t.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020062#include <haproxy/stream_interface.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020063#include <haproxy/task.h>
Willy Tarreau51cd5952020-06-05 12:25:38 +020064#include <haproxy/tcpcheck.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020065#include <haproxy/thread.h>
66#include <haproxy/time.h>
67#include <haproxy/tools.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020068#include <haproxy/vars.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020069
Olivier Houchard9130a962017-10-17 17:33:43 +020070
Christopher Faulet61cc8522020-04-20 14:54:42 +020071static int wake_srv_chk(struct conn_stream *cs);
72struct data_cb check_conn_cb = {
73 .wake = wake_srv_chk,
74 .name = "CHCK",
75};
Christopher Fauletd7e63962020-04-17 20:15:59 +020076
Christopher Faulet5d503fc2020-03-30 20:34:34 +020077
Gaetan Rivet05d692d2020-02-14 17:42:54 +010078/* Dummy frontend used to create all checks sessions. */
Willy Tarreau51cd5952020-06-05 12:25:38 +020079struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020080
Christopher Faulet61cc8522020-04-20 14:54:42 +020081/**************************************************************************/
82/************************ Handle check results ****************************/
83/**************************************************************************/
84struct check_status {
85 short result; /* one of SRV_CHK_* */
86 char *info; /* human readable short info */
87 char *desc; /* long description */
88};
89
90struct analyze_status {
91 char *desc; /* description */
92 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
93};
94
Simon Horman63a4a822012-03-19 07:24:41 +090095static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +010096 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
97 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +020098 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020099
Willy Tarreau23964182014-05-20 20:56:30 +0200100 /* Below we have finished checks */
101 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100102 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100103
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100104 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200105
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100106 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
107 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
108 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200109
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100110 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
111 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
112 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200113
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100114 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
115 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200116
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200117 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200118
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100119 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
120 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
121 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900122
123 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
124 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200125 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200126};
127
Simon Horman63a4a822012-03-19 07:24:41 +0900128static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100129 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
130
131 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
132 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
133
134 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
135 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
136 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
137 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
138
139 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
140 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
141 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
142};
143
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100144/* checks if <err> is a real error for errno or one that can be ignored, and
145 * return 0 for these ones or <err> for real ones.
146 */
147static inline int unclean_errno(int err)
148{
149 if (err == EAGAIN || err == EINPROGRESS ||
150 err == EISCONN || err == EALREADY)
151 return 0;
152 return err;
153}
154
Christopher Faulet61cc8522020-04-20 14:54:42 +0200155/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200156const char *get_check_status_description(short check_status) {
157
158 const char *desc;
159
160 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200161 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200162 else
163 desc = NULL;
164
165 if (desc && *desc)
166 return desc;
167 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200168 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200169}
170
Christopher Faulet61cc8522020-04-20 14:54:42 +0200171/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200172const char *get_check_status_info(short check_status) {
173
174 const char *info;
175
176 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200177 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200178 else
179 info = NULL;
180
181 if (info && *info)
182 return info;
183 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200184 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200185}
186
Christopher Faulet61cc8522020-04-20 14:54:42 +0200187/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100188const char *get_analyze_status(short analyze_status) {
189
190 const char *desc;
191
192 if (analyze_status < HANA_STATUS_SIZE)
193 desc = analyze_statuses[analyze_status].desc;
194 else
195 desc = NULL;
196
197 if (desc && *desc)
198 return desc;
199 else
200 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
201}
202
Christopher Faulet61cc8522020-04-20 14:54:42 +0200203/* Sets check->status, update check->duration and fill check->result with an
204 * adequate CHK_RES_* value. The new check->health is computed based on the
205 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200206 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200207 * Shows information in logs about failed health check if server is UP or
208 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200209 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200210void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100211{
Simon Horman4a741432013-02-23 15:35:38 +0900212 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200213 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200214 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900215
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200216 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100217 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900218 check->desc[0] = '\0';
219 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200220 return;
221 }
222
Simon Horman4a741432013-02-23 15:35:38 +0900223 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200224 return;
225
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200226 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900227 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
228 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200229 } else
Simon Horman4a741432013-02-23 15:35:38 +0900230 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200231
Simon Horman4a741432013-02-23 15:35:38 +0900232 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200233 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900234 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200235
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100236 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900237 check->duration = -1;
238 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200239 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900240 check->duration = tv_ms_elapsed(&check->start, &now);
241 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200242 }
243
Willy Tarreau23964182014-05-20 20:56:30 +0200244 /* no change is expected if no state change occurred */
245 if (check->result == CHK_RES_NEUTRAL)
246 return;
247
Olivier Houchard0923fa42019-01-11 18:43:04 +0100248 /* If the check was really just sending a mail, it won't have an
249 * associated server, so we're done now.
250 */
251 if (!s)
252 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200253 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200254
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200255 switch (check->result) {
256 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200257 /* Failure to connect to the agent as a secondary check should not
258 * cause the server to be marked down.
259 */
260 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900261 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200262 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100263 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200264 report = 1;
265 check->health--;
266 if (check->health < check->rise)
267 check->health = 0;
268 }
269 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200270
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200271 case CHK_RES_PASSED:
272 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
273 if ((check->health < check->rise + check->fall - 1) &&
274 (check->result == CHK_RES_PASSED || check->health > 0)) {
275 report = 1;
276 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200277
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200278 if (check->health >= check->rise)
279 check->health = check->rise + check->fall - 1; /* OK now */
280 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200281
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200282 /* clear consecutive_errors if observing is enabled */
283 if (s->onerror)
284 s->consecutive_errors = 0;
285 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100286
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200287 default:
288 break;
289 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200290
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200291 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
292 (status != prev_status || report)) {
293 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200294 "%s check for %sserver %s/%s %s%s",
295 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200296 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100297 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100298 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200299 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200300
Emeric Brun5a133512017-10-19 14:42:30 +0200301 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200302
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100303 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200304 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
305 (check->health >= check->rise) ? check->fall : check->rise,
306 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200307
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200308 ha_warning("%s.\n", trash.area);
309 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
310 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200311 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200312}
313
Willy Tarreau4eec5472014-05-20 22:32:27 +0200314/* Marks the check <check>'s server down if the current check is already failed
315 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200316 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200317void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200318{
Simon Horman4a741432013-02-23 15:35:38 +0900319 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900320
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200321 /* The agent secondary check should only cause a server to be marked
322 * as down if check->status is HCHK_STATUS_L7STS, which indicates
323 * that the agent returned "fail", "stopped" or "down".
324 * The implication here is that failure to connect to the agent
325 * as a secondary check should not cause the server to be marked
326 * down. */
327 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
328 return;
329
Willy Tarreau4eec5472014-05-20 22:32:27 +0200330 if (check->health > 0)
331 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100332
Willy Tarreau4eec5472014-05-20 22:32:27 +0200333 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200334 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200335}
336
Willy Tarreauaf549582014-05-16 17:37:50 +0200337/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200338 * it isn't in maintenance, it is not tracking a down server and other checks
339 * comply. The rule is simple : by default, a server is up, unless any of the
340 * following conditions is true :
341 * - health check failed (check->health < rise)
342 * - agent check failed (agent->health < rise)
343 * - the server tracks a down server (track && track->state == STOPPED)
344 * Note that if the server has a slowstart, it will switch to STARTING instead
345 * of RUNNING. Also, only the health checks support the nolb mode, so the
346 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200347 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200348void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200349{
Simon Horman4a741432013-02-23 15:35:38 +0900350 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100351
Emeric Brun52a91d32017-08-31 14:41:55 +0200352 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200353 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100354
Emeric Brun52a91d32017-08-31 14:41:55 +0200355 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200356 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100357
Willy Tarreau3e048382014-05-21 10:30:54 +0200358 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
359 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100360
Willy Tarreau3e048382014-05-21 10:30:54 +0200361 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
362 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200363
Emeric Brun52a91d32017-08-31 14:41:55 +0200364 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200365 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100366
Emeric Brun5a133512017-10-19 14:42:30 +0200367 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100368}
369
Willy Tarreaudb58b792014-05-21 13:57:23 +0200370/* Marks the check <check> as valid and tries to set its server into stopping mode
371 * if it was running or starting, and provided it isn't in maintenance and other
372 * checks comply. The conditions for the server to be marked in stopping mode are
373 * the same as for it to be turned up. Also, only the health checks support the
374 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200375 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200376void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200377{
Simon Horman4a741432013-02-23 15:35:38 +0900378 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100379
Emeric Brun52a91d32017-08-31 14:41:55 +0200380 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200381 return;
382
Willy Tarreaudb58b792014-05-21 13:57:23 +0200383 if (check->state & CHK_ST_AGENT)
384 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100385
Emeric Brun52a91d32017-08-31 14:41:55 +0200386 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200387 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100388
Willy Tarreaudb58b792014-05-21 13:57:23 +0200389 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
390 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100391
Willy Tarreaudb58b792014-05-21 13:57:23 +0200392 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
393 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100394
Willy Tarreaub26881a2017-12-23 11:16:49 +0100395 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100396}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200397
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100398/* note: use health_adjust() only, which first checks that the observe mode is
399 * enabled.
400 */
401void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100402{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100403 int failed;
404 int expire;
405
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100406 if (s->observe >= HANA_OBS_SIZE)
407 return;
408
Willy Tarreaubb956662013-01-24 00:37:39 +0100409 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100410 return;
411
412 switch (analyze_statuses[status].lr[s->observe - 1]) {
413 case 1:
414 failed = 1;
415 break;
416
417 case 2:
418 failed = 0;
419 break;
420
421 default:
422 return;
423 }
424
425 if (!failed) {
426 /* good: clear consecutive_errors */
427 s->consecutive_errors = 0;
428 return;
429 }
430
Olivier Houchard7059c552019-03-08 18:49:32 +0100431 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100432
433 if (s->consecutive_errors < s->consecutive_errors_limit)
434 return;
435
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100436 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
437 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100438
439 switch (s->onerror) {
440 case HANA_ONERR_FASTINTER:
441 /* force fastinter - nothing to do here as all modes force it */
442 break;
443
444 case HANA_ONERR_SUDDTH:
445 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900446 if (s->check.health > s->check.rise)
447 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100448
Tim Duesterhus588b3142020-05-29 14:35:51 +0200449 /* fall through */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100450
451 case HANA_ONERR_FAILCHK:
452 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200453 set_server_check_status(&s->check, HCHK_STATUS_HANA,
454 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200455 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100456 break;
457
458 case HANA_ONERR_MARKDWN:
459 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900460 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200461 set_server_check_status(&s->check, HCHK_STATUS_HANA,
462 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200463 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100464 break;
465
466 default:
467 /* write a warning? */
468 break;
469 }
470
471 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100472 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100473
Simon Horman66183002013-02-23 10:16:43 +0900474 if (s->check.fastinter) {
475 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300476 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200477 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300478 /* requeue check task with new expire */
479 task_queue(s->check.task);
480 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100481 }
Willy Tarreauef781042010-01-27 11:53:01 +0100482}
483
Christopher Faulet61cc8522020-04-20 14:54:42 +0200484/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100485 * closed, keep errno intact as it is supposed to contain the valid error code.
486 * If no error is reported, check the socket's error queue using getsockopt().
487 * Warning, this must be done only once when returning from poll, and never
488 * after an I/O error was attempted, otherwise the error queue might contain
489 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
490 * socket. Returns non-zero if an error was reported, zero if everything is
491 * clean (including a properly closed socket).
492 */
493static int retrieve_errno_from_socket(struct connection *conn)
494{
495 int skerr;
496 socklen_t lskerr = sizeof(skerr);
497
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100498 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100499 return 1;
500
Willy Tarreau3c728722014-01-23 13:50:42 +0100501 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100502 return 0;
503
Willy Tarreau585744b2017-08-24 14:31:19 +0200504 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100505 errno = skerr;
506
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100507 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100508
509 if (!errno) {
510 /* we could not retrieve an error, that does not mean there is
511 * none. Just don't change anything and only report the prior
512 * error if any.
513 */
514 if (conn->flags & CO_FL_ERROR)
515 return 1;
516 else
517 return 0;
518 }
519
520 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
521 return 1;
522}
523
Christopher Faulet61cc8522020-04-20 14:54:42 +0200524/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100525 * and adjust the server status accordingly. It may make use of <errno_bck>
526 * if non-null when the caller is absolutely certain of its validity (eg:
527 * checked just after a syscall). If the caller doesn't have a valid errno,
528 * it can pass zero, and retrieve_errno_from_socket() will be called to try
529 * to extract errno from the socket. If no error is reported, it will consider
530 * the <expired> flag. This is intended to be used when a connection error was
531 * reported in conn->flags or when a timeout was reported in <expired>. The
532 * function takes care of not updating a server status which was already set.
533 * All situations where at least one of <expired> or CO_FL_ERROR are set
534 * produce a status.
535 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200536void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100537{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200538 struct conn_stream *cs = check->cs;
539 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100540 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200541 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200542 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100543
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100544 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100545 return;
546
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100547 errno = unclean_errno(errno_bck);
548 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100549 retrieve_errno_from_socket(conn);
550
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200551 if (conn && !(conn->flags & CO_FL_ERROR) &&
552 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100553 return;
554
555 /* we'll try to build a meaningful error message depending on the
556 * context of the error possibly present in conn->err_code, and the
557 * socket error possibly collected above. This is useful to know the
558 * exact step of the L6 layer (eg: SSL handshake).
559 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200560 chk = get_trash_chunk();
561
Christopher Faulet799f3a42020-04-07 12:06:14 +0200562 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200563 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200564 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200565 if (!step)
566 chunk_printf(chk, " at initial connection step of tcp-check");
567 else {
568 chunk_printf(chk, " at step %d of tcp-check", step);
569 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200570 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
571 if (check->current_step->connect.port)
572 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200573 else
574 chunk_appendf(chk, " (connect)");
575 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200576 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
577 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100578
579 switch (expect->type) {
580 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200581 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100582 break;
583 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200584 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100585 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200586 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200587 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100588 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200589 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100590 chunk_appendf(chk, " (expect binary regex)");
591 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200592 case TCPCHK_EXPECT_STRING_LF:
593 chunk_appendf(chk, " (expect log-format string)");
594 break;
595 case TCPCHK_EXPECT_BINARY_LF:
596 chunk_appendf(chk, " (expect log-format binary)");
597 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200598 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200599 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200600 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200601 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200602 chunk_appendf(chk, " (expect HTTP status regex)");
603 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200604 case TCPCHK_EXPECT_HTTP_HEADER:
605 chunk_appendf(chk, " (expect HTTP header pattern)");
606 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200607 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200608 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200609 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200610 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200611 chunk_appendf(chk, " (expect HTTP body regex)");
612 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200613 case TCPCHK_EXPECT_HTTP_BODY_LF:
614 chunk_appendf(chk, " (expect log-format HTTP body)");
615 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200616 case TCPCHK_EXPECT_CUSTOM:
617 chunk_appendf(chk, " (expect custom function)");
618 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100619 case TCPCHK_EXPECT_UNDEF:
620 chunk_appendf(chk, " (undefined expect!)");
621 break;
622 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200623 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200624 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200625 chunk_appendf(chk, " (send)");
626 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200627
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200628 if (check->current_step && check->current_step->comment)
629 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200630 }
631 }
632
Willy Tarreau00149122017-10-04 18:05:01 +0200633 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100634 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200635 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
636 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100637 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200638 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
639 chk->area);
640 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100641 }
642 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100643 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200644 chunk_printf(&trash, "%s%s", strerror(errno),
645 chk->area);
646 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100647 }
648 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200649 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100650 }
651 }
652
Willy Tarreau00149122017-10-04 18:05:01 +0200653 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200654 /* NOTE: this is reported after <fall> tries */
655 chunk_printf(chk, "No port available for the TCP connection");
656 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
657 }
658
Christopher Faulet5e293762020-10-26 11:10:49 +0100659 if (!conn || !conn->ctrl) {
660 /* error before any connection attempt (connection allocation error or no control layer) */
Willy Tarreau00149122017-10-04 18:05:01 +0200661 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
662 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100663 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100664 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200665 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100666 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
667 else if (expired)
668 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200669
670 /*
671 * might be due to a server IP change.
672 * Let's trigger a DNS resolution if none are currently running.
673 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100674 if (check->server)
675 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200676
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100677 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100678 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100679 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200680 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100681 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
682 else if (expired)
683 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
684 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200685 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100686 /* I/O error after connection was established and before we could diagnose */
687 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
688 }
689 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200690 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
691
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100692 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200693 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
694 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200695 tout = check->current_step->expect.tout_status;
696 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100697 }
698
699 return;
700}
701
Simon Horman98637e52014-06-20 12:30:16 +0900702
Christopher Faulet61cc8522020-04-20 14:54:42 +0200703/* Builds the server state header used by HTTP health-checks */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200704int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +0900705{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200706 int sv_state;
707 int ratio;
708 char addr[46];
709 char port[6];
710 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
711 "UP %d/%d", "UP",
712 "NOLB %d/%d", "NOLB",
713 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +0900714
Christopher Faulet61cc8522020-04-20 14:54:42 +0200715 if (!(s->check.state & CHK_ST_ENABLED))
716 sv_state = 6;
717 else if (s->cur_state != SRV_ST_STOPPED) {
718 if (s->check.health == s->check.rise + s->check.fall - 1)
719 sv_state = 3; /* UP */
720 else
721 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +0900722
Christopher Faulet61cc8522020-04-20 14:54:42 +0200723 if (s->cur_state == SRV_ST_STOPPING)
724 sv_state += 2;
725 } else {
726 if (s->check.health)
727 sv_state = 1; /* going up */
728 else
729 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +0900730 }
Willy Tarreaub7b24782016-06-21 15:32:29 +0200731
Christopher Faulet61cc8522020-04-20 14:54:42 +0200732 chunk_appendf(buf, srv_hlt_st[sv_state],
733 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
734 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +0200735
Christopher Faulet61cc8522020-04-20 14:54:42 +0200736 addr_to_str(&s->addr, addr, sizeof(addr));
737 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
738 snprintf(port, sizeof(port), "%u", s->svc_port);
739 else
740 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +0200741
Christopher Faulet61cc8522020-04-20 14:54:42 +0200742 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
743 addr, port, s->proxy->id, s->id,
744 global.node,
745 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
746 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
747 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
748 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +0100749
Christopher Faulet61cc8522020-04-20 14:54:42 +0200750 if ((s->cur_state == SRV_ST_STARTING) &&
751 now.tv_sec < s->last_change + s->slowstart &&
752 now.tv_sec >= s->last_change) {
753 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
754 chunk_appendf(buf, "; throttle=%d%%", ratio);
755 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200756
Christopher Faulet61cc8522020-04-20 14:54:42 +0200757 return b_data(buf);
758}
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200759
Willy Tarreau51cd5952020-06-05 12:25:38 +0200760/**************************************************************************/
Willy Tarreau51cd5952020-06-05 12:25:38 +0200761/***************** Health-checks based on connections *********************/
762/**************************************************************************/
763/* This function is used only for server health-checks. It handles connection
764 * status updates including errors. If necessary, it wakes the check task up.
765 * It returns 0 on normal cases, <0 if at least one close() has happened on the
766 * connection (eg: reconnect). It relies on tcpcheck_main().
Christopher Faulet61cc8522020-04-20 14:54:42 +0200767 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200768static int wake_srv_chk(struct conn_stream *cs)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200769{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200770 struct connection *conn = cs->conn;
771 struct check *check = cs->data;
772 struct email_alertq *q = container_of(check, typeof(*q), check);
773 int ret = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200774
Willy Tarreau51cd5952020-06-05 12:25:38 +0200775 if (check->server)
776 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
777 else
778 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200779
Willy Tarreau51cd5952020-06-05 12:25:38 +0200780 /* we may have to make progress on the TCP checks */
781 ret = tcpcheck_main(check);
Christopher Fauletaaab0832020-05-05 15:54:22 +0200782
Willy Tarreau51cd5952020-06-05 12:25:38 +0200783 cs = check->cs;
784 conn = cs->conn;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200785
Willy Tarreau51cd5952020-06-05 12:25:38 +0200786 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
787 /* We may get error reports bypassing the I/O handlers, typically
788 * the case when sending a pure TCP check which fails, then the I/O
789 * handlers above are not called. This is completely handled by the
790 * main processing task so let's simply wake it up. If we get here,
791 * we expect errno to still be valid.
792 */
793 chk_report_conn_err(check, errno, 0);
794 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200795 }
796
Willy Tarreau51cd5952020-06-05 12:25:38 +0200797 if (check->result != CHK_RES_UNKNOWN) {
798 /* Check complete or aborted. If connection not yet closed do it
799 * now and wake the check task up to be sure the result is
800 * handled ASAP. */
801 conn_sock_drain(conn);
802 cs_close(cs);
803 ret = -1;
804 /* We may have been scheduled to run, and the
805 * I/O handler expects to have a cs, so remove
806 * the tasklet
807 */
808 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
809 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200810 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200811
Willy Tarreau51cd5952020-06-05 12:25:38 +0200812 if (check->server)
813 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
814 else
815 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200816
Willy Tarreau51cd5952020-06-05 12:25:38 +0200817 /* if a connection got replaced, we must absolutely prevent the connection
818 * handler from touching its fd, and perform the FD polling updates ourselves
819 */
820 if (ret < 0)
821 conn_cond_update_polling(conn);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200822
Christopher Faulet61cc8522020-04-20 14:54:42 +0200823 return ret;
824}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200825
Willy Tarreau51cd5952020-06-05 12:25:38 +0200826/* This function checks if any I/O is wanted, and if so, attempts to do so */
827static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200828{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200829 struct check *check = ctx;
830 struct conn_stream *cs = check->cs;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200831
Willy Tarreau51cd5952020-06-05 12:25:38 +0200832 wake_srv_chk(cs);
833 return NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200834}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200835
Willy Tarreau51cd5952020-06-05 12:25:38 +0200836/* manages a server health-check that uses a connection. Returns
837 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Faulet61cc8522020-04-20 14:54:42 +0200838 *
839 * Please do NOT place any return statement in this function and only leave
Willy Tarreau51cd5952020-06-05 12:25:38 +0200840 * via the out_unlock label.
Christopher Faulet61cc8522020-04-20 14:54:42 +0200841 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200842static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200843{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200844 struct check *check = context;
845 struct proxy *proxy = check->proxy;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200846 struct conn_stream *cs = check->cs;
847 struct connection *conn = cs_conn(cs);
Willy Tarreau51cd5952020-06-05 12:25:38 +0200848 int rv;
849 int expired = tick_is_expired(t->expire, now_ms);
Willy Tarreaudeccd112018-06-14 18:38:55 +0200850
Willy Tarreau51cd5952020-06-05 12:25:38 +0200851 if (check->server)
852 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
853 if (!(check->state & CHK_ST_INPROGRESS)) {
854 /* no check currently running */
855 if (!expired) /* woke up too early */
856 goto out_unlock;
Willy Tarreauabca5b62013-12-06 14:19:25 +0100857
Willy Tarreau51cd5952020-06-05 12:25:38 +0200858 /* we don't send any health-checks when the proxy is
859 * stopped, the server should not be checked or the check
860 * is disabled.
861 */
862 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
Willy Tarreauc3914d42020-09-24 08:39:22 +0200863 proxy->disabled)
Willy Tarreau51cd5952020-06-05 12:25:38 +0200864 goto reschedule;
Christopher Faulet404f9192020-04-09 23:13:54 +0200865
Willy Tarreau51cd5952020-06-05 12:25:38 +0200866 /* we'll initiate a new check */
867 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet404f9192020-04-09 23:13:54 +0200868
Willy Tarreau51cd5952020-06-05 12:25:38 +0200869 check->state |= CHK_ST_INPROGRESS;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200870
Willy Tarreau51cd5952020-06-05 12:25:38 +0200871 task_set_affinity(t, tid_bit);
872
873 check->current_step = NULL;
874 tcpcheck_main(check);
875 goto out_unlock;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200876 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200877 else {
878 /* there was a test running.
879 * First, let's check whether there was an uncaught error,
880 * which can happen on connect timeout or error.
881 */
882 if (check->result == CHK_RES_UNKNOWN) {
883 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
884 chk_report_conn_err(check, 0, expired);
885 }
886 else
887 goto out_unlock; /* timeout not reached, wait again */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200888 }
Christopher Faulet404f9192020-04-09 23:13:54 +0200889
Willy Tarreau51cd5952020-06-05 12:25:38 +0200890 /* check complete or aborted */
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200891
Willy Tarreau51cd5952020-06-05 12:25:38 +0200892 check->current_step = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200893
Willy Tarreau51cd5952020-06-05 12:25:38 +0200894 if (conn && conn->xprt) {
895 /* The check was aborted and the connection was not yet closed.
896 * This can happen upon timeout, or when an external event such
897 * as a failed response coupled with "observe layer7" caused the
898 * server state to be suddenly changed.
899 */
900 conn_sock_drain(conn);
901 cs_close(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200902 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200903
Willy Tarreau51cd5952020-06-05 12:25:38 +0200904 if (cs) {
905 if (check->wait_list.events)
906 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
907 /* We may have been scheduled to run, and the
908 * I/O handler expects to have a cs, so remove
909 * the tasklet
910 */
911 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
912 cs_destroy(cs);
913 cs = check->cs = NULL;
914 conn = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200915 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200916
917 if (check->sess != NULL) {
918 vars_prune(&check->vars, check->sess, NULL);
919 session_free(check->sess);
920 check->sess = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200921 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200922
923 if (check->server) {
924 if (check->result == CHK_RES_FAILED) {
925 /* a failure or timeout detected */
926 check_notify_failure(check);
927 }
928 else if (check->result == CHK_RES_CONDPASS) {
929 /* check is OK but asks for stopping mode */
930 check_notify_stopping(check);
931 }
932 else if (check->result == CHK_RES_PASSED) {
933 /* a success was detected */
934 check_notify_success(check);
935 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200936 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200937 task_set_affinity(t, MAX_THREADS_MASK);
Christopher Fauletb51e0372020-11-25 13:47:00 +0100938 check_release_buf(check, &check->bi);
939 check_release_buf(check, &check->bo);
940 check->state &= ~(CHK_ST_INPROGRESS|CHK_ST_IN_ALLOC|CHK_ST_OUT_ALLOC);
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200941
Willy Tarreau51cd5952020-06-05 12:25:38 +0200942 if (check->server) {
943 rv = 0;
944 if (global.spread_checks > 0) {
945 rv = srv_getinter(check) * global.spread_checks / 100;
946 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
Christopher Faulet61cc8522020-04-20 14:54:42 +0200947 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200948 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet61cc8522020-04-20 14:54:42 +0200949 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200950 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200951
952 reschedule:
953 while (tick_is_expired(t->expire, now_ms))
954 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
955 out_unlock:
956 if (check->server)
957 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
958 return t;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200959}
960
Willy Tarreau51cd5952020-06-05 12:25:38 +0200961
Christopher Faulet61cc8522020-04-20 14:54:42 +0200962/**************************************************************************/
963/************************** Init/deinit checks ****************************/
964/**************************************************************************/
Christopher Fauletb51e0372020-11-25 13:47:00 +0100965/*
966 * Tries to grab a buffer and to re-enables processing on check <target>. The
967 * check flags are used to figure what buffer was requested. It returns 1 if the
968 * allocation succeeds, in which case the I/O tasklet is woken up, or 0 if it's
969 * impossible to wake up and we prefer to be woken up later.
970 */
971int check_buf_available(void *target)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200972{
Christopher Fauletb51e0372020-11-25 13:47:00 +0100973 struct check *check = target;
974
975 if ((check->state & CHK_ST_IN_ALLOC) && b_alloc_margin(&check->bi, 0)) {
976 check->state &= ~CHK_ST_IN_ALLOC;
977 tasklet_wakeup(check->wait_list.tasklet);
978 return 1;
979 }
980 if ((check->state & CHK_ST_OUT_ALLOC) && b_alloc_margin(&check->bo, 0)) {
981 check->state &= ~CHK_ST_OUT_ALLOC;
982 tasklet_wakeup(check->wait_list.tasklet);
983 return 1;
984 }
985
986 return 0;
987}
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200988
Christopher Fauletb51e0372020-11-25 13:47:00 +0100989/*
990 * Allocate a buffer. If if fails, it adds the check in buffer wait queue.
991 */
992struct buffer *check_get_buf(struct check *check, struct buffer *bptr)
993{
994 struct buffer *buf = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200995
Christopher Fauletb51e0372020-11-25 13:47:00 +0100996 if (likely(!MT_LIST_ADDED(&check->buf_wait.list)) &&
997 unlikely((buf = b_alloc_margin(bptr, 0)) == NULL)) {
998 check->buf_wait.target = check;
999 check->buf_wait.wakeup_cb = check_buf_available;
1000 MT_LIST_ADDQ(&buffer_wq, &check->buf_wait.list);
1001 }
1002 return buf;
1003}
1004
1005/*
1006 * Release a buffer, if any, and try to wake up entities waiting in the buffer
1007 * wait queue.
1008 */
1009void check_release_buf(struct check *check, struct buffer *bptr)
1010{
1011 if (bptr->size) {
1012 b_free(bptr);
1013 offer_buffers(check->buf_wait.target, tasks_run_queue);
1014 }
1015}
1016
1017const char *init_check(struct check *check, int type)
1018{
1019 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001020
Christopher Fauletb51e0372020-11-25 13:47:00 +01001021 check->bi = BUF_NULL;
1022 check->bo = BUF_NULL;
1023 MT_LIST_INIT(&check->buf_wait.list);
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001024
Christopher Faulet61cc8522020-04-20 14:54:42 +02001025 check->wait_list.tasklet = tasklet_new();
1026 if (!check->wait_list.tasklet)
1027 return "out of memory while allocating check tasklet";
1028 check->wait_list.events = 0;
1029 check->wait_list.tasklet->process = event_srv_chk_io;
1030 check->wait_list.tasklet->context = check;
1031 return NULL;
1032}
1033
1034void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001035{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001036 task_destroy(check->task);
1037 if (check->wait_list.tasklet)
1038 tasklet_free(check->wait_list.tasklet);
1039
Christopher Fauletb51e0372020-11-25 13:47:00 +01001040 check_release_buf(check, &check->bi);
1041 check_release_buf(check, &check->bo);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001042 if (check->cs) {
1043 free(check->cs->conn);
1044 check->cs->conn = NULL;
1045 cs_free(check->cs);
1046 check->cs = NULL;
1047 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001048}
1049
Christopher Faulet61cc8522020-04-20 14:54:42 +02001050/* manages a server health-check. Returns the time the task accepts to wait, or
1051 * TIME_ETERNITY for infinity.
1052 */
Willy Tarreaucee013e2020-06-05 11:40:38 +02001053struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001054{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001055 struct check *check = context;
1056
1057 if (check->type == PR_O2_EXT_CHK)
1058 return process_chk_proc(t, context, state);
1059 return process_chk_conn(t, context, state);
1060
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001061}
1062
Christopher Faulet61cc8522020-04-20 14:54:42 +02001063
1064static int start_check_task(struct check *check, int mininter,
1065 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001066{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001067 struct task *t;
1068 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001069
Christopher Faulet61cc8522020-04-20 14:54:42 +02001070 if (check->type == PR_O2_EXT_CHK)
1071 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001072
Christopher Faulet61cc8522020-04-20 14:54:42 +02001073 /* task for the check */
1074 if ((t = task_new(thread_mask)) == NULL) {
1075 ha_alert("Starting [%s:%s] check: out of memory.\n",
1076 check->server->proxy->id, check->server->id);
1077 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001078 }
1079
Christopher Faulet61cc8522020-04-20 14:54:42 +02001080 check->task = t;
1081 t->process = process_chk;
1082 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001083
Christopher Faulet61cc8522020-04-20 14:54:42 +02001084 if (mininter < srv_getinter(check))
1085 mininter = srv_getinter(check);
1086
1087 if (global.max_spread_checks && mininter > global.max_spread_checks)
1088 mininter = global.max_spread_checks;
1089
1090 /* check this every ms */
1091 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
1092 check->start = now;
1093 task_queue(t);
1094
1095 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001096}
1097
Christopher Faulet61cc8522020-04-20 14:54:42 +02001098/* updates the server's weight during a warmup stage. Once the final weight is
1099 * reached, the task automatically stops. Note that any server status change
1100 * must have updated s->last_change accordingly.
1101 */
1102static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001103{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001104 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001105
Christopher Faulet61cc8522020-04-20 14:54:42 +02001106 /* by default, plan on stopping the task */
1107 t->expire = TICK_ETERNITY;
1108 if ((s->next_admin & SRV_ADMF_MAINT) ||
1109 (s->next_state != SRV_ST_STARTING))
1110 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02001111
Christopher Faulet61cc8522020-04-20 14:54:42 +02001112 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001113
Christopher Faulet61cc8522020-04-20 14:54:42 +02001114 /* recalculate the weights and update the state */
1115 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02001116
Christopher Faulet61cc8522020-04-20 14:54:42 +02001117 /* probably that we can refill this server with a bit more connections */
1118 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02001119
Christopher Faulet61cc8522020-04-20 14:54:42 +02001120 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02001121
Christopher Faulet61cc8522020-04-20 14:54:42 +02001122 /* get back there in 1 second or 1/20th of the slowstart interval,
1123 * whichever is greater, resulting in small 5% steps.
1124 */
1125 if (s->next_state == SRV_ST_STARTING)
1126 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
1127 return t;
1128}
1129
1130/*
1131 * Start health-check.
1132 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
1133 */
1134static int start_checks()
1135{
1136
1137 struct proxy *px;
1138 struct server *s;
1139 struct task *t;
1140 int nbcheck=0, mininter=0, srvpos=0;
1141
1142 /* 0- init the dummy frontend used to create all checks sessions */
1143 init_new_proxy(&checks_fe);
1144 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
1145 checks_fe.mode = PR_MODE_TCP;
1146 checks_fe.maxconn = 0;
1147 checks_fe.conn_retries = CONN_RETRIES;
1148 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
1149 checks_fe.timeout.client = TICK_ETERNITY;
1150
1151 /* 1- count the checkers to run simultaneously.
1152 * We also determine the minimum interval among all of those which
1153 * have an interval larger than SRV_CHK_INTER_THRES. This interval
1154 * will be used to spread their start-up date. Those which have
1155 * a shorter interval will start independently and will not dictate
1156 * too short an interval for all others.
1157 */
1158 for (px = proxies_list; px; px = px->next) {
1159 for (s = px->srv; s; s = s->next) {
1160 if (s->slowstart) {
1161 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
1162 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1163 return ERR_ALERT | ERR_FATAL;
1164 }
1165 /* We need a warmup task that will be called when the server
1166 * state switches from down to up.
1167 */
1168 s->warmup = t;
1169 t->process = server_warmup;
1170 t->context = s;
1171 /* server can be in this state only because of */
1172 if (s->next_state == SRV_ST_STARTING)
1173 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 +02001174 }
1175
Christopher Faulet61cc8522020-04-20 14:54:42 +02001176 if (s->check.state & CHK_ST_CONFIGURED) {
1177 nbcheck++;
1178 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
1179 (!mininter || mininter > srv_getinter(&s->check)))
1180 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02001181 }
1182
Christopher Faulet61cc8522020-04-20 14:54:42 +02001183 if (s->agent.state & CHK_ST_CONFIGURED) {
1184 nbcheck++;
1185 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
1186 (!mininter || mininter > srv_getinter(&s->agent)))
1187 mininter = srv_getinter(&s->agent);
1188 }
Christopher Faulet5c288742020-03-31 08:15:58 +02001189 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001190 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001191
Christopher Faulet61cc8522020-04-20 14:54:42 +02001192 if (!nbcheck)
1193 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001194
Christopher Faulet61cc8522020-04-20 14:54:42 +02001195 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02001196
Christopher Faulet61cc8522020-04-20 14:54:42 +02001197 /*
1198 * 2- start them as far as possible from each others. For this, we will
1199 * start them after their interval set to the min interval divided by
1200 * the number of servers, weighted by the server's position in the list.
1201 */
1202 for (px = proxies_list; px; px = px->next) {
1203 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
1204 if (init_pid_list()) {
1205 ha_alert("Starting [%s] check: out of memory.\n", px->id);
1206 return ERR_ALERT | ERR_FATAL;
1207 }
1208 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001209
Christopher Faulet61cc8522020-04-20 14:54:42 +02001210 for (s = px->srv; s; s = s->next) {
1211 /* A task for the main check */
1212 if (s->check.state & CHK_ST_CONFIGURED) {
1213 if (s->check.type == PR_O2_EXT_CHK) {
1214 if (!prepare_external_check(&s->check))
1215 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001216 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001217 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
1218 return ERR_ALERT | ERR_FATAL;
1219 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02001220 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001221
Christopher Faulet61cc8522020-04-20 14:54:42 +02001222 /* A task for a auxiliary agent check */
1223 if (s->agent.state & CHK_ST_CONFIGURED) {
1224 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
1225 return ERR_ALERT | ERR_FATAL;
1226 }
1227 srvpos++;
1228 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001229 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001230 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001231 return 0;
1232}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001233
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001234
Christopher Faulet61cc8522020-04-20 14:54:42 +02001235/*
1236 * Return value:
1237 * the port to be used for the health check
1238 * 0 in case no port could be found for the check
1239 */
1240static int srv_check_healthcheck_port(struct check *chk)
1241{
1242 int i = 0;
1243 struct server *srv = NULL;
1244
1245 srv = chk->server;
1246
1247 /* by default, we use the health check port ocnfigured */
1248 if (chk->port > 0)
1249 return chk->port;
1250
1251 /* try to get the port from check_core.addr if check.port not set */
1252 i = get_host_port(&chk->addr);
1253 if (i > 0)
1254 return i;
1255
1256 /* try to get the port from server address */
1257 /* prevent MAPPORTS from working at this point, since checks could
1258 * not be performed in such case (MAPPORTS impose a relative ports
1259 * based on live traffic)
1260 */
1261 if (srv->flags & SRV_F_MAPPORTS)
1262 return 0;
1263
1264 i = srv->svc_port; /* by default */
1265 if (i > 0)
1266 return i;
1267
1268 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001269}
1270
Christopher Faulet61cc8522020-04-20 14:54:42 +02001271/* Initializes an health-check attached to the server <srv>. Non-zero is returned
1272 * if an error occurred.
1273 */
1274static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001275{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001276 const char *err;
1277 struct tcpcheck_rule *r;
1278 int ret = 0;
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001279 int check_type;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001280
Christopher Faulet61cc8522020-04-20 14:54:42 +02001281 if (!srv->do_check)
1282 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001283
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001284 check_type = srv->check.tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001285
Christopher Faulet61cc8522020-04-20 14:54:42 +02001286 /* If neither a port nor an addr was specified and no check transport
1287 * layer is forced, then the transport layer used by the checks is the
1288 * same as for the production traffic. Otherwise we use raw_sock by
1289 * default, unless one is specified.
1290 */
1291 if (!srv->check.port && !is_addr(&srv->check.addr)) {
1292 if (!srv->check.use_ssl && srv->use_ssl != -1) {
1293 srv->check.use_ssl = srv->use_ssl;
1294 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001295 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001296 else if (srv->check.use_ssl == 1)
1297 srv->check.xprt = xprt_get(XPRT_SSL);
1298 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001299 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02001300 else if (srv->check.use_ssl == 1)
1301 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001302
Christopher Faulet12882cf2020-04-23 15:50:18 +02001303 /* Inherit the mux protocol from the server if not already defined for
1304 * the check
1305 */
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001306 if (srv->mux_proto && !srv->check.mux_proto &&
1307 ((srv->mux_proto->mode == PROTO_MODE_HTTP && check_type == TCPCHK_RULES_HTTP_CHK) ||
1308 (srv->mux_proto->mode == PROTO_MODE_TCP && check_type != TCPCHK_RULES_HTTP_CHK))) {
Christopher Faulet12882cf2020-04-23 15:50:18 +02001309 srv->check.mux_proto = srv->mux_proto;
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001310 }
Amaury Denoyelle92c8ac12020-11-13 12:34:57 +01001311 /* test that check proto is valid if explicitly defined */
1312 else if (srv->check.mux_proto &&
1313 ((srv->check.mux_proto->mode == PROTO_MODE_HTTP && check_type != TCPCHK_RULES_HTTP_CHK) ||
1314 (srv->check.mux_proto->mode == PROTO_MODE_TCP && check_type == TCPCHK_RULES_HTTP_CHK))) {
1315 ha_alert("config: %s '%s': server '%s' uses an incompatible MUX protocol for the selected check type\n",
1316 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1317 ret |= ERR_ALERT | ERR_FATAL;
1318 goto out;
1319 }
Christopher Faulet12882cf2020-04-23 15:50:18 +02001320
Christopher Faulet61cc8522020-04-20 14:54:42 +02001321 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001322
Christopher Faulet61cc8522020-04-20 14:54:42 +02001323 /* We need at least a service port, a check port or the first tcp-check
1324 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
1325 */
1326 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
1327 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
1328 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001329
Christopher Faulet61cc8522020-04-20 14:54:42 +02001330 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
1331 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
1332 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1333 ret |= ERR_ALERT | ERR_ABORT;
1334 goto out;
1335 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001336
Christopher Faulet61cc8522020-04-20 14:54:42 +02001337 /* search the first action (connect / send / expect) in the list */
1338 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
1339 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
1340 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
1341 "nor tcp_check rule 'connect' with port information.\n",
1342 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1343 ret |= ERR_ALERT | ERR_ABORT;
1344 goto out;
1345 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001346
Christopher Faulet61cc8522020-04-20 14:54:42 +02001347 /* scan the tcp-check ruleset to ensure a port has been configured */
1348 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
1349 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
1350 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
1351 "and a tcp_check rule 'connect' with no port information.\n",
1352 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1353 ret |= ERR_ALERT | ERR_ABORT;
1354 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001355 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001356 }
1357
Christopher Faulet61cc8522020-04-20 14:54:42 +02001358 init:
1359 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
1360 struct tcpcheck_ruleset *rs = NULL;
1361 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
1362 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001363
Christopher Faulet61cc8522020-04-20 14:54:42 +02001364 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
1365 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02001366
Christopher Faulet61cc8522020-04-20 14:54:42 +02001367 rs = find_tcpcheck_ruleset("*tcp-check");
1368 if (!rs) {
1369 rs = create_tcpcheck_ruleset("*tcp-check");
1370 if (rs == NULL) {
1371 ha_alert("config: %s '%s': out of memory.\n",
1372 proxy_type_str(srv->proxy), srv->proxy->id);
1373 ret |= ERR_ALERT | ERR_FATAL;
1374 goto out;
1375 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001376 }
1377
Christopher Faulet61cc8522020-04-20 14:54:42 +02001378 free_tcpcheck_vars(&rules->preset_vars);
1379 rules->list = &rs->rules;
1380 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001381 }
1382
Christopher Faulet61cc8522020-04-20 14:54:42 +02001383 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
1384 if (err) {
1385 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
1386 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1387 ret |= ERR_ALERT | ERR_ABORT;
1388 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001389 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001390 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
1391 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02001392
Christopher Faulet61cc8522020-04-20 14:54:42 +02001393 out:
1394 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001395}
1396
Christopher Faulet61cc8522020-04-20 14:54:42 +02001397/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
1398 * if an error occurred.
1399 */
1400static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02001401{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001402 struct tcpcheck_rule *chk;
1403 const char *err;
1404 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001405
Christopher Faulet61cc8522020-04-20 14:54:42 +02001406 if (!srv->do_agent)
1407 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001408
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001409 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02001410 * implicit one is inserted before all others.
1411 */
1412 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
1413 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
1414 chk = calloc(1, sizeof(*chk));
1415 if (!chk) {
1416 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
1417 " to agent-check for server '%s' (out of memory).\n",
1418 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1419 ret |= ERR_ALERT | ERR_FATAL;
1420 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001421 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001422 chk->action = TCPCHK_ACT_CONNECT;
1423 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
1424 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02001425 }
1426
Christopher Faulete5870d82020-04-15 11:32:03 +02001427
Christopher Faulet61cc8522020-04-20 14:54:42 +02001428 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
1429 if (err) {
1430 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
1431 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1432 ret |= ERR_ALERT | ERR_ABORT;
1433 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001434 }
1435
Christopher Faulet61cc8522020-04-20 14:54:42 +02001436 if (!srv->agent.inter)
1437 srv->agent.inter = srv->check.inter;
1438
1439 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
1440 global.maxsock++;
1441
1442 out:
1443 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001444}
1445
Christopher Faulet61cc8522020-04-20 14:54:42 +02001446static void deinit_srv_check(struct server *srv)
1447{
1448 if (srv->check.state & CHK_ST_CONFIGURED)
1449 free_check(&srv->check);
1450 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
1451 srv->do_check = 0;
1452}
Christopher Faulete5870d82020-04-15 11:32:03 +02001453
Christopher Faulet61cc8522020-04-20 14:54:42 +02001454
1455static void deinit_srv_agent_check(struct server *srv)
1456{
1457 if (srv->agent.tcpcheck_rules) {
1458 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
1459 free(srv->agent.tcpcheck_rules);
1460 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001461 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001462
Christopher Faulet61cc8522020-04-20 14:54:42 +02001463 if (srv->agent.state & CHK_ST_CONFIGURED)
1464 free_check(&srv->agent);
1465
1466 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
1467 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001468}
1469
Willy Tarreaucee013e2020-06-05 11:40:38 +02001470REGISTER_POST_SERVER_CHECK(init_srv_check);
1471REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
Willy Tarreaucee013e2020-06-05 11:40:38 +02001472REGISTER_POST_CHECK(start_checks);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001473
Willy Tarreaucee013e2020-06-05 11:40:38 +02001474REGISTER_SERVER_DEINIT(deinit_srv_check);
1475REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001476
Christopher Faulet61cc8522020-04-20 14:54:42 +02001477
1478/**************************************************************************/
1479/************************** Check sample fetches **************************/
1480/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001481
Christopher Faulet61cc8522020-04-20 14:54:42 +02001482static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001483 { /* END */ },
1484}};
1485
1486INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
1487
1488
1489/**************************************************************************/
1490/************************ Check's parsing functions ***********************/
1491/**************************************************************************/
Christopher Faulet51b129f2020-04-09 15:54:18 +02001492/* Parses the "http-check" proxy keyword */
1493static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
1494 struct proxy *defpx, const char *file, int line,
1495 char **errmsg)
1496{
Christopher Faulete5870d82020-04-15 11:32:03 +02001497 struct tcpcheck_ruleset *rs = NULL;
1498 struct tcpcheck_rule *chk = NULL;
1499 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001500
1501 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
1502 ret = 1;
1503
1504 cur_arg = 1;
1505 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
1506 /* enable a graceful server shutdown on an HTTP 404 response */
1507 curpx->options |= PR_O_DISABLE404;
1508 if (too_many_args(1, args, errmsg, NULL))
1509 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001510 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001511 }
1512 else if (strcmp(args[cur_arg], "send-state") == 0) {
1513 /* enable emission of the apparent state of a server in HTTP checks */
1514 curpx->options2 |= PR_O2_CHK_SNDST;
1515 if (too_many_args(1, args, errmsg, NULL))
1516 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001517 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001518 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001519
Christopher Faulete5870d82020-04-15 11:32:03 +02001520 /* Deduce the ruleset name from the proxy info */
1521 chunk_printf(&trash, "*http-check-%s_%s-%d",
1522 ((curpx == defpx) ? "defaults" : curpx->id),
1523 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001524
Christopher Faulet61cc8522020-04-20 14:54:42 +02001525 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001526 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001527 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001528 if (rs == NULL) {
1529 memprintf(errmsg, "out of memory.\n");
1530 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001531 }
1532 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001533
Christopher Faulete5870d82020-04-15 11:32:03 +02001534 index = 0;
1535 if (!LIST_ISEMPTY(&rs->rules)) {
1536 chk = LIST_PREV(&rs->rules, typeof(chk), list);
1537 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
1538 index = chk->index + 1;
1539 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001540
Christopher Faulete5870d82020-04-15 11:32:03 +02001541 if (strcmp(args[cur_arg], "connect") == 0)
1542 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1543 else if (strcmp(args[cur_arg], "send") == 0)
1544 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1545 else if (strcmp(args[cur_arg], "expect") == 0)
1546 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
1547 file, line, errmsg);
1548 else if (strcmp(args[cur_arg], "comment") == 0)
1549 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1550 else {
1551 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001552
Christopher Faulete5870d82020-04-15 11:32:03 +02001553 if (!kw) {
1554 action_kw_tcp_check_build_list(&trash);
1555 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
1556 " 'send', 'expect'%s%s. but got '%s'",
1557 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
1558 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001559 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001560 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
1561 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001562
Christopher Faulete5870d82020-04-15 11:32:03 +02001563 if (!chk) {
1564 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1565 goto error;
1566 }
1567 ret = (*errmsg != NULL); /* Handle warning */
1568
1569 chk->index = index;
1570 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
1571 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
1572 /* Use this ruleset if the proxy already has http-check enabled */
1573 curpx->tcpcheck_rules.list = &rs->rules;
1574 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
1575 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
1576 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1577 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001578 goto error;
1579 }
1580 }
1581 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02001582 /* mark this ruleset as unused for now */
1583 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
1584 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001585 }
1586
Christopher Faulete5870d82020-04-15 11:32:03 +02001587 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02001588 return ret;
1589
1590 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02001591 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001592 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001593 return -1;
1594}
1595
Christopher Faulet430e4802020-04-09 15:28:16 +02001596/* Parses the "option tcp-check" proxy keyword */
1597int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1598 const char *file, int line)
1599{
Christopher Faulet404f9192020-04-09 23:13:54 +02001600 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02001601 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1602 int err_code = 0;
1603
1604 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1605 err_code |= ERR_WARN;
1606
1607 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1608 goto out;
1609
Christopher Faulet404f9192020-04-09 23:13:54 +02001610 curpx->options2 &= ~PR_O2_CHK_ANY;
1611 curpx->options2 |= PR_O2_TCPCHK_CHK;
1612
Christopher Fauletd7e63962020-04-17 20:15:59 +02001613 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02001614 /* If a tcp-check rulesset is already set, do nothing */
1615 if (rules->list)
1616 goto out;
1617
1618 /* If a tcp-check ruleset is waiting to be used for the current proxy,
1619 * get it.
1620 */
1621 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
1622 goto curpx_ruleset;
1623
1624 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
1625 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001626 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001627 if (rs)
1628 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02001629 }
1630
Christopher Faulet404f9192020-04-09 23:13:54 +02001631 curpx_ruleset:
1632 /* Deduce the ruleset name from the proxy info */
1633 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
1634 ((curpx == defpx) ? "defaults" : curpx->id),
1635 curpx->conf.file, curpx->conf.line);
1636
Christopher Faulet61cc8522020-04-20 14:54:42 +02001637 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001638 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001639 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001640 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02001641 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1642 goto error;
1643 }
Christopher Faulet430e4802020-04-09 15:28:16 +02001644 }
1645
Christopher Faulet404f9192020-04-09 23:13:54 +02001646 ruleset_found:
1647 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02001648 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001649 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Fauletd7e63962020-04-17 20:15:59 +02001650 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02001651
1652 out:
1653 return err_code;
1654
1655 error:
1656 err_code |= ERR_ALERT | ERR_FATAL;
1657 goto out;
1658}
Christopher Faulet33f05df2020-04-01 11:08:50 +02001659
1660/* Parses the "option redis-check" proxy keyword */
1661int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1662 const char *file, int line)
1663{
1664 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
1665 static char *redis_res = "+PONG\r\n";
1666
1667 struct tcpcheck_ruleset *rs = NULL;
1668 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1669 struct tcpcheck_rule *chk;
1670 char *errmsg = NULL;
1671 int err_code = 0;
1672
1673 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1674 err_code |= ERR_WARN;
1675
1676 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1677 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001678
1679 curpx->options2 &= ~PR_O2_CHK_ANY;
1680 curpx->options2 |= PR_O2_TCPCHK_CHK;
1681
1682 free_tcpcheck_vars(&rules->preset_vars);
1683 rules->list = NULL;
1684 rules->flags = 0;
1685
Christopher Faulet61cc8522020-04-20 14:54:42 +02001686 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001687 if (rs)
1688 goto ruleset_found;
1689
Christopher Faulet61cc8522020-04-20 14:54:42 +02001690 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001691 if (rs == NULL) {
1692 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1693 goto error;
1694 }
1695
1696 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
1697 1, curpx, &rs->rules, file, line, &errmsg);
1698 if (!chk) {
1699 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1700 goto error;
1701 }
1702 chk->index = 0;
1703 LIST_ADDQ(&rs->rules, &chk->list);
1704
1705 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
1706 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001707 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02001708 "on-success", "Redis server is ok",
1709 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001710 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001711 if (!chk) {
1712 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1713 goto error;
1714 }
1715 chk->index = 1;
1716 LIST_ADDQ(&rs->rules, &chk->list);
1717
Christopher Faulet33f05df2020-04-01 11:08:50 +02001718 ruleset_found:
1719 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001720 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001721 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001722
1723 out:
1724 free(errmsg);
1725 return err_code;
1726
1727 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001728 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001729 err_code |= ERR_ALERT | ERR_FATAL;
1730 goto out;
1731}
1732
Christopher Faulet811f78c2020-04-01 11:10:27 +02001733
1734/* Parses the "option ssl-hello-chk" proxy keyword */
1735int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1736 const char *file, int line)
1737{
1738 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
1739 * ssl-hello-chk option to ensure that the remote server speaks SSL.
1740 *
1741 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
1742 */
1743 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001744 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02001745 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
1746 "0079" /* ContentLength : 0x79 bytes after this one */
1747 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
1748 "000075" /* HandshakeLength : 0x75 bytes after this one */
1749 "0300" /* Hello Version : 0x0300 = v3 */
1750 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
1751 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
1752 "00" /* Session ID length : empty (no session ID) */
1753 "004E" /* Cipher Suite Length : 78 bytes after this one */
1754 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
1755 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
1756 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
1757 "000D" "000E" "000F" "0010" /* various bit lengths, */
1758 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
1759 "0015" "0016" "0017" "0018"
1760 "0019" "001A" "001B" "002F"
1761 "0030" "0031" "0032" "0033"
1762 "0034" "0035" "0036" "0037"
1763 "0038" "0039" "003A"
1764 "01" /* Compression Length : 0x01 = 1 byte for types */
1765 "00" /* Compression Type : 0x00 = NULL compression */
1766 };
1767
1768 struct tcpcheck_ruleset *rs = NULL;
1769 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1770 struct tcpcheck_rule *chk;
1771 char *errmsg = NULL;
1772 int err_code = 0;
1773
1774 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1775 err_code |= ERR_WARN;
1776
1777 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1778 goto out;
1779
Christopher Faulet811f78c2020-04-01 11:10:27 +02001780 curpx->options2 &= ~PR_O2_CHK_ANY;
1781 curpx->options2 |= PR_O2_TCPCHK_CHK;
1782
1783 free_tcpcheck_vars(&rules->preset_vars);
1784 rules->list = NULL;
1785 rules->flags = 0;
1786
Christopher Faulet61cc8522020-04-20 14:54:42 +02001787 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001788 if (rs)
1789 goto ruleset_found;
1790
Christopher Faulet61cc8522020-04-20 14:54:42 +02001791 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001792 if (rs == NULL) {
1793 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1794 goto error;
1795 }
1796
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001797 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02001798 1, curpx, &rs->rules, file, line, &errmsg);
1799 if (!chk) {
1800 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1801 goto error;
1802 }
1803 chk->index = 0;
1804 LIST_ADDQ(&rs->rules, &chk->list);
1805
1806 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02001807 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02001808 "error-status", "L6RSP", "tout-status", "L6TOUT",
1809 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001810 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001811 if (!chk) {
1812 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1813 goto error;
1814 }
1815 chk->index = 1;
1816 LIST_ADDQ(&rs->rules, &chk->list);
1817
Christopher Faulet811f78c2020-04-01 11:10:27 +02001818 ruleset_found:
1819 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001820 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001821 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02001822
1823 out:
1824 free(errmsg);
1825 return err_code;
1826
1827 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001828 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001829 err_code |= ERR_ALERT | ERR_FATAL;
1830 goto out;
1831}
1832
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001833/* Parses the "option smtpchk" proxy keyword */
1834int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1835 const char *file, int line)
1836{
1837 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
1838
1839 struct tcpcheck_ruleset *rs = NULL;
1840 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1841 struct tcpcheck_rule *chk;
1842 struct tcpcheck_var *var = NULL;
1843 char *cmd = NULL, *errmsg = NULL;
1844 int err_code = 0;
1845
1846 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1847 err_code |= ERR_WARN;
1848
1849 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1850 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001851
1852 curpx->options2 &= ~PR_O2_CHK_ANY;
1853 curpx->options2 |= PR_O2_TCPCHK_CHK;
1854
1855 free_tcpcheck_vars(&rules->preset_vars);
1856 rules->list = NULL;
1857 rules->flags = 0;
1858
1859 cur_arg += 2;
1860 if (*args[cur_arg] && *args[cur_arg+1] &&
1861 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
Tim Duesterhus2867b402020-06-12 15:58:48 +02001862 /* <EHLO|HELO> + space (1) + <host> + null byte (1) */
1863 cmd = calloc(strlen(args[cur_arg]) + 1 + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001864 if (cmd)
1865 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
1866 }
1867 else {
1868 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
1869 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
1870 cmd = strdup("HELO localhost");
1871 }
1872
Christopher Fauletb61caf42020-04-21 10:57:42 +02001873 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001874 if (cmd == NULL || var == NULL) {
1875 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1876 goto error;
1877 }
1878 var->data.type = SMP_T_STR;
1879 var->data.u.str.area = cmd;
1880 var->data.u.str.data = strlen(cmd);
1881 LIST_INIT(&var->list);
1882 LIST_ADDQ(&rules->preset_vars, &var->list);
1883 cmd = NULL;
1884 var = NULL;
1885
Christopher Faulet61cc8522020-04-20 14:54:42 +02001886 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001887 if (rs)
1888 goto ruleset_found;
1889
Christopher Faulet61cc8522020-04-20 14:54:42 +02001890 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001891 if (rs == NULL) {
1892 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1893 goto error;
1894 }
1895
1896 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
1897 1, curpx, &rs->rules, file, line, &errmsg);
1898 if (!chk) {
1899 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1900 goto error;
1901 }
1902 chk->index = 0;
1903 LIST_ADDQ(&rs->rules, &chk->list);
1904
1905 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
1906 "min-recv", "4",
1907 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02001908 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001909 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001910 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001911 if (!chk) {
1912 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1913 goto error;
1914 }
1915 chk->index = 1;
1916 LIST_ADDQ(&rs->rules, &chk->list);
1917
1918 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
1919 "min-recv", "4",
1920 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001921 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1922 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001923 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001924 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001925 if (!chk) {
1926 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1927 goto error;
1928 }
1929 chk->index = 2;
1930 LIST_ADDQ(&rs->rules, &chk->list);
1931
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001932 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001933 1, curpx, &rs->rules, file, line, &errmsg);
1934 if (!chk) {
1935 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1936 goto error;
1937 }
1938 chk->index = 3;
1939 LIST_ADDQ(&rs->rules, &chk->list);
1940
1941 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
1942 "min-recv", "4",
1943 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001944 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1945 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1946 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001947 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001948 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001949 if (!chk) {
1950 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1951 goto error;
1952 }
1953 chk->index = 4;
1954 LIST_ADDQ(&rs->rules, &chk->list);
1955
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001956 ruleset_found:
1957 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001958 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001959 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001960
1961 out:
1962 free(errmsg);
1963 return err_code;
1964
1965 error:
1966 free(cmd);
1967 free(var);
1968 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001969 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001970 err_code |= ERR_ALERT | ERR_FATAL;
1971 goto out;
1972}
Christopher Faulet811f78c2020-04-01 11:10:27 +02001973
Christopher Fauletce355072020-04-02 11:44:39 +02001974/* Parses the "option pgsql-check" proxy keyword */
1975int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1976 const char *file, int line)
1977{
1978 static char pgsql_req[] = {
1979 "%[var(check.plen),htonl,hex]" /* The packet length*/
1980 "00030000" /* the version 3.0 */
1981 "7573657200" /* "user" key */
1982 "%[var(check.username),hex]00" /* the username */
1983 "00"
1984 };
1985
1986 struct tcpcheck_ruleset *rs = NULL;
1987 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1988 struct tcpcheck_rule *chk;
1989 struct tcpcheck_var *var = NULL;
1990 char *user = NULL, *errmsg = NULL;
1991 size_t packetlen = 0;
1992 int err_code = 0;
1993
1994 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1995 err_code |= ERR_WARN;
1996
1997 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1998 goto out;
1999
Christopher Fauletce355072020-04-02 11:44:39 +02002000 curpx->options2 &= ~PR_O2_CHK_ANY;
2001 curpx->options2 |= PR_O2_TCPCHK_CHK;
2002
2003 free_tcpcheck_vars(&rules->preset_vars);
2004 rules->list = NULL;
2005 rules->flags = 0;
2006
2007 cur_arg += 2;
2008 if (!*args[cur_arg] || !*args[cur_arg+1]) {
2009 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
2010 file, line, args[0], args[1]);
2011 goto error;
2012 }
2013 if (strcmp(args[cur_arg], "user") == 0) {
2014 packetlen = 15 + strlen(args[cur_arg+1]);
2015 user = strdup(args[cur_arg+1]);
2016
Christopher Fauletb61caf42020-04-21 10:57:42 +02002017 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02002018 if (user == NULL || var == NULL) {
2019 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2020 goto error;
2021 }
2022 var->data.type = SMP_T_STR;
2023 var->data.u.str.area = user;
2024 var->data.u.str.data = strlen(user);
2025 LIST_INIT(&var->list);
2026 LIST_ADDQ(&rules->preset_vars, &var->list);
2027 user = NULL;
2028 var = NULL;
2029
Christopher Fauletb61caf42020-04-21 10:57:42 +02002030 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02002031 if (var == NULL) {
2032 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2033 goto error;
2034 }
2035 var->data.type = SMP_T_SINT;
2036 var->data.u.sint = packetlen;
2037 LIST_INIT(&var->list);
2038 LIST_ADDQ(&rules->preset_vars, &var->list);
2039 var = NULL;
2040 }
2041 else {
2042 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
2043 file, line, args[0], args[1]);
2044 goto error;
2045 }
2046
Christopher Faulet61cc8522020-04-20 14:54:42 +02002047 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002048 if (rs)
2049 goto ruleset_found;
2050
Christopher Faulet61cc8522020-04-20 14:54:42 +02002051 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002052 if (rs == NULL) {
2053 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2054 goto error;
2055 }
2056
2057 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2058 1, curpx, &rs->rules, file, line, &errmsg);
2059 if (!chk) {
2060 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2061 goto error;
2062 }
2063 chk->index = 0;
2064 LIST_ADDQ(&rs->rules, &chk->list);
2065
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002066 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02002067 1, curpx, &rs->rules, file, line, &errmsg);
2068 if (!chk) {
2069 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2070 goto error;
2071 }
2072 chk->index = 1;
2073 LIST_ADDQ(&rs->rules, &chk->list);
2074
2075 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
2076 "min-recv", "5",
2077 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02002078 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02002079 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002080 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002081 if (!chk) {
2082 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2083 goto error;
2084 }
2085 chk->index = 2;
2086 LIST_ADDQ(&rs->rules, &chk->list);
2087
Christopher Fauletb841c742020-04-27 18:29:49 +02002088 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 +02002089 "min-recv", "9",
2090 "error-status", "L7STS",
2091 "on-success", "PostgreSQL server is ok",
2092 "on-error", "PostgreSQL unknown error",
2093 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002094 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002095 if (!chk) {
2096 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2097 goto error;
2098 }
2099 chk->index = 3;
2100 LIST_ADDQ(&rs->rules, &chk->list);
2101
Christopher Fauletce355072020-04-02 11:44:39 +02002102 ruleset_found:
2103 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002104 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002105 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02002106
2107 out:
2108 free(errmsg);
2109 return err_code;
2110
2111 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002112 free(user);
2113 free(var);
2114 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002115 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002116 err_code |= ERR_ALERT | ERR_FATAL;
2117 goto out;
2118}
2119
2120
2121/* Parses the "option mysql-check" proxy keyword */
2122int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2123 const char *file, int line)
2124{
2125 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
2126 * const char mysql40_client_auth_pkt[] = {
2127 * "\x0e\x00\x00" // packet length
2128 * "\x01" // packet number
2129 * "\x00\x00" // client capabilities
2130 * "\x00\x00\x01" // max packet
2131 * "haproxy\x00" // username (null terminated string)
2132 * "\x00" // filler (always 0x00)
2133 * "\x01\x00\x00" // packet length
2134 * "\x00" // packet number
2135 * "\x01" // COM_QUIT command
2136 * };
2137 */
2138 static char mysql40_rsname[] = "*mysql40-check";
2139 static char mysql40_req[] = {
2140 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2141 "0080" /* client capabilities */
2142 "000001" /* max packet */
2143 "%[var(check.username),hex]00" /* the username */
2144 "00" /* filler (always 0x00) */
2145 "010000" /* packet length*/
2146 "00" /* sequence ID */
2147 "01" /* COM_QUIT command */
2148 };
2149
2150 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
2151 * const char mysql41_client_auth_pkt[] = {
2152 * "\x0e\x00\x00\" // packet length
2153 * "\x01" // packet number
2154 * "\x00\x00\x00\x00" // client capabilities
2155 * "\x00\x00\x00\x01" // max packet
2156 * "\x21" // character set (UTF-8)
2157 * char[23] // All zeroes
2158 * "haproxy\x00" // username (null terminated string)
2159 * "\x00" // filler (always 0x00)
2160 * "\x01\x00\x00" // packet length
2161 * "\x00" // packet number
2162 * "\x01" // COM_QUIT command
2163 * };
2164 */
2165 static char mysql41_rsname[] = "*mysql41-check";
2166 static char mysql41_req[] = {
2167 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2168 "00820000" /* client capabilities */
2169 "00800001" /* max packet */
2170 "21" /* character set (UTF-8) */
2171 "000000000000000000000000" /* 23 bytes, al zeroes */
2172 "0000000000000000000000"
2173 "%[var(check.username),hex]00" /* the username */
2174 "00" /* filler (always 0x00) */
2175 "010000" /* packet length*/
2176 "00" /* sequence ID */
2177 "01" /* COM_QUIT command */
2178 };
2179
2180 struct tcpcheck_ruleset *rs = NULL;
2181 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2182 struct tcpcheck_rule *chk;
2183 struct tcpcheck_var *var = NULL;
2184 char *mysql_rsname = "*mysql-check";
2185 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
2186 int index = 0, err_code = 0;
2187
2188 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2189 err_code |= ERR_WARN;
2190
2191 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2192 goto out;
2193
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002194 curpx->options2 &= ~PR_O2_CHK_ANY;
2195 curpx->options2 |= PR_O2_TCPCHK_CHK;
2196
2197 free_tcpcheck_vars(&rules->preset_vars);
2198 rules->list = NULL;
2199 rules->flags = 0;
2200
2201 cur_arg += 2;
2202 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002203 int packetlen, userlen;
2204
2205 if (strcmp(args[cur_arg], "user") != 0) {
2206 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
2207 file, line, args[0], args[1], args[cur_arg]);
2208 goto error;
2209 }
2210
2211 if (*(args[cur_arg+1]) == 0) {
2212 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
2213 file, line, args[0], args[1], args[cur_arg]);
2214 goto error;
2215 }
2216
2217 hdr = calloc(4, sizeof(*hdr));
2218 user = strdup(args[cur_arg+1]);
2219 userlen = strlen(args[cur_arg+1]);
2220
2221 if (hdr == NULL || user == NULL) {
2222 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2223 goto error;
2224 }
2225
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002226 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002227 packetlen = userlen + 7 + 27;
2228 mysql_req = mysql41_req;
2229 mysql_rsname = mysql41_rsname;
2230 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002231 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002232 packetlen = userlen + 7;
2233 mysql_req = mysql40_req;
2234 mysql_rsname = mysql40_rsname;
2235 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002236 else {
2237 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
2238 file, line, args[cur_arg], args[cur_arg+2]);
2239 goto error;
2240 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002241
2242 hdr[0] = (unsigned char)(packetlen & 0xff);
2243 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
2244 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
2245 hdr[3] = 1;
2246
Christopher Fauletb61caf42020-04-21 10:57:42 +02002247 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002248 if (var == NULL) {
2249 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2250 goto error;
2251 }
2252 var->data.type = SMP_T_STR;
2253 var->data.u.str.area = hdr;
2254 var->data.u.str.data = 4;
2255 LIST_INIT(&var->list);
2256 LIST_ADDQ(&rules->preset_vars, &var->list);
2257 hdr = NULL;
2258 var = NULL;
2259
Christopher Fauletb61caf42020-04-21 10:57:42 +02002260 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002261 if (var == NULL) {
2262 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2263 goto error;
2264 }
2265 var->data.type = SMP_T_STR;
2266 var->data.u.str.area = user;
2267 var->data.u.str.data = strlen(user);
2268 LIST_INIT(&var->list);
2269 LIST_ADDQ(&rules->preset_vars, &var->list);
2270 user = NULL;
2271 var = NULL;
2272 }
2273
Christopher Faulet61cc8522020-04-20 14:54:42 +02002274 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002275 if (rs)
2276 goto ruleset_found;
2277
Christopher Faulet61cc8522020-04-20 14:54:42 +02002278 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002279 if (rs == NULL) {
2280 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2281 goto error;
2282 }
2283
2284 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2285 1, curpx, &rs->rules, file, line, &errmsg);
2286 if (!chk) {
2287 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2288 goto error;
2289 }
2290 chk->index = index++;
2291 LIST_ADDQ(&rs->rules, &chk->list);
2292
2293 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002294 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002295 1, curpx, &rs->rules, file, line, &errmsg);
2296 if (!chk) {
2297 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2298 goto error;
2299 }
2300 chk->index = index++;
2301 LIST_ADDQ(&rs->rules, &chk->list);
2302 }
2303
2304 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002305 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002306 if (!chk) {
2307 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2308 goto error;
2309 }
2310 chk->expect.custom = tcpcheck_mysql_expect_iniths;
2311 chk->index = index++;
2312 LIST_ADDQ(&rs->rules, &chk->list);
2313
2314 if (mysql_req) {
2315 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002316 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002317 if (!chk) {
2318 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2319 goto error;
2320 }
2321 chk->expect.custom = tcpcheck_mysql_expect_ok;
2322 chk->index = index++;
2323 LIST_ADDQ(&rs->rules, &chk->list);
2324 }
2325
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002326 ruleset_found:
2327 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002328 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002329 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002330
2331 out:
2332 free(errmsg);
2333 return err_code;
2334
2335 error:
2336 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02002337 free(user);
2338 free(var);
2339 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002340 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02002341 err_code |= ERR_ALERT | ERR_FATAL;
2342 goto out;
2343}
2344
Christopher Faulet1997eca2020-04-03 23:13:50 +02002345int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2346 const char *file, int line)
2347{
2348 static char *ldap_req = "300C020101600702010304008000";
2349
2350 struct tcpcheck_ruleset *rs = NULL;
2351 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2352 struct tcpcheck_rule *chk;
2353 char *errmsg = NULL;
2354 int err_code = 0;
2355
2356 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2357 err_code |= ERR_WARN;
2358
2359 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2360 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002361
2362 curpx->options2 &= ~PR_O2_CHK_ANY;
2363 curpx->options2 |= PR_O2_TCPCHK_CHK;
2364
2365 free_tcpcheck_vars(&rules->preset_vars);
2366 rules->list = NULL;
2367 rules->flags = 0;
2368
Christopher Faulet61cc8522020-04-20 14:54:42 +02002369 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002370 if (rs)
2371 goto ruleset_found;
2372
Christopher Faulet61cc8522020-04-20 14:54:42 +02002373 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002374 if (rs == NULL) {
2375 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2376 goto error;
2377 }
2378
2379 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
2380 1, curpx, &rs->rules, file, line, &errmsg);
2381 if (!chk) {
2382 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2383 goto error;
2384 }
2385 chk->index = 0;
2386 LIST_ADDQ(&rs->rules, &chk->list);
2387
2388 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
2389 "min-recv", "14",
2390 "on-error", "Not LDAPv3 protocol",
2391 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002392 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002393 if (!chk) {
2394 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2395 goto error;
2396 }
2397 chk->index = 1;
2398 LIST_ADDQ(&rs->rules, &chk->list);
2399
2400 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002401 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002402 if (!chk) {
2403 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2404 goto error;
2405 }
2406 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
2407 chk->index = 2;
2408 LIST_ADDQ(&rs->rules, &chk->list);
2409
Christopher Faulet1997eca2020-04-03 23:13:50 +02002410 ruleset_found:
2411 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002412 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002413 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002414
2415 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02002416 free(errmsg);
2417 return err_code;
2418
2419 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002420 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002421 err_code |= ERR_ALERT | ERR_FATAL;
2422 goto out;
2423}
2424
2425int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2426 const char *file, int line)
2427{
2428 struct tcpcheck_ruleset *rs = NULL;
2429 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2430 struct tcpcheck_rule *chk;
2431 char *spop_req = NULL;
2432 char *errmsg = NULL;
2433 int spop_len = 0, err_code = 0;
2434
2435 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2436 err_code |= ERR_WARN;
2437
2438 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2439 goto out;
2440
Christopher Faulet267b01b2020-04-04 10:27:09 +02002441 curpx->options2 &= ~PR_O2_CHK_ANY;
2442 curpx->options2 |= PR_O2_TCPCHK_CHK;
2443
2444 free_tcpcheck_vars(&rules->preset_vars);
2445 rules->list = NULL;
2446 rules->flags = 0;
2447
2448
Christopher Faulet61cc8522020-04-20 14:54:42 +02002449 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002450 if (rs)
2451 goto ruleset_found;
2452
Christopher Faulet61cc8522020-04-20 14:54:42 +02002453 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002454 if (rs == NULL) {
2455 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2456 goto error;
2457 }
2458
2459 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
2460 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2461 goto error;
2462 }
2463 chunk_reset(&trash);
2464 dump_binary(&trash, spop_req, spop_len);
2465 trash.area[trash.data] = '\0';
2466
2467 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
2468 1, curpx, &rs->rules, file, line, &errmsg);
2469 if (!chk) {
2470 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2471 goto error;
2472 }
2473 chk->index = 0;
2474 LIST_ADDQ(&rs->rules, &chk->list);
2475
2476 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002477 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002478 if (!chk) {
2479 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2480 goto error;
2481 }
2482 chk->expect.custom = tcpcheck_spop_expect_agenthello;
2483 chk->index = 1;
2484 LIST_ADDQ(&rs->rules, &chk->list);
2485
Christopher Faulet267b01b2020-04-04 10:27:09 +02002486 ruleset_found:
2487 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002488 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002489 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002490
2491 out:
2492 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002493 free(errmsg);
2494 return err_code;
2495
2496 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002497 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002498 err_code |= ERR_ALERT | ERR_FATAL;
2499 goto out;
2500}
Christopher Fauletce355072020-04-02 11:44:39 +02002501
Christopher Faulete5870d82020-04-15 11:32:03 +02002502
2503struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
2504{
2505 struct tcpcheck_rule *chk = NULL;
2506 struct tcpcheck_http_hdr *hdr = NULL;
2507 char *meth = NULL, *uri = NULL, *vsn = NULL;
2508 char *hdrs, *body;
2509
2510 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
2511 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
2512 if (hdrs == body)
2513 hdrs = NULL;
2514 if (hdrs) {
2515 *hdrs = '\0';
2516 hdrs +=2;
2517 }
2518 if (body) {
2519 *body = '\0';
2520 body += 4;
2521 }
2522 if (hdrs || body) {
2523 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
2524 " Please, consider to use 'http-check send' directive instead.");
2525 }
2526
2527 chk = calloc(1, sizeof(*chk));
2528 if (!chk) {
2529 memprintf(errmsg, "out of memory");
2530 goto error;
2531 }
2532 chk->action = TCPCHK_ACT_SEND;
2533 chk->send.type = TCPCHK_SEND_HTTP;
2534 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
2535 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
2536 LIST_INIT(&chk->send.http.hdrs);
2537
2538 /* Copy the method, uri and version */
2539 if (*args[cur_arg]) {
2540 if (!*args[cur_arg+1])
2541 uri = args[cur_arg];
2542 else
2543 meth = args[cur_arg];
2544 }
2545 if (*args[cur_arg+1])
2546 uri = args[cur_arg+1];
2547 if (*args[cur_arg+2])
2548 vsn = args[cur_arg+2];
2549
2550 if (meth) {
2551 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
2552 chk->send.http.meth.str.area = strdup(meth);
2553 chk->send.http.meth.str.data = strlen(meth);
2554 if (!chk->send.http.meth.str.area) {
2555 memprintf(errmsg, "out of memory");
2556 goto error;
2557 }
2558 }
2559 if (uri) {
2560 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002561 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002562 memprintf(errmsg, "out of memory");
2563 goto error;
2564 }
2565 }
2566 if (vsn) {
2567 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002568 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002569 memprintf(errmsg, "out of memory");
2570 goto error;
2571 }
2572 }
2573
2574 /* Copy the header */
2575 if (hdrs) {
2576 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
2577 struct h1m h1m;
2578 int i, ret;
2579
2580 /* Build and parse the request */
2581 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
2582
2583 h1m.flags = H1_MF_HDRS_ONLY;
2584 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
2585 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
2586 &h1m, NULL);
2587 if (ret <= 0) {
2588 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
2589 goto error;
2590 }
2591
Christopher Fauletb61caf42020-04-21 10:57:42 +02002592 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002593 hdr = calloc(1, sizeof(*hdr));
2594 if (!hdr) {
2595 memprintf(errmsg, "out of memory");
2596 goto error;
2597 }
2598 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002599 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02002600 if (!hdr->name.ptr) {
2601 memprintf(errmsg, "out of memory");
2602 goto error;
2603 }
2604
Christopher Fauletb61caf42020-04-21 10:57:42 +02002605 ist0(tmp_hdrs[i].v);
2606 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 +02002607 goto error;
2608 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
2609 }
2610 }
2611
2612 /* Copy the body */
2613 if (body) {
2614 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002615 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002616 memprintf(errmsg, "out of memory");
2617 goto error;
2618 }
2619 }
2620
2621 return chk;
2622
2623 error:
2624 free_tcpcheck_http_hdr(hdr);
2625 free_tcpcheck(chk, 0);
2626 return NULL;
2627}
2628
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002629int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2630 const char *file, int line)
2631{
Christopher Faulete5870d82020-04-15 11:32:03 +02002632 struct tcpcheck_ruleset *rs = NULL;
2633 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2634 struct tcpcheck_rule *chk;
2635 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002636 int err_code = 0;
2637
2638 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2639 err_code |= ERR_WARN;
2640
2641 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2642 goto out;
2643
Christopher Faulete5870d82020-04-15 11:32:03 +02002644 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
2645 if (!chk) {
2646 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2647 goto error;
2648 }
2649 if (errmsg) {
2650 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
2651 err_code |= ERR_WARN;
2652 free(errmsg);
2653 errmsg = NULL;
2654 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002655
Christopher Faulete5870d82020-04-15 11:32:03 +02002656 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002657 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02002658 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002659
Christopher Faulete5870d82020-04-15 11:32:03 +02002660 free_tcpcheck_vars(&rules->preset_vars);
2661 rules->list = NULL;
2662 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002663
Christopher Faulete5870d82020-04-15 11:32:03 +02002664 /* Deduce the ruleset name from the proxy info */
2665 chunk_printf(&trash, "*http-check-%s_%s-%d",
2666 ((curpx == defpx) ? "defaults" : curpx->id),
2667 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002668
Christopher Faulet61cc8522020-04-20 14:54:42 +02002669 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002670 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002671 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002672 if (rs == NULL) {
2673 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2674 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002675 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002676 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002677
Christopher Faulete5870d82020-04-15 11:32:03 +02002678 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002679 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulete5870d82020-04-15 11:32:03 +02002680 rules->flags |= TCPCHK_RULES_HTTP_CHK;
2681 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
2682 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2683 rules->list = NULL;
2684 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002685 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002686
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002687 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02002688 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002689 return err_code;
2690
2691 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002692 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02002693 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002694 err_code |= ERR_ALERT | ERR_FATAL;
2695 goto out;
2696}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002697
Christopher Fauletce8111e2020-04-06 15:04:11 +02002698/* Parse the "addr" server keyword */
2699static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2700 char **errmsg)
2701{
2702 struct sockaddr_storage *sk;
Christopher Fauletce8111e2020-04-06 15:04:11 +02002703 int port1, port2, err_code = 0;
2704
2705
2706 if (!*args[*cur_arg+1]) {
2707 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
2708 goto error;
2709 }
2710
Willy Tarreau65ec4e32020-09-16 19:17:08 +02002711 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, NULL, errmsg, NULL, NULL,
2712 PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
Christopher Fauletce8111e2020-04-06 15:04:11 +02002713 if (!sk) {
2714 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
2715 goto error;
2716 }
2717
Christopher Fauletce8111e2020-04-06 15:04:11 +02002718 srv->check.addr = srv->agent.addr = *sk;
2719 srv->flags |= SRV_F_CHECKADDR;
2720 srv->flags |= SRV_F_AGENTADDR;
2721
2722 out:
2723 return err_code;
2724
2725 error:
2726 err_code |= ERR_ALERT | ERR_FATAL;
2727 goto out;
2728}
2729
2730
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002731/* Parse the "agent-addr" server keyword */
2732static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2733 char **errmsg)
2734{
2735 int err_code = 0;
2736
2737 if (!*(args[*cur_arg+1])) {
2738 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
2739 goto error;
2740 }
2741 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
2742 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
2743 goto error;
2744 }
2745
2746 out:
2747 return err_code;
2748
2749 error:
2750 err_code |= ERR_ALERT | ERR_FATAL;
2751 goto out;
2752}
2753
2754/* Parse the "agent-check" server keyword */
2755static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2756 char **errmsg)
2757{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002758 struct tcpcheck_ruleset *rs = NULL;
2759 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2760 struct tcpcheck_rule *chk;
2761 int err_code = 0;
2762
2763 if (srv->do_agent)
2764 goto out;
2765
2766 if (!rules) {
2767 rules = calloc(1, sizeof(*rules));
2768 if (!rules) {
2769 memprintf(errmsg, "out of memory.");
2770 goto error;
2771 }
2772 LIST_INIT(&rules->preset_vars);
2773 srv->agent.tcpcheck_rules = rules;
2774 }
2775 rules->list = NULL;
2776 rules->flags = 0;
2777
Christopher Faulet61cc8522020-04-20 14:54:42 +02002778 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002779 if (rs)
2780 goto ruleset_found;
2781
Christopher Faulet61cc8522020-04-20 14:54:42 +02002782 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002783 if (rs == NULL) {
2784 memprintf(errmsg, "out of memory.");
2785 goto error;
2786 }
2787
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002788 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002789 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
2790 if (!chk) {
2791 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2792 goto error;
2793 }
2794 chk->index = 0;
2795 LIST_ADDQ(&rs->rules, &chk->list);
2796
2797 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002798 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
2799 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002800 if (!chk) {
2801 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2802 goto error;
2803 }
2804 chk->expect.custom = tcpcheck_agent_expect_reply;
2805 chk->index = 1;
2806 LIST_ADDQ(&rs->rules, &chk->list);
2807
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002808 ruleset_found:
2809 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002810 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002811 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002812 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002813
2814 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002815 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002816
2817 error:
2818 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002819 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002820 err_code |= ERR_ALERT | ERR_FATAL;
2821 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002822}
2823
2824/* Parse the "agent-inter" server keyword */
2825static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2826 char **errmsg)
2827{
2828 const char *err = NULL;
2829 unsigned int delay;
2830 int err_code = 0;
2831
2832 if (!*(args[*cur_arg+1])) {
2833 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
2834 goto error;
2835 }
2836
2837 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
2838 if (err == PARSE_TIME_OVER) {
2839 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
2840 args[*cur_arg+1], args[*cur_arg], srv->id);
2841 goto error;
2842 }
2843 else if (err == PARSE_TIME_UNDER) {
2844 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
2845 args[*cur_arg+1], args[*cur_arg], srv->id);
2846 goto error;
2847 }
2848 else if (err) {
2849 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
2850 *err, srv->id);
2851 goto error;
2852 }
2853 if (delay <= 0) {
2854 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
2855 delay, args[*cur_arg], srv->id);
2856 goto error;
2857 }
2858 srv->agent.inter = delay;
2859
2860 out:
2861 return err_code;
2862
2863 error:
2864 err_code |= ERR_ALERT | ERR_FATAL;
2865 goto out;
2866}
2867
2868/* Parse the "agent-port" server keyword */
2869static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2870 char **errmsg)
2871{
2872 int err_code = 0;
2873
2874 if (!*(args[*cur_arg+1])) {
2875 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
2876 goto error;
2877 }
2878
2879 global.maxsock++;
2880 srv->agent.port = atol(args[*cur_arg+1]);
2881
2882 out:
2883 return err_code;
2884
2885 error:
2886 err_code |= ERR_ALERT | ERR_FATAL;
2887 goto out;
2888}
2889
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002890int set_srv_agent_send(struct server *srv, const char *send)
2891{
2892 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2893 struct tcpcheck_var *var = NULL;
2894 char *str;
2895
2896 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002897 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002898 if (str == NULL || var == NULL)
2899 goto error;
2900
2901 free_tcpcheck_vars(&rules->preset_vars);
2902
2903 var->data.type = SMP_T_STR;
2904 var->data.u.str.area = str;
2905 var->data.u.str.data = strlen(str);
2906 LIST_INIT(&var->list);
2907 LIST_ADDQ(&rules->preset_vars, &var->list);
2908
2909 return 1;
2910
2911 error:
2912 free(str);
2913 free(var);
2914 return 0;
2915}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002916
2917/* Parse the "agent-send" server keyword */
2918static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2919 char **errmsg)
2920{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002921 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002922 int err_code = 0;
2923
2924 if (!*(args[*cur_arg+1])) {
2925 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
2926 goto error;
2927 }
2928
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002929 if (!rules) {
2930 rules = calloc(1, sizeof(*rules));
2931 if (!rules) {
2932 memprintf(errmsg, "out of memory.");
2933 goto error;
2934 }
2935 LIST_INIT(&rules->preset_vars);
2936 srv->agent.tcpcheck_rules = rules;
2937 }
2938
2939 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002940 memprintf(errmsg, "out of memory.");
2941 goto error;
2942 }
2943
2944 out:
2945 return err_code;
2946
2947 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002948 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002949 err_code |= ERR_ALERT | ERR_FATAL;
2950 goto out;
2951}
2952
2953/* Parse the "no-agent-send" server keyword */
2954static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2955 char **errmsg)
2956{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002957 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002958 return 0;
2959}
2960
Christopher Fauletce8111e2020-04-06 15:04:11 +02002961/* Parse the "check" server keyword */
2962static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2963 char **errmsg)
2964{
2965 srv->do_check = 1;
2966 return 0;
2967}
2968
2969/* Parse the "check-send-proxy" server keyword */
2970static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2971 char **errmsg)
2972{
2973 srv->check.send_proxy = 1;
2974 return 0;
2975}
2976
2977/* Parse the "check-via-socks4" server keyword */
2978static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2979 char **errmsg)
2980{
2981 srv->check.via_socks4 = 1;
2982 return 0;
2983}
2984
2985/* Parse the "no-check" server keyword */
2986static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2987 char **errmsg)
2988{
2989 deinit_srv_check(srv);
2990 return 0;
2991}
2992
2993/* Parse the "no-check-send-proxy" server keyword */
2994static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2995 char **errmsg)
2996{
2997 srv->check.send_proxy = 0;
2998 return 0;
2999}
3000
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003001/* parse the "check-proto" server keyword */
3002static int srv_parse_check_proto(char **args, int *cur_arg,
3003 struct proxy *px, struct server *newsrv, char **err)
3004{
3005 int err_code = 0;
3006
3007 if (!*args[*cur_arg + 1]) {
3008 memprintf(err, "'%s' : missing value", args[*cur_arg]);
3009 goto error;
3010 }
3011 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
3012 if (!newsrv->check.mux_proto) {
3013 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
3014 goto error;
3015 }
3016
3017 out:
3018 return err_code;
3019
3020 error:
3021 err_code |= ERR_ALERT | ERR_FATAL;
3022 goto out;
3023}
3024
3025
Christopher Fauletce8111e2020-04-06 15:04:11 +02003026/* Parse the "rise" server keyword */
3027static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3028 char **errmsg)
3029{
3030 int err_code = 0;
3031
3032 if (!*args[*cur_arg + 1]) {
3033 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3034 goto error;
3035 }
3036
3037 srv->check.rise = atol(args[*cur_arg+1]);
3038 if (srv->check.rise <= 0) {
3039 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3040 goto error;
3041 }
3042
3043 if (srv->check.health)
3044 srv->check.health = srv->check.rise;
3045
3046 out:
3047 return err_code;
3048
3049 error:
3050 deinit_srv_agent_check(srv);
3051 err_code |= ERR_ALERT | ERR_FATAL;
3052 goto out;
3053 return 0;
3054}
3055
3056/* Parse the "fall" server keyword */
3057static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3058 char **errmsg)
3059{
3060 int err_code = 0;
3061
3062 if (!*args[*cur_arg + 1]) {
3063 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3064 goto error;
3065 }
3066
3067 srv->check.fall = atol(args[*cur_arg+1]);
3068 if (srv->check.fall <= 0) {
3069 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3070 goto error;
3071 }
3072
3073 out:
3074 return err_code;
3075
3076 error:
3077 deinit_srv_agent_check(srv);
3078 err_code |= ERR_ALERT | ERR_FATAL;
3079 goto out;
3080 return 0;
3081}
3082
3083/* Parse the "inter" server keyword */
3084static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3085 char **errmsg)
3086{
3087 const char *err = NULL;
3088 unsigned int delay;
3089 int err_code = 0;
3090
3091 if (!*(args[*cur_arg+1])) {
3092 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3093 goto error;
3094 }
3095
3096 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3097 if (err == PARSE_TIME_OVER) {
3098 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3099 args[*cur_arg+1], args[*cur_arg], srv->id);
3100 goto error;
3101 }
3102 else if (err == PARSE_TIME_UNDER) {
3103 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3104 args[*cur_arg+1], args[*cur_arg], srv->id);
3105 goto error;
3106 }
3107 else if (err) {
3108 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3109 *err, srv->id);
3110 goto error;
3111 }
3112 if (delay <= 0) {
3113 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3114 delay, args[*cur_arg], srv->id);
3115 goto error;
3116 }
3117 srv->check.inter = delay;
3118
3119 out:
3120 return err_code;
3121
3122 error:
3123 err_code |= ERR_ALERT | ERR_FATAL;
3124 goto out;
3125}
3126
3127
3128/* Parse the "fastinter" server keyword */
3129static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3130 char **errmsg)
3131{
3132 const char *err = NULL;
3133 unsigned int delay;
3134 int err_code = 0;
3135
3136 if (!*(args[*cur_arg+1])) {
3137 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3138 goto error;
3139 }
3140
3141 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3142 if (err == PARSE_TIME_OVER) {
3143 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3144 args[*cur_arg+1], args[*cur_arg], srv->id);
3145 goto error;
3146 }
3147 else if (err == PARSE_TIME_UNDER) {
3148 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3149 args[*cur_arg+1], args[*cur_arg], srv->id);
3150 goto error;
3151 }
3152 else if (err) {
3153 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3154 *err, srv->id);
3155 goto error;
3156 }
3157 if (delay <= 0) {
3158 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3159 delay, args[*cur_arg], srv->id);
3160 goto error;
3161 }
3162 srv->check.fastinter = delay;
3163
3164 out:
3165 return err_code;
3166
3167 error:
3168 err_code |= ERR_ALERT | ERR_FATAL;
3169 goto out;
3170}
3171
3172
3173/* Parse the "downinter" server keyword */
3174static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3175 char **errmsg)
3176{
3177 const char *err = NULL;
3178 unsigned int delay;
3179 int err_code = 0;
3180
3181 if (!*(args[*cur_arg+1])) {
3182 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3183 goto error;
3184 }
3185
3186 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3187 if (err == PARSE_TIME_OVER) {
3188 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3189 args[*cur_arg+1], args[*cur_arg], srv->id);
3190 goto error;
3191 }
3192 else if (err == PARSE_TIME_UNDER) {
3193 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3194 args[*cur_arg+1], args[*cur_arg], srv->id);
3195 goto error;
3196 }
3197 else if (err) {
3198 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3199 *err, srv->id);
3200 goto error;
3201 }
3202 if (delay <= 0) {
3203 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3204 delay, args[*cur_arg], srv->id);
3205 goto error;
3206 }
3207 srv->check.downinter = delay;
3208
3209 out:
3210 return err_code;
3211
3212 error:
3213 err_code |= ERR_ALERT | ERR_FATAL;
3214 goto out;
3215}
3216
3217/* Parse the "port" server keyword */
3218static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3219 char **errmsg)
3220{
3221 int err_code = 0;
3222
3223 if (!*(args[*cur_arg+1])) {
3224 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
3225 goto error;
3226 }
3227
3228 global.maxsock++;
3229 srv->check.port = atol(args[*cur_arg+1]);
3230 srv->flags |= SRV_F_CHECKPORT;
3231
3232 out:
3233 return err_code;
3234
3235 error:
3236 err_code |= ERR_ALERT | ERR_FATAL;
3237 goto out;
3238}
3239
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003240static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02003241 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003242 { 0, NULL, NULL },
3243}};
3244
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003245static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02003246 { "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 +02003247 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
3248 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
3249 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
3250 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
3251 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003252 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003253 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003254 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
3255 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003256 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003257 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
3258 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
3259 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
3260 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
3261 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
3262 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
3263 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
3264 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003265 { NULL, NULL, 0 },
3266}};
3267
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003268INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003269INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003270
Willy Tarreaubd741542010-03-16 18:46:54 +01003271/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02003272 * Local variables:
3273 * c-indent-level: 8
3274 * c-basic-offset: 8
3275 * End:
3276 */