blob: 82f18e3a00ce5c351b567f0d4a5c45f238ccf71f [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
Willy Tarreau00149122017-10-04 18:05:01 +0200658 if (!conn) {
659 /* connection allocation error before the connection was established */
660 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) ||
862 proxy->state == PR_STSTOPPED)
863 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
Christopher Faulet61cc8522020-04-20 14:54:42 +0200971 check->bi.area = calloc(check->bi.size, sizeof(char));
972 check->bo.area = calloc(check->bo.size, sizeof(char));
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;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001231
Christopher Faulet61cc8522020-04-20 14:54:42 +02001232 if (!srv->do_check)
1233 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001234
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001235
Christopher Faulet61cc8522020-04-20 14:54:42 +02001236 /* If neither a port nor an addr was specified and no check transport
1237 * layer is forced, then the transport layer used by the checks is the
1238 * same as for the production traffic. Otherwise we use raw_sock by
1239 * default, unless one is specified.
1240 */
1241 if (!srv->check.port && !is_addr(&srv->check.addr)) {
1242 if (!srv->check.use_ssl && srv->use_ssl != -1) {
1243 srv->check.use_ssl = srv->use_ssl;
1244 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001245 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001246 else if (srv->check.use_ssl == 1)
1247 srv->check.xprt = xprt_get(XPRT_SSL);
1248 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001249 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02001250 else if (srv->check.use_ssl == 1)
1251 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001252
Christopher Faulet12882cf2020-04-23 15:50:18 +02001253 /* Inherit the mux protocol from the server if not already defined for
1254 * the check
1255 */
1256 if (srv->mux_proto && !srv->check.mux_proto)
1257 srv->check.mux_proto = srv->mux_proto;
1258
Christopher Faulet61cc8522020-04-20 14:54:42 +02001259 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001260
Christopher Faulet61cc8522020-04-20 14:54:42 +02001261 /* We need at least a service port, a check port or the first tcp-check
1262 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
1263 */
1264 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
1265 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
1266 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001267
Christopher Faulet61cc8522020-04-20 14:54:42 +02001268 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
1269 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
1270 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1271 ret |= ERR_ALERT | ERR_ABORT;
1272 goto out;
1273 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001274
Christopher Faulet61cc8522020-04-20 14:54:42 +02001275 /* search the first action (connect / send / expect) in the list */
1276 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
1277 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
1278 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
1279 "nor tcp_check rule 'connect' with port information.\n",
1280 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1281 ret |= ERR_ALERT | ERR_ABORT;
1282 goto out;
1283 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001284
Christopher Faulet61cc8522020-04-20 14:54:42 +02001285 /* scan the tcp-check ruleset to ensure a port has been configured */
1286 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
1287 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
1288 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
1289 "and a tcp_check rule 'connect' with no port information.\n",
1290 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1291 ret |= ERR_ALERT | ERR_ABORT;
1292 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001293 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001294 }
1295
Christopher Faulet61cc8522020-04-20 14:54:42 +02001296 init:
1297 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
1298 struct tcpcheck_ruleset *rs = NULL;
1299 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
1300 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001301
Christopher Faulet61cc8522020-04-20 14:54:42 +02001302 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
1303 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02001304
Christopher Faulet61cc8522020-04-20 14:54:42 +02001305 rs = find_tcpcheck_ruleset("*tcp-check");
1306 if (!rs) {
1307 rs = create_tcpcheck_ruleset("*tcp-check");
1308 if (rs == NULL) {
1309 ha_alert("config: %s '%s': out of memory.\n",
1310 proxy_type_str(srv->proxy), srv->proxy->id);
1311 ret |= ERR_ALERT | ERR_FATAL;
1312 goto out;
1313 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001314 }
1315
Christopher Faulet61cc8522020-04-20 14:54:42 +02001316 free_tcpcheck_vars(&rules->preset_vars);
1317 rules->list = &rs->rules;
1318 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001319 }
1320
Christopher Faulet61cc8522020-04-20 14:54:42 +02001321 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
1322 if (err) {
1323 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
1324 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1325 ret |= ERR_ALERT | ERR_ABORT;
1326 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001327 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001328 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
1329 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02001330
Christopher Faulet61cc8522020-04-20 14:54:42 +02001331 out:
1332 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001333}
1334
Christopher Faulet61cc8522020-04-20 14:54:42 +02001335/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
1336 * if an error occurred.
1337 */
1338static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02001339{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001340 struct tcpcheck_rule *chk;
1341 const char *err;
1342 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001343
Christopher Faulet61cc8522020-04-20 14:54:42 +02001344 if (!srv->do_agent)
1345 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001346
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001347 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02001348 * implicit one is inserted before all others.
1349 */
1350 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
1351 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
1352 chk = calloc(1, sizeof(*chk));
1353 if (!chk) {
1354 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
1355 " to agent-check for server '%s' (out of memory).\n",
1356 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1357 ret |= ERR_ALERT | ERR_FATAL;
1358 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001359 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001360 chk->action = TCPCHK_ACT_CONNECT;
1361 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
1362 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02001363 }
1364
Christopher Faulete5870d82020-04-15 11:32:03 +02001365
Christopher Faulet61cc8522020-04-20 14:54:42 +02001366 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
1367 if (err) {
1368 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
1369 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1370 ret |= ERR_ALERT | ERR_ABORT;
1371 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001372 }
1373
Christopher Faulet61cc8522020-04-20 14:54:42 +02001374 if (!srv->agent.inter)
1375 srv->agent.inter = srv->check.inter;
1376
1377 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
1378 global.maxsock++;
1379
1380 out:
1381 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001382}
1383
Christopher Faulet61cc8522020-04-20 14:54:42 +02001384static void deinit_srv_check(struct server *srv)
1385{
1386 if (srv->check.state & CHK_ST_CONFIGURED)
1387 free_check(&srv->check);
1388 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
1389 srv->do_check = 0;
1390}
Christopher Faulete5870d82020-04-15 11:32:03 +02001391
Christopher Faulet61cc8522020-04-20 14:54:42 +02001392
1393static void deinit_srv_agent_check(struct server *srv)
1394{
1395 if (srv->agent.tcpcheck_rules) {
1396 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
1397 free(srv->agent.tcpcheck_rules);
1398 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001399 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001400
Christopher Faulet61cc8522020-04-20 14:54:42 +02001401 if (srv->agent.state & CHK_ST_CONFIGURED)
1402 free_check(&srv->agent);
1403
1404 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
1405 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001406}
1407
Willy Tarreaucee013e2020-06-05 11:40:38 +02001408REGISTER_POST_SERVER_CHECK(init_srv_check);
1409REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
Willy Tarreaucee013e2020-06-05 11:40:38 +02001410REGISTER_POST_CHECK(start_checks);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001411
Willy Tarreaucee013e2020-06-05 11:40:38 +02001412REGISTER_SERVER_DEINIT(deinit_srv_check);
1413REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001414
Christopher Faulet61cc8522020-04-20 14:54:42 +02001415
1416/**************************************************************************/
1417/************************** Check sample fetches **************************/
1418/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001419
Christopher Faulet61cc8522020-04-20 14:54:42 +02001420static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001421 { /* END */ },
1422}};
1423
1424INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
1425
1426
1427/**************************************************************************/
1428/************************ Check's parsing functions ***********************/
1429/**************************************************************************/
Christopher Faulet51b129f2020-04-09 15:54:18 +02001430/* Parses the "http-check" proxy keyword */
1431static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
1432 struct proxy *defpx, const char *file, int line,
1433 char **errmsg)
1434{
Christopher Faulete5870d82020-04-15 11:32:03 +02001435 struct tcpcheck_ruleset *rs = NULL;
1436 struct tcpcheck_rule *chk = NULL;
1437 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001438
1439 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
1440 ret = 1;
1441
1442 cur_arg = 1;
1443 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
1444 /* enable a graceful server shutdown on an HTTP 404 response */
1445 curpx->options |= PR_O_DISABLE404;
1446 if (too_many_args(1, args, errmsg, NULL))
1447 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001448 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001449 }
1450 else if (strcmp(args[cur_arg], "send-state") == 0) {
1451 /* enable emission of the apparent state of a server in HTTP checks */
1452 curpx->options2 |= PR_O2_CHK_SNDST;
1453 if (too_many_args(1, args, errmsg, NULL))
1454 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001455 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001456 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001457
Christopher Faulete5870d82020-04-15 11:32:03 +02001458 /* Deduce the ruleset name from the proxy info */
1459 chunk_printf(&trash, "*http-check-%s_%s-%d",
1460 ((curpx == defpx) ? "defaults" : curpx->id),
1461 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001462
Christopher Faulet61cc8522020-04-20 14:54:42 +02001463 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001464 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001465 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001466 if (rs == NULL) {
1467 memprintf(errmsg, "out of memory.\n");
1468 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001469 }
1470 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001471
Christopher Faulete5870d82020-04-15 11:32:03 +02001472 index = 0;
1473 if (!LIST_ISEMPTY(&rs->rules)) {
1474 chk = LIST_PREV(&rs->rules, typeof(chk), list);
1475 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
1476 index = chk->index + 1;
1477 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001478
Christopher Faulete5870d82020-04-15 11:32:03 +02001479 if (strcmp(args[cur_arg], "connect") == 0)
1480 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1481 else if (strcmp(args[cur_arg], "send") == 0)
1482 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1483 else if (strcmp(args[cur_arg], "expect") == 0)
1484 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
1485 file, line, errmsg);
1486 else if (strcmp(args[cur_arg], "comment") == 0)
1487 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1488 else {
1489 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001490
Christopher Faulete5870d82020-04-15 11:32:03 +02001491 if (!kw) {
1492 action_kw_tcp_check_build_list(&trash);
1493 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
1494 " 'send', 'expect'%s%s. but got '%s'",
1495 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
1496 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001497 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001498 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
1499 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001500
Christopher Faulete5870d82020-04-15 11:32:03 +02001501 if (!chk) {
1502 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1503 goto error;
1504 }
1505 ret = (*errmsg != NULL); /* Handle warning */
1506
1507 chk->index = index;
1508 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
1509 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
1510 /* Use this ruleset if the proxy already has http-check enabled */
1511 curpx->tcpcheck_rules.list = &rs->rules;
1512 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
1513 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
1514 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1515 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001516 goto error;
1517 }
1518 }
1519 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02001520 /* mark this ruleset as unused for now */
1521 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
1522 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001523 }
1524
Christopher Faulete5870d82020-04-15 11:32:03 +02001525 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02001526 return ret;
1527
1528 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02001529 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001530 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001531 return -1;
1532}
1533
Christopher Faulet430e4802020-04-09 15:28:16 +02001534/* Parses the "option tcp-check" proxy keyword */
1535int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1536 const char *file, int line)
1537{
Christopher Faulet404f9192020-04-09 23:13:54 +02001538 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02001539 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1540 int err_code = 0;
1541
1542 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1543 err_code |= ERR_WARN;
1544
1545 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1546 goto out;
1547
Christopher Faulet404f9192020-04-09 23:13:54 +02001548 curpx->options2 &= ~PR_O2_CHK_ANY;
1549 curpx->options2 |= PR_O2_TCPCHK_CHK;
1550
Christopher Fauletd7e63962020-04-17 20:15:59 +02001551 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02001552 /* If a tcp-check rulesset is already set, do nothing */
1553 if (rules->list)
1554 goto out;
1555
1556 /* If a tcp-check ruleset is waiting to be used for the current proxy,
1557 * get it.
1558 */
1559 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
1560 goto curpx_ruleset;
1561
1562 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
1563 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001564 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001565 if (rs)
1566 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02001567 }
1568
Christopher Faulet404f9192020-04-09 23:13:54 +02001569 curpx_ruleset:
1570 /* Deduce the ruleset name from the proxy info */
1571 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
1572 ((curpx == defpx) ? "defaults" : curpx->id),
1573 curpx->conf.file, curpx->conf.line);
1574
Christopher Faulet61cc8522020-04-20 14:54:42 +02001575 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001576 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001577 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001578 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02001579 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1580 goto error;
1581 }
Christopher Faulet430e4802020-04-09 15:28:16 +02001582 }
1583
Christopher Faulet404f9192020-04-09 23:13:54 +02001584 ruleset_found:
1585 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02001586 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02001587 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02001588
1589 out:
1590 return err_code;
1591
1592 error:
1593 err_code |= ERR_ALERT | ERR_FATAL;
1594 goto out;
1595}
Christopher Faulet33f05df2020-04-01 11:08:50 +02001596
1597/* Parses the "option redis-check" proxy keyword */
1598int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1599 const char *file, int line)
1600{
1601 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
1602 static char *redis_res = "+PONG\r\n";
1603
1604 struct tcpcheck_ruleset *rs = NULL;
1605 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1606 struct tcpcheck_rule *chk;
1607 char *errmsg = NULL;
1608 int err_code = 0;
1609
1610 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1611 err_code |= ERR_WARN;
1612
1613 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1614 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001615
1616 curpx->options2 &= ~PR_O2_CHK_ANY;
1617 curpx->options2 |= PR_O2_TCPCHK_CHK;
1618
1619 free_tcpcheck_vars(&rules->preset_vars);
1620 rules->list = NULL;
1621 rules->flags = 0;
1622
Christopher Faulet61cc8522020-04-20 14:54:42 +02001623 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001624 if (rs)
1625 goto ruleset_found;
1626
Christopher Faulet61cc8522020-04-20 14:54:42 +02001627 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001628 if (rs == NULL) {
1629 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1630 goto error;
1631 }
1632
1633 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
1634 1, curpx, &rs->rules, file, line, &errmsg);
1635 if (!chk) {
1636 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1637 goto error;
1638 }
1639 chk->index = 0;
1640 LIST_ADDQ(&rs->rules, &chk->list);
1641
1642 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
1643 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001644 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02001645 "on-success", "Redis server is ok",
1646 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001647 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001648 if (!chk) {
1649 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1650 goto error;
1651 }
1652 chk->index = 1;
1653 LIST_ADDQ(&rs->rules, &chk->list);
1654
Christopher Faulet33f05df2020-04-01 11:08:50 +02001655 ruleset_found:
1656 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02001657 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001658
1659 out:
1660 free(errmsg);
1661 return err_code;
1662
1663 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001664 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001665 err_code |= ERR_ALERT | ERR_FATAL;
1666 goto out;
1667}
1668
Christopher Faulet811f78c2020-04-01 11:10:27 +02001669
1670/* Parses the "option ssl-hello-chk" proxy keyword */
1671int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1672 const char *file, int line)
1673{
1674 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
1675 * ssl-hello-chk option to ensure that the remote server speaks SSL.
1676 *
1677 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
1678 */
1679 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001680 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02001681 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
1682 "0079" /* ContentLength : 0x79 bytes after this one */
1683 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
1684 "000075" /* HandshakeLength : 0x75 bytes after this one */
1685 "0300" /* Hello Version : 0x0300 = v3 */
1686 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
1687 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
1688 "00" /* Session ID length : empty (no session ID) */
1689 "004E" /* Cipher Suite Length : 78 bytes after this one */
1690 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
1691 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
1692 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
1693 "000D" "000E" "000F" "0010" /* various bit lengths, */
1694 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
1695 "0015" "0016" "0017" "0018"
1696 "0019" "001A" "001B" "002F"
1697 "0030" "0031" "0032" "0033"
1698 "0034" "0035" "0036" "0037"
1699 "0038" "0039" "003A"
1700 "01" /* Compression Length : 0x01 = 1 byte for types */
1701 "00" /* Compression Type : 0x00 = NULL compression */
1702 };
1703
1704 struct tcpcheck_ruleset *rs = NULL;
1705 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1706 struct tcpcheck_rule *chk;
1707 char *errmsg = NULL;
1708 int err_code = 0;
1709
1710 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1711 err_code |= ERR_WARN;
1712
1713 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1714 goto out;
1715
Christopher Faulet811f78c2020-04-01 11:10:27 +02001716 curpx->options2 &= ~PR_O2_CHK_ANY;
1717 curpx->options2 |= PR_O2_TCPCHK_CHK;
1718
1719 free_tcpcheck_vars(&rules->preset_vars);
1720 rules->list = NULL;
1721 rules->flags = 0;
1722
Christopher Faulet61cc8522020-04-20 14:54:42 +02001723 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001724 if (rs)
1725 goto ruleset_found;
1726
Christopher Faulet61cc8522020-04-20 14:54:42 +02001727 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001728 if (rs == NULL) {
1729 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1730 goto error;
1731 }
1732
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001733 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02001734 1, curpx, &rs->rules, file, line, &errmsg);
1735 if (!chk) {
1736 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1737 goto error;
1738 }
1739 chk->index = 0;
1740 LIST_ADDQ(&rs->rules, &chk->list);
1741
1742 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02001743 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02001744 "error-status", "L6RSP", "tout-status", "L6TOUT",
1745 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001746 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001747 if (!chk) {
1748 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1749 goto error;
1750 }
1751 chk->index = 1;
1752 LIST_ADDQ(&rs->rules, &chk->list);
1753
Christopher Faulet811f78c2020-04-01 11:10:27 +02001754 ruleset_found:
1755 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02001756 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02001757
1758 out:
1759 free(errmsg);
1760 return err_code;
1761
1762 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001763 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001764 err_code |= ERR_ALERT | ERR_FATAL;
1765 goto out;
1766}
1767
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001768/* Parses the "option smtpchk" proxy keyword */
1769int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1770 const char *file, int line)
1771{
1772 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
1773
1774 struct tcpcheck_ruleset *rs = NULL;
1775 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1776 struct tcpcheck_rule *chk;
1777 struct tcpcheck_var *var = NULL;
1778 char *cmd = NULL, *errmsg = NULL;
1779 int err_code = 0;
1780
1781 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1782 err_code |= ERR_WARN;
1783
1784 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1785 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001786
1787 curpx->options2 &= ~PR_O2_CHK_ANY;
1788 curpx->options2 |= PR_O2_TCPCHK_CHK;
1789
1790 free_tcpcheck_vars(&rules->preset_vars);
1791 rules->list = NULL;
1792 rules->flags = 0;
1793
1794 cur_arg += 2;
1795 if (*args[cur_arg] && *args[cur_arg+1] &&
1796 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
1797 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
1798 if (cmd)
1799 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
1800 }
1801 else {
1802 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
1803 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
1804 cmd = strdup("HELO localhost");
1805 }
1806
Christopher Fauletb61caf42020-04-21 10:57:42 +02001807 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001808 if (cmd == NULL || var == NULL) {
1809 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1810 goto error;
1811 }
1812 var->data.type = SMP_T_STR;
1813 var->data.u.str.area = cmd;
1814 var->data.u.str.data = strlen(cmd);
1815 LIST_INIT(&var->list);
1816 LIST_ADDQ(&rules->preset_vars, &var->list);
1817 cmd = NULL;
1818 var = NULL;
1819
Christopher Faulet61cc8522020-04-20 14:54:42 +02001820 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001821 if (rs)
1822 goto ruleset_found;
1823
Christopher Faulet61cc8522020-04-20 14:54:42 +02001824 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001825 if (rs == NULL) {
1826 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1827 goto error;
1828 }
1829
1830 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
1831 1, curpx, &rs->rules, file, line, &errmsg);
1832 if (!chk) {
1833 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1834 goto error;
1835 }
1836 chk->index = 0;
1837 LIST_ADDQ(&rs->rules, &chk->list);
1838
1839 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
1840 "min-recv", "4",
1841 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02001842 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001843 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001844 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001845 if (!chk) {
1846 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1847 goto error;
1848 }
1849 chk->index = 1;
1850 LIST_ADDQ(&rs->rules, &chk->list);
1851
1852 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
1853 "min-recv", "4",
1854 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001855 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1856 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001857 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001858 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001859 if (!chk) {
1860 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1861 goto error;
1862 }
1863 chk->index = 2;
1864 LIST_ADDQ(&rs->rules, &chk->list);
1865
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001866 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001867 1, curpx, &rs->rules, file, line, &errmsg);
1868 if (!chk) {
1869 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1870 goto error;
1871 }
1872 chk->index = 3;
1873 LIST_ADDQ(&rs->rules, &chk->list);
1874
1875 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
1876 "min-recv", "4",
1877 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001878 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1879 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1880 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001881 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001882 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001883 if (!chk) {
1884 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1885 goto error;
1886 }
1887 chk->index = 4;
1888 LIST_ADDQ(&rs->rules, &chk->list);
1889
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001890 ruleset_found:
1891 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02001892 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001893
1894 out:
1895 free(errmsg);
1896 return err_code;
1897
1898 error:
1899 free(cmd);
1900 free(var);
1901 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001902 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001903 err_code |= ERR_ALERT | ERR_FATAL;
1904 goto out;
1905}
Christopher Faulet811f78c2020-04-01 11:10:27 +02001906
Christopher Fauletce355072020-04-02 11:44:39 +02001907/* Parses the "option pgsql-check" proxy keyword */
1908int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1909 const char *file, int line)
1910{
1911 static char pgsql_req[] = {
1912 "%[var(check.plen),htonl,hex]" /* The packet length*/
1913 "00030000" /* the version 3.0 */
1914 "7573657200" /* "user" key */
1915 "%[var(check.username),hex]00" /* the username */
1916 "00"
1917 };
1918
1919 struct tcpcheck_ruleset *rs = NULL;
1920 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1921 struct tcpcheck_rule *chk;
1922 struct tcpcheck_var *var = NULL;
1923 char *user = NULL, *errmsg = NULL;
1924 size_t packetlen = 0;
1925 int err_code = 0;
1926
1927 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1928 err_code |= ERR_WARN;
1929
1930 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1931 goto out;
1932
Christopher Fauletce355072020-04-02 11:44:39 +02001933 curpx->options2 &= ~PR_O2_CHK_ANY;
1934 curpx->options2 |= PR_O2_TCPCHK_CHK;
1935
1936 free_tcpcheck_vars(&rules->preset_vars);
1937 rules->list = NULL;
1938 rules->flags = 0;
1939
1940 cur_arg += 2;
1941 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1942 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
1943 file, line, args[0], args[1]);
1944 goto error;
1945 }
1946 if (strcmp(args[cur_arg], "user") == 0) {
1947 packetlen = 15 + strlen(args[cur_arg+1]);
1948 user = strdup(args[cur_arg+1]);
1949
Christopher Fauletb61caf42020-04-21 10:57:42 +02001950 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02001951 if (user == NULL || var == NULL) {
1952 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1953 goto error;
1954 }
1955 var->data.type = SMP_T_STR;
1956 var->data.u.str.area = user;
1957 var->data.u.str.data = strlen(user);
1958 LIST_INIT(&var->list);
1959 LIST_ADDQ(&rules->preset_vars, &var->list);
1960 user = NULL;
1961 var = NULL;
1962
Christopher Fauletb61caf42020-04-21 10:57:42 +02001963 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02001964 if (var == NULL) {
1965 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1966 goto error;
1967 }
1968 var->data.type = SMP_T_SINT;
1969 var->data.u.sint = packetlen;
1970 LIST_INIT(&var->list);
1971 LIST_ADDQ(&rules->preset_vars, &var->list);
1972 var = NULL;
1973 }
1974 else {
1975 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
1976 file, line, args[0], args[1]);
1977 goto error;
1978 }
1979
Christopher Faulet61cc8522020-04-20 14:54:42 +02001980 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02001981 if (rs)
1982 goto ruleset_found;
1983
Christopher Faulet61cc8522020-04-20 14:54:42 +02001984 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02001985 if (rs == NULL) {
1986 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1987 goto error;
1988 }
1989
1990 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
1991 1, curpx, &rs->rules, file, line, &errmsg);
1992 if (!chk) {
1993 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1994 goto error;
1995 }
1996 chk->index = 0;
1997 LIST_ADDQ(&rs->rules, &chk->list);
1998
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001999 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02002000 1, curpx, &rs->rules, file, line, &errmsg);
2001 if (!chk) {
2002 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2003 goto error;
2004 }
2005 chk->index = 1;
2006 LIST_ADDQ(&rs->rules, &chk->list);
2007
2008 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
2009 "min-recv", "5",
2010 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02002011 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02002012 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002013 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002014 if (!chk) {
2015 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2016 goto error;
2017 }
2018 chk->index = 2;
2019 LIST_ADDQ(&rs->rules, &chk->list);
2020
Christopher Fauletb841c742020-04-27 18:29:49 +02002021 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 +02002022 "min-recv", "9",
2023 "error-status", "L7STS",
2024 "on-success", "PostgreSQL server is ok",
2025 "on-error", "PostgreSQL unknown error",
2026 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002027 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002028 if (!chk) {
2029 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2030 goto error;
2031 }
2032 chk->index = 3;
2033 LIST_ADDQ(&rs->rules, &chk->list);
2034
Christopher Fauletce355072020-04-02 11:44:39 +02002035 ruleset_found:
2036 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002037 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02002038
2039 out:
2040 free(errmsg);
2041 return err_code;
2042
2043 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002044 free(user);
2045 free(var);
2046 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002047 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002048 err_code |= ERR_ALERT | ERR_FATAL;
2049 goto out;
2050}
2051
2052
2053/* Parses the "option mysql-check" proxy keyword */
2054int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2055 const char *file, int line)
2056{
2057 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
2058 * const char mysql40_client_auth_pkt[] = {
2059 * "\x0e\x00\x00" // packet length
2060 * "\x01" // packet number
2061 * "\x00\x00" // client capabilities
2062 * "\x00\x00\x01" // max packet
2063 * "haproxy\x00" // username (null terminated string)
2064 * "\x00" // filler (always 0x00)
2065 * "\x01\x00\x00" // packet length
2066 * "\x00" // packet number
2067 * "\x01" // COM_QUIT command
2068 * };
2069 */
2070 static char mysql40_rsname[] = "*mysql40-check";
2071 static char mysql40_req[] = {
2072 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2073 "0080" /* client capabilities */
2074 "000001" /* max packet */
2075 "%[var(check.username),hex]00" /* the username */
2076 "00" /* filler (always 0x00) */
2077 "010000" /* packet length*/
2078 "00" /* sequence ID */
2079 "01" /* COM_QUIT command */
2080 };
2081
2082 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
2083 * const char mysql41_client_auth_pkt[] = {
2084 * "\x0e\x00\x00\" // packet length
2085 * "\x01" // packet number
2086 * "\x00\x00\x00\x00" // client capabilities
2087 * "\x00\x00\x00\x01" // max packet
2088 * "\x21" // character set (UTF-8)
2089 * char[23] // All zeroes
2090 * "haproxy\x00" // username (null terminated string)
2091 * "\x00" // filler (always 0x00)
2092 * "\x01\x00\x00" // packet length
2093 * "\x00" // packet number
2094 * "\x01" // COM_QUIT command
2095 * };
2096 */
2097 static char mysql41_rsname[] = "*mysql41-check";
2098 static char mysql41_req[] = {
2099 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2100 "00820000" /* client capabilities */
2101 "00800001" /* max packet */
2102 "21" /* character set (UTF-8) */
2103 "000000000000000000000000" /* 23 bytes, al zeroes */
2104 "0000000000000000000000"
2105 "%[var(check.username),hex]00" /* the username */
2106 "00" /* filler (always 0x00) */
2107 "010000" /* packet length*/
2108 "00" /* sequence ID */
2109 "01" /* COM_QUIT command */
2110 };
2111
2112 struct tcpcheck_ruleset *rs = NULL;
2113 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2114 struct tcpcheck_rule *chk;
2115 struct tcpcheck_var *var = NULL;
2116 char *mysql_rsname = "*mysql-check";
2117 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
2118 int index = 0, err_code = 0;
2119
2120 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2121 err_code |= ERR_WARN;
2122
2123 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2124 goto out;
2125
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002126 curpx->options2 &= ~PR_O2_CHK_ANY;
2127 curpx->options2 |= PR_O2_TCPCHK_CHK;
2128
2129 free_tcpcheck_vars(&rules->preset_vars);
2130 rules->list = NULL;
2131 rules->flags = 0;
2132
2133 cur_arg += 2;
2134 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002135 int packetlen, userlen;
2136
2137 if (strcmp(args[cur_arg], "user") != 0) {
2138 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
2139 file, line, args[0], args[1], args[cur_arg]);
2140 goto error;
2141 }
2142
2143 if (*(args[cur_arg+1]) == 0) {
2144 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
2145 file, line, args[0], args[1], args[cur_arg]);
2146 goto error;
2147 }
2148
2149 hdr = calloc(4, sizeof(*hdr));
2150 user = strdup(args[cur_arg+1]);
2151 userlen = strlen(args[cur_arg+1]);
2152
2153 if (hdr == NULL || user == NULL) {
2154 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2155 goto error;
2156 }
2157
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002158 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002159 packetlen = userlen + 7 + 27;
2160 mysql_req = mysql41_req;
2161 mysql_rsname = mysql41_rsname;
2162 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002163 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002164 packetlen = userlen + 7;
2165 mysql_req = mysql40_req;
2166 mysql_rsname = mysql40_rsname;
2167 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002168 else {
2169 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
2170 file, line, args[cur_arg], args[cur_arg+2]);
2171 goto error;
2172 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002173
2174 hdr[0] = (unsigned char)(packetlen & 0xff);
2175 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
2176 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
2177 hdr[3] = 1;
2178
Christopher Fauletb61caf42020-04-21 10:57:42 +02002179 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002180 if (var == NULL) {
2181 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2182 goto error;
2183 }
2184 var->data.type = SMP_T_STR;
2185 var->data.u.str.area = hdr;
2186 var->data.u.str.data = 4;
2187 LIST_INIT(&var->list);
2188 LIST_ADDQ(&rules->preset_vars, &var->list);
2189 hdr = NULL;
2190 var = NULL;
2191
Christopher Fauletb61caf42020-04-21 10:57:42 +02002192 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002193 if (var == NULL) {
2194 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2195 goto error;
2196 }
2197 var->data.type = SMP_T_STR;
2198 var->data.u.str.area = user;
2199 var->data.u.str.data = strlen(user);
2200 LIST_INIT(&var->list);
2201 LIST_ADDQ(&rules->preset_vars, &var->list);
2202 user = NULL;
2203 var = NULL;
2204 }
2205
Christopher Faulet61cc8522020-04-20 14:54:42 +02002206 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002207 if (rs)
2208 goto ruleset_found;
2209
Christopher Faulet61cc8522020-04-20 14:54:42 +02002210 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002211 if (rs == NULL) {
2212 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2213 goto error;
2214 }
2215
2216 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2217 1, curpx, &rs->rules, file, line, &errmsg);
2218 if (!chk) {
2219 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2220 goto error;
2221 }
2222 chk->index = index++;
2223 LIST_ADDQ(&rs->rules, &chk->list);
2224
2225 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002226 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002227 1, curpx, &rs->rules, file, line, &errmsg);
2228 if (!chk) {
2229 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2230 goto error;
2231 }
2232 chk->index = index++;
2233 LIST_ADDQ(&rs->rules, &chk->list);
2234 }
2235
2236 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002237 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002238 if (!chk) {
2239 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2240 goto error;
2241 }
2242 chk->expect.custom = tcpcheck_mysql_expect_iniths;
2243 chk->index = index++;
2244 LIST_ADDQ(&rs->rules, &chk->list);
2245
2246 if (mysql_req) {
2247 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002248 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002249 if (!chk) {
2250 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2251 goto error;
2252 }
2253 chk->expect.custom = tcpcheck_mysql_expect_ok;
2254 chk->index = index++;
2255 LIST_ADDQ(&rs->rules, &chk->list);
2256 }
2257
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002258 ruleset_found:
2259 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002260 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002261
2262 out:
2263 free(errmsg);
2264 return err_code;
2265
2266 error:
2267 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02002268 free(user);
2269 free(var);
2270 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002271 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02002272 err_code |= ERR_ALERT | ERR_FATAL;
2273 goto out;
2274}
2275
Christopher Faulet1997eca2020-04-03 23:13:50 +02002276int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2277 const char *file, int line)
2278{
2279 static char *ldap_req = "300C020101600702010304008000";
2280
2281 struct tcpcheck_ruleset *rs = NULL;
2282 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2283 struct tcpcheck_rule *chk;
2284 char *errmsg = NULL;
2285 int err_code = 0;
2286
2287 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2288 err_code |= ERR_WARN;
2289
2290 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2291 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002292
2293 curpx->options2 &= ~PR_O2_CHK_ANY;
2294 curpx->options2 |= PR_O2_TCPCHK_CHK;
2295
2296 free_tcpcheck_vars(&rules->preset_vars);
2297 rules->list = NULL;
2298 rules->flags = 0;
2299
Christopher Faulet61cc8522020-04-20 14:54:42 +02002300 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002301 if (rs)
2302 goto ruleset_found;
2303
Christopher Faulet61cc8522020-04-20 14:54:42 +02002304 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002305 if (rs == NULL) {
2306 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2307 goto error;
2308 }
2309
2310 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
2311 1, curpx, &rs->rules, file, line, &errmsg);
2312 if (!chk) {
2313 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2314 goto error;
2315 }
2316 chk->index = 0;
2317 LIST_ADDQ(&rs->rules, &chk->list);
2318
2319 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
2320 "min-recv", "14",
2321 "on-error", "Not LDAPv3 protocol",
2322 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002323 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002324 if (!chk) {
2325 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2326 goto error;
2327 }
2328 chk->index = 1;
2329 LIST_ADDQ(&rs->rules, &chk->list);
2330
2331 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002332 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002333 if (!chk) {
2334 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2335 goto error;
2336 }
2337 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
2338 chk->index = 2;
2339 LIST_ADDQ(&rs->rules, &chk->list);
2340
Christopher Faulet1997eca2020-04-03 23:13:50 +02002341 ruleset_found:
2342 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002343 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002344
2345 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02002346 free(errmsg);
2347 return err_code;
2348
2349 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002350 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002351 err_code |= ERR_ALERT | ERR_FATAL;
2352 goto out;
2353}
2354
2355int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2356 const char *file, int line)
2357{
2358 struct tcpcheck_ruleset *rs = NULL;
2359 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2360 struct tcpcheck_rule *chk;
2361 char *spop_req = NULL;
2362 char *errmsg = NULL;
2363 int spop_len = 0, err_code = 0;
2364
2365 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2366 err_code |= ERR_WARN;
2367
2368 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2369 goto out;
2370
Christopher Faulet267b01b2020-04-04 10:27:09 +02002371 curpx->options2 &= ~PR_O2_CHK_ANY;
2372 curpx->options2 |= PR_O2_TCPCHK_CHK;
2373
2374 free_tcpcheck_vars(&rules->preset_vars);
2375 rules->list = NULL;
2376 rules->flags = 0;
2377
2378
Christopher Faulet61cc8522020-04-20 14:54:42 +02002379 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002380 if (rs)
2381 goto ruleset_found;
2382
Christopher Faulet61cc8522020-04-20 14:54:42 +02002383 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002384 if (rs == NULL) {
2385 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2386 goto error;
2387 }
2388
2389 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
2390 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2391 goto error;
2392 }
2393 chunk_reset(&trash);
2394 dump_binary(&trash, spop_req, spop_len);
2395 trash.area[trash.data] = '\0';
2396
2397 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
2398 1, curpx, &rs->rules, file, line, &errmsg);
2399 if (!chk) {
2400 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2401 goto error;
2402 }
2403 chk->index = 0;
2404 LIST_ADDQ(&rs->rules, &chk->list);
2405
2406 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002407 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002408 if (!chk) {
2409 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2410 goto error;
2411 }
2412 chk->expect.custom = tcpcheck_spop_expect_agenthello;
2413 chk->index = 1;
2414 LIST_ADDQ(&rs->rules, &chk->list);
2415
Christopher Faulet267b01b2020-04-04 10:27:09 +02002416 ruleset_found:
2417 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002418 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002419
2420 out:
2421 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002422 free(errmsg);
2423 return err_code;
2424
2425 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002426 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002427 err_code |= ERR_ALERT | ERR_FATAL;
2428 goto out;
2429}
Christopher Fauletce355072020-04-02 11:44:39 +02002430
Christopher Faulete5870d82020-04-15 11:32:03 +02002431
2432struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
2433{
2434 struct tcpcheck_rule *chk = NULL;
2435 struct tcpcheck_http_hdr *hdr = NULL;
2436 char *meth = NULL, *uri = NULL, *vsn = NULL;
2437 char *hdrs, *body;
2438
2439 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
2440 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
2441 if (hdrs == body)
2442 hdrs = NULL;
2443 if (hdrs) {
2444 *hdrs = '\0';
2445 hdrs +=2;
2446 }
2447 if (body) {
2448 *body = '\0';
2449 body += 4;
2450 }
2451 if (hdrs || body) {
2452 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
2453 " Please, consider to use 'http-check send' directive instead.");
2454 }
2455
2456 chk = calloc(1, sizeof(*chk));
2457 if (!chk) {
2458 memprintf(errmsg, "out of memory");
2459 goto error;
2460 }
2461 chk->action = TCPCHK_ACT_SEND;
2462 chk->send.type = TCPCHK_SEND_HTTP;
2463 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
2464 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
2465 LIST_INIT(&chk->send.http.hdrs);
2466
2467 /* Copy the method, uri and version */
2468 if (*args[cur_arg]) {
2469 if (!*args[cur_arg+1])
2470 uri = args[cur_arg];
2471 else
2472 meth = args[cur_arg];
2473 }
2474 if (*args[cur_arg+1])
2475 uri = args[cur_arg+1];
2476 if (*args[cur_arg+2])
2477 vsn = args[cur_arg+2];
2478
2479 if (meth) {
2480 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
2481 chk->send.http.meth.str.area = strdup(meth);
2482 chk->send.http.meth.str.data = strlen(meth);
2483 if (!chk->send.http.meth.str.area) {
2484 memprintf(errmsg, "out of memory");
2485 goto error;
2486 }
2487 }
2488 if (uri) {
2489 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002490 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002491 memprintf(errmsg, "out of memory");
2492 goto error;
2493 }
2494 }
2495 if (vsn) {
2496 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002497 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002498 memprintf(errmsg, "out of memory");
2499 goto error;
2500 }
2501 }
2502
2503 /* Copy the header */
2504 if (hdrs) {
2505 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
2506 struct h1m h1m;
2507 int i, ret;
2508
2509 /* Build and parse the request */
2510 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
2511
2512 h1m.flags = H1_MF_HDRS_ONLY;
2513 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
2514 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
2515 &h1m, NULL);
2516 if (ret <= 0) {
2517 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
2518 goto error;
2519 }
2520
Christopher Fauletb61caf42020-04-21 10:57:42 +02002521 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002522 hdr = calloc(1, sizeof(*hdr));
2523 if (!hdr) {
2524 memprintf(errmsg, "out of memory");
2525 goto error;
2526 }
2527 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002528 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02002529 if (!hdr->name.ptr) {
2530 memprintf(errmsg, "out of memory");
2531 goto error;
2532 }
2533
Christopher Fauletb61caf42020-04-21 10:57:42 +02002534 ist0(tmp_hdrs[i].v);
2535 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 +02002536 goto error;
2537 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
2538 }
2539 }
2540
2541 /* Copy the body */
2542 if (body) {
2543 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002544 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002545 memprintf(errmsg, "out of memory");
2546 goto error;
2547 }
2548 }
2549
2550 return chk;
2551
2552 error:
2553 free_tcpcheck_http_hdr(hdr);
2554 free_tcpcheck(chk, 0);
2555 return NULL;
2556}
2557
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002558int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2559 const char *file, int line)
2560{
Christopher Faulete5870d82020-04-15 11:32:03 +02002561 struct tcpcheck_ruleset *rs = NULL;
2562 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2563 struct tcpcheck_rule *chk;
2564 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002565 int err_code = 0;
2566
2567 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2568 err_code |= ERR_WARN;
2569
2570 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2571 goto out;
2572
Christopher Faulete5870d82020-04-15 11:32:03 +02002573 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
2574 if (!chk) {
2575 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2576 goto error;
2577 }
2578 if (errmsg) {
2579 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
2580 err_code |= ERR_WARN;
2581 free(errmsg);
2582 errmsg = NULL;
2583 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002584
Christopher Faulete5870d82020-04-15 11:32:03 +02002585 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002586 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02002587 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002588
Christopher Faulete5870d82020-04-15 11:32:03 +02002589 free_tcpcheck_vars(&rules->preset_vars);
2590 rules->list = NULL;
2591 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002592
Christopher Faulete5870d82020-04-15 11:32:03 +02002593 /* Deduce the ruleset name from the proxy info */
2594 chunk_printf(&trash, "*http-check-%s_%s-%d",
2595 ((curpx == defpx) ? "defaults" : curpx->id),
2596 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002597
Christopher Faulet61cc8522020-04-20 14:54:42 +02002598 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002599 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002600 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002601 if (rs == NULL) {
2602 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2603 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002604 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002605 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002606
Christopher Faulete5870d82020-04-15 11:32:03 +02002607 rules->list = &rs->rules;
2608 rules->flags |= TCPCHK_RULES_HTTP_CHK;
2609 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
2610 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2611 rules->list = NULL;
2612 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002613 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002614
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002615 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02002616 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002617 return err_code;
2618
2619 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002620 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02002621 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002622 err_code |= ERR_ALERT | ERR_FATAL;
2623 goto out;
2624}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002625
Christopher Fauletce8111e2020-04-06 15:04:11 +02002626/* Parse the "addr" server keyword */
2627static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2628 char **errmsg)
2629{
2630 struct sockaddr_storage *sk;
2631 struct protocol *proto;
2632 int port1, port2, err_code = 0;
2633
2634
2635 if (!*args[*cur_arg+1]) {
2636 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
2637 goto error;
2638 }
2639
2640 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
2641 if (!sk) {
2642 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
2643 goto error;
2644 }
2645
2646 proto = protocol_by_family(sk->ss_family);
2647 if (!proto || !proto->connect) {
2648 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
2649 args[*cur_arg], args[*cur_arg+1]);
2650 goto error;
2651 }
2652
2653 if (port1 != port2) {
2654 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
2655 args[*cur_arg], args[*cur_arg+1]);
2656 goto error;
2657 }
2658
2659 srv->check.addr = srv->agent.addr = *sk;
2660 srv->flags |= SRV_F_CHECKADDR;
2661 srv->flags |= SRV_F_AGENTADDR;
2662
2663 out:
2664 return err_code;
2665
2666 error:
2667 err_code |= ERR_ALERT | ERR_FATAL;
2668 goto out;
2669}
2670
2671
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002672/* Parse the "agent-addr" server keyword */
2673static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2674 char **errmsg)
2675{
2676 int err_code = 0;
2677
2678 if (!*(args[*cur_arg+1])) {
2679 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
2680 goto error;
2681 }
2682 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
2683 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
2684 goto error;
2685 }
2686
2687 out:
2688 return err_code;
2689
2690 error:
2691 err_code |= ERR_ALERT | ERR_FATAL;
2692 goto out;
2693}
2694
2695/* Parse the "agent-check" server keyword */
2696static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2697 char **errmsg)
2698{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002699 struct tcpcheck_ruleset *rs = NULL;
2700 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2701 struct tcpcheck_rule *chk;
2702 int err_code = 0;
2703
2704 if (srv->do_agent)
2705 goto out;
2706
2707 if (!rules) {
2708 rules = calloc(1, sizeof(*rules));
2709 if (!rules) {
2710 memprintf(errmsg, "out of memory.");
2711 goto error;
2712 }
2713 LIST_INIT(&rules->preset_vars);
2714 srv->agent.tcpcheck_rules = rules;
2715 }
2716 rules->list = NULL;
2717 rules->flags = 0;
2718
Christopher Faulet61cc8522020-04-20 14:54:42 +02002719 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002720 if (rs)
2721 goto ruleset_found;
2722
Christopher Faulet61cc8522020-04-20 14:54:42 +02002723 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002724 if (rs == NULL) {
2725 memprintf(errmsg, "out of memory.");
2726 goto error;
2727 }
2728
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002729 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002730 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
2731 if (!chk) {
2732 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2733 goto error;
2734 }
2735 chk->index = 0;
2736 LIST_ADDQ(&rs->rules, &chk->list);
2737
2738 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002739 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
2740 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002741 if (!chk) {
2742 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2743 goto error;
2744 }
2745 chk->expect.custom = tcpcheck_agent_expect_reply;
2746 chk->index = 1;
2747 LIST_ADDQ(&rs->rules, &chk->list);
2748
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002749 ruleset_found:
2750 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002751 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002752 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002753
2754 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002755 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002756
2757 error:
2758 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002759 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002760 err_code |= ERR_ALERT | ERR_FATAL;
2761 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002762}
2763
2764/* Parse the "agent-inter" server keyword */
2765static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2766 char **errmsg)
2767{
2768 const char *err = NULL;
2769 unsigned int delay;
2770 int err_code = 0;
2771
2772 if (!*(args[*cur_arg+1])) {
2773 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
2774 goto error;
2775 }
2776
2777 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
2778 if (err == PARSE_TIME_OVER) {
2779 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
2780 args[*cur_arg+1], args[*cur_arg], srv->id);
2781 goto error;
2782 }
2783 else if (err == PARSE_TIME_UNDER) {
2784 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
2785 args[*cur_arg+1], args[*cur_arg], srv->id);
2786 goto error;
2787 }
2788 else if (err) {
2789 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
2790 *err, srv->id);
2791 goto error;
2792 }
2793 if (delay <= 0) {
2794 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
2795 delay, args[*cur_arg], srv->id);
2796 goto error;
2797 }
2798 srv->agent.inter = delay;
2799
2800 out:
2801 return err_code;
2802
2803 error:
2804 err_code |= ERR_ALERT | ERR_FATAL;
2805 goto out;
2806}
2807
2808/* Parse the "agent-port" server keyword */
2809static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2810 char **errmsg)
2811{
2812 int err_code = 0;
2813
2814 if (!*(args[*cur_arg+1])) {
2815 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
2816 goto error;
2817 }
2818
2819 global.maxsock++;
2820 srv->agent.port = atol(args[*cur_arg+1]);
2821
2822 out:
2823 return err_code;
2824
2825 error:
2826 err_code |= ERR_ALERT | ERR_FATAL;
2827 goto out;
2828}
2829
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002830int set_srv_agent_send(struct server *srv, const char *send)
2831{
2832 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2833 struct tcpcheck_var *var = NULL;
2834 char *str;
2835
2836 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002837 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002838 if (str == NULL || var == NULL)
2839 goto error;
2840
2841 free_tcpcheck_vars(&rules->preset_vars);
2842
2843 var->data.type = SMP_T_STR;
2844 var->data.u.str.area = str;
2845 var->data.u.str.data = strlen(str);
2846 LIST_INIT(&var->list);
2847 LIST_ADDQ(&rules->preset_vars, &var->list);
2848
2849 return 1;
2850
2851 error:
2852 free(str);
2853 free(var);
2854 return 0;
2855}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002856
2857/* Parse the "agent-send" server keyword */
2858static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2859 char **errmsg)
2860{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002861 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002862 int err_code = 0;
2863
2864 if (!*(args[*cur_arg+1])) {
2865 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
2866 goto error;
2867 }
2868
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002869 if (!rules) {
2870 rules = calloc(1, sizeof(*rules));
2871 if (!rules) {
2872 memprintf(errmsg, "out of memory.");
2873 goto error;
2874 }
2875 LIST_INIT(&rules->preset_vars);
2876 srv->agent.tcpcheck_rules = rules;
2877 }
2878
2879 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002880 memprintf(errmsg, "out of memory.");
2881 goto error;
2882 }
2883
2884 out:
2885 return err_code;
2886
2887 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002888 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002889 err_code |= ERR_ALERT | ERR_FATAL;
2890 goto out;
2891}
2892
2893/* Parse the "no-agent-send" server keyword */
2894static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2895 char **errmsg)
2896{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002897 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002898 return 0;
2899}
2900
Christopher Fauletce8111e2020-04-06 15:04:11 +02002901/* Parse the "check" server keyword */
2902static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2903 char **errmsg)
2904{
2905 srv->do_check = 1;
2906 return 0;
2907}
2908
2909/* Parse the "check-send-proxy" server keyword */
2910static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2911 char **errmsg)
2912{
2913 srv->check.send_proxy = 1;
2914 return 0;
2915}
2916
2917/* Parse the "check-via-socks4" server keyword */
2918static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2919 char **errmsg)
2920{
2921 srv->check.via_socks4 = 1;
2922 return 0;
2923}
2924
2925/* Parse the "no-check" server keyword */
2926static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2927 char **errmsg)
2928{
2929 deinit_srv_check(srv);
2930 return 0;
2931}
2932
2933/* Parse the "no-check-send-proxy" server keyword */
2934static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2935 char **errmsg)
2936{
2937 srv->check.send_proxy = 0;
2938 return 0;
2939}
2940
Christopher Fauletedc6ed92020-04-23 16:27:59 +02002941/* parse the "check-proto" server keyword */
2942static int srv_parse_check_proto(char **args, int *cur_arg,
2943 struct proxy *px, struct server *newsrv, char **err)
2944{
2945 int err_code = 0;
2946
2947 if (!*args[*cur_arg + 1]) {
2948 memprintf(err, "'%s' : missing value", args[*cur_arg]);
2949 goto error;
2950 }
2951 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
2952 if (!newsrv->check.mux_proto) {
2953 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
2954 goto error;
2955 }
2956
2957 out:
2958 return err_code;
2959
2960 error:
2961 err_code |= ERR_ALERT | ERR_FATAL;
2962 goto out;
2963}
2964
2965
Christopher Fauletce8111e2020-04-06 15:04:11 +02002966/* Parse the "rise" server keyword */
2967static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2968 char **errmsg)
2969{
2970 int err_code = 0;
2971
2972 if (!*args[*cur_arg + 1]) {
2973 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
2974 goto error;
2975 }
2976
2977 srv->check.rise = atol(args[*cur_arg+1]);
2978 if (srv->check.rise <= 0) {
2979 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
2980 goto error;
2981 }
2982
2983 if (srv->check.health)
2984 srv->check.health = srv->check.rise;
2985
2986 out:
2987 return err_code;
2988
2989 error:
2990 deinit_srv_agent_check(srv);
2991 err_code |= ERR_ALERT | ERR_FATAL;
2992 goto out;
2993 return 0;
2994}
2995
2996/* Parse the "fall" server keyword */
2997static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2998 char **errmsg)
2999{
3000 int err_code = 0;
3001
3002 if (!*args[*cur_arg + 1]) {
3003 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3004 goto error;
3005 }
3006
3007 srv->check.fall = atol(args[*cur_arg+1]);
3008 if (srv->check.fall <= 0) {
3009 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3010 goto error;
3011 }
3012
3013 out:
3014 return err_code;
3015
3016 error:
3017 deinit_srv_agent_check(srv);
3018 err_code |= ERR_ALERT | ERR_FATAL;
3019 goto out;
3020 return 0;
3021}
3022
3023/* Parse the "inter" server keyword */
3024static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3025 char **errmsg)
3026{
3027 const char *err = NULL;
3028 unsigned int delay;
3029 int err_code = 0;
3030
3031 if (!*(args[*cur_arg+1])) {
3032 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3033 goto error;
3034 }
3035
3036 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3037 if (err == PARSE_TIME_OVER) {
3038 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3039 args[*cur_arg+1], args[*cur_arg], srv->id);
3040 goto error;
3041 }
3042 else if (err == PARSE_TIME_UNDER) {
3043 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3044 args[*cur_arg+1], args[*cur_arg], srv->id);
3045 goto error;
3046 }
3047 else if (err) {
3048 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3049 *err, srv->id);
3050 goto error;
3051 }
3052 if (delay <= 0) {
3053 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3054 delay, args[*cur_arg], srv->id);
3055 goto error;
3056 }
3057 srv->check.inter = delay;
3058
3059 out:
3060 return err_code;
3061
3062 error:
3063 err_code |= ERR_ALERT | ERR_FATAL;
3064 goto out;
3065}
3066
3067
3068/* Parse the "fastinter" server keyword */
3069static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3070 char **errmsg)
3071{
3072 const char *err = NULL;
3073 unsigned int delay;
3074 int err_code = 0;
3075
3076 if (!*(args[*cur_arg+1])) {
3077 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3078 goto error;
3079 }
3080
3081 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3082 if (err == PARSE_TIME_OVER) {
3083 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3084 args[*cur_arg+1], args[*cur_arg], srv->id);
3085 goto error;
3086 }
3087 else if (err == PARSE_TIME_UNDER) {
3088 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3089 args[*cur_arg+1], args[*cur_arg], srv->id);
3090 goto error;
3091 }
3092 else if (err) {
3093 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3094 *err, srv->id);
3095 goto error;
3096 }
3097 if (delay <= 0) {
3098 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3099 delay, args[*cur_arg], srv->id);
3100 goto error;
3101 }
3102 srv->check.fastinter = delay;
3103
3104 out:
3105 return err_code;
3106
3107 error:
3108 err_code |= ERR_ALERT | ERR_FATAL;
3109 goto out;
3110}
3111
3112
3113/* Parse the "downinter" server keyword */
3114static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3115 char **errmsg)
3116{
3117 const char *err = NULL;
3118 unsigned int delay;
3119 int err_code = 0;
3120
3121 if (!*(args[*cur_arg+1])) {
3122 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3123 goto error;
3124 }
3125
3126 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3127 if (err == PARSE_TIME_OVER) {
3128 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3129 args[*cur_arg+1], args[*cur_arg], srv->id);
3130 goto error;
3131 }
3132 else if (err == PARSE_TIME_UNDER) {
3133 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3134 args[*cur_arg+1], args[*cur_arg], srv->id);
3135 goto error;
3136 }
3137 else if (err) {
3138 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3139 *err, srv->id);
3140 goto error;
3141 }
3142 if (delay <= 0) {
3143 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3144 delay, args[*cur_arg], srv->id);
3145 goto error;
3146 }
3147 srv->check.downinter = delay;
3148
3149 out:
3150 return err_code;
3151
3152 error:
3153 err_code |= ERR_ALERT | ERR_FATAL;
3154 goto out;
3155}
3156
3157/* Parse the "port" server keyword */
3158static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3159 char **errmsg)
3160{
3161 int err_code = 0;
3162
3163 if (!*(args[*cur_arg+1])) {
3164 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
3165 goto error;
3166 }
3167
3168 global.maxsock++;
3169 srv->check.port = atol(args[*cur_arg+1]);
3170 srv->flags |= SRV_F_CHECKPORT;
3171
3172 out:
3173 return err_code;
3174
3175 error:
3176 err_code |= ERR_ALERT | ERR_FATAL;
3177 goto out;
3178}
3179
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003180static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02003181 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003182 { 0, NULL, NULL },
3183}};
3184
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003185static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02003186 { "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 +02003187 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
3188 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
3189 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
3190 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
3191 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003192 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003193 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003194 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
3195 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003196 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003197 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
3198 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
3199 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
3200 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
3201 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
3202 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
3203 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
3204 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003205 { NULL, NULL, 0 },
3206}};
3207
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003208INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003209INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003210
Willy Tarreaubd741542010-03-16 18:46:54 +01003211/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02003212 * Local variables:
3213 * c-indent-level: 8
3214 * c-basic-offset: 8
3215 * End:
3216 */