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