blob: c63194656ba2abb7104512dc153c9037f85dc654 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
Willy Tarreaud825eef2007-05-12 22:35:00 +02004 * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
Willy Tarreaubaaee002006-06-26 02:48:02 +02005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <errno.h>
14#include <fcntl.h>
15#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020016#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020017#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020018#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020019#include <unistd.h>
20#include <sys/socket.h>
21#include <netinet/in.h>
22#include <arpa/inet.h>
23
Willy Tarreau2dd0d472006-06-29 17:53:05 +020024#include <common/compat.h>
25#include <common/config.h>
26#include <common/mini-clist.h>
Willy Tarreau83749182007-04-15 20:56:27 +020027#include <common/standard.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020028#include <common/time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020029
30#include <types/global.h>
31#include <types/polling.h>
32#include <types/proxy.h>
33#include <types/session.h>
34
35#include <proto/backend.h>
36#include <proto/fd.h>
37#include <proto/log.h>
38#include <proto/queue.h>
Willy Tarreau3d300592007-03-18 18:34:41 +010039#include <proto/proto_http.h>
Willy Tarreau2b5652f2006-12-31 17:46:05 +010040#include <proto/proxy.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020041#include <proto/server.h>
42#include <proto/task.h>
43
Willy Tarreau163c5322006-11-14 16:18:41 +010044#ifdef CONFIG_HAP_CTTPROXY
45#include <import/ip_tproxy.h>
46#endif
47
Willy Tarreau48494c02007-11-30 10:41:39 +010048/* sends a log message when a backend goes down, and also sets last
49 * change date.
50 */
51static void set_backend_down(struct proxy *be)
52{
53 be->last_change = now.tv_sec;
54 be->down_trans++;
55
56 Alert("%s '%s' has no server available!\n", proxy_type_str(be), be->id);
57 send_log(be, LOG_EMERG, "%s %s has no server available!\n", proxy_type_str(be), be->id);
58}
59
60/* Redistribute pending connections when a server goes down. The number of
61 * connections redistributed is returned.
62 */
63static int redistribute_pending(struct server *s)
64{
65 struct pendconn *pc, *pc_bck, *pc_end;
66 int xferred = 0;
67
68 FOREACH_ITEM_SAFE(pc, pc_bck, &s->pendconns, pc_end, struct pendconn *, list) {
69 struct session *sess = pc->sess;
70 if (sess->be->options & PR_O_REDISP) {
71 /* The REDISP option was specified. We will ignore
72 * cookie and force to balance or use the dispatcher.
73 */
Krzysztof Piotr Oledzki25b501a2008-01-06 16:36:16 +010074
75 sess->srv->redispatches++;
76 sess->be->redispatches++;
77
Willy Tarreau48494c02007-11-30 10:41:39 +010078 sess->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
Krzysztof Piotr Oledzki25b501a2008-01-06 16:36:16 +010079 sess->flags |= SN_REDISP;
80
Willy Tarreau48494c02007-11-30 10:41:39 +010081 sess->srv = NULL; /* it's left to the dispatcher to choose a server */
82 http_flush_cookie_flags(&sess->txn);
83 pendconn_free(pc);
84 task_wakeup(sess->task);
85 xferred++;
86 }
87 }
88 return xferred;
89}
90
91/* Check for pending connections at the backend, and assign some of them to
92 * the server coming up. The server's weight is checked before being assigned
93 * connections it may not be able to handle. The total number of transferred
94 * connections is returned.
95 */
96static int check_for_pending(struct server *s)
97{
98 int xferred;
99
100 if (!s->eweight)
101 return 0;
102
103 for (xferred = 0; !s->maxconn || xferred < srv_dynamic_maxconn(s); xferred++) {
104 struct session *sess;
105 struct pendconn *p;
106
107 p = pendconn_from_px(s->proxy);
108 if (!p)
109 break;
110 p->sess->srv = s;
111 sess = p->sess;
112 pendconn_free(p);
113 task_wakeup(sess->task);
114 }
115 return xferred;
116}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200117
118/* Sets server <s> down, notifies by all available means, recounts the
119 * remaining servers on the proxy and transfers queued sessions whenever
Willy Tarreau5af3a692007-07-24 23:32:33 +0200120 * possible to other servers. It automatically recomputes the number of
121 * servers, but not the map.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200122 */
Willy Tarreau83749182007-04-15 20:56:27 +0200123static void set_server_down(struct server *s)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200124{
Willy Tarreaubaaee002006-06-26 02:48:02 +0200125 int xferred;
126
Willy Tarreaubaaee002006-06-26 02:48:02 +0200127 if (s->health == s->rise) {
Willy Tarreau48494c02007-11-30 10:41:39 +0100128 int srv_was_paused = s->state & SRV_GOINGDOWN;
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200129
130 s->last_change = now.tv_sec;
Willy Tarreau48494c02007-11-30 10:41:39 +0100131 s->state &= ~(SRV_RUNNING | SRV_GOINGDOWN);
Willy Tarreaub625a082007-11-26 01:15:43 +0100132 s->proxy->lbprm.set_server_status_down(s);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200133
134 /* we might have sessions queued on this server and waiting for
135 * a connection. Those which are redispatchable will be queued
136 * to another server or to the proxy itself.
137 */
Willy Tarreau48494c02007-11-30 10:41:39 +0100138 xferred = redistribute_pending(s);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200139 sprintf(trash, "%sServer %s/%s is DOWN. %d active and %d backup servers left.%s"
140 " %d sessions active, %d requeued, %d remaining in queue.\n",
141 s->state & SRV_BACKUP ? "Backup " : "",
142 s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
143 (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
144 s->cur_sess, xferred, s->nbpend);
145
146 Warning("%s", trash);
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200147
Willy Tarreau48494c02007-11-30 10:41:39 +0100148 /* we don't send an alert if the server was previously paused */
149 if (srv_was_paused)
150 send_log(s->proxy, LOG_NOTICE, "%s", trash);
151 else
152 send_log(s->proxy, LOG_ALERT, "%s", trash);
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200153
Willy Tarreau48494c02007-11-30 10:41:39 +0100154 if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0)
155 set_backend_down(s->proxy);
156
Willy Tarreaubaaee002006-06-26 02:48:02 +0200157 s->down_trans++;
158 }
159 s->health = 0; /* failure */
160}
161
162
163/*
164 * This function is used only for server health-checks. It handles
165 * the connection acknowledgement. If the proxy requires HTTP health-checks,
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100166 * it sends the request. In other cases, it fills s->result with SRV_CHK_*.
Willy Tarreau83749182007-04-15 20:56:27 +0200167 * The function itself returns 0 if it needs some polling before being called
168 * again, otherwise 1.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200169 */
Willy Tarreau83749182007-04-15 20:56:27 +0200170static int event_srv_chk_w(int fd)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200171{
Willy Tarreau6996e152007-04-30 14:37:43 +0200172 __label__ out_wakeup, out_nowake, out_poll, out_error;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200173 struct task *t = fdtab[fd].owner;
174 struct server *s = t->context;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200175
Willy Tarreau6996e152007-04-30 14:37:43 +0200176 if (unlikely(fdtab[fd].state == FD_STERROR || (fdtab[fd].ev & FD_POLL_ERR)))
177 goto out_error;
178
179 /* here, we know that the connection is established */
Willy Tarreau83749182007-04-15 20:56:27 +0200180
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100181 if (!(s->result & SRV_CHK_ERROR)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +0200182 /* we don't want to mark 'UP' a server on which we detected an error earlier */
Willy Tarreauf3c69202006-07-09 16:42:34 +0200183 if ((s->proxy->options & PR_O_HTTP_CHK) ||
Willy Tarreau23677902007-05-08 23:50:35 +0200184 (s->proxy->options & PR_O_SSL3_CHK) ||
185 (s->proxy->options & PR_O_SMTP_CHK)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +0200186 int ret;
Willy Tarreauf3c69202006-07-09 16:42:34 +0200187 /* we want to check if this host replies to HTTP or SSLv3 requests
Willy Tarreaubaaee002006-06-26 02:48:02 +0200188 * so we'll send the request, and won't wake the checker up now.
189 */
Willy Tarreauf3c69202006-07-09 16:42:34 +0200190
191 if (s->proxy->options & PR_O_SSL3_CHK) {
192 /* SSL requires that we put Unix time in the request */
193 int gmt_time = htonl(now.tv_sec);
194 memcpy(s->proxy->check_req + 11, &gmt_time, 4);
195 }
196
Willy Tarreaubaaee002006-06-26 02:48:02 +0200197#ifndef MSG_NOSIGNAL
198 ret = send(fd, s->proxy->check_req, s->proxy->check_len, MSG_DONTWAIT);
199#else
200 ret = send(fd, s->proxy->check_req, s->proxy->check_len, MSG_DONTWAIT | MSG_NOSIGNAL);
201#endif
202 if (ret == s->proxy->check_len) {
Willy Tarreauf161a342007-04-08 16:59:42 +0200203 EV_FD_SET(fd, DIR_RD); /* prepare for reading reply */
Willy Tarreau83749182007-04-15 20:56:27 +0200204 goto out_nowake;
205 }
Willy Tarreau6996e152007-04-30 14:37:43 +0200206 else if (ret == 0 || errno == EAGAIN)
207 goto out_poll;
208 else
209 goto out_error;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200210 }
211 else {
Willy Tarreau6996e152007-04-30 14:37:43 +0200212 /* We have no data to send to check the connection, and
213 * getsockopt() will not inform us whether the connection
214 * is still pending. So we'll reuse connect() to check the
215 * state of the socket. This has the advantage of givig us
216 * the following info :
217 * - error
218 * - connecting (EALREADY, EINPROGRESS)
219 * - connected (EISCONN, 0)
220 */
221
222 struct sockaddr_in sa;
223
224 sa = (s->check_addr.sin_addr.s_addr) ? s->check_addr : s->addr;
225 sa.sin_port = htons(s->check_port);
226
227 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == 0)
228 errno = 0;
229
230 if (errno == EALREADY || errno == EINPROGRESS)
231 goto out_poll;
232
233 if (errno && errno != EISCONN)
234 goto out_error;
235
Willy Tarreaubaaee002006-06-26 02:48:02 +0200236 /* good TCP connection is enough */
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100237 s->result |= SRV_CHK_RUNNING;
Willy Tarreau6996e152007-04-30 14:37:43 +0200238 goto out_wakeup;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200239 }
240 }
Willy Tarreau83749182007-04-15 20:56:27 +0200241 out_wakeup:
Willy Tarreau96bcfd72007-04-29 10:41:56 +0200242 task_wakeup(t);
Willy Tarreau83749182007-04-15 20:56:27 +0200243 out_nowake:
244 EV_FD_CLR(fd, DIR_WR); /* nothing more to write */
245 fdtab[fd].ev &= ~FD_POLL_WR;
246 return 1;
Willy Tarreau6996e152007-04-30 14:37:43 +0200247 out_poll:
248 /* The connection is still pending. We'll have to poll it
249 * before attempting to go further. */
250 fdtab[fd].ev &= ~FD_POLL_WR;
251 return 0;
252 out_error:
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100253 s->result |= SRV_CHK_ERROR;
Willy Tarreau6996e152007-04-30 14:37:43 +0200254 fdtab[fd].state = FD_STERROR;
255 goto out_wakeup;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200256}
257
258
259/*
Willy Tarreauf3c69202006-07-09 16:42:34 +0200260 * This function is used only for server health-checks. It handles the server's
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100261 * reply to an HTTP request or SSL HELLO. It sets s->result to SRV_CHK_RUNNING
262 * if an HTTP server replies HTTP 2xx or 3xx (valid responses), if an SMTP
263 * server returns 2xx, or if an SSL server returns at least 5 bytes in response
264 * to an SSL HELLO (the principle is that this is enough to distinguish between
265 * an SSL server and a pure TCP relay). All other cases will set s->result to
266 * SRV_CHK_ERROR. The function returns 0 if it needs to be called again after
267 * some polling, otherwise non-zero..
Willy Tarreaubaaee002006-06-26 02:48:02 +0200268 */
Willy Tarreau83749182007-04-15 20:56:27 +0200269static int event_srv_chk_r(int fd)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200270{
Willy Tarreau83749182007-04-15 20:56:27 +0200271 __label__ out_wakeup;
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100272 int len;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200273 struct task *t = fdtab[fd].owner;
274 struct server *s = t->context;
275 int skerr;
276 socklen_t lskerr = sizeof(skerr);
277
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100278 len = -1;
Willy Tarreau83749182007-04-15 20:56:27 +0200279
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100280 if (unlikely((s->result & SRV_CHK_ERROR) ||
281 (fdtab[fd].state == FD_STERROR) ||
Willy Tarreau83749182007-04-15 20:56:27 +0200282 (fdtab[fd].ev & FD_POLL_ERR) ||
283 (getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == -1) ||
284 (skerr != 0))) {
285 /* in case of TCP only, this tells us if the connection failed */
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100286 s->result |= SRV_CHK_ERROR;
Willy Tarreau83749182007-04-15 20:56:27 +0200287 goto out_wakeup;
288 }
289
Willy Tarreaubaaee002006-06-26 02:48:02 +0200290#ifndef MSG_NOSIGNAL
Krzysztof Oledzki6b3f8b42007-10-11 18:41:08 +0200291 len = recv(fd, trash, sizeof(trash), 0);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200292#else
Willy Tarreau83749182007-04-15 20:56:27 +0200293 /* Warning! Linux returns EAGAIN on SO_ERROR if data are still available
294 * but the connection was closed on the remote end. Fortunately, recv still
295 * works correctly and we don't need to do the getsockopt() on linux.
296 */
Krzysztof Oledzki6b3f8b42007-10-11 18:41:08 +0200297 len = recv(fd, trash, sizeof(trash), MSG_NOSIGNAL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200298#endif
Willy Tarreau83749182007-04-15 20:56:27 +0200299 if (unlikely(len < 0 && errno == EAGAIN)) {
300 /* we want some polling to happen first */
301 fdtab[fd].ev &= ~FD_POLL_RD;
302 return 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200303 }
304
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100305 /* Note: the response will only be accepted if read at once */
306 if (s->proxy->options & PR_O_HTTP_CHK) {
307 /* Check if the server speaks HTTP 1.X */
308 if ((len < strlen("HTTP/1.0 000\r")) ||
309 (memcmp(trash, "HTTP/1.", 7) != 0)) {
310 s->result |= SRV_CHK_ERROR;
311 goto out_wakeup;
312 }
313
314 /* check the reply : HTTP/1.X 2xx and 3xx are OK */
315 if (trash[9] == '2' || trash[9] == '3')
316 s->result |= SRV_CHK_RUNNING;
Willy Tarreau48494c02007-11-30 10:41:39 +0100317 else if ((s->proxy->options & PR_O_DISABLE404) &&
318 (s->state & SRV_RUNNING) &&
319 (memcmp(&trash[9], "404", 3) == 0)) {
320 /* 404 may be accepted as "stopping" only if the server was up */
321 s->result |= SRV_CHK_RUNNING | SRV_CHK_DISABLE;
322 }
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100323 else
324 s->result |= SRV_CHK_ERROR;
325 }
326 else if (s->proxy->options & PR_O_SSL3_CHK) {
327 /* Check for SSLv3 alert or handshake */
328 if ((len >= 5) && (trash[0] == 0x15 || trash[0] == 0x16))
329 s->result |= SRV_CHK_RUNNING;
330 else
331 s->result |= SRV_CHK_ERROR;
Willy Tarreau6996e152007-04-30 14:37:43 +0200332 }
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100333 else if (s->proxy->options & PR_O_SMTP_CHK) {
334 /* Check for SMTP code 2xx (should be 250) */
335 if ((len >= 3) && (trash[0] == '2'))
336 s->result |= SRV_CHK_RUNNING;
337 else
338 s->result |= SRV_CHK_ERROR;
Willy Tarreau6996e152007-04-30 14:37:43 +0200339 }
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100340 else {
341 /* other checks are valid if the connection succeeded anyway */
342 s->result |= SRV_CHK_RUNNING;
Willy Tarreau23677902007-05-08 23:50:35 +0200343 }
Willy Tarreau83749182007-04-15 20:56:27 +0200344
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100345 out_wakeup:
346 if (s->result & SRV_CHK_ERROR)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200347 fdtab[fd].state = FD_STERROR;
348
Willy Tarreauf161a342007-04-08 16:59:42 +0200349 EV_FD_CLR(fd, DIR_RD);
Willy Tarreau96bcfd72007-04-29 10:41:56 +0200350 task_wakeup(t);
Willy Tarreau83749182007-04-15 20:56:27 +0200351 fdtab[fd].ev &= ~FD_POLL_RD;
352 return 1;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200353}
354
355/*
356 * manages a server health-check. Returns
357 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
358 */
Willy Tarreaud825eef2007-05-12 22:35:00 +0200359void process_chk(struct task *t, struct timeval *next)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200360{
Willy Tarreau7317eb52007-05-09 00:54:10 +0200361 __label__ new_chk, out;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200362 struct server *s = t->context;
363 struct sockaddr_in sa;
Willy Tarreau48494c02007-11-30 10:41:39 +0100364 int xferred;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200365 int fd;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +0200366 int rv;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200367
368 //fprintf(stderr, "process_chk: task=%p\n", t);
369
370 new_chk:
371 fd = s->curfd;
372 if (fd < 0) { /* no check currently running */
373 //fprintf(stderr, "process_chk: 2\n");
Willy Tarreaua8b55e32007-05-13 16:08:19 +0200374 if (!tv_isle(&t->expire, &now)) { /* not good time yet */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200375 task_queue(t); /* restore t to its place in the task list */
Willy Tarreaud825eef2007-05-12 22:35:00 +0200376 *next = t->expire;
Willy Tarreau7317eb52007-05-09 00:54:10 +0200377 goto out;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200378 }
379
380 /* we don't send any health-checks when the proxy is stopped or when
381 * the server should not be checked.
382 */
383 if (!(s->state & SRV_CHECKED) || s->proxy->state == PR_STSTOPPED) {
Willy Tarreaua8b55e32007-05-13 16:08:19 +0200384 while (tv_isle(&t->expire, &now))
Willy Tarreau42aae5c2007-04-29 17:43:56 +0200385 tv_ms_add(&t->expire, &t->expire, s->inter);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200386 task_queue(t); /* restore t to its place in the task list */
Willy Tarreaud825eef2007-05-12 22:35:00 +0200387 *next = t->expire;
Willy Tarreau7317eb52007-05-09 00:54:10 +0200388 goto out;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200389 }
390
391 /* we'll initiate a new check */
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100392 s->result = SRV_CHK_UNKNOWN; /* no result yet */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200393 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) {
394 if ((fd < global.maxsock) &&
395 (fcntl(fd, F_SETFL, O_NONBLOCK) != -1) &&
396 (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) {
397 //fprintf(stderr, "process_chk: 3\n");
398
Willy Tarreau9edd1612007-10-18 18:07:48 +0200399 if (s->proxy->options & PR_O_TCP_NOLING) {
400 /* We don't want to useless data */
401 setsockopt(fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger));
402 }
Willy Tarreau2ea3abb2007-03-25 16:45:16 +0200403
Willy Tarreau0f03c6f2007-03-25 20:46:19 +0200404 if (s->check_addr.sin_addr.s_addr)
405 /* we'll connect to the check addr specified on the server */
Willy Tarreau2ea3abb2007-03-25 16:45:16 +0200406 sa = s->check_addr;
Willy Tarreau2ea3abb2007-03-25 16:45:16 +0200407 else
Willy Tarreau0f03c6f2007-03-25 20:46:19 +0200408 /* we'll connect to the addr on the server */
Willy Tarreau2ea3abb2007-03-25 16:45:16 +0200409 sa = s->addr;
Willy Tarreau0f03c6f2007-03-25 20:46:19 +0200410
Willy Tarreaubaaee002006-06-26 02:48:02 +0200411 /* we'll connect to the check port on the server */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200412 sa.sin_port = htons(s->check_port);
413
414 /* allow specific binding :
415 * - server-specific at first
416 * - proxy-specific next
417 */
418 if (s->state & SRV_BIND_SRC) {
419 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
420 if (bind(fd, (struct sockaddr *)&s->source_addr, sizeof(s->source_addr)) == -1) {
421 Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n",
422 s->proxy->id, s->id);
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100423 s->result |= SRV_CHK_ERROR;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200424 }
Willy Tarreau163c5322006-11-14 16:18:41 +0100425#ifdef CONFIG_HAP_CTTPROXY
426 if ((s->state & SRV_TPROXY_MASK) == SRV_TPROXY_ADDR) {
427 struct in_tproxy itp1, itp2;
428 memset(&itp1, 0, sizeof(itp1));
429
430 itp1.op = TPROXY_ASSIGN;
431 itp1.v.addr.faddr = s->tproxy_addr.sin_addr;
432 itp1.v.addr.fport = s->tproxy_addr.sin_port;
433
434 /* set connect flag on socket */
435 itp2.op = TPROXY_FLAGS;
436 itp2.v.flags = ITP_CONNECT | ITP_ONCE;
437
438 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp1, sizeof(itp1)) == -1 ||
439 setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) {
440 Alert("Cannot bind to tproxy source address before connect() for server %s/%s. Aborting.\n",
441 s->proxy->id, s->id);
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100442 s->result |= SRV_CHK_ERROR;
Willy Tarreau163c5322006-11-14 16:18:41 +0100443 }
444 }
445#endif
Willy Tarreaubaaee002006-06-26 02:48:02 +0200446 }
447 else if (s->proxy->options & PR_O_BIND_SRC) {
448 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
449 if (bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) {
Willy Tarreau2b5652f2006-12-31 17:46:05 +0100450 Alert("Cannot bind to source address before connect() for %s '%s'. Aborting.\n",
451 proxy_type_str(s->proxy), s->proxy->id);
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100452 s->result |= SRV_CHK_ERROR;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200453 }
Willy Tarreau163c5322006-11-14 16:18:41 +0100454#ifdef CONFIG_HAP_CTTPROXY
455 if ((s->proxy->options & PR_O_TPXY_MASK) == PR_O_TPXY_ADDR) {
456 struct in_tproxy itp1, itp2;
457 memset(&itp1, 0, sizeof(itp1));
458
459 itp1.op = TPROXY_ASSIGN;
460 itp1.v.addr.faddr = s->tproxy_addr.sin_addr;
461 itp1.v.addr.fport = s->tproxy_addr.sin_port;
462
463 /* set connect flag on socket */
464 itp2.op = TPROXY_FLAGS;
465 itp2.v.flags = ITP_CONNECT | ITP_ONCE;
466
467 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp1, sizeof(itp1)) == -1 ||
468 setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) {
Willy Tarreau2b5652f2006-12-31 17:46:05 +0100469 Alert("Cannot bind to tproxy source address before connect() for %s '%s'. Aborting.\n",
470 proxy_type_str(s->proxy), s->proxy->id);
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100471 s->result |= SRV_CHK_ERROR;
Willy Tarreau163c5322006-11-14 16:18:41 +0100472 }
473 }
474#endif
Willy Tarreaubaaee002006-06-26 02:48:02 +0200475 }
476
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100477 if (s->result == SRV_CHK_UNKNOWN) {
Willy Tarreaubaaee002006-06-26 02:48:02 +0200478 if ((connect(fd, (struct sockaddr *)&sa, sizeof(sa)) != -1) || (errno == EINPROGRESS)) {
479 /* OK, connection in progress or established */
480
481 //fprintf(stderr, "process_chk: 4\n");
482
483 s->curfd = fd; /* that's how we know a test is in progress ;-) */
Willy Tarreau7a966482007-04-15 10:58:02 +0200484 fd_insert(fd);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200485 fdtab[fd].owner = t;
Willy Tarreau54469402006-07-29 16:59:06 +0200486 fdtab[fd].cb[DIR_RD].f = &event_srv_chk_r;
487 fdtab[fd].cb[DIR_RD].b = NULL;
488 fdtab[fd].cb[DIR_WR].f = &event_srv_chk_w;
489 fdtab[fd].cb[DIR_WR].b = NULL;
Willy Tarreaue94ebd02007-10-09 17:14:37 +0200490 fdtab[fd].peeraddr = (struct sockaddr *)&sa;
491 fdtab[fd].peerlen = sizeof(sa);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200492 fdtab[fd].state = FD_STCONN; /* connection in progress */
Willy Tarreau3d32d3a2007-04-15 11:31:05 +0200493 fdtab[fd].ev = 0;
Willy Tarreauf161a342007-04-08 16:59:42 +0200494 EV_FD_SET(fd, DIR_WR); /* for connect status */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200495#ifdef DEBUG_FULL
Willy Tarreauf161a342007-04-08 16:59:42 +0200496 assert (!EV_FD_ISSET(fd, DIR_RD));
Willy Tarreaubaaee002006-06-26 02:48:02 +0200497#endif
Willy Tarreaubaaee002006-06-26 02:48:02 +0200498 /* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
Willy Tarreau42aae5c2007-04-29 17:43:56 +0200499 tv_ms_add(&t->expire, &now, s->inter);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200500 task_queue(t); /* restore t to its place in the task list */
Willy Tarreaud825eef2007-05-12 22:35:00 +0200501 *next = t->expire;
Willy Tarreau8eee9c82007-05-14 03:40:11 +0200502 return;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200503 }
504 else if (errno != EALREADY && errno != EISCONN && errno != EAGAIN) {
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100505 s->result |= SRV_CHK_ERROR; /* a real error */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200506 }
507 }
508 }
509 close(fd); /* socket creation error */
510 }
511
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100512 if (s->result == SRV_CHK_UNKNOWN) { /* nothing done */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200513 //fprintf(stderr, "process_chk: 6\n");
Willy Tarreaua8b55e32007-05-13 16:08:19 +0200514 while (tv_isle(&t->expire, &now))
Willy Tarreau42aae5c2007-04-29 17:43:56 +0200515 tv_ms_add(&t->expire, &t->expire, s->inter);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200516 goto new_chk; /* may be we should initialize a new check */
517 }
518
519 /* here, we have seen a failure */
520 if (s->health > s->rise) {
521 s->health--; /* still good */
522 s->failed_checks++;
523 }
524 else
525 set_server_down(s);
526
527 //fprintf(stderr, "process_chk: 7\n");
528 /* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
Willy Tarreaua8b55e32007-05-13 16:08:19 +0200529 while (tv_isle(&t->expire, &now))
Willy Tarreau42aae5c2007-04-29 17:43:56 +0200530 tv_ms_add(&t->expire, &t->expire, s->inter);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200531 goto new_chk;
532 }
533 else {
534 //fprintf(stderr, "process_chk: 8\n");
535 /* there was a test running */
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100536 if ((s->result & (SRV_CHK_ERROR|SRV_CHK_RUNNING)) == SRV_CHK_RUNNING) { /* good server detected */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200537 //fprintf(stderr, "process_chk: 9\n");
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200538
Willy Tarreau9909fc12007-11-30 17:42:05 +0100539 if (s->state & SRV_WARMINGUP) {
540 if (now.tv_sec < s->last_change || now.tv_sec >= s->last_change + s->slowstart) {
541 s->state &= ~SRV_WARMINGUP;
542 if (s->proxy->lbprm.algo & BE_LB_PROP_DYN)
543 s->eweight = s->uweight * BE_WEIGHT_SCALE;
544 if (s->proxy->lbprm.update_server_eweight)
545 s->proxy->lbprm.update_server_eweight(s);
546 }
547 else if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) {
548 /* for dynamic algorithms, let's update the weight */
Willy Tarreau5542af62007-12-03 02:04:00 +0100549 s->eweight = (BE_WEIGHT_SCALE * (now.tv_sec - s->last_change) +
550 s->slowstart - 1) / s->slowstart;
Willy Tarreau9909fc12007-11-30 17:42:05 +0100551 s->eweight *= s->uweight;
552 if (s->proxy->lbprm.update_server_eweight)
553 s->proxy->lbprm.update_server_eweight(s);
554 }
555 /* probably that we can refill this server with a bit more connections */
556 check_for_pending(s);
557 }
558
Willy Tarreau48494c02007-11-30 10:41:39 +0100559 /* we may have to add/remove this server from the LB group */
560 if ((s->state & SRV_RUNNING) && (s->proxy->options & PR_O_DISABLE404)) {
561 if ((s->state & SRV_GOINGDOWN) &&
562 ((s->result & (SRV_CHK_RUNNING|SRV_CHK_DISABLE)) == SRV_CHK_RUNNING)) {
563 /* server enabled again */
564 s->state &= ~SRV_GOINGDOWN;
565 s->proxy->lbprm.set_server_status_up(s);
566
567 /* check if we can handle some connections queued at the proxy. We
568 * will take as many as we can handle.
569 */
570 xferred = check_for_pending(s);
571
572 sprintf(trash,
573 "Load-balancing on %sServer %s/%s is enabled again. %d active and %d backup servers online.%s"
574 " %d sessions requeued, %d total in queue.\n",
575 s->state & SRV_BACKUP ? "Backup " : "",
576 s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
577 (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
578 xferred, s->nbpend);
579
580 Warning("%s", trash);
581 send_log(s->proxy, LOG_NOTICE, "%s", trash);
582 }
583 else if (!(s->state & SRV_GOINGDOWN) &&
584 ((s->result & (SRV_CHK_RUNNING | SRV_CHK_DISABLE)) ==
585 (SRV_CHK_RUNNING | SRV_CHK_DISABLE))) {
586 /* server disabled */
587 s->state |= SRV_GOINGDOWN;
588 s->proxy->lbprm.set_server_status_down(s);
589
590 /* we might have sessions queued on this server and waiting for
591 * a connection. Those which are redispatchable will be queued
592 * to another server or to the proxy itself.
593 */
594 xferred = redistribute_pending(s);
595
596 sprintf(trash,
597 "Load-balancing on %sServer %s/%s is disabled. %d active and %d backup servers online.%s"
598 " %d sessions requeued, %d total in queue.\n",
599 s->state & SRV_BACKUP ? "Backup " : "",
600 s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
601 (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
602 xferred, s->nbpend);
603
604 Warning("%s", trash);
605
606 send_log(s->proxy, LOG_NOTICE, "%s", trash);
607 if (!s->proxy->srv_bck && !s->proxy->srv_act)
608 set_backend_down(s->proxy);
609 }
610 }
611
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200612 if (s->health < s->rise + s->fall - 1) {
613 s->health++; /* was bad, stays for a while */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200614
615 if (s->health == s->rise) {
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200616 if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) {
617 if (s->proxy->last_change < now.tv_sec) // ignore negative times
618 s->proxy->down_time += now.tv_sec - s->proxy->last_change;
619 s->proxy->last_change = now.tv_sec;
620 }
621
Willy Tarreaub625a082007-11-26 01:15:43 +0100622 if (s->last_change < now.tv_sec) // ignore negative times
623 s->down_time += now.tv_sec - s->last_change;
624
625 s->last_change = now.tv_sec;
626 s->state |= SRV_RUNNING;
Willy Tarreau9909fc12007-11-30 17:42:05 +0100627 if (s->slowstart > 0) {
628 s->state |= SRV_WARMINGUP;
629 if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) {
630 /* For dynamic algorithms, start at the first step of the weight,
631 * without multiplying by BE_WEIGHT_SCALE.
632 */
633 s->eweight = s->uweight;
634 if (s->proxy->lbprm.update_server_eweight)
635 s->proxy->lbprm.update_server_eweight(s);
636 }
637 }
Willy Tarreaub625a082007-11-26 01:15:43 +0100638 s->proxy->lbprm.set_server_status_up(s);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200639
640 /* check if we can handle some connections queued at the proxy. We
641 * will take as many as we can handle.
642 */
Willy Tarreau48494c02007-11-30 10:41:39 +0100643 xferred = check_for_pending(s);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200644
645 sprintf(trash,
646 "%sServer %s/%s is UP. %d active and %d backup servers online.%s"
647 " %d sessions requeued, %d total in queue.\n",
648 s->state & SRV_BACKUP ? "Backup " : "",
649 s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
650 (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
651 xferred, s->nbpend);
652
653 Warning("%s", trash);
654 send_log(s->proxy, LOG_NOTICE, "%s", trash);
655 }
656
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200657 if (s->health >= s->rise)
658 s->health = s->rise + s->fall - 1; /* OK now */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200659 }
660 s->curfd = -1; /* no check running anymore */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200661 fd_delete(fd);
Willy Tarreau44ec0f02007-10-14 23:47:04 +0200662
663 rv = 0;
664 if (global.spread_checks > 0) {
665 rv = s->inter * global.spread_checks / 100;
666 rv -= (int) (2 * rv * (rand() / (RAND_MAX + 1.0)));
667 //fprintf(stderr, "process_chk(%p): (%d+/-%d%%) random=%d\n", s, s->inter, global.spread_checks, rv);
668 }
669 tv_ms_add(&t->expire, &now, s->inter + rv);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200670 goto new_chk;
671 }
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100672 else if ((s->result & SRV_CHK_ERROR) || tv_isle(&t->expire, &now)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +0200673 //fprintf(stderr, "process_chk: 10\n");
674 /* failure or timeout detected */
675 if (s->health > s->rise) {
676 s->health--; /* still good */
677 s->failed_checks++;
678 }
679 else
680 set_server_down(s);
681 s->curfd = -1;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200682 fd_delete(fd);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +0200683
684 rv = 0;
685 if (global.spread_checks > 0) {
686 rv = s->inter * global.spread_checks / 100;
687 rv -= (int) (2 * rv * (rand() / (RAND_MAX + 1.0)));
Willy Tarreau44ec0f02007-10-14 23:47:04 +0200688 //fprintf(stderr, "process_chk(%p): (%d+/-%d%%) random=%d\n", s, s->inter, global.spread_checks, rv);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +0200689 }
Willy Tarreau44ec0f02007-10-14 23:47:04 +0200690 tv_ms_add(&t->expire, &now, s->inter + rv);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200691 goto new_chk;
692 }
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100693 /* if result is unknown and there's no timeout, we have to wait again */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200694 }
695 //fprintf(stderr, "process_chk: 11\n");
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100696 s->result = SRV_CHK_UNKNOWN;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200697 task_queue(t); /* restore t to its place in the task list */
Willy Tarreaud825eef2007-05-12 22:35:00 +0200698 *next = t->expire;
Willy Tarreau7317eb52007-05-09 00:54:10 +0200699 out:
Willy Tarreaud825eef2007-05-12 22:35:00 +0200700 return;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200701}
702
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +0200703/*
704 * Start health-check.
705 * Returns 0 if OK, -1 if error, and prints the error in this case.
706 */
707int start_checks() {
708
709 struct proxy *px;
710 struct server *s;
711 struct task *t;
712 int nbchk=0, mininter=0, srvpos=0;
713
Willy Tarreau2c43a1e2007-10-14 23:05:39 +0200714 /* 1- count the checkers to run simultaneously.
715 * We also determine the minimum interval among all of those which
716 * have an interval larger than SRV_CHK_INTER_THRES. This interval
717 * will be used to spread their start-up date. Those which have
718 * a shorter interval will start independantly and will not dictate
719 * too short an interval for all others.
720 */
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +0200721 for (px = proxy; px; px = px->next) {
722 for (s = px->srv; s; s = s->next) {
723 if (!(s->state & SRV_CHECKED))
724 continue;
725
Willy Tarreau2c43a1e2007-10-14 23:05:39 +0200726 if ((s->inter >= SRV_CHK_INTER_THRES) &&
727 (!mininter || mininter > s->inter))
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +0200728 mininter = s->inter;
729
730 nbchk++;
731 }
732 }
733
734 if (!nbchk)
735 return 0;
736
737 srand((unsigned)time(NULL));
738
739 /*
740 * 2- start them as far as possible from each others. For this, we will
741 * start them after their interval set to the min interval divided by
742 * the number of servers, weighted by the server's position in the list.
743 */
744 for (px = proxy; px; px = px->next) {
745 for (s = px->srv; s; s = s->next) {
746 if (!(s->state & SRV_CHECKED))
747 continue;
748
749 if ((t = pool_alloc2(pool2_task)) == NULL) {
750 Alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
751 return -1;
752 }
753
754 t->wq = NULL;
755 t->qlist.p = NULL;
756 t->state = TASK_IDLE;
757 t->process = process_chk;
758 t->context = s;
759
760 /* check this every ms */
Willy Tarreau2c43a1e2007-10-14 23:05:39 +0200761 tv_ms_add(&t->expire, &now,
762 ((mininter && mininter >= s->inter) ? mininter : s->inter) * srvpos / nbchk);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +0200763 task_queue(t);
764
765 srvpos++;
766 }
767 }
768 return 0;
769}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200770
771/*
772 * Local variables:
773 * c-indent-level: 8
774 * c-basic-offset: 8
775 * End:
776 */