xref: /openbsd/usr.sbin/relayd/hce.c (revision 6e07057b)
1*6e07057bSblambert /*	$OpenBSD: hce.c,v 1.66 2014/11/19 10:24:40 blambert 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 
19feb9ff76Sreyk #include <sys/param.h>
200ca734d7Sreyk #include <sys/queue.h>
21feb9ff76Sreyk #include <sys/time.h>
22feb9ff76Sreyk #include <sys/stat.h>
23feb9ff76Sreyk #include <sys/socket.h>
24feb9ff76Sreyk #include <sys/un.h>
250ca734d7Sreyk 
260ca734d7Sreyk #include <net/if.h>
27feb9ff76Sreyk #include <netinet/in.h>
28feb9ff76Sreyk #include <netinet/ip.h>
290ca734d7Sreyk 
30feb9ff76Sreyk #include <errno.h>
31feb9ff76Sreyk #include <event.h>
32feb9ff76Sreyk #include <fcntl.h>
33feb9ff76Sreyk #include <stdlib.h>
34feb9ff76Sreyk #include <string.h>
35feb9ff76Sreyk #include <unistd.h>
36feb9ff76Sreyk #include <err.h>
37feb9ff76Sreyk #include <pwd.h>
38feb9ff76Sreyk 
39e8fb3979Spyr #include <openssl/ssl.h>
40e8fb3979Spyr 
41748ceb64Sreyk #include "relayd.h"
42feb9ff76Sreyk 
430325c666Sreyk void	 hce_init(struct privsep *, struct privsep_proc *p, void *);
44feb9ff76Sreyk void	 hce_sig_handler(int sig, short, void *);
45feb9ff76Sreyk void	 hce_launch_checks(int, short, void *);
46641b8bafSpyr void	 hce_setup_events(void);
47641b8bafSpyr void	 hce_disable_events(void);
48feb9ff76Sreyk 
490325c666Sreyk int	 hce_dispatch_parent(int, struct privsep_proc *, struct imsg *);
500325c666Sreyk int	 hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
51*6e07057bSblambert int	 hce_dispatch_relay(int, struct privsep_proc *, struct imsg *);
520325c666Sreyk 
53748ceb64Sreyk static struct relayd *env = NULL;
54780e8f79Spyr int			 running = 0;
55feb9ff76Sreyk 
560325c666Sreyk static struct privsep_proc procs[] = {
570325c666Sreyk 	{ "parent",	PROC_PARENT,	hce_dispatch_parent },
580325c666Sreyk 	{ "pfe",	PROC_PFE,	hce_dispatch_pfe },
59*6e07057bSblambert 	{ "relay",	PROC_RELAY,	hce_dispatch_relay },
600325c666Sreyk };
61feb9ff76Sreyk 
62feb9ff76Sreyk pid_t
630325c666Sreyk hce(struct privsep *ps, struct privsep_proc *p)
64feb9ff76Sreyk {
650325c666Sreyk 	env = ps->ps_env;
66feb9ff76Sreyk 
6701d85ec5Sreyk 	/* this is needed for icmp tests */
6801d85ec5Sreyk 	icmp_init(env);
6901d85ec5Sreyk 
700325c666Sreyk 	return (proc_run(ps, p, procs, nitems(procs), hce_init, NULL));
710325c666Sreyk }
72feb9ff76Sreyk 
730325c666Sreyk void
740325c666Sreyk hce_init(struct privsep *ps, struct privsep_proc *p, void *arg)
750325c666Sreyk {
76a2195becSreyk 	if (config_init(ps->ps_env) == -1)
77a2195becSreyk 		fatal("failed to initialize configuration");
780325c666Sreyk 
790325c666Sreyk 	env->sc_id = getpid() & 0xffff;
80feb9ff76Sreyk 
81bb8fa3feSreyk 	/* Allow maximum available sockets for TCP checks */
82bb8fa3feSreyk 	socket_rlimit(-1);
83780e8f79Spyr }
84780e8f79Spyr 
85780e8f79Spyr void
86641b8bafSpyr hce_setup_events(void)
87780e8f79Spyr {
88780e8f79Spyr 	struct timeval	 tv;
89780e8f79Spyr 	struct table	*table;
90780e8f79Spyr 
91a2195becSreyk 	if (!(TAILQ_EMPTY(env->sc_tables) ||
92a2195becSreyk 	    event_initialized(&env->sc_ev))) {
9335d10c30Sreyk 		evtimer_set(&env->sc_ev, hce_launch_checks, env);
9401d85ec5Sreyk 		bzero(&tv, sizeof(tv));
9535d10c30Sreyk 		evtimer_add(&env->sc_ev, &tv);
962edd718bSreyk 	}
97feb9ff76Sreyk 
9835d10c30Sreyk 	if (env->sc_flags & F_SSL) {
9935d10c30Sreyk 		TAILQ_FOREACH(table, env->sc_tables, entry) {
100a2195becSreyk 			if (!(table->conf.flags & F_SSL) ||
101a2195becSreyk 			    table->ssl_ctx != NULL)
102e8fb3979Spyr 				continue;
103e8fb3979Spyr 			table->ssl_ctx = ssl_ctx_create(env);
104e8fb3979Spyr 		}
105e8fb3979Spyr 	}
106641b8bafSpyr }
107641b8bafSpyr 
108641b8bafSpyr void
109641b8bafSpyr hce_disable_events(void)
110641b8bafSpyr {
111641b8bafSpyr 	struct table	*table;
112641b8bafSpyr 	struct host	*host;
113641b8bafSpyr 
11435d10c30Sreyk 	evtimer_del(&env->sc_ev);
11535d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
116641b8bafSpyr 		TAILQ_FOREACH(host, &table->hosts, entry) {
117c0dc99f6Sreyk 			host->he = HCE_ABORT;
118a2195becSreyk 			if (event_initialized(&host->cte.ev)) {
119641b8bafSpyr 				event_del(&host->cte.ev);
120641b8bafSpyr 				close(host->cte.s);
121641b8bafSpyr 			}
122641b8bafSpyr 		}
123a2195becSreyk 	}
12435d10c30Sreyk 	if (env->sc_has_icmp) {
12535d10c30Sreyk 		event_del(&env->sc_icmp_send.ev);
12635d10c30Sreyk 		event_del(&env->sc_icmp_recv.ev);
127641b8bafSpyr 	}
12835d10c30Sreyk 	if (env->sc_has_icmp6) {
12935d10c30Sreyk 		event_del(&env->sc_icmp6_send.ev);
13035d10c30Sreyk 		event_del(&env->sc_icmp6_recv.ev);
131641b8bafSpyr 	}
132feb9ff76Sreyk }
133feb9ff76Sreyk 
134feb9ff76Sreyk void
135feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg)
136feb9ff76Sreyk {
137feb9ff76Sreyk 	struct host		*host;
138feb9ff76Sreyk 	struct table		*table;
13901d85ec5Sreyk 	struct timeval		 tv;
14001d85ec5Sreyk 
14101d85ec5Sreyk 	/*
14201d85ec5Sreyk 	 * notify pfe checks are done and schedule next check
14301d85ec5Sreyk 	 */
1440325c666Sreyk 	proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_SYNC, -1, NULL, 0);
14535d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
14601d85ec5Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
147c0dc99f6Sreyk 			if ((host->flags & F_CHECK_DONE) == 0)
148c0dc99f6Sreyk 				host->he = HCE_INTERVAL_TIMEOUT;
14901d85ec5Sreyk 			host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
150a2195becSreyk 			if (event_initialized(&host->cte.ev)) {
15101d85ec5Sreyk 				event_del(&host->cte.ev);
152a2195becSreyk 				close(host->cte.s);
153a2195becSreyk 			}
154a2195becSreyk 			host->cte.s = -1;
15501d85ec5Sreyk 		}
15601d85ec5Sreyk 	}
15701d85ec5Sreyk 
158fd1841a3Sreyk 	getmonotime(&tv);
159feb9ff76Sreyk 
16035d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
16168b79041Spyr 		if (table->conf.flags & F_DISABLE)
1627e351ffdSreyk 			continue;
16318de159aSpyr 		if (table->conf.skip_cnt) {
16418de159aSpyr 			if (table->skipped++ > table->conf.skip_cnt)
16518de159aSpyr 				table->skipped = 0;
16618de159aSpyr 			if (table->skipped != 1)
16718de159aSpyr 				continue;
16818de159aSpyr 		}
16968b79041Spyr 		if (table->conf.check == CHECK_NOCHECK)
1707e351ffdSreyk 			fatalx("hce_launch_checks: unknown check type");
17101d85ec5Sreyk 
1727e351ffdSreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
173c723f8edSreyk 			if (host->flags & F_DISABLE || host->conf.parentid)
1747e351ffdSreyk 				continue;
175a7da800aSsthen 			bcopy(&tv, &host->cte.tv_start,
176a7da800aSsthen 			    sizeof(host->cte.tv_start));
1774156152fSreyk 			switch (table->conf.check) {
1784156152fSreyk 			case CHECK_ICMP:
17901d85ec5Sreyk 				schedule_icmp(env, host);
1804156152fSreyk 				break;
1814156152fSreyk 			case CHECK_SCRIPT:
1820325c666Sreyk 				check_script(env, host);
1834156152fSreyk 				break;
1844156152fSreyk 			default:
18501d85ec5Sreyk 				/* Any other TCP-style checks */
1867e351ffdSreyk 				host->last_up = host->up;
1877e351ffdSreyk 				host->cte.host = host;
1887e351ffdSreyk 				host->cte.table = table;
1897e351ffdSreyk 				check_tcp(&host->cte);
1904156152fSreyk 				break;
1914156152fSreyk 			}
1927e351ffdSreyk 		}
1937e351ffdSreyk 	}
19401d85ec5Sreyk 	check_icmp(env, &tv);
19501d85ec5Sreyk 
19635d10c30Sreyk 	bcopy(&env->sc_interval, &tv, sizeof(tv));
19735d10c30Sreyk 	evtimer_add(&env->sc_ev, &tv);
1987e351ffdSreyk }
1997e351ffdSreyk 
2007e351ffdSreyk void
201c0dc99f6Sreyk hce_notify_done(struct host *host, enum host_error he)
2027e351ffdSreyk {
203609cf3a7Sreyk 	struct table		*table;
2047e351ffdSreyk 	struct ctl_status	 st;
205609cf3a7Sreyk 	struct timeval		 tv_now, tv_dur;
206609cf3a7Sreyk 	u_long			 duration;
207609cf3a7Sreyk 	u_int			 logopt;
20874a0367eSgiovanni 	struct host		*h, *hostnst;
209c723f8edSreyk 	int			 hostup;
210c0dc99f6Sreyk 	const char		*msg;
211c723f8edSreyk 
21274a0367eSgiovanni 	if ((hostnst = host_find(env, host->conf.id)) == NULL)
21374a0367eSgiovanni 		fatalx("hce_notify_done: desynchronized");
21474a0367eSgiovanni 
21574a0367eSgiovanni 	if ((table = table_find(env, host->conf.tableid)) == NULL)
21674a0367eSgiovanni 		fatalx("hce_notify_done: invalid table id");
21774a0367eSgiovanni 
21874a0367eSgiovanni 	if (hostnst->flags & F_DISABLE) {
21974a0367eSgiovanni 		if (env->sc_opts & RELAYD_OPT_LOGUPDATE) {
22074a0367eSgiovanni 			log_info("host %s, check %s%s (ignoring result, "
22174a0367eSgiovanni 			    "host disabled)",
22274a0367eSgiovanni 			    host->conf.name, table_check(table->conf.check),
22374a0367eSgiovanni 			    (table->conf.flags & F_SSL) ? " use ssl" : "");
22474a0367eSgiovanni 		}
22574a0367eSgiovanni 		host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
22674a0367eSgiovanni 		return;
22774a0367eSgiovanni 	}
22874a0367eSgiovanni 
229c723f8edSreyk 	hostup = host->up;
230c0dc99f6Sreyk 	host->he = he;
2317e351ffdSreyk 
2322edd718bSreyk 	if (host->up == HOST_DOWN && host->retry_cnt) {
23385a8c65fSreyk 		log_debug("%s: host %s retry %d", __func__,
23468b79041Spyr 		    host->conf.name, host->retry_cnt);
235bd2508bcSreyk 		host->up = host->last_up;
2362edd718bSreyk 		host->retry_cnt--;
2372edd718bSreyk 	} else
23868b79041Spyr 		host->retry_cnt = host->conf.retry;
2392edd718bSreyk 	if (host->up != HOST_UNKNOWN) {
2402edd718bSreyk 		host->check_cnt++;
2412edd718bSreyk 		if (host->up == HOST_UP)
2422edd718bSreyk 			host->up_cnt++;
2432edd718bSreyk 	}
24468b79041Spyr 	st.id = host->conf.id;
245feb9ff76Sreyk 	st.up = host->up;
2462edd718bSreyk 	st.check_cnt = host->check_cnt;
2472edd718bSreyk 	st.retry_cnt = host->retry_cnt;
248c0dc99f6Sreyk 	st.he = he;
24901d85ec5Sreyk 	host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
250c0dc99f6Sreyk 	msg = host_error(he);
2517e351ffdSreyk 	if (msg)
25285a8c65fSreyk 		log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
253609cf3a7Sreyk 
2540325c666Sreyk 	proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_HOST_STATUS,
2550325c666Sreyk 	    -1, &st, sizeof(st));
2562edd718bSreyk 	if (host->up != host->last_up)
257748ceb64Sreyk 		logopt = RELAYD_OPT_LOGUPDATE;
2582edd718bSreyk 	else
259748ceb64Sreyk 		logopt = RELAYD_OPT_LOGNOTIFY;
260609cf3a7Sreyk 
261fd1841a3Sreyk 	getmonotime(&tv_now);
262609cf3a7Sreyk 	timersub(&tv_now, &host->cte.tv_start, &tv_dur);
263609cf3a7Sreyk 	if (timercmp(&host->cte.tv_start, &tv_dur, >))
264609cf3a7Sreyk 		duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
265609cf3a7Sreyk 	else
266609cf3a7Sreyk 		duration = 0;
267609cf3a7Sreyk 
26835d10c30Sreyk 	if (env->sc_opts & logopt) {
2692edd718bSreyk 		log_info("host %s, check %s%s (%lums), state %s -> %s, "
2702edd718bSreyk 		    "availability %s",
27168b79041Spyr 		    host->conf.name, table_check(table->conf.check),
27268b79041Spyr 		    (table->conf.flags & F_SSL) ? " use ssl" : "", duration,
2732edd718bSreyk 		    host_status(host->last_up), host_status(host->up),
2742edd718bSreyk 		    print_availability(host->check_cnt, host->up_cnt));
275feb9ff76Sreyk 	}
276fe250497Sreyk 
277609cf3a7Sreyk 	host->last_up = host->up;
278c723f8edSreyk 
2791584e35dSreyk 	if (SLIST_EMPTY(&host->children))
280c723f8edSreyk 		return;
281c723f8edSreyk 
282c723f8edSreyk 	/* Notify for all other hosts that inherit the state from this one */
2831584e35dSreyk 	SLIST_FOREACH(h, &host->children, child) {
284c723f8edSreyk 		h->up = hostup;
285c0dc99f6Sreyk 		hce_notify_done(h, he);
286c723f8edSreyk 	}
287feb9ff76Sreyk }
288feb9ff76Sreyk 
2890325c666Sreyk int
2900325c666Sreyk hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
291feb9ff76Sreyk {
292feb9ff76Sreyk 	objid_t			 id;
293feb9ff76Sreyk 	struct host		*host;
294feb9ff76Sreyk 	struct table		*table;
295feb9ff76Sreyk 
2960325c666Sreyk 	switch (imsg->hdr.type) {
297feb9ff76Sreyk 	case IMSG_HOST_DISABLE:
2980325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
299feb9ff76Sreyk 		if ((host = host_find(env, id)) == NULL)
3004f416e82Scamield 			fatalx("hce_dispatch_pfe: desynchronized");
301feb9ff76Sreyk 		host->flags |= F_DISABLE;
302feb9ff76Sreyk 		host->up = HOST_UNKNOWN;
3032edd718bSreyk 		host->check_cnt = 0;
3042edd718bSreyk 		host->up_cnt = 0;
305c0dc99f6Sreyk 		host->he = HCE_NONE;
306feb9ff76Sreyk 		break;
307feb9ff76Sreyk 	case IMSG_HOST_ENABLE:
3080325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
309feb9ff76Sreyk 		if ((host = host_find(env, id)) == NULL)
3104f416e82Scamield 			fatalx("hce_dispatch_pfe: desynchronized");
311feb9ff76Sreyk 		host->flags &= ~(F_DISABLE);
312feb9ff76Sreyk 		host->up = HOST_UNKNOWN;
313c0dc99f6Sreyk 		host->he = HCE_NONE;
314feb9ff76Sreyk 		break;
315feb9ff76Sreyk 	case IMSG_TABLE_DISABLE:
3160325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
317feb9ff76Sreyk 		if ((table = table_find(env, id)) == NULL)
3184f416e82Scamield 			fatalx("hce_dispatch_pfe: desynchronized");
31968b79041Spyr 		table->conf.flags |= F_DISABLE;
320feb9ff76Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry)
321feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
322feb9ff76Sreyk 		break;
323feb9ff76Sreyk 	case IMSG_TABLE_ENABLE:
3240325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
325feb9ff76Sreyk 		if ((table = table_find(env, id)) == NULL)
3264f416e82Scamield 			fatalx("hce_dispatch_pfe: desynchronized");
32768b79041Spyr 		table->conf.flags &= ~(F_DISABLE);
328feb9ff76Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry)
329feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
330feb9ff76Sreyk 		break;
331cd65ce7bSpyr 	case IMSG_CTL_POLL:
33235d10c30Sreyk 		evtimer_del(&env->sc_ev);
33335d10c30Sreyk 		TAILQ_FOREACH(table, env->sc_tables, entry)
334f98352d8Spyr 			table->skipped = 0;
335cd65ce7bSpyr 		hce_launch_checks(-1, EV_TIMEOUT, env);
336cd65ce7bSpyr 		break;
337feb9ff76Sreyk 	default:
3380325c666Sreyk 		return (-1);
339feb9ff76Sreyk 	}
340feb9ff76Sreyk 
3410325c666Sreyk 	return (0);
3420325c666Sreyk }
3430325c666Sreyk 
3440325c666Sreyk int
3450325c666Sreyk hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
346feb9ff76Sreyk {
3474156152fSreyk 	struct ctl_script	 scr;
348feb9ff76Sreyk 
3490325c666Sreyk 	switch (imsg->hdr.type) {
3504156152fSreyk 	case IMSG_SCRIPT:
3510325c666Sreyk 		IMSG_SIZE_CHECK(imsg, &scr);
3520325c666Sreyk 		bcopy(imsg->data, &scr, sizeof(scr));
3534156152fSreyk 		script_done(env, &scr);
3544156152fSreyk 		break;
355a2195becSreyk 	case IMSG_CFG_TABLE:
356a2195becSreyk 		config_gettable(env, imsg);
357adf98c11Spyr 		break;
358a2195becSreyk 	case IMSG_CFG_HOST:
359a2195becSreyk 		config_gethost(env, imsg);
360adf98c11Spyr 		break;
361a2195becSreyk 	case IMSG_CFG_DONE:
362a2195becSreyk 		config_getcfg(env, imsg);
3630dcba380Scamield 		break;
3640dcba380Scamield 	case IMSG_CTL_START:
365adf98c11Spyr 		hce_setup_events();
366adf98c11Spyr 		break;
367a2195becSreyk 	case IMSG_CTL_RESET:
368a2195becSreyk 		config_getreset(env, imsg);
369a2195becSreyk 		break;
370feb9ff76Sreyk 	default:
3710325c666Sreyk 		return (-1);
372feb9ff76Sreyk 	}
3730325c666Sreyk 
3740325c666Sreyk 	return (0);
375feb9ff76Sreyk }
376*6e07057bSblambert 
377*6e07057bSblambert int
378*6e07057bSblambert hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
379*6e07057bSblambert {
380*6e07057bSblambert 	switch (imsg->hdr.type) {
381*6e07057bSblambert 	default:
382*6e07057bSblambert 		break;
383*6e07057bSblambert 	}
384*6e07057bSblambert 
385*6e07057bSblambert 	return (-1);
386*6e07057bSblambert }
387