xref: /openbsd/usr.sbin/relayd/hce.c (revision e87883d9)
1*e87883d9Stb /*	$OpenBSD: hce.c,v 1.81 2022/06/03 13:23:16 tb 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 
19f04ff968Sreyk #include <sys/types.h>
200ca734d7Sreyk #include <sys/queue.h>
21feb9ff76Sreyk #include <sys/time.h>
22f04ff968Sreyk #include <sys/uio.h>
230ca734d7Sreyk 
24feb9ff76Sreyk #include <event.h>
25feb9ff76Sreyk #include <stdlib.h>
26feb9ff76Sreyk #include <string.h>
27feb9ff76Sreyk #include <unistd.h>
28f04ff968Sreyk #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 
51f910ac11Sreyk void
hce(struct privsep * ps,struct privsep_proc * p)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 
59f910ac11Sreyk 	proc_run(ps, p, procs, nitems(procs), hce_init, NULL);
600325c666Sreyk }
61feb9ff76Sreyk 
620325c666Sreyk void
hce_init(struct privsep * ps,struct privsep_proc * p,void * arg)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);
72cf9e83c3Sbenno 
733d6ff6edSreyk 	if (pledge("stdio recvfd inet", NULL) == -1)
74efc39811Sbenno 		fatal("%s: pledge", __func__);
75780e8f79Spyr }
76780e8f79Spyr 
77780e8f79Spyr void
hce_setup_events(void)78641b8bafSpyr hce_setup_events(void)
79780e8f79Spyr {
80780e8f79Spyr 	struct timeval	 tv;
81780e8f79Spyr 	struct table	*table;
82780e8f79Spyr 
839ba713feSbenno 	if (!event_initialized(&env->sc_ev)) {
8435d10c30Sreyk 		evtimer_set(&env->sc_ev, hce_launch_checks, env);
8501d85ec5Sreyk 		bzero(&tv, sizeof(tv));
8635d10c30Sreyk 		evtimer_add(&env->sc_ev, &tv);
872edd718bSreyk 	}
88feb9ff76Sreyk 
89586b5f8aSreyk 	if (env->sc_conf.flags & F_TLS) {
9035d10c30Sreyk 		TAILQ_FOREACH(table, env->sc_tables, entry) {
917bb52228Sreyk 			if (!(table->conf.flags & F_TLS) ||
9285e5f500Sclaudio 			    table->tls_cfg != NULL)
93e8fb3979Spyr 				continue;
9485e5f500Sclaudio 			table->tls_cfg = tls_config_new();
95*e87883d9Stb 			if (table->tls_cfg == NULL)
96*e87883d9Stb 				fatalx("%s: tls_config_new", __func__);
9785e5f500Sclaudio 			tls_config_insecure_noverifycert(table->tls_cfg);
9885e5f500Sclaudio 			tls_config_insecure_noverifyname(table->tls_cfg);
99e8fb3979Spyr 		}
100e8fb3979Spyr 	}
101641b8bafSpyr }
102641b8bafSpyr 
103641b8bafSpyr void
hce_disable_events(void)104641b8bafSpyr hce_disable_events(void)
105641b8bafSpyr {
106641b8bafSpyr 	struct table	*table;
107641b8bafSpyr 	struct host	*host;
108641b8bafSpyr 
10935d10c30Sreyk 	evtimer_del(&env->sc_ev);
11035d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
111641b8bafSpyr 		TAILQ_FOREACH(host, &table->hosts, entry) {
112c0dc99f6Sreyk 			host->he = HCE_ABORT;
113a2195becSreyk 			if (event_initialized(&host->cte.ev)) {
114641b8bafSpyr 				event_del(&host->cte.ev);
115641b8bafSpyr 				close(host->cte.s);
116641b8bafSpyr 			}
117641b8bafSpyr 		}
118a2195becSreyk 	}
11935d10c30Sreyk 	if (env->sc_has_icmp) {
12035d10c30Sreyk 		event_del(&env->sc_icmp_send.ev);
12135d10c30Sreyk 		event_del(&env->sc_icmp_recv.ev);
122641b8bafSpyr 	}
12335d10c30Sreyk 	if (env->sc_has_icmp6) {
12435d10c30Sreyk 		event_del(&env->sc_icmp6_send.ev);
12535d10c30Sreyk 		event_del(&env->sc_icmp6_recv.ev);
126641b8bafSpyr 	}
127feb9ff76Sreyk }
128feb9ff76Sreyk 
129feb9ff76Sreyk void
hce_launch_checks(int fd,short event,void * arg)130feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg)
131feb9ff76Sreyk {
132feb9ff76Sreyk 	struct host		*host;
133feb9ff76Sreyk 	struct table		*table;
13401d85ec5Sreyk 	struct timeval		 tv;
13501d85ec5Sreyk 
13601d85ec5Sreyk 	/*
13701d85ec5Sreyk 	 * notify pfe checks are done and schedule next check
13801d85ec5Sreyk 	 */
139c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_PFE, IMSG_SYNC, NULL, 0);
14035d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
14101d85ec5Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
142c0dc99f6Sreyk 			if ((host->flags & F_CHECK_DONE) == 0)
143c0dc99f6Sreyk 				host->he = HCE_INTERVAL_TIMEOUT;
144a2195becSreyk 			if (event_initialized(&host->cte.ev)) {
14501d85ec5Sreyk 				event_del(&host->cte.ev);
146a2195becSreyk 				close(host->cte.s);
147a2195becSreyk 			}
148a2195becSreyk 			host->cte.s = -1;
14901d85ec5Sreyk 		}
15001d85ec5Sreyk 	}
15101d85ec5Sreyk 
152fd1841a3Sreyk 	getmonotime(&tv);
153feb9ff76Sreyk 
15435d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
15568b79041Spyr 		if (table->conf.flags & F_DISABLE)
1567e351ffdSreyk 			continue;
15718de159aSpyr 		if (table->conf.skip_cnt) {
15818de159aSpyr 			if (table->skipped++ > table->conf.skip_cnt)
15918de159aSpyr 				table->skipped = 0;
16018de159aSpyr 			if (table->skipped != 1)
16118de159aSpyr 				continue;
16218de159aSpyr 		}
16368b79041Spyr 		if (table->conf.check == CHECK_NOCHECK)
164efc39811Sbenno 			fatalx("%s: unknown check type", __func__);
16501d85ec5Sreyk 
1667e351ffdSreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
167c723f8edSreyk 			if (host->flags & F_DISABLE || host->conf.parentid)
1687e351ffdSreyk 				continue;
169a7da800aSsthen 			bcopy(&tv, &host->cte.tv_start,
170a7da800aSsthen 			    sizeof(host->cte.tv_start));
1714156152fSreyk 			switch (table->conf.check) {
1724156152fSreyk 			case CHECK_ICMP:
17301d85ec5Sreyk 				schedule_icmp(env, host);
1744156152fSreyk 				break;
1754156152fSreyk 			case CHECK_SCRIPT:
1760325c666Sreyk 				check_script(env, host);
1774156152fSreyk 				break;
1784156152fSreyk 			default:
17901d85ec5Sreyk 				/* Any other TCP-style checks */
1807e351ffdSreyk 				host->last_up = host->up;
1817e351ffdSreyk 				host->cte.host = host;
1827e351ffdSreyk 				host->cte.table = table;
1837e351ffdSreyk 				check_tcp(&host->cte);
1844156152fSreyk 				break;
1854156152fSreyk 			}
1867e351ffdSreyk 		}
1877e351ffdSreyk 	}
18801d85ec5Sreyk 	check_icmp(env, &tv);
18901d85ec5Sreyk 
190586b5f8aSreyk 	bcopy(&env->sc_conf.interval, &tv, sizeof(tv));
19135d10c30Sreyk 	evtimer_add(&env->sc_ev, &tv);
1927e351ffdSreyk }
1937e351ffdSreyk 
1947e351ffdSreyk void
hce_notify_done(struct host * host,enum host_error he)195c0dc99f6Sreyk hce_notify_done(struct host *host, enum host_error he)
1967e351ffdSreyk {
197609cf3a7Sreyk 	struct table		*table;
1987e351ffdSreyk 	struct ctl_status	 st;
199609cf3a7Sreyk 	struct timeval		 tv_now, tv_dur;
200609cf3a7Sreyk 	u_long			 duration;
2010be9d00aSbenno 	u_int			 logopt = RELAYD_OPT_LOGHOSTCHECK;
20274a0367eSgiovanni 	struct host		*h, *hostnst;
203c723f8edSreyk 	int			 hostup;
204c0dc99f6Sreyk 	const char		*msg;
205c48fdb1dSbenno 	char			*codemsg = NULL;
206c723f8edSreyk 
20774a0367eSgiovanni 	if ((hostnst = host_find(env, host->conf.id)) == NULL)
208efc39811Sbenno 		fatalx("%s: desynchronized", __func__);
20974a0367eSgiovanni 
21074a0367eSgiovanni 	if ((table = table_find(env, host->conf.tableid)) == NULL)
211efc39811Sbenno 		fatalx("%s: invalid table id", __func__);
21274a0367eSgiovanni 
21374a0367eSgiovanni 	if (hostnst->flags & F_DISABLE) {
214586b5f8aSreyk 		if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE) {
21574a0367eSgiovanni 			log_info("host %s, check %s%s (ignoring result, "
21674a0367eSgiovanni 			    "host disabled)",
21774a0367eSgiovanni 			    host->conf.name, table_check(table->conf.check),
2187bb52228Sreyk 			    (table->conf.flags & F_TLS) ? " use tls" : "");
21974a0367eSgiovanni 		}
22074a0367eSgiovanni 		host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
22174a0367eSgiovanni 		return;
22274a0367eSgiovanni 	}
22374a0367eSgiovanni 
224c723f8edSreyk 	hostup = host->up;
225c0dc99f6Sreyk 	host->he = he;
2267e351ffdSreyk 
2272edd718bSreyk 	if (host->up == HOST_DOWN && host->retry_cnt) {
22885a8c65fSreyk 		log_debug("%s: host %s retry %d", __func__,
22968b79041Spyr 		    host->conf.name, host->retry_cnt);
230bd2508bcSreyk 		host->up = host->last_up;
2312edd718bSreyk 		host->retry_cnt--;
2322edd718bSreyk 	} else
23368b79041Spyr 		host->retry_cnt = host->conf.retry;
2342edd718bSreyk 	if (host->up != HOST_UNKNOWN) {
2352edd718bSreyk 		host->check_cnt++;
2362edd718bSreyk 		if (host->up == HOST_UP)
2372edd718bSreyk 			host->up_cnt++;
2382edd718bSreyk 	}
23968b79041Spyr 	st.id = host->conf.id;
240feb9ff76Sreyk 	st.up = host->up;
2412edd718bSreyk 	st.check_cnt = host->check_cnt;
2422edd718bSreyk 	st.retry_cnt = host->retry_cnt;
243c0dc99f6Sreyk 	st.he = he;
24401d85ec5Sreyk 	host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
245c0dc99f6Sreyk 	msg = host_error(he);
2467e351ffdSreyk 	if (msg)
24785a8c65fSreyk 		log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
248609cf3a7Sreyk 
249c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_PFE, IMSG_HOST_STATUS, &st, sizeof(st));
2502edd718bSreyk 	if (host->up != host->last_up)
251748ceb64Sreyk 		logopt = RELAYD_OPT_LOGUPDATE;
252609cf3a7Sreyk 
253fd1841a3Sreyk 	getmonotime(&tv_now);
254609cf3a7Sreyk 	timersub(&tv_now, &host->cte.tv_start, &tv_dur);
255609cf3a7Sreyk 	if (timercmp(&host->cte.tv_start, &tv_dur, >))
256609cf3a7Sreyk 		duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
257609cf3a7Sreyk 	else
258609cf3a7Sreyk 		duration = 0;
259609cf3a7Sreyk 
260586b5f8aSreyk 	if (env->sc_conf.opts & logopt) {
261c48fdb1dSbenno 		if (host->code > 0)
262c48fdb1dSbenno 		    asprintf(&codemsg, ",%d", host->code);
263c48fdb1dSbenno 		log_info("host %s, check %s%s (%lums,%s%s), state %s -> %s, "
2642edd718bSreyk 		    "availability %s",
26568b79041Spyr 		    host->conf.name, table_check(table->conf.check),
2667bb52228Sreyk 		    (table->conf.flags & F_TLS) ? " use tls" : "", duration,
267c48fdb1dSbenno 		    msg, (codemsg != NULL) ? codemsg : "",
2682edd718bSreyk 		    host_status(host->last_up), host_status(host->up),
2692edd718bSreyk 		    print_availability(host->check_cnt, host->up_cnt));
270c48fdb1dSbenno 		free(codemsg);
271feb9ff76Sreyk 	}
272fe250497Sreyk 
273609cf3a7Sreyk 	host->last_up = host->up;
274c723f8edSreyk 
2751584e35dSreyk 	if (SLIST_EMPTY(&host->children))
276c723f8edSreyk 		return;
277c723f8edSreyk 
278c723f8edSreyk 	/* Notify for all other hosts that inherit the state from this one */
2791584e35dSreyk 	SLIST_FOREACH(h, &host->children, child) {
280c723f8edSreyk 		h->up = hostup;
281c0dc99f6Sreyk 		hce_notify_done(h, he);
282c723f8edSreyk 	}
283feb9ff76Sreyk }
284feb9ff76Sreyk 
2850325c666Sreyk int
hce_dispatch_pfe(int fd,struct privsep_proc * p,struct imsg * imsg)2860325c666Sreyk hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
287feb9ff76Sreyk {
288feb9ff76Sreyk 	objid_t			 id;
289feb9ff76Sreyk 	struct host		*host;
290feb9ff76Sreyk 	struct table		*table;
291feb9ff76Sreyk 
2920325c666Sreyk 	switch (imsg->hdr.type) {
293feb9ff76Sreyk 	case IMSG_HOST_DISABLE:
2940325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
295feb9ff76Sreyk 		if ((host = host_find(env, id)) == NULL)
296efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
297feb9ff76Sreyk 		host->flags |= F_DISABLE;
298feb9ff76Sreyk 		host->up = HOST_UNKNOWN;
2992edd718bSreyk 		host->check_cnt = 0;
3002edd718bSreyk 		host->up_cnt = 0;
301c0dc99f6Sreyk 		host->he = HCE_NONE;
302feb9ff76Sreyk 		break;
303feb9ff76Sreyk 	case IMSG_HOST_ENABLE:
3040325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
305feb9ff76Sreyk 		if ((host = host_find(env, id)) == NULL)
306efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
307feb9ff76Sreyk 		host->flags &= ~(F_DISABLE);
308feb9ff76Sreyk 		host->up = HOST_UNKNOWN;
309c0dc99f6Sreyk 		host->he = HCE_NONE;
310feb9ff76Sreyk 		break;
311feb9ff76Sreyk 	case IMSG_TABLE_DISABLE:
3120325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
313feb9ff76Sreyk 		if ((table = table_find(env, id)) == NULL)
314efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
31568b79041Spyr 		table->conf.flags |= F_DISABLE;
316feb9ff76Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry)
317feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
318feb9ff76Sreyk 		break;
319feb9ff76Sreyk 	case IMSG_TABLE_ENABLE:
3200325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
321feb9ff76Sreyk 		if ((table = table_find(env, id)) == NULL)
322efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
32368b79041Spyr 		table->conf.flags &= ~(F_DISABLE);
324feb9ff76Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry)
325feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
326feb9ff76Sreyk 		break;
327cd65ce7bSpyr 	case IMSG_CTL_POLL:
32835d10c30Sreyk 		evtimer_del(&env->sc_ev);
32935d10c30Sreyk 		TAILQ_FOREACH(table, env->sc_tables, entry)
330f98352d8Spyr 			table->skipped = 0;
331cd65ce7bSpyr 		hce_launch_checks(-1, EV_TIMEOUT, env);
332cd65ce7bSpyr 		break;
333feb9ff76Sreyk 	default:
3340325c666Sreyk 		return (-1);
335feb9ff76Sreyk 	}
336feb9ff76Sreyk 
3370325c666Sreyk 	return (0);
3380325c666Sreyk }
3390325c666Sreyk 
3400325c666Sreyk int
hce_dispatch_parent(int fd,struct privsep_proc * p,struct imsg * imsg)3410325c666Sreyk hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
342feb9ff76Sreyk {
3434156152fSreyk 	struct ctl_script	 scr;
344feb9ff76Sreyk 
3450325c666Sreyk 	switch (imsg->hdr.type) {
3464156152fSreyk 	case IMSG_SCRIPT:
3470325c666Sreyk 		IMSG_SIZE_CHECK(imsg, &scr);
3480325c666Sreyk 		bcopy(imsg->data, &scr, sizeof(scr));
3494156152fSreyk 		script_done(env, &scr);
3504156152fSreyk 		break;
351a2195becSreyk 	case IMSG_CFG_TABLE:
352a2195becSreyk 		config_gettable(env, imsg);
353adf98c11Spyr 		break;
354a2195becSreyk 	case IMSG_CFG_HOST:
355a2195becSreyk 		config_gethost(env, imsg);
356adf98c11Spyr 		break;
357a2195becSreyk 	case IMSG_CFG_DONE:
358a2195becSreyk 		config_getcfg(env, imsg);
3590dcba380Scamield 		break;
3600dcba380Scamield 	case IMSG_CTL_START:
361adf98c11Spyr 		hce_setup_events();
362adf98c11Spyr 		break;
363a2195becSreyk 	case IMSG_CTL_RESET:
364a2195becSreyk 		config_getreset(env, imsg);
365a2195becSreyk 		break;
366feb9ff76Sreyk 	default:
3670325c666Sreyk 		return (-1);
368feb9ff76Sreyk 	}
3690325c666Sreyk 
3700325c666Sreyk 	return (0);
371feb9ff76Sreyk }
3726e07057bSblambert 
3736e07057bSblambert int
hce_dispatch_relay(int fd,struct privsep_proc * p,struct imsg * imsg)3746e07057bSblambert hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
3756e07057bSblambert {
3766e07057bSblambert 	switch (imsg->hdr.type) {
3776e07057bSblambert 	default:
3786e07057bSblambert 		break;
3796e07057bSblambert 	}
3806e07057bSblambert 
3816e07057bSblambert 	return (-1);
3826e07057bSblambert }
383