blob: 7e8a1d9d4c6cdaf7b9c4338477466e4f21152ead [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 Tarreau6be78492020-06-05 00:00:29 +020034#include <haproxy/cfgparse.h>
Willy Tarreau4aa573d2020-06-04 18:21:56 +020035#include <haproxy/check.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020036#include <haproxy/chunk.h>
Willy Tarreaueb92deb2020-06-04 10:53:16 +020037#include <haproxy/dns.h>
Willy Tarreaubcc67332020-06-05 15:31:31 +020038#include <haproxy/extcheck.h>
Willy Tarreau2741c8c2020-06-02 11:28:02 +020039#include <haproxy/istbuf.h>
Willy Tarreau853b2972020-05-27 18:01:47 +020040#include <haproxy/list.h>
Willy Tarreaucee013e2020-06-05 11:40:38 +020041#include <haproxy/mailers.h>
Willy Tarreaua55c4542020-06-04 22:59:39 +020042#include <haproxy/queue.h>
Willy Tarreau7cd8b6e2020-06-02 17:32:26 +020043#include <haproxy/regex.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020044#include <haproxy/tools.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020045#include <haproxy/time.h>
Willy Tarreau3f567e42020-05-28 15:29:19 +020046#include <haproxy/thread.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020047#include <haproxy/http.h>
Willy Tarreau87735332020-06-04 09:08:41 +020048#include <haproxy/http_htx.h>
Willy Tarreau5413a872020-06-02 19:33:08 +020049#include <haproxy/h1.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020050#include <haproxy/htx.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020051#include <haproxy/log.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020052#include <haproxy/proxy.h>
Willy Tarreau1e56f922020-06-04 23:20:13 +020053#include <haproxy/server.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020054#include <haproxy/ssl_sock.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020055#include <haproxy/stats-t.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020056#include <haproxy/stream_interface.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020057#include <haproxy/task.h>
Willy Tarreau51cd5952020-06-05 12:25:38 +020058#include <haproxy/tcpcheck.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020059#include <haproxy/vars.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020060
Willy Tarreauf268ee82020-06-04 17:05:57 +020061#include <haproxy/global.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020062
Willy Tarreauaa74c4e2020-06-04 10:19:23 +020063#include <haproxy/arg.h>
Willy Tarreau0f6ffd62020-06-03 19:33:00 +020064#include <haproxy/fd.h>
Willy Tarreaufc8f6a82020-06-03 19:20:59 +020065#include <haproxy/port_range.h>
Willy Tarreaufc774542020-06-04 17:31:04 +020066#include <haproxy/proto_tcp.h>
Willy Tarreau2dd7c352020-06-03 15:26:55 +020067#include <haproxy/protocol.h>
Willy Tarreau832ce652020-06-04 08:36:05 +020068#include <haproxy/proto_udp.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020069#include <haproxy/sample.h>
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
449 /* no break - fall through */
450
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
Willy Tarreau00149122017-10-04 18:05:01 +0200659 if (!conn) {
660 /* connection allocation error before the connection was established */
661 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) ||
863 proxy->state == PR_STSTOPPED)
864 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;
870 b_reset(&check->bi);
871 b_reset(&check->bo);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200872
Willy Tarreau51cd5952020-06-05 12:25:38 +0200873 task_set_affinity(t, tid_bit);
874
875 check->current_step = NULL;
876 tcpcheck_main(check);
877 goto out_unlock;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200878 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200879 else {
880 /* there was a test running.
881 * First, let's check whether there was an uncaught error,
882 * which can happen on connect timeout or error.
883 */
884 if (check->result == CHK_RES_UNKNOWN) {
885 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
886 chk_report_conn_err(check, 0, expired);
887 }
888 else
889 goto out_unlock; /* timeout not reached, wait again */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200890 }
Christopher Faulet404f9192020-04-09 23:13:54 +0200891
Willy Tarreau51cd5952020-06-05 12:25:38 +0200892 /* check complete or aborted */
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200893
Willy Tarreau51cd5952020-06-05 12:25:38 +0200894 check->current_step = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200895
Willy Tarreau51cd5952020-06-05 12:25:38 +0200896 if (conn && conn->xprt) {
897 /* The check was aborted and the connection was not yet closed.
898 * This can happen upon timeout, or when an external event such
899 * as a failed response coupled with "observe layer7" caused the
900 * server state to be suddenly changed.
901 */
902 conn_sock_drain(conn);
903 cs_close(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200904 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200905
Willy Tarreau51cd5952020-06-05 12:25:38 +0200906 if (cs) {
907 if (check->wait_list.events)
908 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
909 /* We may have been scheduled to run, and the
910 * I/O handler expects to have a cs, so remove
911 * the tasklet
912 */
913 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
914 cs_destroy(cs);
915 cs = check->cs = NULL;
916 conn = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200917 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200918
919 if (check->sess != NULL) {
920 vars_prune(&check->vars, check->sess, NULL);
921 session_free(check->sess);
922 check->sess = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200923 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200924
925 if (check->server) {
926 if (check->result == CHK_RES_FAILED) {
927 /* a failure or timeout detected */
928 check_notify_failure(check);
929 }
930 else if (check->result == CHK_RES_CONDPASS) {
931 /* check is OK but asks for stopping mode */
932 check_notify_stopping(check);
933 }
934 else if (check->result == CHK_RES_PASSED) {
935 /* a success was detected */
936 check_notify_success(check);
937 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200938 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200939 task_set_affinity(t, MAX_THREADS_MASK);
940 check->state &= ~CHK_ST_INPROGRESS;
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/**************************************************************************/
Willy Tarreaucee013e2020-06-05 11:40:38 +0200965const char *init_check(struct check *check, int type)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200966{
967 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200968
Christopher Faulet61cc8522020-04-20 14:54:42 +0200969 b_reset(&check->bi); check->bi.size = global.tune.chksize;
970 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200971
Christopher Faulet61cc8522020-04-20 14:54:42 +0200972 check->bi.area = calloc(check->bi.size, sizeof(char));
973 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200974
Christopher Faulet61cc8522020-04-20 14:54:42 +0200975 if (!check->bi.area || !check->bo.area)
976 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +0100977
Christopher Faulet61cc8522020-04-20 14:54:42 +0200978 check->wait_list.tasklet = tasklet_new();
979 if (!check->wait_list.tasklet)
980 return "out of memory while allocating check tasklet";
981 check->wait_list.events = 0;
982 check->wait_list.tasklet->process = event_srv_chk_io;
983 check->wait_list.tasklet->context = check;
984 return NULL;
985}
986
987void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +0100988{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200989 task_destroy(check->task);
990 if (check->wait_list.tasklet)
991 tasklet_free(check->wait_list.tasklet);
992
993 free(check->bi.area);
994 free(check->bo.area);
995 if (check->cs) {
996 free(check->cs->conn);
997 check->cs->conn = NULL;
998 cs_free(check->cs);
999 check->cs = NULL;
1000 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001001}
1002
Christopher Faulet61cc8522020-04-20 14:54:42 +02001003/* manages a server health-check. Returns the time the task accepts to wait, or
1004 * TIME_ETERNITY for infinity.
1005 */
Willy Tarreaucee013e2020-06-05 11:40:38 +02001006struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001007{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001008 struct check *check = context;
1009
1010 if (check->type == PR_O2_EXT_CHK)
1011 return process_chk_proc(t, context, state);
1012 return process_chk_conn(t, context, state);
1013
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001014}
1015
Christopher Faulet61cc8522020-04-20 14:54:42 +02001016
1017static int start_check_task(struct check *check, int mininter,
1018 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001019{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001020 struct task *t;
1021 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001022
Christopher Faulet61cc8522020-04-20 14:54:42 +02001023 if (check->type == PR_O2_EXT_CHK)
1024 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001025
Christopher Faulet61cc8522020-04-20 14:54:42 +02001026 /* task for the check */
1027 if ((t = task_new(thread_mask)) == NULL) {
1028 ha_alert("Starting [%s:%s] check: out of memory.\n",
1029 check->server->proxy->id, check->server->id);
1030 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001031 }
1032
Christopher Faulet61cc8522020-04-20 14:54:42 +02001033 check->task = t;
1034 t->process = process_chk;
1035 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001036
Christopher Faulet61cc8522020-04-20 14:54:42 +02001037 if (mininter < srv_getinter(check))
1038 mininter = srv_getinter(check);
1039
1040 if (global.max_spread_checks && mininter > global.max_spread_checks)
1041 mininter = global.max_spread_checks;
1042
1043 /* check this every ms */
1044 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
1045 check->start = now;
1046 task_queue(t);
1047
1048 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001049}
1050
Christopher Faulet61cc8522020-04-20 14:54:42 +02001051/* updates the server's weight during a warmup stage. Once the final weight is
1052 * reached, the task automatically stops. Note that any server status change
1053 * must have updated s->last_change accordingly.
1054 */
1055static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001056{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001057 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001058
Christopher Faulet61cc8522020-04-20 14:54:42 +02001059 /* by default, plan on stopping the task */
1060 t->expire = TICK_ETERNITY;
1061 if ((s->next_admin & SRV_ADMF_MAINT) ||
1062 (s->next_state != SRV_ST_STARTING))
1063 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02001064
Christopher Faulet61cc8522020-04-20 14:54:42 +02001065 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001066
Christopher Faulet61cc8522020-04-20 14:54:42 +02001067 /* recalculate the weights and update the state */
1068 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02001069
Christopher Faulet61cc8522020-04-20 14:54:42 +02001070 /* probably that we can refill this server with a bit more connections */
1071 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02001072
Christopher Faulet61cc8522020-04-20 14:54:42 +02001073 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02001074
Christopher Faulet61cc8522020-04-20 14:54:42 +02001075 /* get back there in 1 second or 1/20th of the slowstart interval,
1076 * whichever is greater, resulting in small 5% steps.
1077 */
1078 if (s->next_state == SRV_ST_STARTING)
1079 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
1080 return t;
1081}
1082
1083/*
1084 * Start health-check.
1085 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
1086 */
1087static int start_checks()
1088{
1089
1090 struct proxy *px;
1091 struct server *s;
1092 struct task *t;
1093 int nbcheck=0, mininter=0, srvpos=0;
1094
1095 /* 0- init the dummy frontend used to create all checks sessions */
1096 init_new_proxy(&checks_fe);
1097 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
1098 checks_fe.mode = PR_MODE_TCP;
1099 checks_fe.maxconn = 0;
1100 checks_fe.conn_retries = CONN_RETRIES;
1101 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
1102 checks_fe.timeout.client = TICK_ETERNITY;
1103
1104 /* 1- count the checkers to run simultaneously.
1105 * We also determine the minimum interval among all of those which
1106 * have an interval larger than SRV_CHK_INTER_THRES. This interval
1107 * will be used to spread their start-up date. Those which have
1108 * a shorter interval will start independently and will not dictate
1109 * too short an interval for all others.
1110 */
1111 for (px = proxies_list; px; px = px->next) {
1112 for (s = px->srv; s; s = s->next) {
1113 if (s->slowstart) {
1114 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
1115 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1116 return ERR_ALERT | ERR_FATAL;
1117 }
1118 /* We need a warmup task that will be called when the server
1119 * state switches from down to up.
1120 */
1121 s->warmup = t;
1122 t->process = server_warmup;
1123 t->context = s;
1124 /* server can be in this state only because of */
1125 if (s->next_state == SRV_ST_STARTING)
1126 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 +02001127 }
1128
Christopher Faulet61cc8522020-04-20 14:54:42 +02001129 if (s->check.state & CHK_ST_CONFIGURED) {
1130 nbcheck++;
1131 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
1132 (!mininter || mininter > srv_getinter(&s->check)))
1133 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02001134 }
1135
Christopher Faulet61cc8522020-04-20 14:54:42 +02001136 if (s->agent.state & CHK_ST_CONFIGURED) {
1137 nbcheck++;
1138 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
1139 (!mininter || mininter > srv_getinter(&s->agent)))
1140 mininter = srv_getinter(&s->agent);
1141 }
Christopher Faulet5c288742020-03-31 08:15:58 +02001142 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001143 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001144
Christopher Faulet61cc8522020-04-20 14:54:42 +02001145 if (!nbcheck)
1146 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001147
Christopher Faulet61cc8522020-04-20 14:54:42 +02001148 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02001149
Christopher Faulet61cc8522020-04-20 14:54:42 +02001150 /*
1151 * 2- start them as far as possible from each others. For this, we will
1152 * start them after their interval set to the min interval divided by
1153 * the number of servers, weighted by the server's position in the list.
1154 */
1155 for (px = proxies_list; px; px = px->next) {
1156 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
1157 if (init_pid_list()) {
1158 ha_alert("Starting [%s] check: out of memory.\n", px->id);
1159 return ERR_ALERT | ERR_FATAL;
1160 }
1161 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001162
Christopher Faulet61cc8522020-04-20 14:54:42 +02001163 for (s = px->srv; s; s = s->next) {
1164 /* A task for the main check */
1165 if (s->check.state & CHK_ST_CONFIGURED) {
1166 if (s->check.type == PR_O2_EXT_CHK) {
1167 if (!prepare_external_check(&s->check))
1168 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001169 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001170 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
1171 return ERR_ALERT | ERR_FATAL;
1172 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02001173 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001174
Christopher Faulet61cc8522020-04-20 14:54:42 +02001175 /* A task for a auxiliary agent check */
1176 if (s->agent.state & CHK_ST_CONFIGURED) {
1177 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
1178 return ERR_ALERT | ERR_FATAL;
1179 }
1180 srvpos++;
1181 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001182 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001183 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001184 return 0;
1185}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001186
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001187
Christopher Faulet61cc8522020-04-20 14:54:42 +02001188/*
1189 * Return value:
1190 * the port to be used for the health check
1191 * 0 in case no port could be found for the check
1192 */
1193static int srv_check_healthcheck_port(struct check *chk)
1194{
1195 int i = 0;
1196 struct server *srv = NULL;
1197
1198 srv = chk->server;
1199
1200 /* by default, we use the health check port ocnfigured */
1201 if (chk->port > 0)
1202 return chk->port;
1203
1204 /* try to get the port from check_core.addr if check.port not set */
1205 i = get_host_port(&chk->addr);
1206 if (i > 0)
1207 return i;
1208
1209 /* try to get the port from server address */
1210 /* prevent MAPPORTS from working at this point, since checks could
1211 * not be performed in such case (MAPPORTS impose a relative ports
1212 * based on live traffic)
1213 */
1214 if (srv->flags & SRV_F_MAPPORTS)
1215 return 0;
1216
1217 i = srv->svc_port; /* by default */
1218 if (i > 0)
1219 return i;
1220
1221 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001222}
1223
Christopher Faulet61cc8522020-04-20 14:54:42 +02001224/* Initializes an health-check attached to the server <srv>. Non-zero is returned
1225 * if an error occurred.
1226 */
1227static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001228{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001229 const char *err;
1230 struct tcpcheck_rule *r;
1231 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001232
Christopher Faulet61cc8522020-04-20 14:54:42 +02001233 if (!srv->do_check)
1234 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001235
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001236
Christopher Faulet61cc8522020-04-20 14:54:42 +02001237 /* If neither a port nor an addr was specified and no check transport
1238 * layer is forced, then the transport layer used by the checks is the
1239 * same as for the production traffic. Otherwise we use raw_sock by
1240 * default, unless one is specified.
1241 */
1242 if (!srv->check.port && !is_addr(&srv->check.addr)) {
1243 if (!srv->check.use_ssl && srv->use_ssl != -1) {
1244 srv->check.use_ssl = srv->use_ssl;
1245 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001246 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001247 else if (srv->check.use_ssl == 1)
1248 srv->check.xprt = xprt_get(XPRT_SSL);
1249 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001250 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02001251 else if (srv->check.use_ssl == 1)
1252 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001253
Christopher Faulet12882cf2020-04-23 15:50:18 +02001254 /* Inherit the mux protocol from the server if not already defined for
1255 * the check
1256 */
1257 if (srv->mux_proto && !srv->check.mux_proto)
1258 srv->check.mux_proto = srv->mux_proto;
1259
Christopher Faulet61cc8522020-04-20 14:54:42 +02001260 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001261
Christopher Faulet61cc8522020-04-20 14:54:42 +02001262 /* We need at least a service port, a check port or the first tcp-check
1263 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
1264 */
1265 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
1266 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
1267 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001268
Christopher Faulet61cc8522020-04-20 14:54:42 +02001269 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
1270 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
1271 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1272 ret |= ERR_ALERT | ERR_ABORT;
1273 goto out;
1274 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001275
Christopher Faulet61cc8522020-04-20 14:54:42 +02001276 /* search the first action (connect / send / expect) in the list */
1277 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
1278 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
1279 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
1280 "nor tcp_check rule 'connect' with port information.\n",
1281 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1282 ret |= ERR_ALERT | ERR_ABORT;
1283 goto out;
1284 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001285
Christopher Faulet61cc8522020-04-20 14:54:42 +02001286 /* scan the tcp-check ruleset to ensure a port has been configured */
1287 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
1288 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
1289 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
1290 "and a tcp_check rule 'connect' with no port information.\n",
1291 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1292 ret |= ERR_ALERT | ERR_ABORT;
1293 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001294 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001295 }
1296
Christopher Faulet61cc8522020-04-20 14:54:42 +02001297 init:
1298 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
1299 struct tcpcheck_ruleset *rs = NULL;
1300 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
1301 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001302
Christopher Faulet61cc8522020-04-20 14:54:42 +02001303 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
1304 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02001305
Christopher Faulet61cc8522020-04-20 14:54:42 +02001306 rs = find_tcpcheck_ruleset("*tcp-check");
1307 if (!rs) {
1308 rs = create_tcpcheck_ruleset("*tcp-check");
1309 if (rs == NULL) {
1310 ha_alert("config: %s '%s': out of memory.\n",
1311 proxy_type_str(srv->proxy), srv->proxy->id);
1312 ret |= ERR_ALERT | ERR_FATAL;
1313 goto out;
1314 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001315 }
1316
Christopher Faulet61cc8522020-04-20 14:54:42 +02001317 free_tcpcheck_vars(&rules->preset_vars);
1318 rules->list = &rs->rules;
1319 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001320 }
1321
Christopher Faulet61cc8522020-04-20 14:54:42 +02001322 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
1323 if (err) {
1324 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
1325 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1326 ret |= ERR_ALERT | ERR_ABORT;
1327 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001328 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001329 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
1330 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02001331
Christopher Faulet61cc8522020-04-20 14:54:42 +02001332 out:
1333 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001334}
1335
Christopher Faulet61cc8522020-04-20 14:54:42 +02001336/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
1337 * if an error occurred.
1338 */
1339static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02001340{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001341 struct tcpcheck_rule *chk;
1342 const char *err;
1343 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001344
Christopher Faulet61cc8522020-04-20 14:54:42 +02001345 if (!srv->do_agent)
1346 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001347
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001348 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02001349 * implicit one is inserted before all others.
1350 */
1351 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
1352 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
1353 chk = calloc(1, sizeof(*chk));
1354 if (!chk) {
1355 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
1356 " to agent-check for server '%s' (out of memory).\n",
1357 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1358 ret |= ERR_ALERT | ERR_FATAL;
1359 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001360 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001361 chk->action = TCPCHK_ACT_CONNECT;
1362 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
1363 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02001364 }
1365
Christopher Faulete5870d82020-04-15 11:32:03 +02001366
Christopher Faulet61cc8522020-04-20 14:54:42 +02001367 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
1368 if (err) {
1369 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
1370 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1371 ret |= ERR_ALERT | ERR_ABORT;
1372 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001373 }
1374
Christopher Faulet61cc8522020-04-20 14:54:42 +02001375 if (!srv->agent.inter)
1376 srv->agent.inter = srv->check.inter;
1377
1378 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
1379 global.maxsock++;
1380
1381 out:
1382 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001383}
1384
Christopher Faulet61cc8522020-04-20 14:54:42 +02001385static void deinit_srv_check(struct server *srv)
1386{
1387 if (srv->check.state & CHK_ST_CONFIGURED)
1388 free_check(&srv->check);
1389 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
1390 srv->do_check = 0;
1391}
Christopher Faulete5870d82020-04-15 11:32:03 +02001392
Christopher Faulet61cc8522020-04-20 14:54:42 +02001393
1394static void deinit_srv_agent_check(struct server *srv)
1395{
1396 if (srv->agent.tcpcheck_rules) {
1397 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
1398 free(srv->agent.tcpcheck_rules);
1399 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001400 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001401
Christopher Faulet61cc8522020-04-20 14:54:42 +02001402 if (srv->agent.state & CHK_ST_CONFIGURED)
1403 free_check(&srv->agent);
1404
1405 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
1406 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001407}
1408
Willy Tarreaucee013e2020-06-05 11:40:38 +02001409REGISTER_POST_SERVER_CHECK(init_srv_check);
1410REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
Willy Tarreaucee013e2020-06-05 11:40:38 +02001411REGISTER_POST_CHECK(start_checks);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001412
Willy Tarreaucee013e2020-06-05 11:40:38 +02001413REGISTER_SERVER_DEINIT(deinit_srv_check);
1414REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001415
Christopher Faulet61cc8522020-04-20 14:54:42 +02001416
1417/**************************************************************************/
1418/************************** Check sample fetches **************************/
1419/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001420
Christopher Faulet61cc8522020-04-20 14:54:42 +02001421static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001422 { /* END */ },
1423}};
1424
1425INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
1426
1427
1428/**************************************************************************/
1429/************************ Check's parsing functions ***********************/
1430/**************************************************************************/
Christopher Faulet51b129f2020-04-09 15:54:18 +02001431/* Parses the "http-check" proxy keyword */
1432static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
1433 struct proxy *defpx, const char *file, int line,
1434 char **errmsg)
1435{
Christopher Faulete5870d82020-04-15 11:32:03 +02001436 struct tcpcheck_ruleset *rs = NULL;
1437 struct tcpcheck_rule *chk = NULL;
1438 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001439
1440 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
1441 ret = 1;
1442
1443 cur_arg = 1;
1444 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
1445 /* enable a graceful server shutdown on an HTTP 404 response */
1446 curpx->options |= PR_O_DISABLE404;
1447 if (too_many_args(1, args, errmsg, NULL))
1448 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001449 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001450 }
1451 else if (strcmp(args[cur_arg], "send-state") == 0) {
1452 /* enable emission of the apparent state of a server in HTTP checks */
1453 curpx->options2 |= PR_O2_CHK_SNDST;
1454 if (too_many_args(1, args, errmsg, NULL))
1455 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001456 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001457 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001458
Christopher Faulete5870d82020-04-15 11:32:03 +02001459 /* Deduce the ruleset name from the proxy info */
1460 chunk_printf(&trash, "*http-check-%s_%s-%d",
1461 ((curpx == defpx) ? "defaults" : curpx->id),
1462 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001463
Christopher Faulet61cc8522020-04-20 14:54:42 +02001464 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001465 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001466 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001467 if (rs == NULL) {
1468 memprintf(errmsg, "out of memory.\n");
1469 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001470 }
1471 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001472
Christopher Faulete5870d82020-04-15 11:32:03 +02001473 index = 0;
1474 if (!LIST_ISEMPTY(&rs->rules)) {
1475 chk = LIST_PREV(&rs->rules, typeof(chk), list);
1476 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
1477 index = chk->index + 1;
1478 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001479
Christopher Faulete5870d82020-04-15 11:32:03 +02001480 if (strcmp(args[cur_arg], "connect") == 0)
1481 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1482 else if (strcmp(args[cur_arg], "send") == 0)
1483 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1484 else if (strcmp(args[cur_arg], "expect") == 0)
1485 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
1486 file, line, errmsg);
1487 else if (strcmp(args[cur_arg], "comment") == 0)
1488 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1489 else {
1490 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001491
Christopher Faulete5870d82020-04-15 11:32:03 +02001492 if (!kw) {
1493 action_kw_tcp_check_build_list(&trash);
1494 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
1495 " 'send', 'expect'%s%s. but got '%s'",
1496 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
1497 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001498 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001499 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
1500 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001501
Christopher Faulete5870d82020-04-15 11:32:03 +02001502 if (!chk) {
1503 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1504 goto error;
1505 }
1506 ret = (*errmsg != NULL); /* Handle warning */
1507
1508 chk->index = index;
1509 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
1510 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
1511 /* Use this ruleset if the proxy already has http-check enabled */
1512 curpx->tcpcheck_rules.list = &rs->rules;
1513 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
1514 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
1515 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1516 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001517 goto error;
1518 }
1519 }
1520 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02001521 /* mark this ruleset as unused for now */
1522 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
1523 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001524 }
1525
Christopher Faulete5870d82020-04-15 11:32:03 +02001526 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02001527 return ret;
1528
1529 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02001530 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001531 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001532 return -1;
1533}
1534
Christopher Faulet430e4802020-04-09 15:28:16 +02001535/* Parses the "option tcp-check" proxy keyword */
1536int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1537 const char *file, int line)
1538{
Christopher Faulet404f9192020-04-09 23:13:54 +02001539 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02001540 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1541 int err_code = 0;
1542
1543 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1544 err_code |= ERR_WARN;
1545
1546 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1547 goto out;
1548
Christopher Faulet404f9192020-04-09 23:13:54 +02001549 curpx->options2 &= ~PR_O2_CHK_ANY;
1550 curpx->options2 |= PR_O2_TCPCHK_CHK;
1551
Christopher Fauletd7e63962020-04-17 20:15:59 +02001552 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02001553 /* If a tcp-check rulesset is already set, do nothing */
1554 if (rules->list)
1555 goto out;
1556
1557 /* If a tcp-check ruleset is waiting to be used for the current proxy,
1558 * get it.
1559 */
1560 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
1561 goto curpx_ruleset;
1562
1563 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
1564 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001565 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001566 if (rs)
1567 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02001568 }
1569
Christopher Faulet404f9192020-04-09 23:13:54 +02001570 curpx_ruleset:
1571 /* Deduce the ruleset name from the proxy info */
1572 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
1573 ((curpx == defpx) ? "defaults" : curpx->id),
1574 curpx->conf.file, curpx->conf.line);
1575
Christopher Faulet61cc8522020-04-20 14:54:42 +02001576 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001577 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001578 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001579 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02001580 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1581 goto error;
1582 }
Christopher Faulet430e4802020-04-09 15:28:16 +02001583 }
1584
Christopher Faulet404f9192020-04-09 23:13:54 +02001585 ruleset_found:
1586 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02001587 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02001588 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02001589
1590 out:
1591 return err_code;
1592
1593 error:
1594 err_code |= ERR_ALERT | ERR_FATAL;
1595 goto out;
1596}
Christopher Faulet33f05df2020-04-01 11:08:50 +02001597
1598/* Parses the "option redis-check" proxy keyword */
1599int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1600 const char *file, int line)
1601{
1602 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
1603 static char *redis_res = "+PONG\r\n";
1604
1605 struct tcpcheck_ruleset *rs = NULL;
1606 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1607 struct tcpcheck_rule *chk;
1608 char *errmsg = NULL;
1609 int err_code = 0;
1610
1611 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1612 err_code |= ERR_WARN;
1613
1614 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1615 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001616
1617 curpx->options2 &= ~PR_O2_CHK_ANY;
1618 curpx->options2 |= PR_O2_TCPCHK_CHK;
1619
1620 free_tcpcheck_vars(&rules->preset_vars);
1621 rules->list = NULL;
1622 rules->flags = 0;
1623
Christopher Faulet61cc8522020-04-20 14:54:42 +02001624 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001625 if (rs)
1626 goto ruleset_found;
1627
Christopher Faulet61cc8522020-04-20 14:54:42 +02001628 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001629 if (rs == NULL) {
1630 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1631 goto error;
1632 }
1633
1634 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
1635 1, curpx, &rs->rules, file, line, &errmsg);
1636 if (!chk) {
1637 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1638 goto error;
1639 }
1640 chk->index = 0;
1641 LIST_ADDQ(&rs->rules, &chk->list);
1642
1643 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
1644 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001645 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02001646 "on-success", "Redis server is ok",
1647 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001648 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001649 if (!chk) {
1650 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1651 goto error;
1652 }
1653 chk->index = 1;
1654 LIST_ADDQ(&rs->rules, &chk->list);
1655
Christopher Faulet33f05df2020-04-01 11:08:50 +02001656 ruleset_found:
1657 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02001658 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001659
1660 out:
1661 free(errmsg);
1662 return err_code;
1663
1664 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001665 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001666 err_code |= ERR_ALERT | ERR_FATAL;
1667 goto out;
1668}
1669
Christopher Faulet811f78c2020-04-01 11:10:27 +02001670
1671/* Parses the "option ssl-hello-chk" proxy keyword */
1672int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1673 const char *file, int line)
1674{
1675 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
1676 * ssl-hello-chk option to ensure that the remote server speaks SSL.
1677 *
1678 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
1679 */
1680 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001681 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02001682 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
1683 "0079" /* ContentLength : 0x79 bytes after this one */
1684 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
1685 "000075" /* HandshakeLength : 0x75 bytes after this one */
1686 "0300" /* Hello Version : 0x0300 = v3 */
1687 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
1688 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
1689 "00" /* Session ID length : empty (no session ID) */
1690 "004E" /* Cipher Suite Length : 78 bytes after this one */
1691 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
1692 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
1693 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
1694 "000D" "000E" "000F" "0010" /* various bit lengths, */
1695 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
1696 "0015" "0016" "0017" "0018"
1697 "0019" "001A" "001B" "002F"
1698 "0030" "0031" "0032" "0033"
1699 "0034" "0035" "0036" "0037"
1700 "0038" "0039" "003A"
1701 "01" /* Compression Length : 0x01 = 1 byte for types */
1702 "00" /* Compression Type : 0x00 = NULL compression */
1703 };
1704
1705 struct tcpcheck_ruleset *rs = NULL;
1706 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1707 struct tcpcheck_rule *chk;
1708 char *errmsg = NULL;
1709 int err_code = 0;
1710
1711 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1712 err_code |= ERR_WARN;
1713
1714 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1715 goto out;
1716
Christopher Faulet811f78c2020-04-01 11:10:27 +02001717 curpx->options2 &= ~PR_O2_CHK_ANY;
1718 curpx->options2 |= PR_O2_TCPCHK_CHK;
1719
1720 free_tcpcheck_vars(&rules->preset_vars);
1721 rules->list = NULL;
1722 rules->flags = 0;
1723
Christopher Faulet61cc8522020-04-20 14:54:42 +02001724 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001725 if (rs)
1726 goto ruleset_found;
1727
Christopher Faulet61cc8522020-04-20 14:54:42 +02001728 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001729 if (rs == NULL) {
1730 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1731 goto error;
1732 }
1733
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001734 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02001735 1, curpx, &rs->rules, file, line, &errmsg);
1736 if (!chk) {
1737 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1738 goto error;
1739 }
1740 chk->index = 0;
1741 LIST_ADDQ(&rs->rules, &chk->list);
1742
1743 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02001744 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02001745 "error-status", "L6RSP", "tout-status", "L6TOUT",
1746 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001747 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001748 if (!chk) {
1749 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1750 goto error;
1751 }
1752 chk->index = 1;
1753 LIST_ADDQ(&rs->rules, &chk->list);
1754
Christopher Faulet811f78c2020-04-01 11:10:27 +02001755 ruleset_found:
1756 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02001757 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02001758
1759 out:
1760 free(errmsg);
1761 return err_code;
1762
1763 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001764 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001765 err_code |= ERR_ALERT | ERR_FATAL;
1766 goto out;
1767}
1768
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001769/* Parses the "option smtpchk" proxy keyword */
1770int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1771 const char *file, int line)
1772{
1773 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
1774
1775 struct tcpcheck_ruleset *rs = NULL;
1776 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1777 struct tcpcheck_rule *chk;
1778 struct tcpcheck_var *var = NULL;
1779 char *cmd = NULL, *errmsg = NULL;
1780 int err_code = 0;
1781
1782 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1783 err_code |= ERR_WARN;
1784
1785 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1786 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001787
1788 curpx->options2 &= ~PR_O2_CHK_ANY;
1789 curpx->options2 |= PR_O2_TCPCHK_CHK;
1790
1791 free_tcpcheck_vars(&rules->preset_vars);
1792 rules->list = NULL;
1793 rules->flags = 0;
1794
1795 cur_arg += 2;
1796 if (*args[cur_arg] && *args[cur_arg+1] &&
1797 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
1798 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
1799 if (cmd)
1800 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
1801 }
1802 else {
1803 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
1804 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
1805 cmd = strdup("HELO localhost");
1806 }
1807
Christopher Fauletb61caf42020-04-21 10:57:42 +02001808 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001809 if (cmd == NULL || var == NULL) {
1810 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1811 goto error;
1812 }
1813 var->data.type = SMP_T_STR;
1814 var->data.u.str.area = cmd;
1815 var->data.u.str.data = strlen(cmd);
1816 LIST_INIT(&var->list);
1817 LIST_ADDQ(&rules->preset_vars, &var->list);
1818 cmd = NULL;
1819 var = NULL;
1820
Christopher Faulet61cc8522020-04-20 14:54:42 +02001821 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001822 if (rs)
1823 goto ruleset_found;
1824
Christopher Faulet61cc8522020-04-20 14:54:42 +02001825 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001826 if (rs == NULL) {
1827 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1828 goto error;
1829 }
1830
1831 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
1832 1, curpx, &rs->rules, file, line, &errmsg);
1833 if (!chk) {
1834 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1835 goto error;
1836 }
1837 chk->index = 0;
1838 LIST_ADDQ(&rs->rules, &chk->list);
1839
1840 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
1841 "min-recv", "4",
1842 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02001843 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001844 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001845 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001846 if (!chk) {
1847 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1848 goto error;
1849 }
1850 chk->index = 1;
1851 LIST_ADDQ(&rs->rules, &chk->list);
1852
1853 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
1854 "min-recv", "4",
1855 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001856 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1857 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001858 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001859 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001860 if (!chk) {
1861 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1862 goto error;
1863 }
1864 chk->index = 2;
1865 LIST_ADDQ(&rs->rules, &chk->list);
1866
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001867 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001868 1, curpx, &rs->rules, file, line, &errmsg);
1869 if (!chk) {
1870 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1871 goto error;
1872 }
1873 chk->index = 3;
1874 LIST_ADDQ(&rs->rules, &chk->list);
1875
1876 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
1877 "min-recv", "4",
1878 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001879 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1880 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1881 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001882 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001883 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001884 if (!chk) {
1885 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1886 goto error;
1887 }
1888 chk->index = 4;
1889 LIST_ADDQ(&rs->rules, &chk->list);
1890
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001891 ruleset_found:
1892 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02001893 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001894
1895 out:
1896 free(errmsg);
1897 return err_code;
1898
1899 error:
1900 free(cmd);
1901 free(var);
1902 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001903 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001904 err_code |= ERR_ALERT | ERR_FATAL;
1905 goto out;
1906}
Christopher Faulet811f78c2020-04-01 11:10:27 +02001907
Christopher Fauletce355072020-04-02 11:44:39 +02001908/* Parses the "option pgsql-check" proxy keyword */
1909int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1910 const char *file, int line)
1911{
1912 static char pgsql_req[] = {
1913 "%[var(check.plen),htonl,hex]" /* The packet length*/
1914 "00030000" /* the version 3.0 */
1915 "7573657200" /* "user" key */
1916 "%[var(check.username),hex]00" /* the username */
1917 "00"
1918 };
1919
1920 struct tcpcheck_ruleset *rs = NULL;
1921 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1922 struct tcpcheck_rule *chk;
1923 struct tcpcheck_var *var = NULL;
1924 char *user = NULL, *errmsg = NULL;
1925 size_t packetlen = 0;
1926 int err_code = 0;
1927
1928 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1929 err_code |= ERR_WARN;
1930
1931 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1932 goto out;
1933
Christopher Fauletce355072020-04-02 11:44:39 +02001934 curpx->options2 &= ~PR_O2_CHK_ANY;
1935 curpx->options2 |= PR_O2_TCPCHK_CHK;
1936
1937 free_tcpcheck_vars(&rules->preset_vars);
1938 rules->list = NULL;
1939 rules->flags = 0;
1940
1941 cur_arg += 2;
1942 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1943 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
1944 file, line, args[0], args[1]);
1945 goto error;
1946 }
1947 if (strcmp(args[cur_arg], "user") == 0) {
1948 packetlen = 15 + strlen(args[cur_arg+1]);
1949 user = strdup(args[cur_arg+1]);
1950
Christopher Fauletb61caf42020-04-21 10:57:42 +02001951 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02001952 if (user == NULL || var == NULL) {
1953 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1954 goto error;
1955 }
1956 var->data.type = SMP_T_STR;
1957 var->data.u.str.area = user;
1958 var->data.u.str.data = strlen(user);
1959 LIST_INIT(&var->list);
1960 LIST_ADDQ(&rules->preset_vars, &var->list);
1961 user = NULL;
1962 var = NULL;
1963
Christopher Fauletb61caf42020-04-21 10:57:42 +02001964 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02001965 if (var == NULL) {
1966 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1967 goto error;
1968 }
1969 var->data.type = SMP_T_SINT;
1970 var->data.u.sint = packetlen;
1971 LIST_INIT(&var->list);
1972 LIST_ADDQ(&rules->preset_vars, &var->list);
1973 var = NULL;
1974 }
1975 else {
1976 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
1977 file, line, args[0], args[1]);
1978 goto error;
1979 }
1980
Christopher Faulet61cc8522020-04-20 14:54:42 +02001981 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02001982 if (rs)
1983 goto ruleset_found;
1984
Christopher Faulet61cc8522020-04-20 14:54:42 +02001985 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02001986 if (rs == NULL) {
1987 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1988 goto error;
1989 }
1990
1991 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
1992 1, curpx, &rs->rules, file, line, &errmsg);
1993 if (!chk) {
1994 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1995 goto error;
1996 }
1997 chk->index = 0;
1998 LIST_ADDQ(&rs->rules, &chk->list);
1999
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002000 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02002001 1, curpx, &rs->rules, file, line, &errmsg);
2002 if (!chk) {
2003 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2004 goto error;
2005 }
2006 chk->index = 1;
2007 LIST_ADDQ(&rs->rules, &chk->list);
2008
2009 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
2010 "min-recv", "5",
2011 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02002012 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02002013 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002014 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002015 if (!chk) {
2016 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2017 goto error;
2018 }
2019 chk->index = 2;
2020 LIST_ADDQ(&rs->rules, &chk->list);
2021
Christopher Fauletb841c742020-04-27 18:29:49 +02002022 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 +02002023 "min-recv", "9",
2024 "error-status", "L7STS",
2025 "on-success", "PostgreSQL server is ok",
2026 "on-error", "PostgreSQL unknown error",
2027 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002028 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002029 if (!chk) {
2030 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2031 goto error;
2032 }
2033 chk->index = 3;
2034 LIST_ADDQ(&rs->rules, &chk->list);
2035
Christopher Fauletce355072020-04-02 11:44:39 +02002036 ruleset_found:
2037 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002038 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02002039
2040 out:
2041 free(errmsg);
2042 return err_code;
2043
2044 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002045 free(user);
2046 free(var);
2047 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002048 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002049 err_code |= ERR_ALERT | ERR_FATAL;
2050 goto out;
2051}
2052
2053
2054/* Parses the "option mysql-check" proxy keyword */
2055int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2056 const char *file, int line)
2057{
2058 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
2059 * const char mysql40_client_auth_pkt[] = {
2060 * "\x0e\x00\x00" // packet length
2061 * "\x01" // packet number
2062 * "\x00\x00" // client capabilities
2063 * "\x00\x00\x01" // max packet
2064 * "haproxy\x00" // username (null terminated string)
2065 * "\x00" // filler (always 0x00)
2066 * "\x01\x00\x00" // packet length
2067 * "\x00" // packet number
2068 * "\x01" // COM_QUIT command
2069 * };
2070 */
2071 static char mysql40_rsname[] = "*mysql40-check";
2072 static char mysql40_req[] = {
2073 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2074 "0080" /* client capabilities */
2075 "000001" /* max packet */
2076 "%[var(check.username),hex]00" /* the username */
2077 "00" /* filler (always 0x00) */
2078 "010000" /* packet length*/
2079 "00" /* sequence ID */
2080 "01" /* COM_QUIT command */
2081 };
2082
2083 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
2084 * const char mysql41_client_auth_pkt[] = {
2085 * "\x0e\x00\x00\" // packet length
2086 * "\x01" // packet number
2087 * "\x00\x00\x00\x00" // client capabilities
2088 * "\x00\x00\x00\x01" // max packet
2089 * "\x21" // character set (UTF-8)
2090 * char[23] // All zeroes
2091 * "haproxy\x00" // username (null terminated string)
2092 * "\x00" // filler (always 0x00)
2093 * "\x01\x00\x00" // packet length
2094 * "\x00" // packet number
2095 * "\x01" // COM_QUIT command
2096 * };
2097 */
2098 static char mysql41_rsname[] = "*mysql41-check";
2099 static char mysql41_req[] = {
2100 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2101 "00820000" /* client capabilities */
2102 "00800001" /* max packet */
2103 "21" /* character set (UTF-8) */
2104 "000000000000000000000000" /* 23 bytes, al zeroes */
2105 "0000000000000000000000"
2106 "%[var(check.username),hex]00" /* the username */
2107 "00" /* filler (always 0x00) */
2108 "010000" /* packet length*/
2109 "00" /* sequence ID */
2110 "01" /* COM_QUIT command */
2111 };
2112
2113 struct tcpcheck_ruleset *rs = NULL;
2114 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2115 struct tcpcheck_rule *chk;
2116 struct tcpcheck_var *var = NULL;
2117 char *mysql_rsname = "*mysql-check";
2118 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
2119 int index = 0, err_code = 0;
2120
2121 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2122 err_code |= ERR_WARN;
2123
2124 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2125 goto out;
2126
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002127 curpx->options2 &= ~PR_O2_CHK_ANY;
2128 curpx->options2 |= PR_O2_TCPCHK_CHK;
2129
2130 free_tcpcheck_vars(&rules->preset_vars);
2131 rules->list = NULL;
2132 rules->flags = 0;
2133
2134 cur_arg += 2;
2135 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002136 int packetlen, userlen;
2137
2138 if (strcmp(args[cur_arg], "user") != 0) {
2139 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
2140 file, line, args[0], args[1], args[cur_arg]);
2141 goto error;
2142 }
2143
2144 if (*(args[cur_arg+1]) == 0) {
2145 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
2146 file, line, args[0], args[1], args[cur_arg]);
2147 goto error;
2148 }
2149
2150 hdr = calloc(4, sizeof(*hdr));
2151 user = strdup(args[cur_arg+1]);
2152 userlen = strlen(args[cur_arg+1]);
2153
2154 if (hdr == NULL || user == NULL) {
2155 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2156 goto error;
2157 }
2158
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002159 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002160 packetlen = userlen + 7 + 27;
2161 mysql_req = mysql41_req;
2162 mysql_rsname = mysql41_rsname;
2163 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002164 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002165 packetlen = userlen + 7;
2166 mysql_req = mysql40_req;
2167 mysql_rsname = mysql40_rsname;
2168 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002169 else {
2170 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
2171 file, line, args[cur_arg], args[cur_arg+2]);
2172 goto error;
2173 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002174
2175 hdr[0] = (unsigned char)(packetlen & 0xff);
2176 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
2177 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
2178 hdr[3] = 1;
2179
Christopher Fauletb61caf42020-04-21 10:57:42 +02002180 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002181 if (var == NULL) {
2182 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2183 goto error;
2184 }
2185 var->data.type = SMP_T_STR;
2186 var->data.u.str.area = hdr;
2187 var->data.u.str.data = 4;
2188 LIST_INIT(&var->list);
2189 LIST_ADDQ(&rules->preset_vars, &var->list);
2190 hdr = NULL;
2191 var = NULL;
2192
Christopher Fauletb61caf42020-04-21 10:57:42 +02002193 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002194 if (var == NULL) {
2195 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2196 goto error;
2197 }
2198 var->data.type = SMP_T_STR;
2199 var->data.u.str.area = user;
2200 var->data.u.str.data = strlen(user);
2201 LIST_INIT(&var->list);
2202 LIST_ADDQ(&rules->preset_vars, &var->list);
2203 user = NULL;
2204 var = NULL;
2205 }
2206
Christopher Faulet61cc8522020-04-20 14:54:42 +02002207 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002208 if (rs)
2209 goto ruleset_found;
2210
Christopher Faulet61cc8522020-04-20 14:54:42 +02002211 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002212 if (rs == NULL) {
2213 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2214 goto error;
2215 }
2216
2217 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2218 1, curpx, &rs->rules, file, line, &errmsg);
2219 if (!chk) {
2220 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2221 goto error;
2222 }
2223 chk->index = index++;
2224 LIST_ADDQ(&rs->rules, &chk->list);
2225
2226 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002227 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002228 1, curpx, &rs->rules, file, line, &errmsg);
2229 if (!chk) {
2230 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2231 goto error;
2232 }
2233 chk->index = index++;
2234 LIST_ADDQ(&rs->rules, &chk->list);
2235 }
2236
2237 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002238 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002239 if (!chk) {
2240 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2241 goto error;
2242 }
2243 chk->expect.custom = tcpcheck_mysql_expect_iniths;
2244 chk->index = index++;
2245 LIST_ADDQ(&rs->rules, &chk->list);
2246
2247 if (mysql_req) {
2248 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002249 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002250 if (!chk) {
2251 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2252 goto error;
2253 }
2254 chk->expect.custom = tcpcheck_mysql_expect_ok;
2255 chk->index = index++;
2256 LIST_ADDQ(&rs->rules, &chk->list);
2257 }
2258
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002259 ruleset_found:
2260 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002261 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002262
2263 out:
2264 free(errmsg);
2265 return err_code;
2266
2267 error:
2268 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02002269 free(user);
2270 free(var);
2271 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002272 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02002273 err_code |= ERR_ALERT | ERR_FATAL;
2274 goto out;
2275}
2276
Christopher Faulet1997eca2020-04-03 23:13:50 +02002277int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2278 const char *file, int line)
2279{
2280 static char *ldap_req = "300C020101600702010304008000";
2281
2282 struct tcpcheck_ruleset *rs = NULL;
2283 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2284 struct tcpcheck_rule *chk;
2285 char *errmsg = NULL;
2286 int err_code = 0;
2287
2288 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2289 err_code |= ERR_WARN;
2290
2291 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2292 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002293
2294 curpx->options2 &= ~PR_O2_CHK_ANY;
2295 curpx->options2 |= PR_O2_TCPCHK_CHK;
2296
2297 free_tcpcheck_vars(&rules->preset_vars);
2298 rules->list = NULL;
2299 rules->flags = 0;
2300
Christopher Faulet61cc8522020-04-20 14:54:42 +02002301 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002302 if (rs)
2303 goto ruleset_found;
2304
Christopher Faulet61cc8522020-04-20 14:54:42 +02002305 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002306 if (rs == NULL) {
2307 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2308 goto error;
2309 }
2310
2311 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
2312 1, curpx, &rs->rules, file, line, &errmsg);
2313 if (!chk) {
2314 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2315 goto error;
2316 }
2317 chk->index = 0;
2318 LIST_ADDQ(&rs->rules, &chk->list);
2319
2320 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
2321 "min-recv", "14",
2322 "on-error", "Not LDAPv3 protocol",
2323 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002324 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002325 if (!chk) {
2326 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2327 goto error;
2328 }
2329 chk->index = 1;
2330 LIST_ADDQ(&rs->rules, &chk->list);
2331
2332 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002333 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002334 if (!chk) {
2335 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2336 goto error;
2337 }
2338 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
2339 chk->index = 2;
2340 LIST_ADDQ(&rs->rules, &chk->list);
2341
Christopher Faulet1997eca2020-04-03 23:13:50 +02002342 ruleset_found:
2343 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002344 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002345
2346 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02002347 free(errmsg);
2348 return err_code;
2349
2350 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002351 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002352 err_code |= ERR_ALERT | ERR_FATAL;
2353 goto out;
2354}
2355
2356int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2357 const char *file, int line)
2358{
2359 struct tcpcheck_ruleset *rs = NULL;
2360 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2361 struct tcpcheck_rule *chk;
2362 char *spop_req = NULL;
2363 char *errmsg = NULL;
2364 int spop_len = 0, err_code = 0;
2365
2366 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2367 err_code |= ERR_WARN;
2368
2369 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2370 goto out;
2371
Christopher Faulet267b01b2020-04-04 10:27:09 +02002372 curpx->options2 &= ~PR_O2_CHK_ANY;
2373 curpx->options2 |= PR_O2_TCPCHK_CHK;
2374
2375 free_tcpcheck_vars(&rules->preset_vars);
2376 rules->list = NULL;
2377 rules->flags = 0;
2378
2379
Christopher Faulet61cc8522020-04-20 14:54:42 +02002380 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002381 if (rs)
2382 goto ruleset_found;
2383
Christopher Faulet61cc8522020-04-20 14:54:42 +02002384 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002385 if (rs == NULL) {
2386 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2387 goto error;
2388 }
2389
2390 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
2391 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2392 goto error;
2393 }
2394 chunk_reset(&trash);
2395 dump_binary(&trash, spop_req, spop_len);
2396 trash.area[trash.data] = '\0';
2397
2398 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
2399 1, curpx, &rs->rules, file, line, &errmsg);
2400 if (!chk) {
2401 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2402 goto error;
2403 }
2404 chk->index = 0;
2405 LIST_ADDQ(&rs->rules, &chk->list);
2406
2407 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002408 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002409 if (!chk) {
2410 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2411 goto error;
2412 }
2413 chk->expect.custom = tcpcheck_spop_expect_agenthello;
2414 chk->index = 1;
2415 LIST_ADDQ(&rs->rules, &chk->list);
2416
Christopher Faulet267b01b2020-04-04 10:27:09 +02002417 ruleset_found:
2418 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002419 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002420
2421 out:
2422 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002423 free(errmsg);
2424 return err_code;
2425
2426 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002427 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002428 err_code |= ERR_ALERT | ERR_FATAL;
2429 goto out;
2430}
Christopher Fauletce355072020-04-02 11:44:39 +02002431
Christopher Faulete5870d82020-04-15 11:32:03 +02002432
2433struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
2434{
2435 struct tcpcheck_rule *chk = NULL;
2436 struct tcpcheck_http_hdr *hdr = NULL;
2437 char *meth = NULL, *uri = NULL, *vsn = NULL;
2438 char *hdrs, *body;
2439
2440 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
2441 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
2442 if (hdrs == body)
2443 hdrs = NULL;
2444 if (hdrs) {
2445 *hdrs = '\0';
2446 hdrs +=2;
2447 }
2448 if (body) {
2449 *body = '\0';
2450 body += 4;
2451 }
2452 if (hdrs || body) {
2453 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
2454 " Please, consider to use 'http-check send' directive instead.");
2455 }
2456
2457 chk = calloc(1, sizeof(*chk));
2458 if (!chk) {
2459 memprintf(errmsg, "out of memory");
2460 goto error;
2461 }
2462 chk->action = TCPCHK_ACT_SEND;
2463 chk->send.type = TCPCHK_SEND_HTTP;
2464 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
2465 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
2466 LIST_INIT(&chk->send.http.hdrs);
2467
2468 /* Copy the method, uri and version */
2469 if (*args[cur_arg]) {
2470 if (!*args[cur_arg+1])
2471 uri = args[cur_arg];
2472 else
2473 meth = args[cur_arg];
2474 }
2475 if (*args[cur_arg+1])
2476 uri = args[cur_arg+1];
2477 if (*args[cur_arg+2])
2478 vsn = args[cur_arg+2];
2479
2480 if (meth) {
2481 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
2482 chk->send.http.meth.str.area = strdup(meth);
2483 chk->send.http.meth.str.data = strlen(meth);
2484 if (!chk->send.http.meth.str.area) {
2485 memprintf(errmsg, "out of memory");
2486 goto error;
2487 }
2488 }
2489 if (uri) {
2490 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002491 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002492 memprintf(errmsg, "out of memory");
2493 goto error;
2494 }
2495 }
2496 if (vsn) {
2497 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002498 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002499 memprintf(errmsg, "out of memory");
2500 goto error;
2501 }
2502 }
2503
2504 /* Copy the header */
2505 if (hdrs) {
2506 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
2507 struct h1m h1m;
2508 int i, ret;
2509
2510 /* Build and parse the request */
2511 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
2512
2513 h1m.flags = H1_MF_HDRS_ONLY;
2514 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
2515 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
2516 &h1m, NULL);
2517 if (ret <= 0) {
2518 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
2519 goto error;
2520 }
2521
Christopher Fauletb61caf42020-04-21 10:57:42 +02002522 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002523 hdr = calloc(1, sizeof(*hdr));
2524 if (!hdr) {
2525 memprintf(errmsg, "out of memory");
2526 goto error;
2527 }
2528 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002529 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02002530 if (!hdr->name.ptr) {
2531 memprintf(errmsg, "out of memory");
2532 goto error;
2533 }
2534
Christopher Fauletb61caf42020-04-21 10:57:42 +02002535 ist0(tmp_hdrs[i].v);
2536 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 +02002537 goto error;
2538 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
2539 }
2540 }
2541
2542 /* Copy the body */
2543 if (body) {
2544 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002545 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002546 memprintf(errmsg, "out of memory");
2547 goto error;
2548 }
2549 }
2550
2551 return chk;
2552
2553 error:
2554 free_tcpcheck_http_hdr(hdr);
2555 free_tcpcheck(chk, 0);
2556 return NULL;
2557}
2558
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002559int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2560 const char *file, int line)
2561{
Christopher Faulete5870d82020-04-15 11:32:03 +02002562 struct tcpcheck_ruleset *rs = NULL;
2563 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2564 struct tcpcheck_rule *chk;
2565 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002566 int err_code = 0;
2567
2568 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2569 err_code |= ERR_WARN;
2570
2571 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2572 goto out;
2573
Christopher Faulete5870d82020-04-15 11:32:03 +02002574 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
2575 if (!chk) {
2576 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2577 goto error;
2578 }
2579 if (errmsg) {
2580 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
2581 err_code |= ERR_WARN;
2582 free(errmsg);
2583 errmsg = NULL;
2584 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002585
Christopher Faulete5870d82020-04-15 11:32:03 +02002586 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002587 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02002588 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002589
Christopher Faulete5870d82020-04-15 11:32:03 +02002590 free_tcpcheck_vars(&rules->preset_vars);
2591 rules->list = NULL;
2592 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002593
Christopher Faulete5870d82020-04-15 11:32:03 +02002594 /* Deduce the ruleset name from the proxy info */
2595 chunk_printf(&trash, "*http-check-%s_%s-%d",
2596 ((curpx == defpx) ? "defaults" : curpx->id),
2597 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002598
Christopher Faulet61cc8522020-04-20 14:54:42 +02002599 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002600 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002601 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002602 if (rs == NULL) {
2603 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2604 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002605 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002606 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002607
Christopher Faulete5870d82020-04-15 11:32:03 +02002608 rules->list = &rs->rules;
2609 rules->flags |= TCPCHK_RULES_HTTP_CHK;
2610 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
2611 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2612 rules->list = NULL;
2613 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002614 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002615
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002616 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02002617 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002618 return err_code;
2619
2620 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002621 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02002622 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002623 err_code |= ERR_ALERT | ERR_FATAL;
2624 goto out;
2625}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002626
Christopher Fauletce8111e2020-04-06 15:04:11 +02002627/* Parse the "addr" server keyword */
2628static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2629 char **errmsg)
2630{
2631 struct sockaddr_storage *sk;
2632 struct protocol *proto;
2633 int port1, port2, err_code = 0;
2634
2635
2636 if (!*args[*cur_arg+1]) {
2637 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
2638 goto error;
2639 }
2640
2641 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
2642 if (!sk) {
2643 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
2644 goto error;
2645 }
2646
2647 proto = protocol_by_family(sk->ss_family);
2648 if (!proto || !proto->connect) {
2649 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
2650 args[*cur_arg], args[*cur_arg+1]);
2651 goto error;
2652 }
2653
2654 if (port1 != port2) {
2655 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
2656 args[*cur_arg], args[*cur_arg+1]);
2657 goto error;
2658 }
2659
2660 srv->check.addr = srv->agent.addr = *sk;
2661 srv->flags |= SRV_F_CHECKADDR;
2662 srv->flags |= SRV_F_AGENTADDR;
2663
2664 out:
2665 return err_code;
2666
2667 error:
2668 err_code |= ERR_ALERT | ERR_FATAL;
2669 goto out;
2670}
2671
2672
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002673/* Parse the "agent-addr" server keyword */
2674static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2675 char **errmsg)
2676{
2677 int err_code = 0;
2678
2679 if (!*(args[*cur_arg+1])) {
2680 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
2681 goto error;
2682 }
2683 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
2684 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
2685 goto error;
2686 }
2687
2688 out:
2689 return err_code;
2690
2691 error:
2692 err_code |= ERR_ALERT | ERR_FATAL;
2693 goto out;
2694}
2695
2696/* Parse the "agent-check" server keyword */
2697static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2698 char **errmsg)
2699{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002700 struct tcpcheck_ruleset *rs = NULL;
2701 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2702 struct tcpcheck_rule *chk;
2703 int err_code = 0;
2704
2705 if (srv->do_agent)
2706 goto out;
2707
2708 if (!rules) {
2709 rules = calloc(1, sizeof(*rules));
2710 if (!rules) {
2711 memprintf(errmsg, "out of memory.");
2712 goto error;
2713 }
2714 LIST_INIT(&rules->preset_vars);
2715 srv->agent.tcpcheck_rules = rules;
2716 }
2717 rules->list = NULL;
2718 rules->flags = 0;
2719
Christopher Faulet61cc8522020-04-20 14:54:42 +02002720 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002721 if (rs)
2722 goto ruleset_found;
2723
Christopher Faulet61cc8522020-04-20 14:54:42 +02002724 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002725 if (rs == NULL) {
2726 memprintf(errmsg, "out of memory.");
2727 goto error;
2728 }
2729
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002730 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002731 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
2732 if (!chk) {
2733 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2734 goto error;
2735 }
2736 chk->index = 0;
2737 LIST_ADDQ(&rs->rules, &chk->list);
2738
2739 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002740 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
2741 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002742 if (!chk) {
2743 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2744 goto error;
2745 }
2746 chk->expect.custom = tcpcheck_agent_expect_reply;
2747 chk->index = 1;
2748 LIST_ADDQ(&rs->rules, &chk->list);
2749
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002750 ruleset_found:
2751 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002752 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002753 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002754
2755 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002756 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002757
2758 error:
2759 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002760 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002761 err_code |= ERR_ALERT | ERR_FATAL;
2762 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002763}
2764
2765/* Parse the "agent-inter" server keyword */
2766static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2767 char **errmsg)
2768{
2769 const char *err = NULL;
2770 unsigned int delay;
2771 int err_code = 0;
2772
2773 if (!*(args[*cur_arg+1])) {
2774 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
2775 goto error;
2776 }
2777
2778 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
2779 if (err == PARSE_TIME_OVER) {
2780 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
2781 args[*cur_arg+1], args[*cur_arg], srv->id);
2782 goto error;
2783 }
2784 else if (err == PARSE_TIME_UNDER) {
2785 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
2786 args[*cur_arg+1], args[*cur_arg], srv->id);
2787 goto error;
2788 }
2789 else if (err) {
2790 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
2791 *err, srv->id);
2792 goto error;
2793 }
2794 if (delay <= 0) {
2795 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
2796 delay, args[*cur_arg], srv->id);
2797 goto error;
2798 }
2799 srv->agent.inter = delay;
2800
2801 out:
2802 return err_code;
2803
2804 error:
2805 err_code |= ERR_ALERT | ERR_FATAL;
2806 goto out;
2807}
2808
2809/* Parse the "agent-port" server keyword */
2810static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2811 char **errmsg)
2812{
2813 int err_code = 0;
2814
2815 if (!*(args[*cur_arg+1])) {
2816 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
2817 goto error;
2818 }
2819
2820 global.maxsock++;
2821 srv->agent.port = atol(args[*cur_arg+1]);
2822
2823 out:
2824 return err_code;
2825
2826 error:
2827 err_code |= ERR_ALERT | ERR_FATAL;
2828 goto out;
2829}
2830
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002831int set_srv_agent_send(struct server *srv, const char *send)
2832{
2833 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2834 struct tcpcheck_var *var = NULL;
2835 char *str;
2836
2837 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002838 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002839 if (str == NULL || var == NULL)
2840 goto error;
2841
2842 free_tcpcheck_vars(&rules->preset_vars);
2843
2844 var->data.type = SMP_T_STR;
2845 var->data.u.str.area = str;
2846 var->data.u.str.data = strlen(str);
2847 LIST_INIT(&var->list);
2848 LIST_ADDQ(&rules->preset_vars, &var->list);
2849
2850 return 1;
2851
2852 error:
2853 free(str);
2854 free(var);
2855 return 0;
2856}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002857
2858/* Parse the "agent-send" server keyword */
2859static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2860 char **errmsg)
2861{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002862 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002863 int err_code = 0;
2864
2865 if (!*(args[*cur_arg+1])) {
2866 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
2867 goto error;
2868 }
2869
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002870 if (!rules) {
2871 rules = calloc(1, sizeof(*rules));
2872 if (!rules) {
2873 memprintf(errmsg, "out of memory.");
2874 goto error;
2875 }
2876 LIST_INIT(&rules->preset_vars);
2877 srv->agent.tcpcheck_rules = rules;
2878 }
2879
2880 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002881 memprintf(errmsg, "out of memory.");
2882 goto error;
2883 }
2884
2885 out:
2886 return err_code;
2887
2888 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002889 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002890 err_code |= ERR_ALERT | ERR_FATAL;
2891 goto out;
2892}
2893
2894/* Parse the "no-agent-send" server keyword */
2895static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2896 char **errmsg)
2897{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002898 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002899 return 0;
2900}
2901
Christopher Fauletce8111e2020-04-06 15:04:11 +02002902/* Parse the "check" server keyword */
2903static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2904 char **errmsg)
2905{
2906 srv->do_check = 1;
2907 return 0;
2908}
2909
2910/* Parse the "check-send-proxy" server keyword */
2911static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2912 char **errmsg)
2913{
2914 srv->check.send_proxy = 1;
2915 return 0;
2916}
2917
2918/* Parse the "check-via-socks4" server keyword */
2919static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2920 char **errmsg)
2921{
2922 srv->check.via_socks4 = 1;
2923 return 0;
2924}
2925
2926/* Parse the "no-check" server keyword */
2927static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2928 char **errmsg)
2929{
2930 deinit_srv_check(srv);
2931 return 0;
2932}
2933
2934/* Parse the "no-check-send-proxy" server keyword */
2935static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2936 char **errmsg)
2937{
2938 srv->check.send_proxy = 0;
2939 return 0;
2940}
2941
Christopher Fauletedc6ed92020-04-23 16:27:59 +02002942/* parse the "check-proto" server keyword */
2943static int srv_parse_check_proto(char **args, int *cur_arg,
2944 struct proxy *px, struct server *newsrv, char **err)
2945{
2946 int err_code = 0;
2947
2948 if (!*args[*cur_arg + 1]) {
2949 memprintf(err, "'%s' : missing value", args[*cur_arg]);
2950 goto error;
2951 }
2952 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
2953 if (!newsrv->check.mux_proto) {
2954 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
2955 goto error;
2956 }
2957
2958 out:
2959 return err_code;
2960
2961 error:
2962 err_code |= ERR_ALERT | ERR_FATAL;
2963 goto out;
2964}
2965
2966
Christopher Fauletce8111e2020-04-06 15:04:11 +02002967/* Parse the "rise" server keyword */
2968static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2969 char **errmsg)
2970{
2971 int err_code = 0;
2972
2973 if (!*args[*cur_arg + 1]) {
2974 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
2975 goto error;
2976 }
2977
2978 srv->check.rise = atol(args[*cur_arg+1]);
2979 if (srv->check.rise <= 0) {
2980 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
2981 goto error;
2982 }
2983
2984 if (srv->check.health)
2985 srv->check.health = srv->check.rise;
2986
2987 out:
2988 return err_code;
2989
2990 error:
2991 deinit_srv_agent_check(srv);
2992 err_code |= ERR_ALERT | ERR_FATAL;
2993 goto out;
2994 return 0;
2995}
2996
2997/* Parse the "fall" server keyword */
2998static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2999 char **errmsg)
3000{
3001 int err_code = 0;
3002
3003 if (!*args[*cur_arg + 1]) {
3004 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3005 goto error;
3006 }
3007
3008 srv->check.fall = atol(args[*cur_arg+1]);
3009 if (srv->check.fall <= 0) {
3010 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3011 goto error;
3012 }
3013
3014 out:
3015 return err_code;
3016
3017 error:
3018 deinit_srv_agent_check(srv);
3019 err_code |= ERR_ALERT | ERR_FATAL;
3020 goto out;
3021 return 0;
3022}
3023
3024/* Parse the "inter" server keyword */
3025static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3026 char **errmsg)
3027{
3028 const char *err = NULL;
3029 unsigned int delay;
3030 int err_code = 0;
3031
3032 if (!*(args[*cur_arg+1])) {
3033 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3034 goto error;
3035 }
3036
3037 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3038 if (err == PARSE_TIME_OVER) {
3039 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3040 args[*cur_arg+1], args[*cur_arg], srv->id);
3041 goto error;
3042 }
3043 else if (err == PARSE_TIME_UNDER) {
3044 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3045 args[*cur_arg+1], args[*cur_arg], srv->id);
3046 goto error;
3047 }
3048 else if (err) {
3049 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3050 *err, srv->id);
3051 goto error;
3052 }
3053 if (delay <= 0) {
3054 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3055 delay, args[*cur_arg], srv->id);
3056 goto error;
3057 }
3058 srv->check.inter = delay;
3059
3060 out:
3061 return err_code;
3062
3063 error:
3064 err_code |= ERR_ALERT | ERR_FATAL;
3065 goto out;
3066}
3067
3068
3069/* Parse the "fastinter" server keyword */
3070static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3071 char **errmsg)
3072{
3073 const char *err = NULL;
3074 unsigned int delay;
3075 int err_code = 0;
3076
3077 if (!*(args[*cur_arg+1])) {
3078 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3079 goto error;
3080 }
3081
3082 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3083 if (err == PARSE_TIME_OVER) {
3084 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3085 args[*cur_arg+1], args[*cur_arg], srv->id);
3086 goto error;
3087 }
3088 else if (err == PARSE_TIME_UNDER) {
3089 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3090 args[*cur_arg+1], args[*cur_arg], srv->id);
3091 goto error;
3092 }
3093 else if (err) {
3094 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3095 *err, srv->id);
3096 goto error;
3097 }
3098 if (delay <= 0) {
3099 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3100 delay, args[*cur_arg], srv->id);
3101 goto error;
3102 }
3103 srv->check.fastinter = delay;
3104
3105 out:
3106 return err_code;
3107
3108 error:
3109 err_code |= ERR_ALERT | ERR_FATAL;
3110 goto out;
3111}
3112
3113
3114/* Parse the "downinter" server keyword */
3115static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3116 char **errmsg)
3117{
3118 const char *err = NULL;
3119 unsigned int delay;
3120 int err_code = 0;
3121
3122 if (!*(args[*cur_arg+1])) {
3123 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3124 goto error;
3125 }
3126
3127 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3128 if (err == PARSE_TIME_OVER) {
3129 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3130 args[*cur_arg+1], args[*cur_arg], srv->id);
3131 goto error;
3132 }
3133 else if (err == PARSE_TIME_UNDER) {
3134 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3135 args[*cur_arg+1], args[*cur_arg], srv->id);
3136 goto error;
3137 }
3138 else if (err) {
3139 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3140 *err, srv->id);
3141 goto error;
3142 }
3143 if (delay <= 0) {
3144 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3145 delay, args[*cur_arg], srv->id);
3146 goto error;
3147 }
3148 srv->check.downinter = delay;
3149
3150 out:
3151 return err_code;
3152
3153 error:
3154 err_code |= ERR_ALERT | ERR_FATAL;
3155 goto out;
3156}
3157
3158/* Parse the "port" server keyword */
3159static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3160 char **errmsg)
3161{
3162 int err_code = 0;
3163
3164 if (!*(args[*cur_arg+1])) {
3165 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
3166 goto error;
3167 }
3168
3169 global.maxsock++;
3170 srv->check.port = atol(args[*cur_arg+1]);
3171 srv->flags |= SRV_F_CHECKPORT;
3172
3173 out:
3174 return err_code;
3175
3176 error:
3177 err_code |= ERR_ALERT | ERR_FATAL;
3178 goto out;
3179}
3180
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003181static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02003182 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003183 { 0, NULL, NULL },
3184}};
3185
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003186static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02003187 { "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 +02003188 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
3189 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
3190 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
3191 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
3192 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003193 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003194 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003195 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
3196 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003197 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003198 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
3199 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
3200 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
3201 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
3202 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
3203 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
3204 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
3205 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003206 { NULL, NULL, 0 },
3207}};
3208
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003209INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003210INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003211
Willy Tarreaubd741542010-03-16 18:46:54 +01003212/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02003213 * Local variables:
3214 * c-indent-level: 8
3215 * c-basic-offset: 8
3216 * End:
3217 */