1/*	$OpenBSD: hce.c,v 1.82 2024/05/18 06:34:46 jsg Exp $	*/
2
3/*
4 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/queue.h>
21#include <sys/time.h>
22#include <sys/uio.h>
23
24#include <event.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28#include <imsg.h>
29
30#include "relayd.h"
31
32void	 hce_init(struct privsep *, struct privsep_proc *p, void *);
33void	 hce_launch_checks(int, short, void *);
34void	 hce_setup_events(void);
35void	 hce_disable_events(void);
36
37int	 hce_dispatch_parent(int, struct privsep_proc *, struct imsg *);
38int	 hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
39int	 hce_dispatch_relay(int, struct privsep_proc *, struct imsg *);
40
41static struct relayd *env = NULL;
42int			 running = 0;
43
44static struct privsep_proc procs[] = {
45	{ "parent",	PROC_PARENT,	hce_dispatch_parent },
46	{ "pfe",	PROC_PFE,	hce_dispatch_pfe },
47	{ "relay",	PROC_RELAY,	hce_dispatch_relay },
48};
49
50void
51hce(struct privsep *ps, struct privsep_proc *p)
52{
53	env = ps->ps_env;
54
55	/* this is needed for icmp tests */
56	icmp_init(env);
57
58	proc_run(ps, p, procs, nitems(procs), hce_init, NULL);
59}
60
61void
62hce_init(struct privsep *ps, struct privsep_proc *p, void *arg)
63{
64	if (config_init(ps->ps_env) == -1)
65		fatal("failed to initialize configuration");
66
67	env->sc_id = getpid() & 0xffff;
68
69	/* Allow maximum available sockets for TCP checks */
70	socket_rlimit(-1);
71
72	if (pledge("stdio recvfd inet", NULL) == -1)
73		fatal("%s: pledge", __func__);
74}
75
76void
77hce_setup_events(void)
78{
79	struct timeval	 tv;
80	struct table	*table;
81
82	if (!event_initialized(&env->sc_ev)) {
83		evtimer_set(&env->sc_ev, hce_launch_checks, env);
84		bzero(&tv, sizeof(tv));
85		evtimer_add(&env->sc_ev, &tv);
86	}
87
88	if (env->sc_conf.flags & F_TLS) {
89		TAILQ_FOREACH(table, env->sc_tables, entry) {
90			if (!(table->conf.flags & F_TLS) ||
91			    table->tls_cfg != NULL)
92				continue;
93			table->tls_cfg = tls_config_new();
94			if (table->tls_cfg == NULL)
95				fatalx("%s: tls_config_new", __func__);
96			tls_config_insecure_noverifycert(table->tls_cfg);
97			tls_config_insecure_noverifyname(table->tls_cfg);
98		}
99	}
100}
101
102void
103hce_disable_events(void)
104{
105	struct table	*table;
106	struct host	*host;
107
108	evtimer_del(&env->sc_ev);
109	TAILQ_FOREACH(table, env->sc_tables, entry) {
110		TAILQ_FOREACH(host, &table->hosts, entry) {
111			host->he = HCE_ABORT;
112			if (event_initialized(&host->cte.ev)) {
113				event_del(&host->cte.ev);
114				close(host->cte.s);
115			}
116		}
117	}
118	if (env->sc_has_icmp) {
119		event_del(&env->sc_icmp_send.ev);
120		event_del(&env->sc_icmp_recv.ev);
121	}
122	if (env->sc_has_icmp6) {
123		event_del(&env->sc_icmp6_send.ev);
124		event_del(&env->sc_icmp6_recv.ev);
125	}
126}
127
128void
129hce_launch_checks(int fd, short event, void *arg)
130{
131	struct host		*host;
132	struct table		*table;
133	struct timeval		 tv;
134
135	/*
136	 * notify pfe checks are done and schedule next check
137	 */
138	proc_compose(env->sc_ps, PROC_PFE, IMSG_SYNC, NULL, 0);
139	TAILQ_FOREACH(table, env->sc_tables, entry) {
140		TAILQ_FOREACH(host, &table->hosts, entry) {
141			if ((host->flags & F_CHECK_DONE) == 0)
142				host->he = HCE_INTERVAL_TIMEOUT;
143			if (event_initialized(&host->cte.ev)) {
144				event_del(&host->cte.ev);
145				close(host->cte.s);
146			}
147			host->cte.s = -1;
148		}
149	}
150
151	getmonotime(&tv);
152
153	TAILQ_FOREACH(table, env->sc_tables, entry) {
154		if (table->conf.flags & F_DISABLE)
155			continue;
156		if (table->conf.skip_cnt) {
157			if (table->skipped++ > table->conf.skip_cnt)
158				table->skipped = 0;
159			if (table->skipped != 1)
160				continue;
161		}
162		if (table->conf.check == CHECK_NOCHECK)
163			fatalx("%s: unknown check type", __func__);
164
165		TAILQ_FOREACH(host, &table->hosts, entry) {
166			if (host->flags & F_DISABLE || host->conf.parentid)
167				continue;
168			bcopy(&tv, &host->cte.tv_start,
169			    sizeof(host->cte.tv_start));
170			switch (table->conf.check) {
171			case CHECK_ICMP:
172				schedule_icmp(env, host);
173				break;
174			case CHECK_SCRIPT:
175				check_script(env, host);
176				break;
177			default:
178				/* Any other TCP-style checks */
179				host->last_up = host->up;
180				host->cte.host = host;
181				host->cte.table = table;
182				check_tcp(&host->cte);
183				break;
184			}
185		}
186	}
187	check_icmp(env, &tv);
188
189	bcopy(&env->sc_conf.interval, &tv, sizeof(tv));
190	evtimer_add(&env->sc_ev, &tv);
191}
192
193void
194hce_notify_done(struct host *host, enum host_error he)
195{
196	struct table		*table;
197	struct ctl_status	 st;
198	struct timeval		 tv_now, tv_dur;
199	u_long			 duration;
200	u_int			 logopt = RELAYD_OPT_LOGHOSTCHECK;
201	struct host		*h, *hostnst;
202	int			 hostup;
203	const char		*msg;
204	char			*codemsg = NULL;
205
206	if ((hostnst = host_find(env, host->conf.id)) == NULL)
207		fatalx("%s: desynchronized", __func__);
208
209	if ((table = table_find(env, host->conf.tableid)) == NULL)
210		fatalx("%s: invalid table id", __func__);
211
212	if (hostnst->flags & F_DISABLE) {
213		if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE) {
214			log_info("host %s, check %s%s (ignoring result, "
215			    "host disabled)",
216			    host->conf.name, table_check(table->conf.check),
217			    (table->conf.flags & F_TLS) ? " use tls" : "");
218		}
219		host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
220		return;
221	}
222
223	hostup = host->up;
224	host->he = he;
225
226	if (host->up == HOST_DOWN && host->retry_cnt) {
227		log_debug("%s: host %s retry %d", __func__,
228		    host->conf.name, host->retry_cnt);
229		host->up = host->last_up;
230		host->retry_cnt--;
231	} else
232		host->retry_cnt = host->conf.retry;
233	if (host->up != HOST_UNKNOWN) {
234		host->check_cnt++;
235		if (host->up == HOST_UP)
236			host->up_cnt++;
237	}
238	st.id = host->conf.id;
239	st.up = host->up;
240	st.check_cnt = host->check_cnt;
241	st.retry_cnt = host->retry_cnt;
242	st.he = he;
243	host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
244	msg = host_error(he);
245	if (msg)
246		log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
247
248	proc_compose(env->sc_ps, PROC_PFE, IMSG_HOST_STATUS, &st, sizeof(st));
249	if (host->up != host->last_up)
250		logopt = RELAYD_OPT_LOGUPDATE;
251
252	getmonotime(&tv_now);
253	timersub(&tv_now, &host->cte.tv_start, &tv_dur);
254	if (timercmp(&host->cte.tv_start, &tv_dur, >))
255		duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
256	else
257		duration = 0;
258
259	if (env->sc_conf.opts & logopt) {
260		if (host->code > 0)
261		    asprintf(&codemsg, ",%d", host->code);
262		log_info("host %s, check %s%s (%lums,%s%s), state %s -> %s, "
263		    "availability %s",
264		    host->conf.name, table_check(table->conf.check),
265		    (table->conf.flags & F_TLS) ? " use tls" : "", duration,
266		    msg, (codemsg != NULL) ? codemsg : "",
267		    host_status(host->last_up), host_status(host->up),
268		    print_availability(host->check_cnt, host->up_cnt));
269		free(codemsg);
270	}
271
272	host->last_up = host->up;
273
274	if (SLIST_EMPTY(&host->children))
275		return;
276
277	/* Notify for all other hosts that inherit the state from this one */
278	SLIST_FOREACH(h, &host->children, child) {
279		h->up = hostup;
280		hce_notify_done(h, he);
281	}
282}
283
284int
285hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
286{
287	objid_t			 id;
288	struct host		*host;
289	struct table		*table;
290
291	switch (imsg->hdr.type) {
292	case IMSG_HOST_DISABLE:
293		memcpy(&id, imsg->data, sizeof(id));
294		if ((host = host_find(env, id)) == NULL)
295			fatalx("%s: desynchronized", __func__);
296		host->flags |= F_DISABLE;
297		host->up = HOST_UNKNOWN;
298		host->check_cnt = 0;
299		host->up_cnt = 0;
300		host->he = HCE_NONE;
301		break;
302	case IMSG_HOST_ENABLE:
303		memcpy(&id, imsg->data, sizeof(id));
304		if ((host = host_find(env, id)) == NULL)
305			fatalx("%s: desynchronized", __func__);
306		host->flags &= ~(F_DISABLE);
307		host->up = HOST_UNKNOWN;
308		host->he = HCE_NONE;
309		break;
310	case IMSG_TABLE_DISABLE:
311		memcpy(&id, imsg->data, sizeof(id));
312		if ((table = table_find(env, id)) == NULL)
313			fatalx("%s: desynchronized", __func__);
314		table->conf.flags |= F_DISABLE;
315		TAILQ_FOREACH(host, &table->hosts, entry)
316			host->up = HOST_UNKNOWN;
317		break;
318	case IMSG_TABLE_ENABLE:
319		memcpy(&id, imsg->data, sizeof(id));
320		if ((table = table_find(env, id)) == NULL)
321			fatalx("%s: desynchronized", __func__);
322		table->conf.flags &= ~(F_DISABLE);
323		TAILQ_FOREACH(host, &table->hosts, entry)
324			host->up = HOST_UNKNOWN;
325		break;
326	case IMSG_CTL_POLL:
327		evtimer_del(&env->sc_ev);
328		TAILQ_FOREACH(table, env->sc_tables, entry)
329			table->skipped = 0;
330		hce_launch_checks(-1, EV_TIMEOUT, env);
331		break;
332	default:
333		return (-1);
334	}
335
336	return (0);
337}
338
339int
340hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
341{
342	struct ctl_script	 scr;
343
344	switch (imsg->hdr.type) {
345	case IMSG_SCRIPT:
346		IMSG_SIZE_CHECK(imsg, &scr);
347		bcopy(imsg->data, &scr, sizeof(scr));
348		script_done(env, &scr);
349		break;
350	case IMSG_CFG_TABLE:
351		config_gettable(env, imsg);
352		break;
353	case IMSG_CFG_HOST:
354		config_gethost(env, imsg);
355		break;
356	case IMSG_CFG_DONE:
357		config_getcfg(env, imsg);
358		break;
359	case IMSG_CTL_START:
360		hce_setup_events();
361		break;
362	case IMSG_CTL_RESET:
363		config_getreset(env, imsg);
364		break;
365	default:
366		return (-1);
367	}
368
369	return (0);
370}
371
372int
373hce_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
374{
375	switch (imsg->hdr.type) {
376	default:
377		break;
378	}
379
380	return (-1);
381}
382