xref: /openbsd-src/usr.sbin/relayd/hce.c (revision e15601b92a455e88bc02e7402d07250a11bffa0a)
1*e15601b9Sjsg /*	$OpenBSD: hce.c,v 1.82 2024/05/18 06:34:46 jsg 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_launch_checks(int, short, void *);
34641b8bafSpyr void	 hce_setup_events(void);
35641b8bafSpyr void	 hce_disable_events(void);
36feb9ff76Sreyk 
370325c666Sreyk int	 hce_dispatch_parent(int, struct privsep_proc *, struct imsg *);
380325c666Sreyk int	 hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
396e07057bSblambert int	 hce_dispatch_relay(int, struct privsep_proc *, struct imsg *);
400325c666Sreyk 
41748ceb64Sreyk static struct relayd *env = NULL;
42780e8f79Spyr int			 running = 0;
43feb9ff76Sreyk 
440325c666Sreyk static struct privsep_proc procs[] = {
450325c666Sreyk 	{ "parent",	PROC_PARENT,	hce_dispatch_parent },
460325c666Sreyk 	{ "pfe",	PROC_PFE,	hce_dispatch_pfe },
476e07057bSblambert 	{ "relay",	PROC_RELAY,	hce_dispatch_relay },
480325c666Sreyk };
49feb9ff76Sreyk 
50f910ac11Sreyk void
hce(struct privsep * ps,struct privsep_proc * p)510325c666Sreyk hce(struct privsep *ps, struct privsep_proc *p)
52feb9ff76Sreyk {
530325c666Sreyk 	env = ps->ps_env;
54feb9ff76Sreyk 
5501d85ec5Sreyk 	/* this is needed for icmp tests */
5601d85ec5Sreyk 	icmp_init(env);
5701d85ec5Sreyk 
58f910ac11Sreyk 	proc_run(ps, p, procs, nitems(procs), hce_init, NULL);
590325c666Sreyk }
60feb9ff76Sreyk 
610325c666Sreyk void
hce_init(struct privsep * ps,struct privsep_proc * p,void * arg)620325c666Sreyk hce_init(struct privsep *ps, struct privsep_proc *p, void *arg)
630325c666Sreyk {
64a2195becSreyk 	if (config_init(ps->ps_env) == -1)
65a2195becSreyk 		fatal("failed to initialize configuration");
660325c666Sreyk 
670325c666Sreyk 	env->sc_id = getpid() & 0xffff;
68feb9ff76Sreyk 
69bb8fa3feSreyk 	/* Allow maximum available sockets for TCP checks */
70bb8fa3feSreyk 	socket_rlimit(-1);
71cf9e83c3Sbenno 
723d6ff6edSreyk 	if (pledge("stdio recvfd inet", NULL) == -1)
73efc39811Sbenno 		fatal("%s: pledge", __func__);
74780e8f79Spyr }
75780e8f79Spyr 
76780e8f79Spyr void
hce_setup_events(void)77641b8bafSpyr hce_setup_events(void)
78780e8f79Spyr {
79780e8f79Spyr 	struct timeval	 tv;
80780e8f79Spyr 	struct table	*table;
81780e8f79Spyr 
829ba713feSbenno 	if (!event_initialized(&env->sc_ev)) {
8335d10c30Sreyk 		evtimer_set(&env->sc_ev, hce_launch_checks, env);
8401d85ec5Sreyk 		bzero(&tv, sizeof(tv));
8535d10c30Sreyk 		evtimer_add(&env->sc_ev, &tv);
862edd718bSreyk 	}
87feb9ff76Sreyk 
88586b5f8aSreyk 	if (env->sc_conf.flags & F_TLS) {
8935d10c30Sreyk 		TAILQ_FOREACH(table, env->sc_tables, entry) {
907bb52228Sreyk 			if (!(table->conf.flags & F_TLS) ||
9185e5f500Sclaudio 			    table->tls_cfg != NULL)
92e8fb3979Spyr 				continue;
9385e5f500Sclaudio 			table->tls_cfg = tls_config_new();
94e87883d9Stb 			if (table->tls_cfg == NULL)
95e87883d9Stb 				fatalx("%s: tls_config_new", __func__);
9685e5f500Sclaudio 			tls_config_insecure_noverifycert(table->tls_cfg);
9785e5f500Sclaudio 			tls_config_insecure_noverifyname(table->tls_cfg);
98e8fb3979Spyr 		}
99e8fb3979Spyr 	}
100641b8bafSpyr }
101641b8bafSpyr 
102641b8bafSpyr void
hce_disable_events(void)103641b8bafSpyr hce_disable_events(void)
104641b8bafSpyr {
105641b8bafSpyr 	struct table	*table;
106641b8bafSpyr 	struct host	*host;
107641b8bafSpyr 
10835d10c30Sreyk 	evtimer_del(&env->sc_ev);
10935d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
110641b8bafSpyr 		TAILQ_FOREACH(host, &table->hosts, entry) {
111c0dc99f6Sreyk 			host->he = HCE_ABORT;
112a2195becSreyk 			if (event_initialized(&host->cte.ev)) {
113641b8bafSpyr 				event_del(&host->cte.ev);
114641b8bafSpyr 				close(host->cte.s);
115641b8bafSpyr 			}
116641b8bafSpyr 		}
117a2195becSreyk 	}
11835d10c30Sreyk 	if (env->sc_has_icmp) {
11935d10c30Sreyk 		event_del(&env->sc_icmp_send.ev);
12035d10c30Sreyk 		event_del(&env->sc_icmp_recv.ev);
121641b8bafSpyr 	}
12235d10c30Sreyk 	if (env->sc_has_icmp6) {
12335d10c30Sreyk 		event_del(&env->sc_icmp6_send.ev);
12435d10c30Sreyk 		event_del(&env->sc_icmp6_recv.ev);
125641b8bafSpyr 	}
126feb9ff76Sreyk }
127feb9ff76Sreyk 
128feb9ff76Sreyk void
hce_launch_checks(int fd,short event,void * arg)129feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg)
130feb9ff76Sreyk {
131feb9ff76Sreyk 	struct host		*host;
132feb9ff76Sreyk 	struct table		*table;
13301d85ec5Sreyk 	struct timeval		 tv;
13401d85ec5Sreyk 
13501d85ec5Sreyk 	/*
13601d85ec5Sreyk 	 * notify pfe checks are done and schedule next check
13701d85ec5Sreyk 	 */
138c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_PFE, IMSG_SYNC, NULL, 0);
13935d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
14001d85ec5Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
141c0dc99f6Sreyk 			if ((host->flags & F_CHECK_DONE) == 0)
142c0dc99f6Sreyk 				host->he = HCE_INTERVAL_TIMEOUT;
143a2195becSreyk 			if (event_initialized(&host->cte.ev)) {
14401d85ec5Sreyk 				event_del(&host->cte.ev);
145a2195becSreyk 				close(host->cte.s);
146a2195becSreyk 			}
147a2195becSreyk 			host->cte.s = -1;
14801d85ec5Sreyk 		}
14901d85ec5Sreyk 	}
15001d85ec5Sreyk 
151fd1841a3Sreyk 	getmonotime(&tv);
152feb9ff76Sreyk 
15335d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
15468b79041Spyr 		if (table->conf.flags & F_DISABLE)
1557e351ffdSreyk 			continue;
15618de159aSpyr 		if (table->conf.skip_cnt) {
15718de159aSpyr 			if (table->skipped++ > table->conf.skip_cnt)
15818de159aSpyr 				table->skipped = 0;
15918de159aSpyr 			if (table->skipped != 1)
16018de159aSpyr 				continue;
16118de159aSpyr 		}
16268b79041Spyr 		if (table->conf.check == CHECK_NOCHECK)
163efc39811Sbenno 			fatalx("%s: unknown check type", __func__);
16401d85ec5Sreyk 
1657e351ffdSreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
166c723f8edSreyk 			if (host->flags & F_DISABLE || host->conf.parentid)
1677e351ffdSreyk 				continue;
168a7da800aSsthen 			bcopy(&tv, &host->cte.tv_start,
169a7da800aSsthen 			    sizeof(host->cte.tv_start));
1704156152fSreyk 			switch (table->conf.check) {
1714156152fSreyk 			case CHECK_ICMP:
17201d85ec5Sreyk 				schedule_icmp(env, host);
1734156152fSreyk 				break;
1744156152fSreyk 			case CHECK_SCRIPT:
1750325c666Sreyk 				check_script(env, host);
1764156152fSreyk 				break;
1774156152fSreyk 			default:
17801d85ec5Sreyk 				/* Any other TCP-style checks */
1797e351ffdSreyk 				host->last_up = host->up;
1807e351ffdSreyk 				host->cte.host = host;
1817e351ffdSreyk 				host->cte.table = table;
1827e351ffdSreyk 				check_tcp(&host->cte);
1834156152fSreyk 				break;
1844156152fSreyk 			}
1857e351ffdSreyk 		}
1867e351ffdSreyk 	}
18701d85ec5Sreyk 	check_icmp(env, &tv);
18801d85ec5Sreyk 
189586b5f8aSreyk 	bcopy(&env->sc_conf.interval, &tv, sizeof(tv));
19035d10c30Sreyk 	evtimer_add(&env->sc_ev, &tv);
1917e351ffdSreyk }
1927e351ffdSreyk 
1937e351ffdSreyk void
hce_notify_done(struct host * host,enum host_error he)194c0dc99f6Sreyk hce_notify_done(struct host *host, enum host_error he)
1957e351ffdSreyk {
196609cf3a7Sreyk 	struct table		*table;
1977e351ffdSreyk 	struct ctl_status	 st;
198609cf3a7Sreyk 	struct timeval		 tv_now, tv_dur;
199609cf3a7Sreyk 	u_long			 duration;
2000be9d00aSbenno 	u_int			 logopt = RELAYD_OPT_LOGHOSTCHECK;
20174a0367eSgiovanni 	struct host		*h, *hostnst;
202c723f8edSreyk 	int			 hostup;
203c0dc99f6Sreyk 	const char		*msg;
204c48fdb1dSbenno 	char			*codemsg = NULL;
205c723f8edSreyk 
20674a0367eSgiovanni 	if ((hostnst = host_find(env, host->conf.id)) == NULL)
207efc39811Sbenno 		fatalx("%s: desynchronized", __func__);
20874a0367eSgiovanni 
20974a0367eSgiovanni 	if ((table = table_find(env, host->conf.tableid)) == NULL)
210efc39811Sbenno 		fatalx("%s: invalid table id", __func__);
21174a0367eSgiovanni 
21274a0367eSgiovanni 	if (hostnst->flags & F_DISABLE) {
213586b5f8aSreyk 		if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE) {
21474a0367eSgiovanni 			log_info("host %s, check %s%s (ignoring result, "
21574a0367eSgiovanni 			    "host disabled)",
21674a0367eSgiovanni 			    host->conf.name, table_check(table->conf.check),
2177bb52228Sreyk 			    (table->conf.flags & F_TLS) ? " use tls" : "");
21874a0367eSgiovanni 		}
21974a0367eSgiovanni 		host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
22074a0367eSgiovanni 		return;
22174a0367eSgiovanni 	}
22274a0367eSgiovanni 
223c723f8edSreyk 	hostup = host->up;
224c0dc99f6Sreyk 	host->he = he;
2257e351ffdSreyk 
2262edd718bSreyk 	if (host->up == HOST_DOWN && host->retry_cnt) {
22785a8c65fSreyk 		log_debug("%s: host %s retry %d", __func__,
22868b79041Spyr 		    host->conf.name, host->retry_cnt);
229bd2508bcSreyk 		host->up = host->last_up;
2302edd718bSreyk 		host->retry_cnt--;
2312edd718bSreyk 	} else
23268b79041Spyr 		host->retry_cnt = host->conf.retry;
2332edd718bSreyk 	if (host->up != HOST_UNKNOWN) {
2342edd718bSreyk 		host->check_cnt++;
2352edd718bSreyk 		if (host->up == HOST_UP)
2362edd718bSreyk 			host->up_cnt++;
2372edd718bSreyk 	}
23868b79041Spyr 	st.id = host->conf.id;
239feb9ff76Sreyk 	st.up = host->up;
2402edd718bSreyk 	st.check_cnt = host->check_cnt;
2412edd718bSreyk 	st.retry_cnt = host->retry_cnt;
242c0dc99f6Sreyk 	st.he = he;
24301d85ec5Sreyk 	host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
244c0dc99f6Sreyk 	msg = host_error(he);
2457e351ffdSreyk 	if (msg)
24685a8c65fSreyk 		log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
247609cf3a7Sreyk 
248c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_PFE, IMSG_HOST_STATUS, &st, sizeof(st));
2492edd718bSreyk 	if (host->up != host->last_up)
250748ceb64Sreyk 		logopt = RELAYD_OPT_LOGUPDATE;
251609cf3a7Sreyk 
252fd1841a3Sreyk 	getmonotime(&tv_now);
253609cf3a7Sreyk 	timersub(&tv_now, &host->cte.tv_start, &tv_dur);
254609cf3a7Sreyk 	if (timercmp(&host->cte.tv_start, &tv_dur, >))
255609cf3a7Sreyk 		duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
256609cf3a7Sreyk 	else
257609cf3a7Sreyk 		duration = 0;
258609cf3a7Sreyk 
259586b5f8aSreyk 	if (env->sc_conf.opts & logopt) {
260c48fdb1dSbenno 		if (host->code > 0)
261c48fdb1dSbenno 		    asprintf(&codemsg, ",%d", host->code);
262c48fdb1dSbenno 		log_info("host %s, check %s%s (%lums,%s%s), state %s -> %s, "
2632edd718bSreyk 		    "availability %s",
26468b79041Spyr 		    host->conf.name, table_check(table->conf.check),
2657bb52228Sreyk 		    (table->conf.flags & F_TLS) ? " use tls" : "", duration,
266c48fdb1dSbenno 		    msg, (codemsg != NULL) ? codemsg : "",
2672edd718bSreyk 		    host_status(host->last_up), host_status(host->up),
2682edd718bSreyk 		    print_availability(host->check_cnt, host->up_cnt));
269c48fdb1dSbenno 		free(codemsg);
270feb9ff76Sreyk 	}
271fe250497Sreyk 
272609cf3a7Sreyk 	host->last_up = host->up;
273c723f8edSreyk 
2741584e35dSreyk 	if (SLIST_EMPTY(&host->children))
275c723f8edSreyk 		return;
276c723f8edSreyk 
277c723f8edSreyk 	/* Notify for all other hosts that inherit the state from this one */
2781584e35dSreyk 	SLIST_FOREACH(h, &host->children, child) {
279c723f8edSreyk 		h->up = hostup;
280c0dc99f6Sreyk 		hce_notify_done(h, he);
281c723f8edSreyk 	}
282feb9ff76Sreyk }
283feb9ff76Sreyk 
2840325c666Sreyk int
hce_dispatch_pfe(int fd,struct privsep_proc * p,struct imsg * imsg)2850325c666Sreyk hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
286feb9ff76Sreyk {
287feb9ff76Sreyk 	objid_t			 id;
288feb9ff76Sreyk 	struct host		*host;
289feb9ff76Sreyk 	struct table		*table;
290feb9ff76Sreyk 
2910325c666Sreyk 	switch (imsg->hdr.type) {
292feb9ff76Sreyk 	case IMSG_HOST_DISABLE:
2930325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
294feb9ff76Sreyk 		if ((host = host_find(env, id)) == NULL)
295efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
296feb9ff76Sreyk 		host->flags |= F_DISABLE;
297feb9ff76Sreyk 		host->up = HOST_UNKNOWN;
2982edd718bSreyk 		host->check_cnt = 0;
2992edd718bSreyk 		host->up_cnt = 0;
300c0dc99f6Sreyk 		host->he = HCE_NONE;
301feb9ff76Sreyk 		break;
302feb9ff76Sreyk 	case IMSG_HOST_ENABLE:
3030325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
304feb9ff76Sreyk 		if ((host = host_find(env, id)) == NULL)
305efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
306feb9ff76Sreyk 		host->flags &= ~(F_DISABLE);
307feb9ff76Sreyk 		host->up = HOST_UNKNOWN;
308c0dc99f6Sreyk 		host->he = HCE_NONE;
309feb9ff76Sreyk 		break;
310feb9ff76Sreyk 	case IMSG_TABLE_DISABLE:
3110325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
312feb9ff76Sreyk 		if ((table = table_find(env, id)) == NULL)
313efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
31468b79041Spyr 		table->conf.flags |= F_DISABLE;
315feb9ff76Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry)
316feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
317feb9ff76Sreyk 		break;
318feb9ff76Sreyk 	case IMSG_TABLE_ENABLE:
3190325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
320feb9ff76Sreyk 		if ((table = table_find(env, id)) == NULL)
321efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
32268b79041Spyr 		table->conf.flags &= ~(F_DISABLE);
323feb9ff76Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry)
324feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
325feb9ff76Sreyk 		break;
326cd65ce7bSpyr 	case IMSG_CTL_POLL:
32735d10c30Sreyk 		evtimer_del(&env->sc_ev);
32835d10c30Sreyk 		TAILQ_FOREACH(table, env->sc_tables, entry)
329f98352d8Spyr 			table->skipped = 0;
330cd65ce7bSpyr 		hce_launch_checks(-1, EV_TIMEOUT, env);
331cd65ce7bSpyr 		break;
332feb9ff76Sreyk 	default:
3330325c666Sreyk 		return (-1);
334feb9ff76Sreyk 	}
335feb9ff76Sreyk 
3360325c666Sreyk 	return (0);
3370325c666Sreyk }
3380325c666Sreyk 
3390325c666Sreyk int
hce_dispatch_parent(int fd,struct privsep_proc * p,struct imsg * imsg)3400325c666Sreyk hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
341feb9ff76Sreyk {
3424156152fSreyk 	struct ctl_script	 scr;
343feb9ff76Sreyk 
3440325c666Sreyk 	switch (imsg->hdr.type) {
3454156152fSreyk 	case IMSG_SCRIPT:
3460325c666Sreyk 		IMSG_SIZE_CHECK(imsg, &scr);
3470325c666Sreyk 		bcopy(imsg->data, &scr, sizeof(scr));
3484156152fSreyk 		script_done(env, &scr);
3494156152fSreyk 		break;
350a2195becSreyk 	case IMSG_CFG_TABLE:
351a2195becSreyk 		config_gettable(env, imsg);
352adf98c11Spyr 		break;
353a2195becSreyk 	case IMSG_CFG_HOST:
354a2195becSreyk 		config_gethost(env, imsg);
355adf98c11Spyr 		break;
356a2195becSreyk 	case IMSG_CFG_DONE:
357a2195becSreyk 		config_getcfg(env, imsg);
3580dcba380Scamield 		break;
3590dcba380Scamield 	case IMSG_CTL_START:
360adf98c11Spyr 		hce_setup_events();
361adf98c11Spyr 		break;
362a2195becSreyk 	case IMSG_CTL_RESET:
363a2195becSreyk 		config_getreset(env, imsg);
364a2195becSreyk 		break;
365feb9ff76Sreyk 	default:
3660325c666Sreyk 		return (-1);
367feb9ff76Sreyk 	}
3680325c666Sreyk 
3690325c666Sreyk 	return (0);
370feb9ff76Sreyk }
3716e07057bSblambert 
3726e07057bSblambert int
hce_dispatch_relay(int fd,struct privsep_proc * p,struct imsg * imsg)3736e07057bSblambert hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
3746e07057bSblambert {
3756e07057bSblambert 	switch (imsg->hdr.type) {
3766e07057bSblambert 	default:
3776e07057bSblambert 		break;
3786e07057bSblambert 	}
3796e07057bSblambert 
3806e07057bSblambert 	return (-1);
3816e07057bSblambert }
382