blob: 9836d360f1d44ce8bd868c09bab774cca1c3c3b2 [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>
Willy Tarreaubcc67332020-06-05 15:31:31 +020040#include <haproxy/extcheck.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020041#include <haproxy/fd.h>
42#include <haproxy/global.h>
43#include <haproxy/h1.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020044#include <haproxy/http.h>
Willy Tarreau87735332020-06-04 09:08:41 +020045#include <haproxy/http_htx.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020046#include <haproxy/htx.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020047#include <haproxy/istbuf.h>
48#include <haproxy/list.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020049#include <haproxy/log.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020050#include <haproxy/mailers.h>
51#include <haproxy/port_range.h>
52#include <haproxy/proto_tcp.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020053#include <haproxy/protocol.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020054#include <haproxy/proxy.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020055#include <haproxy/queue.h>
56#include <haproxy/regex.h>
57#include <haproxy/sample.h>
Willy Tarreau1e56f922020-06-04 23:20:13 +020058#include <haproxy/server.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020059#include <haproxy/ssl_sock.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020060#include <haproxy/stats-t.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020061#include <haproxy/stream_interface.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020062#include <haproxy/task.h>
Willy Tarreau51cd5952020-06-05 12:25:38 +020063#include <haproxy/tcpcheck.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020064#include <haproxy/thread.h>
65#include <haproxy/time.h>
66#include <haproxy/tools.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020067#include <haproxy/vars.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020068
Olivier Houchard9130a962017-10-17 17:33:43 +020069
Christopher Faulet61cc8522020-04-20 14:54:42 +020070static int wake_srv_chk(struct conn_stream *cs);
71struct data_cb check_conn_cb = {
72 .wake = wake_srv_chk,
73 .name = "CHCK",
74};
Christopher Fauletd7e63962020-04-17 20:15:59 +020075
Christopher Faulet5d503fc2020-03-30 20:34:34 +020076
Gaetan Rivet05d692d2020-02-14 17:42:54 +010077/* Dummy frontend used to create all checks sessions. */
Willy Tarreau51cd5952020-06-05 12:25:38 +020078struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020079
Christopher Faulet61cc8522020-04-20 14:54:42 +020080/**************************************************************************/
81/************************ Handle check results ****************************/
82/**************************************************************************/
83struct check_status {
84 short result; /* one of SRV_CHK_* */
85 char *info; /* human readable short info */
86 char *desc; /* long description */
87};
88
89struct analyze_status {
90 char *desc; /* description */
91 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
92};
93
Simon Horman63a4a822012-03-19 07:24:41 +090094static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +010095 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
96 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +020097 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020098
Willy Tarreau23964182014-05-20 20:56:30 +020099 /* Below we have finished checks */
100 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100101 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100102
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100103 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200104
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100105 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
106 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
107 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200108
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100109 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
110 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
111 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200112
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100113 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
114 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200115
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200116 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200117
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100118 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
119 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
120 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900121
122 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
123 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200124 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200125};
126
Simon Horman63a4a822012-03-19 07:24:41 +0900127static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100128 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
129
130 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
131 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
132
133 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
134 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
135 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
136 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
137
138 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
139 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
140 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
141};
142
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100143/* checks if <err> is a real error for errno or one that can be ignored, and
144 * return 0 for these ones or <err> for real ones.
145 */
146static inline int unclean_errno(int err)
147{
148 if (err == EAGAIN || err == EINPROGRESS ||
149 err == EISCONN || err == EALREADY)
150 return 0;
151 return err;
152}
153
Christopher Faulet61cc8522020-04-20 14:54:42 +0200154/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200155const char *get_check_status_description(short check_status) {
156
157 const char *desc;
158
159 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200160 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200161 else
162 desc = NULL;
163
164 if (desc && *desc)
165 return desc;
166 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200167 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200168}
169
Christopher Faulet61cc8522020-04-20 14:54:42 +0200170/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200171const char *get_check_status_info(short check_status) {
172
173 const char *info;
174
175 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200176 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200177 else
178 info = NULL;
179
180 if (info && *info)
181 return info;
182 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200183 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200184}
185
Christopher Faulet61cc8522020-04-20 14:54:42 +0200186/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100187const char *get_analyze_status(short analyze_status) {
188
189 const char *desc;
190
191 if (analyze_status < HANA_STATUS_SIZE)
192 desc = analyze_statuses[analyze_status].desc;
193 else
194 desc = NULL;
195
196 if (desc && *desc)
197 return desc;
198 else
199 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
200}
201
Christopher Faulet61cc8522020-04-20 14:54:42 +0200202/* Sets check->status, update check->duration and fill check->result with an
203 * adequate CHK_RES_* value. The new check->health is computed based on the
204 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200205 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200206 * Shows information in logs about failed health check if server is UP or
207 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200208 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200209void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100210{
Simon Horman4a741432013-02-23 15:35:38 +0900211 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200212 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200213 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900214
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200215 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100216 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900217 check->desc[0] = '\0';
218 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200219 return;
220 }
221
Simon Horman4a741432013-02-23 15:35:38 +0900222 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200223 return;
224
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200225 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900226 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
227 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200228 } else
Simon Horman4a741432013-02-23 15:35:38 +0900229 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200230
Simon Horman4a741432013-02-23 15:35:38 +0900231 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200232 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900233 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200234
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100235 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900236 check->duration = -1;
237 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200238 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900239 check->duration = tv_ms_elapsed(&check->start, &now);
240 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200241 }
242
Willy Tarreau23964182014-05-20 20:56:30 +0200243 /* no change is expected if no state change occurred */
244 if (check->result == CHK_RES_NEUTRAL)
245 return;
246
Olivier Houchard0923fa42019-01-11 18:43:04 +0100247 /* If the check was really just sending a mail, it won't have an
248 * associated server, so we're done now.
249 */
250 if (!s)
251 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200252 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200253
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200254 switch (check->result) {
255 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200256 /* Failure to connect to the agent as a secondary check should not
257 * cause the server to be marked down.
258 */
259 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900260 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200261 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100262 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200263 report = 1;
264 check->health--;
265 if (check->health < check->rise)
266 check->health = 0;
267 }
268 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200269
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200270 case CHK_RES_PASSED:
271 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
272 if ((check->health < check->rise + check->fall - 1) &&
273 (check->result == CHK_RES_PASSED || check->health > 0)) {
274 report = 1;
275 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200276
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200277 if (check->health >= check->rise)
278 check->health = check->rise + check->fall - 1; /* OK now */
279 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200280
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200281 /* clear consecutive_errors if observing is enabled */
282 if (s->onerror)
283 s->consecutive_errors = 0;
284 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100285
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200286 default:
287 break;
288 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200289
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200290 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
291 (status != prev_status || report)) {
292 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200293 "%s check for %sserver %s/%s %s%s",
294 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200295 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100296 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100297 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200298 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200299
Emeric Brun5a133512017-10-19 14:42:30 +0200300 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200301
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100302 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200303 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
304 (check->health >= check->rise) ? check->fall : check->rise,
305 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200306
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200307 ha_warning("%s.\n", trash.area);
308 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
309 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200310 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200311}
312
Willy Tarreau4eec5472014-05-20 22:32:27 +0200313/* Marks the check <check>'s server down if the current check is already failed
314 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200315 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200316void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200317{
Simon Horman4a741432013-02-23 15:35:38 +0900318 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900319
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200320 /* The agent secondary check should only cause a server to be marked
321 * as down if check->status is HCHK_STATUS_L7STS, which indicates
322 * that the agent returned "fail", "stopped" or "down".
323 * The implication here is that failure to connect to the agent
324 * as a secondary check should not cause the server to be marked
325 * down. */
326 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
327 return;
328
Willy Tarreau4eec5472014-05-20 22:32:27 +0200329 if (check->health > 0)
330 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100331
Willy Tarreau4eec5472014-05-20 22:32:27 +0200332 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200333 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200334}
335
Willy Tarreauaf549582014-05-16 17:37:50 +0200336/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200337 * it isn't in maintenance, it is not tracking a down server and other checks
338 * comply. The rule is simple : by default, a server is up, unless any of the
339 * following conditions is true :
340 * - health check failed (check->health < rise)
341 * - agent check failed (agent->health < rise)
342 * - the server tracks a down server (track && track->state == STOPPED)
343 * Note that if the server has a slowstart, it will switch to STARTING instead
344 * of RUNNING. Also, only the health checks support the nolb mode, so the
345 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200346 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200347void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200348{
Simon Horman4a741432013-02-23 15:35:38 +0900349 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100350
Emeric Brun52a91d32017-08-31 14:41:55 +0200351 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200352 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100353
Emeric Brun52a91d32017-08-31 14:41:55 +0200354 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200355 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100356
Willy Tarreau3e048382014-05-21 10:30:54 +0200357 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
358 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100359
Willy Tarreau3e048382014-05-21 10:30:54 +0200360 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
361 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200362
Emeric Brun52a91d32017-08-31 14:41:55 +0200363 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200364 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100365
Emeric Brun5a133512017-10-19 14:42:30 +0200366 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100367}
368
Willy Tarreaudb58b792014-05-21 13:57:23 +0200369/* Marks the check <check> as valid and tries to set its server into stopping mode
370 * if it was running or starting, and provided it isn't in maintenance and other
371 * checks comply. The conditions for the server to be marked in stopping mode are
372 * the same as for it to be turned up. Also, only the health checks support the
373 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200374 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200375void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200376{
Simon Horman4a741432013-02-23 15:35:38 +0900377 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100378
Emeric Brun52a91d32017-08-31 14:41:55 +0200379 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200380 return;
381
Willy Tarreaudb58b792014-05-21 13:57:23 +0200382 if (check->state & CHK_ST_AGENT)
383 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100384
Emeric Brun52a91d32017-08-31 14:41:55 +0200385 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200386 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100387
Willy Tarreaudb58b792014-05-21 13:57:23 +0200388 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
389 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100390
Willy Tarreaudb58b792014-05-21 13:57:23 +0200391 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
392 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100393
Willy Tarreaub26881a2017-12-23 11:16:49 +0100394 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100395}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200396
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100397/* note: use health_adjust() only, which first checks that the observe mode is
398 * enabled.
399 */
400void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100401{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100402 int failed;
403 int expire;
404
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100405 if (s->observe >= HANA_OBS_SIZE)
406 return;
407
Willy Tarreaubb956662013-01-24 00:37:39 +0100408 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100409 return;
410
411 switch (analyze_statuses[status].lr[s->observe - 1]) {
412 case 1:
413 failed = 1;
414 break;
415
416 case 2:
417 failed = 0;
418 break;
419
420 default:
421 return;
422 }
423
424 if (!failed) {
425 /* good: clear consecutive_errors */
426 s->consecutive_errors = 0;
427 return;
428 }
429
Olivier Houchard7059c552019-03-08 18:49:32 +0100430 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100431
432 if (s->consecutive_errors < s->consecutive_errors_limit)
433 return;
434
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100435 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
436 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100437
438 switch (s->onerror) {
439 case HANA_ONERR_FASTINTER:
440 /* force fastinter - nothing to do here as all modes force it */
441 break;
442
443 case HANA_ONERR_SUDDTH:
444 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900445 if (s->check.health > s->check.rise)
446 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100447
Tim Duesterhus588b3142020-05-29 14:35:51 +0200448 /* fall through */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100449
450 case HANA_ONERR_FAILCHK:
451 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200452 set_server_check_status(&s->check, HCHK_STATUS_HANA,
453 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200454 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100455 break;
456
457 case HANA_ONERR_MARKDWN:
458 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900459 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200460 set_server_check_status(&s->check, HCHK_STATUS_HANA,
461 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200462 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100463 break;
464
465 default:
466 /* write a warning? */
467 break;
468 }
469
470 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100471 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100472
Simon Horman66183002013-02-23 10:16:43 +0900473 if (s->check.fastinter) {
474 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300475 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200476 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300477 /* requeue check task with new expire */
478 task_queue(s->check.task);
479 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100480 }
Willy Tarreauef781042010-01-27 11:53:01 +0100481}
482
Christopher Faulet61cc8522020-04-20 14:54:42 +0200483/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100484 * closed, keep errno intact as it is supposed to contain the valid error code.
485 * If no error is reported, check the socket's error queue using getsockopt().
486 * Warning, this must be done only once when returning from poll, and never
487 * after an I/O error was attempted, otherwise the error queue might contain
488 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
489 * socket. Returns non-zero if an error was reported, zero if everything is
490 * clean (including a properly closed socket).
491 */
492static int retrieve_errno_from_socket(struct connection *conn)
493{
494 int skerr;
495 socklen_t lskerr = sizeof(skerr);
496
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100497 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100498 return 1;
499
Willy Tarreau3c728722014-01-23 13:50:42 +0100500 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100501 return 0;
502
Willy Tarreau585744b2017-08-24 14:31:19 +0200503 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100504 errno = skerr;
505
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100506 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100507
508 if (!errno) {
509 /* we could not retrieve an error, that does not mean there is
510 * none. Just don't change anything and only report the prior
511 * error if any.
512 */
513 if (conn->flags & CO_FL_ERROR)
514 return 1;
515 else
516 return 0;
517 }
518
519 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
520 return 1;
521}
522
Christopher Faulet61cc8522020-04-20 14:54:42 +0200523/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100524 * and adjust the server status accordingly. It may make use of <errno_bck>
525 * if non-null when the caller is absolutely certain of its validity (eg:
526 * checked just after a syscall). If the caller doesn't have a valid errno,
527 * it can pass zero, and retrieve_errno_from_socket() will be called to try
528 * to extract errno from the socket. If no error is reported, it will consider
529 * the <expired> flag. This is intended to be used when a connection error was
530 * reported in conn->flags or when a timeout was reported in <expired>. The
531 * function takes care of not updating a server status which was already set.
532 * All situations where at least one of <expired> or CO_FL_ERROR are set
533 * produce a status.
534 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200535void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100536{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200537 struct conn_stream *cs = check->cs;
538 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100539 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200540 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200541 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100542
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100543 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100544 return;
545
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100546 errno = unclean_errno(errno_bck);
547 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100548 retrieve_errno_from_socket(conn);
549
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200550 if (conn && !(conn->flags & CO_FL_ERROR) &&
551 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100552 return;
553
554 /* we'll try to build a meaningful error message depending on the
555 * context of the error possibly present in conn->err_code, and the
556 * socket error possibly collected above. This is useful to know the
557 * exact step of the L6 layer (eg: SSL handshake).
558 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200559 chk = get_trash_chunk();
560
Christopher Faulet799f3a42020-04-07 12:06:14 +0200561 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200562 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200563 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200564 if (!step)
565 chunk_printf(chk, " at initial connection step of tcp-check");
566 else {
567 chunk_printf(chk, " at step %d of tcp-check", step);
568 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200569 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
570 if (check->current_step->connect.port)
571 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200572 else
573 chunk_appendf(chk, " (connect)");
574 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200575 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
576 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100577
578 switch (expect->type) {
579 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200580 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100581 break;
582 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200583 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100584 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200585 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200586 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100587 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200588 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100589 chunk_appendf(chk, " (expect binary regex)");
590 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200591 case TCPCHK_EXPECT_STRING_LF:
592 chunk_appendf(chk, " (expect log-format string)");
593 break;
594 case TCPCHK_EXPECT_BINARY_LF:
595 chunk_appendf(chk, " (expect log-format binary)");
596 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200597 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200598 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200599 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200600 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200601 chunk_appendf(chk, " (expect HTTP status regex)");
602 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200603 case TCPCHK_EXPECT_HTTP_HEADER:
604 chunk_appendf(chk, " (expect HTTP header pattern)");
605 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200606 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200607 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200608 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200609 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200610 chunk_appendf(chk, " (expect HTTP body regex)");
611 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200612 case TCPCHK_EXPECT_HTTP_BODY_LF:
613 chunk_appendf(chk, " (expect log-format HTTP body)");
614 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200615 case TCPCHK_EXPECT_CUSTOM:
616 chunk_appendf(chk, " (expect custom function)");
617 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100618 case TCPCHK_EXPECT_UNDEF:
619 chunk_appendf(chk, " (undefined expect!)");
620 break;
621 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200622 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200623 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200624 chunk_appendf(chk, " (send)");
625 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200626
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200627 if (check->current_step && check->current_step->comment)
628 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200629 }
630 }
631
Willy Tarreau00149122017-10-04 18:05:01 +0200632 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100633 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200634 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
635 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100636 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200637 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
638 chk->area);
639 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100640 }
641 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100642 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200643 chunk_printf(&trash, "%s%s", strerror(errno),
644 chk->area);
645 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100646 }
647 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200648 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100649 }
650 }
651
Willy Tarreau00149122017-10-04 18:05:01 +0200652 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200653 /* NOTE: this is reported after <fall> tries */
654 chunk_printf(chk, "No port available for the TCP connection");
655 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
656 }
657
Christopher Faulet5e293762020-10-26 11:10:49 +0100658 if (!conn || !conn->ctrl) {
659 /* error before any connection attempt (connection allocation error or no control layer) */
Willy Tarreau00149122017-10-04 18:05:01 +0200660 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
661 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100662 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100663 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200664 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100665 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
666 else if (expired)
667 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200668
669 /*
670 * might be due to a server IP change.
671 * Let's trigger a DNS resolution if none are currently running.
672 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100673 if (check->server)
674 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200675
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100676 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100677 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100678 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200679 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100680 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
681 else if (expired)
682 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
683 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200684 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100685 /* I/O error after connection was established and before we could diagnose */
686 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
687 }
688 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200689 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
690
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100691 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200692 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
693 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200694 tout = check->current_step->expect.tout_status;
695 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100696 }
697
698 return;
699}
700
Simon Horman98637e52014-06-20 12:30:16 +0900701
Christopher Faulet61cc8522020-04-20 14:54:42 +0200702/* Builds the server state header used by HTTP health-checks */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200703int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +0900704{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200705 int sv_state;
706 int ratio;
707 char addr[46];
708 char port[6];
709 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
710 "UP %d/%d", "UP",
711 "NOLB %d/%d", "NOLB",
712 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +0900713
Christopher Faulet61cc8522020-04-20 14:54:42 +0200714 if (!(s->check.state & CHK_ST_ENABLED))
715 sv_state = 6;
716 else if (s->cur_state != SRV_ST_STOPPED) {
717 if (s->check.health == s->check.rise + s->check.fall - 1)
718 sv_state = 3; /* UP */
719 else
720 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +0900721
Christopher Faulet61cc8522020-04-20 14:54:42 +0200722 if (s->cur_state == SRV_ST_STOPPING)
723 sv_state += 2;
724 } else {
725 if (s->check.health)
726 sv_state = 1; /* going up */
727 else
728 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +0900729 }
Willy Tarreaub7b24782016-06-21 15:32:29 +0200730
Christopher Faulet61cc8522020-04-20 14:54:42 +0200731 chunk_appendf(buf, srv_hlt_st[sv_state],
732 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
733 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +0200734
Christopher Faulet61cc8522020-04-20 14:54:42 +0200735 addr_to_str(&s->addr, addr, sizeof(addr));
736 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
737 snprintf(port, sizeof(port), "%u", s->svc_port);
738 else
739 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +0200740
Christopher Faulet61cc8522020-04-20 14:54:42 +0200741 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
742 addr, port, s->proxy->id, s->id,
743 global.node,
744 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
745 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
746 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
747 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +0100748
Christopher Faulet61cc8522020-04-20 14:54:42 +0200749 if ((s->cur_state == SRV_ST_STARTING) &&
750 now.tv_sec < s->last_change + s->slowstart &&
751 now.tv_sec >= s->last_change) {
752 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
753 chunk_appendf(buf, "; throttle=%d%%", ratio);
754 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200755
Christopher Faulet61cc8522020-04-20 14:54:42 +0200756 return b_data(buf);
757}
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200758
Willy Tarreau51cd5952020-06-05 12:25:38 +0200759/**************************************************************************/
Willy Tarreau51cd5952020-06-05 12:25:38 +0200760/***************** Health-checks based on connections *********************/
761/**************************************************************************/
762/* This function is used only for server health-checks. It handles connection
763 * status updates including errors. If necessary, it wakes the check task up.
764 * It returns 0 on normal cases, <0 if at least one close() has happened on the
765 * connection (eg: reconnect). It relies on tcpcheck_main().
Christopher Faulet61cc8522020-04-20 14:54:42 +0200766 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200767static int wake_srv_chk(struct conn_stream *cs)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200768{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200769 struct connection *conn = cs->conn;
770 struct check *check = cs->data;
771 struct email_alertq *q = container_of(check, typeof(*q), check);
772 int ret = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200773
Willy Tarreau51cd5952020-06-05 12:25:38 +0200774 if (check->server)
775 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
776 else
777 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200778
Willy Tarreau51cd5952020-06-05 12:25:38 +0200779 /* we may have to make progress on the TCP checks */
780 ret = tcpcheck_main(check);
Christopher Fauletaaab0832020-05-05 15:54:22 +0200781
Willy Tarreau51cd5952020-06-05 12:25:38 +0200782 cs = check->cs;
783 conn = cs->conn;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200784
Willy Tarreau51cd5952020-06-05 12:25:38 +0200785 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
786 /* We may get error reports bypassing the I/O handlers, typically
787 * the case when sending a pure TCP check which fails, then the I/O
788 * handlers above are not called. This is completely handled by the
789 * main processing task so let's simply wake it up. If we get here,
790 * we expect errno to still be valid.
791 */
792 chk_report_conn_err(check, errno, 0);
793 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200794 }
795
Willy Tarreau51cd5952020-06-05 12:25:38 +0200796 if (check->result != CHK_RES_UNKNOWN) {
797 /* Check complete or aborted. If connection not yet closed do it
798 * now and wake the check task up to be sure the result is
799 * handled ASAP. */
800 conn_sock_drain(conn);
801 cs_close(cs);
802 ret = -1;
803 /* We may have been scheduled to run, and the
804 * I/O handler expects to have a cs, so remove
805 * the tasklet
806 */
807 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
808 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200809 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200810
Willy Tarreau51cd5952020-06-05 12:25:38 +0200811 if (check->server)
812 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
813 else
814 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200815
Willy Tarreau51cd5952020-06-05 12:25:38 +0200816 /* if a connection got replaced, we must absolutely prevent the connection
817 * handler from touching its fd, and perform the FD polling updates ourselves
818 */
819 if (ret < 0)
820 conn_cond_update_polling(conn);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200821
Christopher Faulet61cc8522020-04-20 14:54:42 +0200822 return ret;
823}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200824
Willy Tarreau51cd5952020-06-05 12:25:38 +0200825/* This function checks if any I/O is wanted, and if so, attempts to do so */
826static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200827{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200828 struct check *check = ctx;
829 struct conn_stream *cs = check->cs;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200830
Willy Tarreau51cd5952020-06-05 12:25:38 +0200831 wake_srv_chk(cs);
832 return NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200833}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200834
Willy Tarreau51cd5952020-06-05 12:25:38 +0200835/* manages a server health-check that uses a connection. Returns
836 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Faulet61cc8522020-04-20 14:54:42 +0200837 *
838 * Please do NOT place any return statement in this function and only leave
Willy Tarreau51cd5952020-06-05 12:25:38 +0200839 * via the out_unlock label.
Christopher Faulet61cc8522020-04-20 14:54:42 +0200840 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200841static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200842{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200843 struct check *check = context;
844 struct proxy *proxy = check->proxy;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200845 struct conn_stream *cs = check->cs;
846 struct connection *conn = cs_conn(cs);
Willy Tarreau51cd5952020-06-05 12:25:38 +0200847 int rv;
848 int expired = tick_is_expired(t->expire, now_ms);
Willy Tarreaudeccd112018-06-14 18:38:55 +0200849
Willy Tarreau51cd5952020-06-05 12:25:38 +0200850 if (check->server)
851 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
852 if (!(check->state & CHK_ST_INPROGRESS)) {
853 /* no check currently running */
854 if (!expired) /* woke up too early */
855 goto out_unlock;
Willy Tarreauabca5b62013-12-06 14:19:25 +0100856
Willy Tarreau51cd5952020-06-05 12:25:38 +0200857 /* we don't send any health-checks when the proxy is
858 * stopped, the server should not be checked or the check
859 * is disabled.
860 */
861 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
Willy Tarreauc3914d42020-09-24 08:39:22 +0200862 proxy->disabled)
Willy Tarreau51cd5952020-06-05 12:25:38 +0200863 goto reschedule;
Christopher Faulet404f9192020-04-09 23:13:54 +0200864
Willy Tarreau51cd5952020-06-05 12:25:38 +0200865 /* we'll initiate a new check */
866 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet404f9192020-04-09 23:13:54 +0200867
Willy Tarreau51cd5952020-06-05 12:25:38 +0200868 check->state |= CHK_ST_INPROGRESS;
869 b_reset(&check->bi);
870 b_reset(&check->bo);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200871
Willy Tarreau51cd5952020-06-05 12:25:38 +0200872 task_set_affinity(t, tid_bit);
873
874 check->current_step = NULL;
875 tcpcheck_main(check);
876 goto out_unlock;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200877 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200878 else {
879 /* there was a test running.
880 * First, let's check whether there was an uncaught error,
881 * which can happen on connect timeout or error.
882 */
883 if (check->result == CHK_RES_UNKNOWN) {
884 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
885 chk_report_conn_err(check, 0, expired);
886 }
887 else
888 goto out_unlock; /* timeout not reached, wait again */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200889 }
Christopher Faulet404f9192020-04-09 23:13:54 +0200890
Willy Tarreau51cd5952020-06-05 12:25:38 +0200891 /* check complete or aborted */
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200892
Willy Tarreau51cd5952020-06-05 12:25:38 +0200893 check->current_step = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200894
Willy Tarreau51cd5952020-06-05 12:25:38 +0200895 if (conn && conn->xprt) {
896 /* The check was aborted and the connection was not yet closed.
897 * This can happen upon timeout, or when an external event such
898 * as a failed response coupled with "observe layer7" caused the
899 * server state to be suddenly changed.
900 */
901 conn_sock_drain(conn);
902 cs_close(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200903 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200904
Willy Tarreau51cd5952020-06-05 12:25:38 +0200905 if (cs) {
906 if (check->wait_list.events)
907 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
908 /* We may have been scheduled to run, and the
909 * I/O handler expects to have a cs, so remove
910 * the tasklet
911 */
912 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
913 cs_destroy(cs);
914 cs = check->cs = NULL;
915 conn = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200916 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200917
918 if (check->sess != NULL) {
919 vars_prune(&check->vars, check->sess, NULL);
920 session_free(check->sess);
921 check->sess = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200922 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200923
924 if (check->server) {
925 if (check->result == CHK_RES_FAILED) {
926 /* a failure or timeout detected */
927 check_notify_failure(check);
928 }
929 else if (check->result == CHK_RES_CONDPASS) {
930 /* check is OK but asks for stopping mode */
931 check_notify_stopping(check);
932 }
933 else if (check->result == CHK_RES_PASSED) {
934 /* a success was detected */
935 check_notify_success(check);
936 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200937 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200938 task_set_affinity(t, MAX_THREADS_MASK);
939 check->state &= ~CHK_ST_INPROGRESS;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200940
Willy Tarreau51cd5952020-06-05 12:25:38 +0200941 if (check->server) {
942 rv = 0;
943 if (global.spread_checks > 0) {
944 rv = srv_getinter(check) * global.spread_checks / 100;
945 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
Christopher Faulet61cc8522020-04-20 14:54:42 +0200946 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200947 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet61cc8522020-04-20 14:54:42 +0200948 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200949 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200950
951 reschedule:
952 while (tick_is_expired(t->expire, now_ms))
953 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
954 out_unlock:
955 if (check->server)
956 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
957 return t;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200958}
959
Willy Tarreau51cd5952020-06-05 12:25:38 +0200960
Christopher Faulet61cc8522020-04-20 14:54:42 +0200961/**************************************************************************/
962/************************** Init/deinit checks ****************************/
963/**************************************************************************/
Willy Tarreaucee013e2020-06-05 11:40:38 +0200964const char *init_check(struct check *check, int type)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200965{
966 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200967
Christopher Faulet61cc8522020-04-20 14:54:42 +0200968 b_reset(&check->bi); check->bi.size = global.tune.chksize;
969 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200970
Tim Duesterhuse52b6e52020-09-12 20:26:43 +0200971 check->bi.area = calloc(check->bi.size, sizeof(*check->bi.area));
972 check->bo.area = calloc(check->bo.size, sizeof(*check->bo.area));
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200973
Christopher Faulet61cc8522020-04-20 14:54:42 +0200974 if (!check->bi.area || !check->bo.area)
975 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +0100976
Christopher Faulet61cc8522020-04-20 14:54:42 +0200977 check->wait_list.tasklet = tasklet_new();
978 if (!check->wait_list.tasklet)
979 return "out of memory while allocating check tasklet";
980 check->wait_list.events = 0;
981 check->wait_list.tasklet->process = event_srv_chk_io;
982 check->wait_list.tasklet->context = check;
983 return NULL;
984}
985
986void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +0100987{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200988 task_destroy(check->task);
989 if (check->wait_list.tasklet)
990 tasklet_free(check->wait_list.tasklet);
991
992 free(check->bi.area);
993 free(check->bo.area);
994 if (check->cs) {
995 free(check->cs->conn);
996 check->cs->conn = NULL;
997 cs_free(check->cs);
998 check->cs = NULL;
999 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001000}
1001
Christopher Faulet61cc8522020-04-20 14:54:42 +02001002/* manages a server health-check. Returns the time the task accepts to wait, or
1003 * TIME_ETERNITY for infinity.
1004 */
Willy Tarreaucee013e2020-06-05 11:40:38 +02001005struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001006{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001007 struct check *check = context;
1008
1009 if (check->type == PR_O2_EXT_CHK)
1010 return process_chk_proc(t, context, state);
1011 return process_chk_conn(t, context, state);
1012
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001013}
1014
Christopher Faulet61cc8522020-04-20 14:54:42 +02001015
1016static int start_check_task(struct check *check, int mininter,
1017 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001018{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001019 struct task *t;
1020 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001021
Christopher Faulet61cc8522020-04-20 14:54:42 +02001022 if (check->type == PR_O2_EXT_CHK)
1023 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001024
Christopher Faulet61cc8522020-04-20 14:54:42 +02001025 /* task for the check */
1026 if ((t = task_new(thread_mask)) == NULL) {
1027 ha_alert("Starting [%s:%s] check: out of memory.\n",
1028 check->server->proxy->id, check->server->id);
1029 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001030 }
1031
Christopher Faulet61cc8522020-04-20 14:54:42 +02001032 check->task = t;
1033 t->process = process_chk;
1034 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001035
Christopher Faulet61cc8522020-04-20 14:54:42 +02001036 if (mininter < srv_getinter(check))
1037 mininter = srv_getinter(check);
1038
1039 if (global.max_spread_checks && mininter > global.max_spread_checks)
1040 mininter = global.max_spread_checks;
1041
1042 /* check this every ms */
1043 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
1044 check->start = now;
1045 task_queue(t);
1046
1047 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001048}
1049
Christopher Faulet61cc8522020-04-20 14:54:42 +02001050/* updates the server's weight during a warmup stage. Once the final weight is
1051 * reached, the task automatically stops. Note that any server status change
1052 * must have updated s->last_change accordingly.
1053 */
1054static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001055{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001056 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001057
Christopher Faulet61cc8522020-04-20 14:54:42 +02001058 /* by default, plan on stopping the task */
1059 t->expire = TICK_ETERNITY;
1060 if ((s->next_admin & SRV_ADMF_MAINT) ||
1061 (s->next_state != SRV_ST_STARTING))
1062 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02001063
Christopher Faulet61cc8522020-04-20 14:54:42 +02001064 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001065
Christopher Faulet61cc8522020-04-20 14:54:42 +02001066 /* recalculate the weights and update the state */
1067 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02001068
Christopher Faulet61cc8522020-04-20 14:54:42 +02001069 /* probably that we can refill this server with a bit more connections */
1070 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02001071
Christopher Faulet61cc8522020-04-20 14:54:42 +02001072 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02001073
Christopher Faulet61cc8522020-04-20 14:54:42 +02001074 /* get back there in 1 second or 1/20th of the slowstart interval,
1075 * whichever is greater, resulting in small 5% steps.
1076 */
1077 if (s->next_state == SRV_ST_STARTING)
1078 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
1079 return t;
1080}
1081
1082/*
1083 * Start health-check.
1084 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
1085 */
1086static int start_checks()
1087{
1088
1089 struct proxy *px;
1090 struct server *s;
1091 struct task *t;
1092 int nbcheck=0, mininter=0, srvpos=0;
1093
1094 /* 0- init the dummy frontend used to create all checks sessions */
1095 init_new_proxy(&checks_fe);
1096 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
1097 checks_fe.mode = PR_MODE_TCP;
1098 checks_fe.maxconn = 0;
1099 checks_fe.conn_retries = CONN_RETRIES;
1100 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
1101 checks_fe.timeout.client = TICK_ETERNITY;
1102
1103 /* 1- count the checkers to run simultaneously.
1104 * We also determine the minimum interval among all of those which
1105 * have an interval larger than SRV_CHK_INTER_THRES. This interval
1106 * will be used to spread their start-up date. Those which have
1107 * a shorter interval will start independently and will not dictate
1108 * too short an interval for all others.
1109 */
1110 for (px = proxies_list; px; px = px->next) {
1111 for (s = px->srv; s; s = s->next) {
1112 if (s->slowstart) {
1113 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
1114 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1115 return ERR_ALERT | ERR_FATAL;
1116 }
1117 /* We need a warmup task that will be called when the server
1118 * state switches from down to up.
1119 */
1120 s->warmup = t;
1121 t->process = server_warmup;
1122 t->context = s;
1123 /* server can be in this state only because of */
1124 if (s->next_state == SRV_ST_STARTING)
1125 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 +02001126 }
1127
Christopher Faulet61cc8522020-04-20 14:54:42 +02001128 if (s->check.state & CHK_ST_CONFIGURED) {
1129 nbcheck++;
1130 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
1131 (!mininter || mininter > srv_getinter(&s->check)))
1132 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02001133 }
1134
Christopher Faulet61cc8522020-04-20 14:54:42 +02001135 if (s->agent.state & CHK_ST_CONFIGURED) {
1136 nbcheck++;
1137 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
1138 (!mininter || mininter > srv_getinter(&s->agent)))
1139 mininter = srv_getinter(&s->agent);
1140 }
Christopher Faulet5c288742020-03-31 08:15:58 +02001141 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001142 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001143
Christopher Faulet61cc8522020-04-20 14:54:42 +02001144 if (!nbcheck)
1145 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001146
Christopher Faulet61cc8522020-04-20 14:54:42 +02001147 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02001148
Christopher Faulet61cc8522020-04-20 14:54:42 +02001149 /*
1150 * 2- start them as far as possible from each others. For this, we will
1151 * start them after their interval set to the min interval divided by
1152 * the number of servers, weighted by the server's position in the list.
1153 */
1154 for (px = proxies_list; px; px = px->next) {
1155 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
1156 if (init_pid_list()) {
1157 ha_alert("Starting [%s] check: out of memory.\n", px->id);
1158 return ERR_ALERT | ERR_FATAL;
1159 }
1160 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001161
Christopher Faulet61cc8522020-04-20 14:54:42 +02001162 for (s = px->srv; s; s = s->next) {
1163 /* A task for the main check */
1164 if (s->check.state & CHK_ST_CONFIGURED) {
1165 if (s->check.type == PR_O2_EXT_CHK) {
1166 if (!prepare_external_check(&s->check))
1167 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001168 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001169 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
1170 return ERR_ALERT | ERR_FATAL;
1171 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02001172 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001173
Christopher Faulet61cc8522020-04-20 14:54:42 +02001174 /* A task for a auxiliary agent check */
1175 if (s->agent.state & CHK_ST_CONFIGURED) {
1176 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
1177 return ERR_ALERT | ERR_FATAL;
1178 }
1179 srvpos++;
1180 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001181 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001182 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001183 return 0;
1184}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001185
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001186
Christopher Faulet61cc8522020-04-20 14:54:42 +02001187/*
1188 * Return value:
1189 * the port to be used for the health check
1190 * 0 in case no port could be found for the check
1191 */
1192static int srv_check_healthcheck_port(struct check *chk)
1193{
1194 int i = 0;
1195 struct server *srv = NULL;
1196
1197 srv = chk->server;
1198
1199 /* by default, we use the health check port ocnfigured */
1200 if (chk->port > 0)
1201 return chk->port;
1202
1203 /* try to get the port from check_core.addr if check.port not set */
1204 i = get_host_port(&chk->addr);
1205 if (i > 0)
1206 return i;
1207
1208 /* try to get the port from server address */
1209 /* prevent MAPPORTS from working at this point, since checks could
1210 * not be performed in such case (MAPPORTS impose a relative ports
1211 * based on live traffic)
1212 */
1213 if (srv->flags & SRV_F_MAPPORTS)
1214 return 0;
1215
1216 i = srv->svc_port; /* by default */
1217 if (i > 0)
1218 return i;
1219
1220 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001221}
1222
Christopher Faulet61cc8522020-04-20 14:54:42 +02001223/* Initializes an health-check attached to the server <srv>. Non-zero is returned
1224 * if an error occurred.
1225 */
1226static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001227{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001228 const char *err;
1229 struct tcpcheck_rule *r;
1230 int ret = 0;
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001231 int check_type;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001232
Christopher Faulet61cc8522020-04-20 14:54:42 +02001233 if (!srv->do_check)
1234 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001235
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001236 check_type = srv->check.tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001237
Christopher Faulet61cc8522020-04-20 14:54:42 +02001238 /* If neither a port nor an addr was specified and no check transport
1239 * layer is forced, then the transport layer used by the checks is the
1240 * same as for the production traffic. Otherwise we use raw_sock by
1241 * default, unless one is specified.
1242 */
1243 if (!srv->check.port && !is_addr(&srv->check.addr)) {
1244 if (!srv->check.use_ssl && srv->use_ssl != -1) {
1245 srv->check.use_ssl = srv->use_ssl;
1246 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001247 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001248 else if (srv->check.use_ssl == 1)
1249 srv->check.xprt = xprt_get(XPRT_SSL);
1250 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001251 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02001252 else if (srv->check.use_ssl == 1)
1253 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001254
Christopher Faulet12882cf2020-04-23 15:50:18 +02001255 /* Inherit the mux protocol from the server if not already defined for
1256 * the check
1257 */
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001258 if (srv->mux_proto && !srv->check.mux_proto &&
1259 ((srv->mux_proto->mode == PROTO_MODE_HTTP && check_type == TCPCHK_RULES_HTTP_CHK) ||
1260 (srv->mux_proto->mode == PROTO_MODE_TCP && check_type != TCPCHK_RULES_HTTP_CHK))) {
Christopher Faulet12882cf2020-04-23 15:50:18 +02001261 srv->check.mux_proto = srv->mux_proto;
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001262 }
Amaury Denoyelle92c8ac12020-11-13 12:34:57 +01001263 /* test that check proto is valid if explicitly defined */
1264 else if (srv->check.mux_proto &&
1265 ((srv->check.mux_proto->mode == PROTO_MODE_HTTP && check_type != TCPCHK_RULES_HTTP_CHK) ||
1266 (srv->check.mux_proto->mode == PROTO_MODE_TCP && check_type == TCPCHK_RULES_HTTP_CHK))) {
1267 ha_alert("config: %s '%s': server '%s' uses an incompatible MUX protocol for the selected check type\n",
1268 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1269 ret |= ERR_ALERT | ERR_FATAL;
1270 goto out;
1271 }
Christopher Faulet12882cf2020-04-23 15:50:18 +02001272
Christopher Faulet61cc8522020-04-20 14:54:42 +02001273 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001274
Christopher Faulet61cc8522020-04-20 14:54:42 +02001275 /* We need at least a service port, a check port or the first tcp-check
1276 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
1277 */
1278 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
1279 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
1280 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001281
Christopher Faulet61cc8522020-04-20 14:54:42 +02001282 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
1283 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
1284 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1285 ret |= ERR_ALERT | ERR_ABORT;
1286 goto out;
1287 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001288
Christopher Faulet61cc8522020-04-20 14:54:42 +02001289 /* search the first action (connect / send / expect) in the list */
1290 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
1291 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
1292 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
1293 "nor tcp_check rule 'connect' with port information.\n",
1294 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1295 ret |= ERR_ALERT | ERR_ABORT;
1296 goto out;
1297 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001298
Christopher Faulet61cc8522020-04-20 14:54:42 +02001299 /* scan the tcp-check ruleset to ensure a port has been configured */
1300 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
1301 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
1302 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
1303 "and a tcp_check rule 'connect' with no port information.\n",
1304 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1305 ret |= ERR_ALERT | ERR_ABORT;
1306 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001307 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001308 }
1309
Christopher Faulet61cc8522020-04-20 14:54:42 +02001310 init:
1311 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
1312 struct tcpcheck_ruleset *rs = NULL;
1313 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
1314 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001315
Christopher Faulet61cc8522020-04-20 14:54:42 +02001316 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
1317 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02001318
Christopher Faulet61cc8522020-04-20 14:54:42 +02001319 rs = find_tcpcheck_ruleset("*tcp-check");
1320 if (!rs) {
1321 rs = create_tcpcheck_ruleset("*tcp-check");
1322 if (rs == NULL) {
1323 ha_alert("config: %s '%s': out of memory.\n",
1324 proxy_type_str(srv->proxy), srv->proxy->id);
1325 ret |= ERR_ALERT | ERR_FATAL;
1326 goto out;
1327 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001328 }
1329
Christopher Faulet61cc8522020-04-20 14:54:42 +02001330 free_tcpcheck_vars(&rules->preset_vars);
1331 rules->list = &rs->rules;
1332 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001333 }
1334
Christopher Faulet61cc8522020-04-20 14:54:42 +02001335 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
1336 if (err) {
1337 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
1338 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1339 ret |= ERR_ALERT | ERR_ABORT;
1340 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001341 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001342 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
1343 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02001344
Christopher Faulet61cc8522020-04-20 14:54:42 +02001345 out:
1346 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001347}
1348
Christopher Faulet61cc8522020-04-20 14:54:42 +02001349/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
1350 * if an error occurred.
1351 */
1352static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02001353{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001354 struct tcpcheck_rule *chk;
1355 const char *err;
1356 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001357
Christopher Faulet61cc8522020-04-20 14:54:42 +02001358 if (!srv->do_agent)
1359 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001360
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001361 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02001362 * implicit one is inserted before all others.
1363 */
1364 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
1365 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
1366 chk = calloc(1, sizeof(*chk));
1367 if (!chk) {
1368 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
1369 " to agent-check for server '%s' (out of memory).\n",
1370 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1371 ret |= ERR_ALERT | ERR_FATAL;
1372 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001373 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001374 chk->action = TCPCHK_ACT_CONNECT;
1375 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
1376 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02001377 }
1378
Christopher Faulete5870d82020-04-15 11:32:03 +02001379
Christopher Faulet61cc8522020-04-20 14:54:42 +02001380 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
1381 if (err) {
1382 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
1383 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1384 ret |= ERR_ALERT | ERR_ABORT;
1385 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001386 }
1387
Christopher Faulet61cc8522020-04-20 14:54:42 +02001388 if (!srv->agent.inter)
1389 srv->agent.inter = srv->check.inter;
1390
1391 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
1392 global.maxsock++;
1393
1394 out:
1395 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001396}
1397
Christopher Faulet61cc8522020-04-20 14:54:42 +02001398static void deinit_srv_check(struct server *srv)
1399{
1400 if (srv->check.state & CHK_ST_CONFIGURED)
1401 free_check(&srv->check);
1402 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
1403 srv->do_check = 0;
1404}
Christopher Faulete5870d82020-04-15 11:32:03 +02001405
Christopher Faulet61cc8522020-04-20 14:54:42 +02001406
1407static void deinit_srv_agent_check(struct server *srv)
1408{
1409 if (srv->agent.tcpcheck_rules) {
1410 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
1411 free(srv->agent.tcpcheck_rules);
1412 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001413 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001414
Christopher Faulet61cc8522020-04-20 14:54:42 +02001415 if (srv->agent.state & CHK_ST_CONFIGURED)
1416 free_check(&srv->agent);
1417
1418 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
1419 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001420}
1421
Willy Tarreaucee013e2020-06-05 11:40:38 +02001422REGISTER_POST_SERVER_CHECK(init_srv_check);
1423REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
Willy Tarreaucee013e2020-06-05 11:40:38 +02001424REGISTER_POST_CHECK(start_checks);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001425
Willy Tarreaucee013e2020-06-05 11:40:38 +02001426REGISTER_SERVER_DEINIT(deinit_srv_check);
1427REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001428
Christopher Faulet61cc8522020-04-20 14:54:42 +02001429
1430/**************************************************************************/
1431/************************** Check sample fetches **************************/
1432/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001433
Christopher Faulet61cc8522020-04-20 14:54:42 +02001434static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001435 { /* END */ },
1436}};
1437
1438INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
1439
1440
1441/**************************************************************************/
1442/************************ Check's parsing functions ***********************/
1443/**************************************************************************/
Christopher Faulet51b129f2020-04-09 15:54:18 +02001444/* Parses the "http-check" proxy keyword */
1445static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
1446 struct proxy *defpx, const char *file, int line,
1447 char **errmsg)
1448{
Christopher Faulete5870d82020-04-15 11:32:03 +02001449 struct tcpcheck_ruleset *rs = NULL;
1450 struct tcpcheck_rule *chk = NULL;
1451 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001452
1453 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
1454 ret = 1;
1455
1456 cur_arg = 1;
1457 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
1458 /* enable a graceful server shutdown on an HTTP 404 response */
1459 curpx->options |= PR_O_DISABLE404;
1460 if (too_many_args(1, args, errmsg, NULL))
1461 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001462 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001463 }
1464 else if (strcmp(args[cur_arg], "send-state") == 0) {
1465 /* enable emission of the apparent state of a server in HTTP checks */
1466 curpx->options2 |= PR_O2_CHK_SNDST;
1467 if (too_many_args(1, args, errmsg, NULL))
1468 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001469 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001470 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001471
Christopher Faulete5870d82020-04-15 11:32:03 +02001472 /* Deduce the ruleset name from the proxy info */
1473 chunk_printf(&trash, "*http-check-%s_%s-%d",
1474 ((curpx == defpx) ? "defaults" : curpx->id),
1475 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001476
Christopher Faulet61cc8522020-04-20 14:54:42 +02001477 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001478 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001479 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001480 if (rs == NULL) {
1481 memprintf(errmsg, "out of memory.\n");
1482 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001483 }
1484 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001485
Christopher Faulete5870d82020-04-15 11:32:03 +02001486 index = 0;
1487 if (!LIST_ISEMPTY(&rs->rules)) {
1488 chk = LIST_PREV(&rs->rules, typeof(chk), list);
1489 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
1490 index = chk->index + 1;
1491 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001492
Christopher Faulete5870d82020-04-15 11:32:03 +02001493 if (strcmp(args[cur_arg], "connect") == 0)
1494 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1495 else if (strcmp(args[cur_arg], "send") == 0)
1496 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1497 else if (strcmp(args[cur_arg], "expect") == 0)
1498 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
1499 file, line, errmsg);
1500 else if (strcmp(args[cur_arg], "comment") == 0)
1501 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1502 else {
1503 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001504
Christopher Faulete5870d82020-04-15 11:32:03 +02001505 if (!kw) {
1506 action_kw_tcp_check_build_list(&trash);
1507 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
1508 " 'send', 'expect'%s%s. but got '%s'",
1509 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
1510 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001511 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001512 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
1513 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001514
Christopher Faulete5870d82020-04-15 11:32:03 +02001515 if (!chk) {
1516 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1517 goto error;
1518 }
1519 ret = (*errmsg != NULL); /* Handle warning */
1520
1521 chk->index = index;
1522 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
1523 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
1524 /* Use this ruleset if the proxy already has http-check enabled */
1525 curpx->tcpcheck_rules.list = &rs->rules;
1526 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
1527 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
1528 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1529 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001530 goto error;
1531 }
1532 }
1533 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02001534 /* mark this ruleset as unused for now */
1535 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
1536 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001537 }
1538
Christopher Faulete5870d82020-04-15 11:32:03 +02001539 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02001540 return ret;
1541
1542 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02001543 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001544 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001545 return -1;
1546}
1547
Christopher Faulet430e4802020-04-09 15:28:16 +02001548/* Parses the "option tcp-check" proxy keyword */
1549int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1550 const char *file, int line)
1551{
Christopher Faulet404f9192020-04-09 23:13:54 +02001552 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02001553 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1554 int err_code = 0;
1555
1556 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1557 err_code |= ERR_WARN;
1558
1559 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1560 goto out;
1561
Christopher Faulet404f9192020-04-09 23:13:54 +02001562 curpx->options2 &= ~PR_O2_CHK_ANY;
1563 curpx->options2 |= PR_O2_TCPCHK_CHK;
1564
Christopher Fauletd7e63962020-04-17 20:15:59 +02001565 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02001566 /* If a tcp-check rulesset is already set, do nothing */
1567 if (rules->list)
1568 goto out;
1569
1570 /* If a tcp-check ruleset is waiting to be used for the current proxy,
1571 * get it.
1572 */
1573 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
1574 goto curpx_ruleset;
1575
1576 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
1577 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001578 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001579 if (rs)
1580 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02001581 }
1582
Christopher Faulet404f9192020-04-09 23:13:54 +02001583 curpx_ruleset:
1584 /* Deduce the ruleset name from the proxy info */
1585 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
1586 ((curpx == defpx) ? "defaults" : curpx->id),
1587 curpx->conf.file, curpx->conf.line);
1588
Christopher Faulet61cc8522020-04-20 14:54:42 +02001589 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001590 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001591 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001592 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02001593 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1594 goto error;
1595 }
Christopher Faulet430e4802020-04-09 15:28:16 +02001596 }
1597
Christopher Faulet404f9192020-04-09 23:13:54 +02001598 ruleset_found:
1599 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02001600 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001601 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Fauletd7e63962020-04-17 20:15:59 +02001602 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02001603
1604 out:
1605 return err_code;
1606
1607 error:
1608 err_code |= ERR_ALERT | ERR_FATAL;
1609 goto out;
1610}
Christopher Faulet33f05df2020-04-01 11:08:50 +02001611
1612/* Parses the "option redis-check" proxy keyword */
1613int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1614 const char *file, int line)
1615{
1616 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
1617 static char *redis_res = "+PONG\r\n";
1618
1619 struct tcpcheck_ruleset *rs = NULL;
1620 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1621 struct tcpcheck_rule *chk;
1622 char *errmsg = NULL;
1623 int err_code = 0;
1624
1625 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1626 err_code |= ERR_WARN;
1627
1628 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1629 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001630
1631 curpx->options2 &= ~PR_O2_CHK_ANY;
1632 curpx->options2 |= PR_O2_TCPCHK_CHK;
1633
1634 free_tcpcheck_vars(&rules->preset_vars);
1635 rules->list = NULL;
1636 rules->flags = 0;
1637
Christopher Faulet61cc8522020-04-20 14:54:42 +02001638 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001639 if (rs)
1640 goto ruleset_found;
1641
Christopher Faulet61cc8522020-04-20 14:54:42 +02001642 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001643 if (rs == NULL) {
1644 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1645 goto error;
1646 }
1647
1648 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
1649 1, curpx, &rs->rules, file, line, &errmsg);
1650 if (!chk) {
1651 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1652 goto error;
1653 }
1654 chk->index = 0;
1655 LIST_ADDQ(&rs->rules, &chk->list);
1656
1657 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
1658 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001659 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02001660 "on-success", "Redis server is ok",
1661 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001662 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001663 if (!chk) {
1664 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1665 goto error;
1666 }
1667 chk->index = 1;
1668 LIST_ADDQ(&rs->rules, &chk->list);
1669
Christopher Faulet33f05df2020-04-01 11:08:50 +02001670 ruleset_found:
1671 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001672 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001673 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001674
1675 out:
1676 free(errmsg);
1677 return err_code;
1678
1679 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001680 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001681 err_code |= ERR_ALERT | ERR_FATAL;
1682 goto out;
1683}
1684
Christopher Faulet811f78c2020-04-01 11:10:27 +02001685
1686/* Parses the "option ssl-hello-chk" proxy keyword */
1687int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1688 const char *file, int line)
1689{
1690 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
1691 * ssl-hello-chk option to ensure that the remote server speaks SSL.
1692 *
1693 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
1694 */
1695 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001696 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02001697 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
1698 "0079" /* ContentLength : 0x79 bytes after this one */
1699 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
1700 "000075" /* HandshakeLength : 0x75 bytes after this one */
1701 "0300" /* Hello Version : 0x0300 = v3 */
1702 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
1703 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
1704 "00" /* Session ID length : empty (no session ID) */
1705 "004E" /* Cipher Suite Length : 78 bytes after this one */
1706 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
1707 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
1708 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
1709 "000D" "000E" "000F" "0010" /* various bit lengths, */
1710 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
1711 "0015" "0016" "0017" "0018"
1712 "0019" "001A" "001B" "002F"
1713 "0030" "0031" "0032" "0033"
1714 "0034" "0035" "0036" "0037"
1715 "0038" "0039" "003A"
1716 "01" /* Compression Length : 0x01 = 1 byte for types */
1717 "00" /* Compression Type : 0x00 = NULL compression */
1718 };
1719
1720 struct tcpcheck_ruleset *rs = NULL;
1721 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1722 struct tcpcheck_rule *chk;
1723 char *errmsg = NULL;
1724 int err_code = 0;
1725
1726 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1727 err_code |= ERR_WARN;
1728
1729 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1730 goto out;
1731
Christopher Faulet811f78c2020-04-01 11:10:27 +02001732 curpx->options2 &= ~PR_O2_CHK_ANY;
1733 curpx->options2 |= PR_O2_TCPCHK_CHK;
1734
1735 free_tcpcheck_vars(&rules->preset_vars);
1736 rules->list = NULL;
1737 rules->flags = 0;
1738
Christopher Faulet61cc8522020-04-20 14:54:42 +02001739 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001740 if (rs)
1741 goto ruleset_found;
1742
Christopher Faulet61cc8522020-04-20 14:54:42 +02001743 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001744 if (rs == NULL) {
1745 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1746 goto error;
1747 }
1748
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001749 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02001750 1, curpx, &rs->rules, file, line, &errmsg);
1751 if (!chk) {
1752 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1753 goto error;
1754 }
1755 chk->index = 0;
1756 LIST_ADDQ(&rs->rules, &chk->list);
1757
1758 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02001759 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02001760 "error-status", "L6RSP", "tout-status", "L6TOUT",
1761 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001762 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001763 if (!chk) {
1764 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1765 goto error;
1766 }
1767 chk->index = 1;
1768 LIST_ADDQ(&rs->rules, &chk->list);
1769
Christopher Faulet811f78c2020-04-01 11:10:27 +02001770 ruleset_found:
1771 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001772 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001773 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02001774
1775 out:
1776 free(errmsg);
1777 return err_code;
1778
1779 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001780 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001781 err_code |= ERR_ALERT | ERR_FATAL;
1782 goto out;
1783}
1784
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001785/* Parses the "option smtpchk" proxy keyword */
1786int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1787 const char *file, int line)
1788{
1789 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
1790
1791 struct tcpcheck_ruleset *rs = NULL;
1792 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1793 struct tcpcheck_rule *chk;
1794 struct tcpcheck_var *var = NULL;
1795 char *cmd = NULL, *errmsg = NULL;
1796 int err_code = 0;
1797
1798 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1799 err_code |= ERR_WARN;
1800
1801 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1802 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001803
1804 curpx->options2 &= ~PR_O2_CHK_ANY;
1805 curpx->options2 |= PR_O2_TCPCHK_CHK;
1806
1807 free_tcpcheck_vars(&rules->preset_vars);
1808 rules->list = NULL;
1809 rules->flags = 0;
1810
1811 cur_arg += 2;
1812 if (*args[cur_arg] && *args[cur_arg+1] &&
1813 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
Tim Duesterhus2867b402020-06-12 15:58:48 +02001814 /* <EHLO|HELO> + space (1) + <host> + null byte (1) */
1815 cmd = calloc(strlen(args[cur_arg]) + 1 + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001816 if (cmd)
1817 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
1818 }
1819 else {
1820 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
1821 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
1822 cmd = strdup("HELO localhost");
1823 }
1824
Christopher Fauletb61caf42020-04-21 10:57:42 +02001825 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001826 if (cmd == NULL || var == NULL) {
1827 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1828 goto error;
1829 }
1830 var->data.type = SMP_T_STR;
1831 var->data.u.str.area = cmd;
1832 var->data.u.str.data = strlen(cmd);
1833 LIST_INIT(&var->list);
1834 LIST_ADDQ(&rules->preset_vars, &var->list);
1835 cmd = NULL;
1836 var = NULL;
1837
Christopher Faulet61cc8522020-04-20 14:54:42 +02001838 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001839 if (rs)
1840 goto ruleset_found;
1841
Christopher Faulet61cc8522020-04-20 14:54:42 +02001842 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001843 if (rs == NULL) {
1844 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1845 goto error;
1846 }
1847
1848 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
1849 1, curpx, &rs->rules, file, line, &errmsg);
1850 if (!chk) {
1851 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1852 goto error;
1853 }
1854 chk->index = 0;
1855 LIST_ADDQ(&rs->rules, &chk->list);
1856
1857 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
1858 "min-recv", "4",
1859 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02001860 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001861 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001862 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001863 if (!chk) {
1864 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1865 goto error;
1866 }
1867 chk->index = 1;
1868 LIST_ADDQ(&rs->rules, &chk->list);
1869
1870 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
1871 "min-recv", "4",
1872 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001873 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1874 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001875 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001876 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001877 if (!chk) {
1878 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1879 goto error;
1880 }
1881 chk->index = 2;
1882 LIST_ADDQ(&rs->rules, &chk->list);
1883
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001884 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001885 1, curpx, &rs->rules, file, line, &errmsg);
1886 if (!chk) {
1887 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1888 goto error;
1889 }
1890 chk->index = 3;
1891 LIST_ADDQ(&rs->rules, &chk->list);
1892
1893 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
1894 "min-recv", "4",
1895 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001896 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1897 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1898 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001899 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001900 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001901 if (!chk) {
1902 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1903 goto error;
1904 }
1905 chk->index = 4;
1906 LIST_ADDQ(&rs->rules, &chk->list);
1907
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001908 ruleset_found:
1909 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001910 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001911 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001912
1913 out:
1914 free(errmsg);
1915 return err_code;
1916
1917 error:
1918 free(cmd);
1919 free(var);
1920 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001921 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001922 err_code |= ERR_ALERT | ERR_FATAL;
1923 goto out;
1924}
Christopher Faulet811f78c2020-04-01 11:10:27 +02001925
Christopher Fauletce355072020-04-02 11:44:39 +02001926/* Parses the "option pgsql-check" proxy keyword */
1927int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1928 const char *file, int line)
1929{
1930 static char pgsql_req[] = {
1931 "%[var(check.plen),htonl,hex]" /* The packet length*/
1932 "00030000" /* the version 3.0 */
1933 "7573657200" /* "user" key */
1934 "%[var(check.username),hex]00" /* the username */
1935 "00"
1936 };
1937
1938 struct tcpcheck_ruleset *rs = NULL;
1939 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1940 struct tcpcheck_rule *chk;
1941 struct tcpcheck_var *var = NULL;
1942 char *user = NULL, *errmsg = NULL;
1943 size_t packetlen = 0;
1944 int err_code = 0;
1945
1946 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1947 err_code |= ERR_WARN;
1948
1949 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1950 goto out;
1951
Christopher Fauletce355072020-04-02 11:44:39 +02001952 curpx->options2 &= ~PR_O2_CHK_ANY;
1953 curpx->options2 |= PR_O2_TCPCHK_CHK;
1954
1955 free_tcpcheck_vars(&rules->preset_vars);
1956 rules->list = NULL;
1957 rules->flags = 0;
1958
1959 cur_arg += 2;
1960 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1961 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
1962 file, line, args[0], args[1]);
1963 goto error;
1964 }
1965 if (strcmp(args[cur_arg], "user") == 0) {
1966 packetlen = 15 + strlen(args[cur_arg+1]);
1967 user = strdup(args[cur_arg+1]);
1968
Christopher Fauletb61caf42020-04-21 10:57:42 +02001969 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02001970 if (user == NULL || var == NULL) {
1971 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1972 goto error;
1973 }
1974 var->data.type = SMP_T_STR;
1975 var->data.u.str.area = user;
1976 var->data.u.str.data = strlen(user);
1977 LIST_INIT(&var->list);
1978 LIST_ADDQ(&rules->preset_vars, &var->list);
1979 user = NULL;
1980 var = NULL;
1981
Christopher Fauletb61caf42020-04-21 10:57:42 +02001982 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02001983 if (var == NULL) {
1984 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1985 goto error;
1986 }
1987 var->data.type = SMP_T_SINT;
1988 var->data.u.sint = packetlen;
1989 LIST_INIT(&var->list);
1990 LIST_ADDQ(&rules->preset_vars, &var->list);
1991 var = NULL;
1992 }
1993 else {
1994 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
1995 file, line, args[0], args[1]);
1996 goto error;
1997 }
1998
Christopher Faulet61cc8522020-04-20 14:54:42 +02001999 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002000 if (rs)
2001 goto ruleset_found;
2002
Christopher Faulet61cc8522020-04-20 14:54:42 +02002003 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002004 if (rs == NULL) {
2005 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2006 goto error;
2007 }
2008
2009 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2010 1, curpx, &rs->rules, file, line, &errmsg);
2011 if (!chk) {
2012 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2013 goto error;
2014 }
2015 chk->index = 0;
2016 LIST_ADDQ(&rs->rules, &chk->list);
2017
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002018 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02002019 1, curpx, &rs->rules, file, line, &errmsg);
2020 if (!chk) {
2021 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2022 goto error;
2023 }
2024 chk->index = 1;
2025 LIST_ADDQ(&rs->rules, &chk->list);
2026
2027 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
2028 "min-recv", "5",
2029 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02002030 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02002031 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002032 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002033 if (!chk) {
2034 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2035 goto error;
2036 }
2037 chk->index = 2;
2038 LIST_ADDQ(&rs->rules, &chk->list);
2039
Christopher Fauletb841c742020-04-27 18:29:49 +02002040 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 +02002041 "min-recv", "9",
2042 "error-status", "L7STS",
2043 "on-success", "PostgreSQL server is ok",
2044 "on-error", "PostgreSQL unknown error",
2045 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002046 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002047 if (!chk) {
2048 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2049 goto error;
2050 }
2051 chk->index = 3;
2052 LIST_ADDQ(&rs->rules, &chk->list);
2053
Christopher Fauletce355072020-04-02 11:44:39 +02002054 ruleset_found:
2055 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002056 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002057 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02002058
2059 out:
2060 free(errmsg);
2061 return err_code;
2062
2063 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002064 free(user);
2065 free(var);
2066 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002067 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002068 err_code |= ERR_ALERT | ERR_FATAL;
2069 goto out;
2070}
2071
2072
2073/* Parses the "option mysql-check" proxy keyword */
2074int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2075 const char *file, int line)
2076{
2077 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
2078 * const char mysql40_client_auth_pkt[] = {
2079 * "\x0e\x00\x00" // packet length
2080 * "\x01" // packet number
2081 * "\x00\x00" // client capabilities
2082 * "\x00\x00\x01" // max packet
2083 * "haproxy\x00" // username (null terminated string)
2084 * "\x00" // filler (always 0x00)
2085 * "\x01\x00\x00" // packet length
2086 * "\x00" // packet number
2087 * "\x01" // COM_QUIT command
2088 * };
2089 */
2090 static char mysql40_rsname[] = "*mysql40-check";
2091 static char mysql40_req[] = {
2092 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2093 "0080" /* client capabilities */
2094 "000001" /* max packet */
2095 "%[var(check.username),hex]00" /* the username */
2096 "00" /* filler (always 0x00) */
2097 "010000" /* packet length*/
2098 "00" /* sequence ID */
2099 "01" /* COM_QUIT command */
2100 };
2101
2102 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
2103 * const char mysql41_client_auth_pkt[] = {
2104 * "\x0e\x00\x00\" // packet length
2105 * "\x01" // packet number
2106 * "\x00\x00\x00\x00" // client capabilities
2107 * "\x00\x00\x00\x01" // max packet
2108 * "\x21" // character set (UTF-8)
2109 * char[23] // All zeroes
2110 * "haproxy\x00" // username (null terminated string)
2111 * "\x00" // filler (always 0x00)
2112 * "\x01\x00\x00" // packet length
2113 * "\x00" // packet number
2114 * "\x01" // COM_QUIT command
2115 * };
2116 */
2117 static char mysql41_rsname[] = "*mysql41-check";
2118 static char mysql41_req[] = {
2119 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2120 "00820000" /* client capabilities */
2121 "00800001" /* max packet */
2122 "21" /* character set (UTF-8) */
2123 "000000000000000000000000" /* 23 bytes, al zeroes */
2124 "0000000000000000000000"
2125 "%[var(check.username),hex]00" /* the username */
2126 "00" /* filler (always 0x00) */
2127 "010000" /* packet length*/
2128 "00" /* sequence ID */
2129 "01" /* COM_QUIT command */
2130 };
2131
2132 struct tcpcheck_ruleset *rs = NULL;
2133 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2134 struct tcpcheck_rule *chk;
2135 struct tcpcheck_var *var = NULL;
2136 char *mysql_rsname = "*mysql-check";
2137 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
2138 int index = 0, err_code = 0;
2139
2140 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2141 err_code |= ERR_WARN;
2142
2143 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2144 goto out;
2145
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002146 curpx->options2 &= ~PR_O2_CHK_ANY;
2147 curpx->options2 |= PR_O2_TCPCHK_CHK;
2148
2149 free_tcpcheck_vars(&rules->preset_vars);
2150 rules->list = NULL;
2151 rules->flags = 0;
2152
2153 cur_arg += 2;
2154 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002155 int packetlen, userlen;
2156
2157 if (strcmp(args[cur_arg], "user") != 0) {
2158 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
2159 file, line, args[0], args[1], args[cur_arg]);
2160 goto error;
2161 }
2162
2163 if (*(args[cur_arg+1]) == 0) {
2164 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
2165 file, line, args[0], args[1], args[cur_arg]);
2166 goto error;
2167 }
2168
2169 hdr = calloc(4, sizeof(*hdr));
2170 user = strdup(args[cur_arg+1]);
2171 userlen = strlen(args[cur_arg+1]);
2172
2173 if (hdr == NULL || user == NULL) {
2174 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2175 goto error;
2176 }
2177
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002178 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002179 packetlen = userlen + 7 + 27;
2180 mysql_req = mysql41_req;
2181 mysql_rsname = mysql41_rsname;
2182 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002183 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002184 packetlen = userlen + 7;
2185 mysql_req = mysql40_req;
2186 mysql_rsname = mysql40_rsname;
2187 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002188 else {
2189 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
2190 file, line, args[cur_arg], args[cur_arg+2]);
2191 goto error;
2192 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002193
2194 hdr[0] = (unsigned char)(packetlen & 0xff);
2195 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
2196 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
2197 hdr[3] = 1;
2198
Christopher Fauletb61caf42020-04-21 10:57:42 +02002199 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002200 if (var == NULL) {
2201 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2202 goto error;
2203 }
2204 var->data.type = SMP_T_STR;
2205 var->data.u.str.area = hdr;
2206 var->data.u.str.data = 4;
2207 LIST_INIT(&var->list);
2208 LIST_ADDQ(&rules->preset_vars, &var->list);
2209 hdr = NULL;
2210 var = NULL;
2211
Christopher Fauletb61caf42020-04-21 10:57:42 +02002212 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002213 if (var == NULL) {
2214 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2215 goto error;
2216 }
2217 var->data.type = SMP_T_STR;
2218 var->data.u.str.area = user;
2219 var->data.u.str.data = strlen(user);
2220 LIST_INIT(&var->list);
2221 LIST_ADDQ(&rules->preset_vars, &var->list);
2222 user = NULL;
2223 var = NULL;
2224 }
2225
Christopher Faulet61cc8522020-04-20 14:54:42 +02002226 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002227 if (rs)
2228 goto ruleset_found;
2229
Christopher Faulet61cc8522020-04-20 14:54:42 +02002230 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002231 if (rs == NULL) {
2232 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2233 goto error;
2234 }
2235
2236 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2237 1, curpx, &rs->rules, file, line, &errmsg);
2238 if (!chk) {
2239 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2240 goto error;
2241 }
2242 chk->index = index++;
2243 LIST_ADDQ(&rs->rules, &chk->list);
2244
2245 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002246 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002247 1, curpx, &rs->rules, file, line, &errmsg);
2248 if (!chk) {
2249 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2250 goto error;
2251 }
2252 chk->index = index++;
2253 LIST_ADDQ(&rs->rules, &chk->list);
2254 }
2255
2256 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002257 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002258 if (!chk) {
2259 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2260 goto error;
2261 }
2262 chk->expect.custom = tcpcheck_mysql_expect_iniths;
2263 chk->index = index++;
2264 LIST_ADDQ(&rs->rules, &chk->list);
2265
2266 if (mysql_req) {
2267 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002268 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002269 if (!chk) {
2270 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2271 goto error;
2272 }
2273 chk->expect.custom = tcpcheck_mysql_expect_ok;
2274 chk->index = index++;
2275 LIST_ADDQ(&rs->rules, &chk->list);
2276 }
2277
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002278 ruleset_found:
2279 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002280 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002281 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002282
2283 out:
2284 free(errmsg);
2285 return err_code;
2286
2287 error:
2288 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02002289 free(user);
2290 free(var);
2291 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002292 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02002293 err_code |= ERR_ALERT | ERR_FATAL;
2294 goto out;
2295}
2296
Christopher Faulet1997eca2020-04-03 23:13:50 +02002297int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2298 const char *file, int line)
2299{
2300 static char *ldap_req = "300C020101600702010304008000";
2301
2302 struct tcpcheck_ruleset *rs = NULL;
2303 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2304 struct tcpcheck_rule *chk;
2305 char *errmsg = NULL;
2306 int err_code = 0;
2307
2308 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2309 err_code |= ERR_WARN;
2310
2311 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2312 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002313
2314 curpx->options2 &= ~PR_O2_CHK_ANY;
2315 curpx->options2 |= PR_O2_TCPCHK_CHK;
2316
2317 free_tcpcheck_vars(&rules->preset_vars);
2318 rules->list = NULL;
2319 rules->flags = 0;
2320
Christopher Faulet61cc8522020-04-20 14:54:42 +02002321 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002322 if (rs)
2323 goto ruleset_found;
2324
Christopher Faulet61cc8522020-04-20 14:54:42 +02002325 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002326 if (rs == NULL) {
2327 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2328 goto error;
2329 }
2330
2331 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
2332 1, curpx, &rs->rules, file, line, &errmsg);
2333 if (!chk) {
2334 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2335 goto error;
2336 }
2337 chk->index = 0;
2338 LIST_ADDQ(&rs->rules, &chk->list);
2339
2340 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
2341 "min-recv", "14",
2342 "on-error", "Not LDAPv3 protocol",
2343 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002344 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002345 if (!chk) {
2346 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2347 goto error;
2348 }
2349 chk->index = 1;
2350 LIST_ADDQ(&rs->rules, &chk->list);
2351
2352 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002353 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002354 if (!chk) {
2355 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2356 goto error;
2357 }
2358 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
2359 chk->index = 2;
2360 LIST_ADDQ(&rs->rules, &chk->list);
2361
Christopher Faulet1997eca2020-04-03 23:13:50 +02002362 ruleset_found:
2363 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002364 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002365 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002366
2367 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02002368 free(errmsg);
2369 return err_code;
2370
2371 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002372 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002373 err_code |= ERR_ALERT | ERR_FATAL;
2374 goto out;
2375}
2376
2377int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2378 const char *file, int line)
2379{
2380 struct tcpcheck_ruleset *rs = NULL;
2381 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2382 struct tcpcheck_rule *chk;
2383 char *spop_req = NULL;
2384 char *errmsg = NULL;
2385 int spop_len = 0, err_code = 0;
2386
2387 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2388 err_code |= ERR_WARN;
2389
2390 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2391 goto out;
2392
Christopher Faulet267b01b2020-04-04 10:27:09 +02002393 curpx->options2 &= ~PR_O2_CHK_ANY;
2394 curpx->options2 |= PR_O2_TCPCHK_CHK;
2395
2396 free_tcpcheck_vars(&rules->preset_vars);
2397 rules->list = NULL;
2398 rules->flags = 0;
2399
2400
Christopher Faulet61cc8522020-04-20 14:54:42 +02002401 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002402 if (rs)
2403 goto ruleset_found;
2404
Christopher Faulet61cc8522020-04-20 14:54:42 +02002405 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002406 if (rs == NULL) {
2407 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2408 goto error;
2409 }
2410
2411 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
2412 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2413 goto error;
2414 }
2415 chunk_reset(&trash);
2416 dump_binary(&trash, spop_req, spop_len);
2417 trash.area[trash.data] = '\0';
2418
2419 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
2420 1, curpx, &rs->rules, file, line, &errmsg);
2421 if (!chk) {
2422 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2423 goto error;
2424 }
2425 chk->index = 0;
2426 LIST_ADDQ(&rs->rules, &chk->list);
2427
2428 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002429 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002430 if (!chk) {
2431 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2432 goto error;
2433 }
2434 chk->expect.custom = tcpcheck_spop_expect_agenthello;
2435 chk->index = 1;
2436 LIST_ADDQ(&rs->rules, &chk->list);
2437
Christopher Faulet267b01b2020-04-04 10:27:09 +02002438 ruleset_found:
2439 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002440 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002441 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002442
2443 out:
2444 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002445 free(errmsg);
2446 return err_code;
2447
2448 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002449 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002450 err_code |= ERR_ALERT | ERR_FATAL;
2451 goto out;
2452}
Christopher Fauletce355072020-04-02 11:44:39 +02002453
Christopher Faulete5870d82020-04-15 11:32:03 +02002454
2455struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
2456{
2457 struct tcpcheck_rule *chk = NULL;
2458 struct tcpcheck_http_hdr *hdr = NULL;
2459 char *meth = NULL, *uri = NULL, *vsn = NULL;
2460 char *hdrs, *body;
2461
2462 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
2463 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
2464 if (hdrs == body)
2465 hdrs = NULL;
2466 if (hdrs) {
2467 *hdrs = '\0';
2468 hdrs +=2;
2469 }
2470 if (body) {
2471 *body = '\0';
2472 body += 4;
2473 }
2474 if (hdrs || body) {
2475 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
2476 " Please, consider to use 'http-check send' directive instead.");
2477 }
2478
2479 chk = calloc(1, sizeof(*chk));
2480 if (!chk) {
2481 memprintf(errmsg, "out of memory");
2482 goto error;
2483 }
2484 chk->action = TCPCHK_ACT_SEND;
2485 chk->send.type = TCPCHK_SEND_HTTP;
2486 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
2487 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
2488 LIST_INIT(&chk->send.http.hdrs);
2489
2490 /* Copy the method, uri and version */
2491 if (*args[cur_arg]) {
2492 if (!*args[cur_arg+1])
2493 uri = args[cur_arg];
2494 else
2495 meth = args[cur_arg];
2496 }
2497 if (*args[cur_arg+1])
2498 uri = args[cur_arg+1];
2499 if (*args[cur_arg+2])
2500 vsn = args[cur_arg+2];
2501
2502 if (meth) {
2503 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
2504 chk->send.http.meth.str.area = strdup(meth);
2505 chk->send.http.meth.str.data = strlen(meth);
2506 if (!chk->send.http.meth.str.area) {
2507 memprintf(errmsg, "out of memory");
2508 goto error;
2509 }
2510 }
2511 if (uri) {
2512 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002513 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002514 memprintf(errmsg, "out of memory");
2515 goto error;
2516 }
2517 }
2518 if (vsn) {
2519 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002520 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002521 memprintf(errmsg, "out of memory");
2522 goto error;
2523 }
2524 }
2525
2526 /* Copy the header */
2527 if (hdrs) {
2528 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
2529 struct h1m h1m;
2530 int i, ret;
2531
2532 /* Build and parse the request */
2533 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
2534
2535 h1m.flags = H1_MF_HDRS_ONLY;
2536 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
2537 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
2538 &h1m, NULL);
2539 if (ret <= 0) {
2540 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
2541 goto error;
2542 }
2543
Christopher Fauletb61caf42020-04-21 10:57:42 +02002544 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002545 hdr = calloc(1, sizeof(*hdr));
2546 if (!hdr) {
2547 memprintf(errmsg, "out of memory");
2548 goto error;
2549 }
2550 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002551 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02002552 if (!hdr->name.ptr) {
2553 memprintf(errmsg, "out of memory");
2554 goto error;
2555 }
2556
Christopher Fauletb61caf42020-04-21 10:57:42 +02002557 ist0(tmp_hdrs[i].v);
2558 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 +02002559 goto error;
2560 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
2561 }
2562 }
2563
2564 /* Copy the body */
2565 if (body) {
2566 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002567 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002568 memprintf(errmsg, "out of memory");
2569 goto error;
2570 }
2571 }
2572
2573 return chk;
2574
2575 error:
2576 free_tcpcheck_http_hdr(hdr);
2577 free_tcpcheck(chk, 0);
2578 return NULL;
2579}
2580
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002581int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2582 const char *file, int line)
2583{
Christopher Faulete5870d82020-04-15 11:32:03 +02002584 struct tcpcheck_ruleset *rs = NULL;
2585 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2586 struct tcpcheck_rule *chk;
2587 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002588 int err_code = 0;
2589
2590 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2591 err_code |= ERR_WARN;
2592
2593 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2594 goto out;
2595
Christopher Faulete5870d82020-04-15 11:32:03 +02002596 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
2597 if (!chk) {
2598 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2599 goto error;
2600 }
2601 if (errmsg) {
2602 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
2603 err_code |= ERR_WARN;
2604 free(errmsg);
2605 errmsg = NULL;
2606 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002607
Christopher Faulete5870d82020-04-15 11:32:03 +02002608 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002609 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02002610 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002611
Christopher Faulete5870d82020-04-15 11:32:03 +02002612 free_tcpcheck_vars(&rules->preset_vars);
2613 rules->list = NULL;
2614 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002615
Christopher Faulete5870d82020-04-15 11:32:03 +02002616 /* Deduce the ruleset name from the proxy info */
2617 chunk_printf(&trash, "*http-check-%s_%s-%d",
2618 ((curpx == defpx) ? "defaults" : curpx->id),
2619 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002620
Christopher Faulet61cc8522020-04-20 14:54:42 +02002621 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002622 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002623 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002624 if (rs == NULL) {
2625 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2626 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002627 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002628 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002629
Christopher Faulete5870d82020-04-15 11:32:03 +02002630 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002631 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulete5870d82020-04-15 11:32:03 +02002632 rules->flags |= TCPCHK_RULES_HTTP_CHK;
2633 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
2634 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2635 rules->list = NULL;
2636 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002637 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002638
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002639 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02002640 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002641 return err_code;
2642
2643 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002644 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02002645 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002646 err_code |= ERR_ALERT | ERR_FATAL;
2647 goto out;
2648}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002649
Christopher Fauletce8111e2020-04-06 15:04:11 +02002650/* Parse the "addr" server keyword */
2651static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2652 char **errmsg)
2653{
2654 struct sockaddr_storage *sk;
Christopher Fauletce8111e2020-04-06 15:04:11 +02002655 int port1, port2, err_code = 0;
2656
2657
2658 if (!*args[*cur_arg+1]) {
2659 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
2660 goto error;
2661 }
2662
Willy Tarreau65ec4e32020-09-16 19:17:08 +02002663 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, NULL, errmsg, NULL, NULL,
2664 PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
Christopher Fauletce8111e2020-04-06 15:04:11 +02002665 if (!sk) {
2666 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
2667 goto error;
2668 }
2669
Christopher Fauletce8111e2020-04-06 15:04:11 +02002670 srv->check.addr = srv->agent.addr = *sk;
2671 srv->flags |= SRV_F_CHECKADDR;
2672 srv->flags |= SRV_F_AGENTADDR;
2673
2674 out:
2675 return err_code;
2676
2677 error:
2678 err_code |= ERR_ALERT | ERR_FATAL;
2679 goto out;
2680}
2681
2682
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002683/* Parse the "agent-addr" server keyword */
2684static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2685 char **errmsg)
2686{
2687 int err_code = 0;
2688
2689 if (!*(args[*cur_arg+1])) {
2690 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
2691 goto error;
2692 }
2693 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
2694 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
2695 goto error;
2696 }
2697
2698 out:
2699 return err_code;
2700
2701 error:
2702 err_code |= ERR_ALERT | ERR_FATAL;
2703 goto out;
2704}
2705
2706/* Parse the "agent-check" server keyword */
2707static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2708 char **errmsg)
2709{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002710 struct tcpcheck_ruleset *rs = NULL;
2711 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2712 struct tcpcheck_rule *chk;
2713 int err_code = 0;
2714
2715 if (srv->do_agent)
2716 goto out;
2717
2718 if (!rules) {
2719 rules = calloc(1, sizeof(*rules));
2720 if (!rules) {
2721 memprintf(errmsg, "out of memory.");
2722 goto error;
2723 }
2724 LIST_INIT(&rules->preset_vars);
2725 srv->agent.tcpcheck_rules = rules;
2726 }
2727 rules->list = NULL;
2728 rules->flags = 0;
2729
Christopher Faulet61cc8522020-04-20 14:54:42 +02002730 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002731 if (rs)
2732 goto ruleset_found;
2733
Christopher Faulet61cc8522020-04-20 14:54:42 +02002734 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002735 if (rs == NULL) {
2736 memprintf(errmsg, "out of memory.");
2737 goto error;
2738 }
2739
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002740 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002741 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
2742 if (!chk) {
2743 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2744 goto error;
2745 }
2746 chk->index = 0;
2747 LIST_ADDQ(&rs->rules, &chk->list);
2748
2749 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002750 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
2751 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002752 if (!chk) {
2753 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2754 goto error;
2755 }
2756 chk->expect.custom = tcpcheck_agent_expect_reply;
2757 chk->index = 1;
2758 LIST_ADDQ(&rs->rules, &chk->list);
2759
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002760 ruleset_found:
2761 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002762 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002763 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002764 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002765
2766 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002767 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002768
2769 error:
2770 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002771 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002772 err_code |= ERR_ALERT | ERR_FATAL;
2773 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002774}
2775
2776/* Parse the "agent-inter" server keyword */
2777static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2778 char **errmsg)
2779{
2780 const char *err = NULL;
2781 unsigned int delay;
2782 int err_code = 0;
2783
2784 if (!*(args[*cur_arg+1])) {
2785 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
2786 goto error;
2787 }
2788
2789 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
2790 if (err == PARSE_TIME_OVER) {
2791 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
2792 args[*cur_arg+1], args[*cur_arg], srv->id);
2793 goto error;
2794 }
2795 else if (err == PARSE_TIME_UNDER) {
2796 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
2797 args[*cur_arg+1], args[*cur_arg], srv->id);
2798 goto error;
2799 }
2800 else if (err) {
2801 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
2802 *err, srv->id);
2803 goto error;
2804 }
2805 if (delay <= 0) {
2806 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
2807 delay, args[*cur_arg], srv->id);
2808 goto error;
2809 }
2810 srv->agent.inter = delay;
2811
2812 out:
2813 return err_code;
2814
2815 error:
2816 err_code |= ERR_ALERT | ERR_FATAL;
2817 goto out;
2818}
2819
2820/* Parse the "agent-port" server keyword */
2821static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2822 char **errmsg)
2823{
2824 int err_code = 0;
2825
2826 if (!*(args[*cur_arg+1])) {
2827 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
2828 goto error;
2829 }
2830
2831 global.maxsock++;
2832 srv->agent.port = atol(args[*cur_arg+1]);
2833
2834 out:
2835 return err_code;
2836
2837 error:
2838 err_code |= ERR_ALERT | ERR_FATAL;
2839 goto out;
2840}
2841
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002842int set_srv_agent_send(struct server *srv, const char *send)
2843{
2844 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2845 struct tcpcheck_var *var = NULL;
2846 char *str;
2847
2848 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002849 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002850 if (str == NULL || var == NULL)
2851 goto error;
2852
2853 free_tcpcheck_vars(&rules->preset_vars);
2854
2855 var->data.type = SMP_T_STR;
2856 var->data.u.str.area = str;
2857 var->data.u.str.data = strlen(str);
2858 LIST_INIT(&var->list);
2859 LIST_ADDQ(&rules->preset_vars, &var->list);
2860
2861 return 1;
2862
2863 error:
2864 free(str);
2865 free(var);
2866 return 0;
2867}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002868
2869/* Parse the "agent-send" server keyword */
2870static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2871 char **errmsg)
2872{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002873 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002874 int err_code = 0;
2875
2876 if (!*(args[*cur_arg+1])) {
2877 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
2878 goto error;
2879 }
2880
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002881 if (!rules) {
2882 rules = calloc(1, sizeof(*rules));
2883 if (!rules) {
2884 memprintf(errmsg, "out of memory.");
2885 goto error;
2886 }
2887 LIST_INIT(&rules->preset_vars);
2888 srv->agent.tcpcheck_rules = rules;
2889 }
2890
2891 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002892 memprintf(errmsg, "out of memory.");
2893 goto error;
2894 }
2895
2896 out:
2897 return err_code;
2898
2899 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002900 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002901 err_code |= ERR_ALERT | ERR_FATAL;
2902 goto out;
2903}
2904
2905/* Parse the "no-agent-send" server keyword */
2906static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2907 char **errmsg)
2908{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002909 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002910 return 0;
2911}
2912
Christopher Fauletce8111e2020-04-06 15:04:11 +02002913/* Parse the "check" server keyword */
2914static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2915 char **errmsg)
2916{
2917 srv->do_check = 1;
2918 return 0;
2919}
2920
2921/* Parse the "check-send-proxy" server keyword */
2922static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2923 char **errmsg)
2924{
2925 srv->check.send_proxy = 1;
2926 return 0;
2927}
2928
2929/* Parse the "check-via-socks4" server keyword */
2930static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2931 char **errmsg)
2932{
2933 srv->check.via_socks4 = 1;
2934 return 0;
2935}
2936
2937/* Parse the "no-check" server keyword */
2938static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2939 char **errmsg)
2940{
2941 deinit_srv_check(srv);
2942 return 0;
2943}
2944
2945/* Parse the "no-check-send-proxy" server keyword */
2946static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2947 char **errmsg)
2948{
2949 srv->check.send_proxy = 0;
2950 return 0;
2951}
2952
Christopher Fauletedc6ed92020-04-23 16:27:59 +02002953/* parse the "check-proto" server keyword */
2954static int srv_parse_check_proto(char **args, int *cur_arg,
2955 struct proxy *px, struct server *newsrv, char **err)
2956{
2957 int err_code = 0;
2958
2959 if (!*args[*cur_arg + 1]) {
2960 memprintf(err, "'%s' : missing value", args[*cur_arg]);
2961 goto error;
2962 }
2963 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
2964 if (!newsrv->check.mux_proto) {
2965 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
2966 goto error;
2967 }
2968
2969 out:
2970 return err_code;
2971
2972 error:
2973 err_code |= ERR_ALERT | ERR_FATAL;
2974 goto out;
2975}
2976
2977
Christopher Fauletce8111e2020-04-06 15:04:11 +02002978/* Parse the "rise" server keyword */
2979static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2980 char **errmsg)
2981{
2982 int err_code = 0;
2983
2984 if (!*args[*cur_arg + 1]) {
2985 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
2986 goto error;
2987 }
2988
2989 srv->check.rise = atol(args[*cur_arg+1]);
2990 if (srv->check.rise <= 0) {
2991 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
2992 goto error;
2993 }
2994
2995 if (srv->check.health)
2996 srv->check.health = srv->check.rise;
2997
2998 out:
2999 return err_code;
3000
3001 error:
3002 deinit_srv_agent_check(srv);
3003 err_code |= ERR_ALERT | ERR_FATAL;
3004 goto out;
3005 return 0;
3006}
3007
3008/* Parse the "fall" server keyword */
3009static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3010 char **errmsg)
3011{
3012 int err_code = 0;
3013
3014 if (!*args[*cur_arg + 1]) {
3015 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3016 goto error;
3017 }
3018
3019 srv->check.fall = atol(args[*cur_arg+1]);
3020 if (srv->check.fall <= 0) {
3021 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3022 goto error;
3023 }
3024
3025 out:
3026 return err_code;
3027
3028 error:
3029 deinit_srv_agent_check(srv);
3030 err_code |= ERR_ALERT | ERR_FATAL;
3031 goto out;
3032 return 0;
3033}
3034
3035/* Parse the "inter" server keyword */
3036static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3037 char **errmsg)
3038{
3039 const char *err = NULL;
3040 unsigned int delay;
3041 int err_code = 0;
3042
3043 if (!*(args[*cur_arg+1])) {
3044 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3045 goto error;
3046 }
3047
3048 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3049 if (err == PARSE_TIME_OVER) {
3050 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3051 args[*cur_arg+1], args[*cur_arg], srv->id);
3052 goto error;
3053 }
3054 else if (err == PARSE_TIME_UNDER) {
3055 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3056 args[*cur_arg+1], args[*cur_arg], srv->id);
3057 goto error;
3058 }
3059 else if (err) {
3060 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3061 *err, srv->id);
3062 goto error;
3063 }
3064 if (delay <= 0) {
3065 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3066 delay, args[*cur_arg], srv->id);
3067 goto error;
3068 }
3069 srv->check.inter = delay;
3070
3071 out:
3072 return err_code;
3073
3074 error:
3075 err_code |= ERR_ALERT | ERR_FATAL;
3076 goto out;
3077}
3078
3079
3080/* Parse the "fastinter" server keyword */
3081static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3082 char **errmsg)
3083{
3084 const char *err = NULL;
3085 unsigned int delay;
3086 int err_code = 0;
3087
3088 if (!*(args[*cur_arg+1])) {
3089 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3090 goto error;
3091 }
3092
3093 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3094 if (err == PARSE_TIME_OVER) {
3095 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3096 args[*cur_arg+1], args[*cur_arg], srv->id);
3097 goto error;
3098 }
3099 else if (err == PARSE_TIME_UNDER) {
3100 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3101 args[*cur_arg+1], args[*cur_arg], srv->id);
3102 goto error;
3103 }
3104 else if (err) {
3105 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3106 *err, srv->id);
3107 goto error;
3108 }
3109 if (delay <= 0) {
3110 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3111 delay, args[*cur_arg], srv->id);
3112 goto error;
3113 }
3114 srv->check.fastinter = delay;
3115
3116 out:
3117 return err_code;
3118
3119 error:
3120 err_code |= ERR_ALERT | ERR_FATAL;
3121 goto out;
3122}
3123
3124
3125/* Parse the "downinter" server keyword */
3126static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3127 char **errmsg)
3128{
3129 const char *err = NULL;
3130 unsigned int delay;
3131 int err_code = 0;
3132
3133 if (!*(args[*cur_arg+1])) {
3134 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3135 goto error;
3136 }
3137
3138 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3139 if (err == PARSE_TIME_OVER) {
3140 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3141 args[*cur_arg+1], args[*cur_arg], srv->id);
3142 goto error;
3143 }
3144 else if (err == PARSE_TIME_UNDER) {
3145 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3146 args[*cur_arg+1], args[*cur_arg], srv->id);
3147 goto error;
3148 }
3149 else if (err) {
3150 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3151 *err, srv->id);
3152 goto error;
3153 }
3154 if (delay <= 0) {
3155 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3156 delay, args[*cur_arg], srv->id);
3157 goto error;
3158 }
3159 srv->check.downinter = delay;
3160
3161 out:
3162 return err_code;
3163
3164 error:
3165 err_code |= ERR_ALERT | ERR_FATAL;
3166 goto out;
3167}
3168
3169/* Parse the "port" server keyword */
3170static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3171 char **errmsg)
3172{
3173 int err_code = 0;
3174
3175 if (!*(args[*cur_arg+1])) {
3176 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
3177 goto error;
3178 }
3179
3180 global.maxsock++;
3181 srv->check.port = atol(args[*cur_arg+1]);
3182 srv->flags |= SRV_F_CHECKPORT;
3183
3184 out:
3185 return err_code;
3186
3187 error:
3188 err_code |= ERR_ALERT | ERR_FATAL;
3189 goto out;
3190}
3191
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003192static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02003193 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003194 { 0, NULL, NULL },
3195}};
3196
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003197static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02003198 { "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 +02003199 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
3200 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
3201 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
3202 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
3203 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003204 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003205 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003206 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
3207 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003208 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003209 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
3210 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
3211 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
3212 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
3213 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
3214 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
3215 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
3216 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003217 { NULL, NULL, 0 },
3218}};
3219
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003220INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003221INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003222
Willy Tarreaubd741542010-03-16 18:46:54 +01003223/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02003224 * Local variables:
3225 * c-indent-level: 8
3226 * c-basic-offset: 8
3227 * End:
3228 */