xref: /openbsd/usr.sbin/relayd/hce.c (revision f04ff968)
1*f04ff968Sreyk /*	$OpenBSD: hce.c,v 1.69 2015/01/22 17:42:09 reyk Exp $	*/
2feb9ff76Sreyk 
3feb9ff76Sreyk /*
436f5dc5eSpyr  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
5feb9ff76Sreyk  *
6feb9ff76Sreyk  * Permission to use, copy, modify, and distribute this software for any
7feb9ff76Sreyk  * purpose with or without fee is hereby granted, provided that the above
8feb9ff76Sreyk  * copyright notice and this permission notice appear in all copies.
9feb9ff76Sreyk  *
10feb9ff76Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11feb9ff76Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12feb9ff76Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13feb9ff76Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14feb9ff76Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15feb9ff76Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16feb9ff76Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17feb9ff76Sreyk  */
18feb9ff76Sreyk 
19*f04ff968Sreyk #include <sys/types.h>
200ca734d7Sreyk #include <sys/queue.h>
21feb9ff76Sreyk #include <sys/time.h>
22*f04ff968Sreyk #include <sys/uio.h>
230ca734d7Sreyk 
24feb9ff76Sreyk #include <event.h>
25feb9ff76Sreyk #include <stdlib.h>
26feb9ff76Sreyk #include <string.h>
27feb9ff76Sreyk #include <unistd.h>
28*f04ff968Sreyk #include <imsg.h>
29e8fb3979Spyr 
30748ceb64Sreyk #include "relayd.h"
31feb9ff76Sreyk 
320325c666Sreyk void	 hce_init(struct privsep *, struct privsep_proc *p, void *);
33feb9ff76Sreyk void	 hce_sig_handler(int sig, short, void *);
34feb9ff76Sreyk void	 hce_launch_checks(int, short, void *);
35641b8bafSpyr void	 hce_setup_events(void);
36641b8bafSpyr void	 hce_disable_events(void);
37feb9ff76Sreyk 
380325c666Sreyk int	 hce_dispatch_parent(int, struct privsep_proc *, struct imsg *);
390325c666Sreyk int	 hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
406e07057bSblambert int	 hce_dispatch_relay(int, struct privsep_proc *, struct imsg *);
410325c666Sreyk 
42748ceb64Sreyk static struct relayd *env = NULL;
43780e8f79Spyr int			 running = 0;
44feb9ff76Sreyk 
450325c666Sreyk static struct privsep_proc procs[] = {
460325c666Sreyk 	{ "parent",	PROC_PARENT,	hce_dispatch_parent },
470325c666Sreyk 	{ "pfe",	PROC_PFE,	hce_dispatch_pfe },
486e07057bSblambert 	{ "relay",	PROC_RELAY,	hce_dispatch_relay },
490325c666Sreyk };
50feb9ff76Sreyk 
51feb9ff76Sreyk pid_t
520325c666Sreyk hce(struct privsep *ps, struct privsep_proc *p)
53feb9ff76Sreyk {
540325c666Sreyk 	env = ps->ps_env;
55feb9ff76Sreyk 
5601d85ec5Sreyk 	/* this is needed for icmp tests */
5701d85ec5Sreyk 	icmp_init(env);
5801d85ec5Sreyk 
590325c666Sreyk 	return (proc_run(ps, p, procs, nitems(procs), hce_init, NULL));
600325c666Sreyk }
61feb9ff76Sreyk 
620325c666Sreyk void
630325c666Sreyk hce_init(struct privsep *ps, struct privsep_proc *p, void *arg)
640325c666Sreyk {
65a2195becSreyk 	if (config_init(ps->ps_env) == -1)
66a2195becSreyk 		fatal("failed to initialize configuration");
670325c666Sreyk 
680325c666Sreyk 	env->sc_id = getpid() & 0xffff;
69feb9ff76Sreyk 
70bb8fa3feSreyk 	/* Allow maximum available sockets for TCP checks */
71bb8fa3feSreyk 	socket_rlimit(-1);
72780e8f79Spyr }
73780e8f79Spyr 
74780e8f79Spyr void
75641b8bafSpyr hce_setup_events(void)
76780e8f79Spyr {
77780e8f79Spyr 	struct timeval	 tv;
78780e8f79Spyr 	struct table	*table;
79780e8f79Spyr 
80a2195becSreyk 	if (!(TAILQ_EMPTY(env->sc_tables) ||
81a2195becSreyk 	    event_initialized(&env->sc_ev))) {
8235d10c30Sreyk 		evtimer_set(&env->sc_ev, hce_launch_checks, env);
8301d85ec5Sreyk 		bzero(&tv, sizeof(tv));
8435d10c30Sreyk 		evtimer_add(&env->sc_ev, &tv);
852edd718bSreyk 	}
86feb9ff76Sreyk 
877bb52228Sreyk 	if (env->sc_flags & F_TLS) {
8835d10c30Sreyk 		TAILQ_FOREACH(table, env->sc_tables, entry) {
897bb52228Sreyk 			if (!(table->conf.flags & F_TLS) ||
90a2195becSreyk 			    table->ssl_ctx != NULL)
91e8fb3979Spyr 				continue;
92e8fb3979Spyr 			table->ssl_ctx = ssl_ctx_create(env);
93e8fb3979Spyr 		}
94e8fb3979Spyr 	}
95641b8bafSpyr }
96641b8bafSpyr 
97641b8bafSpyr void
98641b8bafSpyr hce_disable_events(void)
99641b8bafSpyr {
100641b8bafSpyr 	struct table	*table;
101641b8bafSpyr 	struct host	*host;
102641b8bafSpyr 
10335d10c30Sreyk 	evtimer_del(&env->sc_ev);
10435d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
105641b8bafSpyr 		TAILQ_FOREACH(host, &table->hosts, entry) {
106c0dc99f6Sreyk 			host->he = HCE_ABORT;
107a2195becSreyk 			if (event_initialized(&host->cte.ev)) {
108641b8bafSpyr 				event_del(&host->cte.ev);
109641b8bafSpyr 				close(host->cte.s);
110641b8bafSpyr 			}
111641b8bafSpyr 		}
112a2195becSreyk 	}
11335d10c30Sreyk 	if (env->sc_has_icmp) {
11435d10c30Sreyk 		event_del(&env->sc_icmp_send.ev);
11535d10c30Sreyk 		event_del(&env->sc_icmp_recv.ev);
116641b8bafSpyr 	}
11735d10c30Sreyk 	if (env->sc_has_icmp6) {
11835d10c30Sreyk 		event_del(&env->sc_icmp6_send.ev);
11935d10c30Sreyk 		event_del(&env->sc_icmp6_recv.ev);
120641b8bafSpyr 	}
121feb9ff76Sreyk }
122feb9ff76Sreyk 
123feb9ff76Sreyk void
124feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg)
125feb9ff76Sreyk {
126feb9ff76Sreyk 	struct host		*host;
127feb9ff76Sreyk 	struct table		*table;
12801d85ec5Sreyk 	struct timeval		 tv;
12901d85ec5Sreyk 
13001d85ec5Sreyk 	/*
13101d85ec5Sreyk 	 * notify pfe checks are done and schedule next check
13201d85ec5Sreyk 	 */
1330325c666Sreyk 	proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_SYNC, -1, NULL, 0);
13435d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
13501d85ec5Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
136c0dc99f6Sreyk 			if ((host->flags & F_CHECK_DONE) == 0)
137c0dc99f6Sreyk 				host->he = HCE_INTERVAL_TIMEOUT;
13801d85ec5Sreyk 			host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
139a2195becSreyk 			if (event_initialized(&host->cte.ev)) {
14001d85ec5Sreyk 				event_del(&host->cte.ev);
141a2195becSreyk 				close(host->cte.s);
142a2195becSreyk 			}
143a2195becSreyk 			host->cte.s = -1;
14401d85ec5Sreyk 		}
14501d85ec5Sreyk 	}
14601d85ec5Sreyk 
147fd1841a3Sreyk 	getmonotime(&tv);
148feb9ff76Sreyk 
14935d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
15068b79041Spyr 		if (table->conf.flags & F_DISABLE)
1517e351ffdSreyk 			continue;
15218de159aSpyr 		if (table->conf.skip_cnt) {
15318de159aSpyr 			if (table->skipped++ > table->conf.skip_cnt)
15418de159aSpyr 				table->skipped = 0;
15518de159aSpyr 			if (table->skipped != 1)
15618de159aSpyr 				continue;
15718de159aSpyr 		}
15868b79041Spyr 		if (table->conf.check == CHECK_NOCHECK)
1597e351ffdSreyk 			fatalx("hce_launch_checks: unknown check type");
16001d85ec5Sreyk 
1617e351ffdSreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
162c723f8edSreyk 			if (host->flags & F_DISABLE || host->conf.parentid)
1637e351ffdSreyk 				continue;
164a7da800aSsthen 			bcopy(&tv, &host->cte.tv_start,
165a7da800aSsthen 			    sizeof(host->cte.tv_start));
1664156152fSreyk 			switch (table->conf.check) {
1674156152fSreyk 			case CHECK_ICMP:
16801d85ec5Sreyk 				schedule_icmp(env, host);
1694156152fSreyk 				break;
1704156152fSreyk 			case CHECK_SCRIPT:
1710325c666Sreyk 				check_script(env, host);
1724156152fSreyk 				break;
1734156152fSreyk 			default:
17401d85ec5Sreyk 				/* Any other TCP-style checks */
1757e351ffdSreyk 				host->last_up = host->up;
1767e351ffdSreyk 				host->cte.host = host;
1777e351ffdSreyk 				host->cte.table = table;
1787e351ffdSreyk 				check_tcp(&host->cte);
1794156152fSreyk 				break;
1804156152fSreyk 			}
1817e351ffdSreyk 		}
1827e351ffdSreyk 	}
18301d85ec5Sreyk 	check_icmp(env, &tv);
18401d85ec5Sreyk 
18535d10c30Sreyk 	bcopy(&env->sc_interval, &tv, sizeof(tv));
18635d10c30Sreyk 	evtimer_add(&env->sc_ev, &tv);
1877e351ffdSreyk }
1887e351ffdSreyk 
1897e351ffdSreyk void
190c0dc99f6Sreyk hce_notify_done(struct host *host, enum host_error he)
1917e351ffdSreyk {
192609cf3a7Sreyk 	struct table		*table;
1937e351ffdSreyk 	struct ctl_status	 st;
194609cf3a7Sreyk 	struct timeval		 tv_now, tv_dur;
195609cf3a7Sreyk 	u_long			 duration;
196609cf3a7Sreyk 	u_int			 logopt;
19774a0367eSgiovanni 	struct host		*h, *hostnst;
198c723f8edSreyk 	int			 hostup;
199c0dc99f6Sreyk 	const char		*msg;
200c723f8edSreyk 
20174a0367eSgiovanni 	if ((hostnst = host_find(env, host->conf.id)) == NULL)
20274a0367eSgiovanni 		fatalx("hce_notify_done: desynchronized");
20374a0367eSgiovanni 
20474a0367eSgiovanni 	if ((table = table_find(env, host->conf.tableid)) == NULL)
20574a0367eSgiovanni 		fatalx("hce_notify_done: invalid table id");
20674a0367eSgiovanni 
20774a0367eSgiovanni 	if (hostnst->flags & F_DISABLE) {
20874a0367eSgiovanni 		if (env->sc_opts & RELAYD_OPT_LOGUPDATE) {
20974a0367eSgiovanni 			log_info("host %s, check %s%s (ignoring result, "
21074a0367eSgiovanni 			    "host disabled)",
21174a0367eSgiovanni 			    host->conf.name, table_check(table->conf.check),
2127bb52228Sreyk 			    (table->conf.flags & F_TLS) ? " use tls" : "");
21374a0367eSgiovanni 		}
21474a0367eSgiovanni 		host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
21574a0367eSgiovanni 		return;
21674a0367eSgiovanni 	}
21774a0367eSgiovanni 
218c723f8edSreyk 	hostup = host->up;
219c0dc99f6Sreyk 	host->he = he;
2207e351ffdSreyk 
2212edd718bSreyk 	if (host->up == HOST_DOWN && host->retry_cnt) {
22285a8c65fSreyk 		log_debug("%s: host %s retry %d", __func__,
22368b79041Spyr 		    host->conf.name, host->retry_cnt);
224bd2508bcSreyk 		host->up = host->last_up;
2252edd718bSreyk 		host->retry_cnt--;
2262edd718bSreyk 	} else
22768b79041Spyr 		host->retry_cnt = host->conf.retry;
2282edd718bSreyk 	if (host->up != HOST_UNKNOWN) {
2292edd718bSreyk 		host->check_cnt++;
2302edd718bSreyk 		if (host->up == HOST_UP)
2312edd718bSreyk 			host->up_cnt++;
2322edd718bSreyk 	}
23368b79041Spyr 	st.id = host->conf.id;
234feb9ff76Sreyk 	st.up = host->up;
2352edd718bSreyk 	st.check_cnt = host->check_cnt;
2362edd718bSreyk 	st.retry_cnt = host->retry_cnt;
237c0dc99f6Sreyk 	st.he = he;
23801d85ec5Sreyk 	host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
239c0dc99f6Sreyk 	msg = host_error(he);
2407e351ffdSreyk 	if (msg)
24185a8c65fSreyk 		log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
242609cf3a7Sreyk 
2430325c666Sreyk 	proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_HOST_STATUS,
2440325c666Sreyk 	    -1, &st, sizeof(st));
2452edd718bSreyk 	if (host->up != host->last_up)
246748ceb64Sreyk 		logopt = RELAYD_OPT_LOGUPDATE;
2472edd718bSreyk 	else
248748ceb64Sreyk 		logopt = RELAYD_OPT_LOGNOTIFY;
249609cf3a7Sreyk 
250fd1841a3Sreyk 	getmonotime(&tv_now);
251609cf3a7Sreyk 	timersub(&tv_now, &host->cte.tv_start, &tv_dur);
252609cf3a7Sreyk 	if (timercmp(&host->cte.tv_start, &tv_dur, >))
253609cf3a7Sreyk 		duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
254609cf3a7Sreyk 	else
255609cf3a7Sreyk 		duration = 0;
256609cf3a7Sreyk 
25735d10c30Sreyk 	if (env->sc_opts & logopt) {
2582edd718bSreyk 		log_info("host %s, check %s%s (%lums), state %s -> %s, "
2592edd718bSreyk 		    "availability %s",
26068b79041Spyr 		    host->conf.name, table_check(table->conf.check),
2617bb52228Sreyk 		    (table->conf.flags & F_TLS) ? " use tls" : "", duration,
2622edd718bSreyk 		    host_status(host->last_up), host_status(host->up),
2632edd718bSreyk 		    print_availability(host->check_cnt, host->up_cnt));
264feb9ff76Sreyk 	}
265fe250497Sreyk 
266609cf3a7Sreyk 	host->last_up = host->up;
267c723f8edSreyk 
2681584e35dSreyk 	if (SLIST_EMPTY(&host->children))
269c723f8edSreyk 		return;
270c723f8edSreyk 
271c723f8edSreyk 	/* Notify for all other hosts that inherit the state from this one */
2721584e35dSreyk 	SLIST_FOREACH(h, &host->children, child) {
273c723f8edSreyk 		h->up = hostup;
274c0dc99f6Sreyk 		hce_notify_done(h, he);
275c723f8edSreyk 	}
276feb9ff76Sreyk }
277feb9ff76Sreyk 
2780325c666Sreyk int
2790325c666Sreyk hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
280feb9ff76Sreyk {
281feb9ff76Sreyk 	objid_t			 id;
282feb9ff76Sreyk 	struct host		*host;
283feb9ff76Sreyk 	struct table		*table;
284feb9ff76Sreyk 
2850325c666Sreyk 	switch (imsg->hdr.type) {
286feb9ff76Sreyk 	case IMSG_HOST_DISABLE:
2870325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
288feb9ff76Sreyk 		if ((host = host_find(env, id)) == NULL)
2894f416e82Scamield 			fatalx("hce_dispatch_pfe: desynchronized");
290feb9ff76Sreyk 		host->flags |= F_DISABLE;
291feb9ff76Sreyk 		host->up = HOST_UNKNOWN;
2922edd718bSreyk 		host->check_cnt = 0;
2932edd718bSreyk 		host->up_cnt = 0;
294c0dc99f6Sreyk 		host->he = HCE_NONE;
295feb9ff76Sreyk 		break;
296feb9ff76Sreyk 	case IMSG_HOST_ENABLE:
2970325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
298feb9ff76Sreyk 		if ((host = host_find(env, id)) == NULL)
2994f416e82Scamield 			fatalx("hce_dispatch_pfe: desynchronized");
300feb9ff76Sreyk 		host->flags &= ~(F_DISABLE);
301feb9ff76Sreyk 		host->up = HOST_UNKNOWN;
302c0dc99f6Sreyk 		host->he = HCE_NONE;
303feb9ff76Sreyk 		break;
304feb9ff76Sreyk 	case IMSG_TABLE_DISABLE:
3050325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
306feb9ff76Sreyk 		if ((table = table_find(env, id)) == NULL)
3074f416e82Scamield 			fatalx("hce_dispatch_pfe: desynchronized");
30868b79041Spyr 		table->conf.flags |= F_DISABLE;
309feb9ff76Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry)
310feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
311feb9ff76Sreyk 		break;
312feb9ff76Sreyk 	case IMSG_TABLE_ENABLE:
3130325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
314feb9ff76Sreyk 		if ((table = table_find(env, id)) == NULL)
3154f416e82Scamield 			fatalx("hce_dispatch_pfe: desynchronized");
31668b79041Spyr 		table->conf.flags &= ~(F_DISABLE);
317feb9ff76Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry)
318feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
319feb9ff76Sreyk 		break;
320cd65ce7bSpyr 	case IMSG_CTL_POLL:
32135d10c30Sreyk 		evtimer_del(&env->sc_ev);
32235d10c30Sreyk 		TAILQ_FOREACH(table, env->sc_tables, entry)
323f98352d8Spyr 			table->skipped = 0;
324cd65ce7bSpyr 		hce_launch_checks(-1, EV_TIMEOUT, env);
325cd65ce7bSpyr 		break;
326feb9ff76Sreyk 	default:
3270325c666Sreyk 		return (-1);
328feb9ff76Sreyk 	}
329feb9ff76Sreyk 
3300325c666Sreyk 	return (0);
3310325c666Sreyk }
3320325c666Sreyk 
3330325c666Sreyk int
3340325c666Sreyk hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
335feb9ff76Sreyk {
3364156152fSreyk 	struct ctl_script	 scr;
337feb9ff76Sreyk 
3380325c666Sreyk 	switch (imsg->hdr.type) {
3394156152fSreyk 	case IMSG_SCRIPT:
3400325c666Sreyk 		IMSG_SIZE_CHECK(imsg, &scr);
3410325c666Sreyk 		bcopy(imsg->data, &scr, sizeof(scr));
3424156152fSreyk 		script_done(env, &scr);
3434156152fSreyk 		break;
344a2195becSreyk 	case IMSG_CFG_TABLE:
345a2195becSreyk 		config_gettable(env, imsg);
346adf98c11Spyr 		break;
347a2195becSreyk 	case IMSG_CFG_HOST:
348a2195becSreyk 		config_gethost(env, imsg);
349adf98c11Spyr 		break;
350a2195becSreyk 	case IMSG_CFG_DONE:
351a2195becSreyk 		config_getcfg(env, imsg);
3520dcba380Scamield 		break;
3530dcba380Scamield 	case IMSG_CTL_START:
354adf98c11Spyr 		hce_setup_events();
355adf98c11Spyr 		break;
356a2195becSreyk 	case IMSG_CTL_RESET:
357a2195becSreyk 		config_getreset(env, imsg);
358a2195becSreyk 		break;
359feb9ff76Sreyk 	default:
3600325c666Sreyk 		return (-1);
361feb9ff76Sreyk 	}
3620325c666Sreyk 
3630325c666Sreyk 	return (0);
364feb9ff76Sreyk }
3656e07057bSblambert 
3666e07057bSblambert int
3676e07057bSblambert hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
3686e07057bSblambert {
3696e07057bSblambert 	switch (imsg->hdr.type) {
3706e07057bSblambert 	default:
3716e07057bSblambert 		break;
3726e07057bSblambert 	}
3736e07057bSblambert 
3746e07057bSblambert 	return (-1);
3756e07057bSblambert }
376