xref: /openbsd/usr.sbin/relayd/hce.c (revision e15601b9)
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 
32 void	 hce_init(struct privsep *, struct privsep_proc *p, void *);
33 void	 hce_launch_checks(int, short, void *);
34 void	 hce_setup_events(void);
35 void	 hce_disable_events(void);
36 
37 int	 hce_dispatch_parent(int, struct privsep_proc *, struct imsg *);
38 int	 hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
39 int	 hce_dispatch_relay(int, struct privsep_proc *, struct imsg *);
40 
41 static struct relayd *env = NULL;
42 int			 running = 0;
43 
44 static 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 
50 void
hce(struct privsep * ps,struct privsep_proc * p)51 hce(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 
61 void
hce_init(struct privsep * ps,struct privsep_proc * p,void * arg)62 hce_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 
76 void
hce_setup_events(void)77 hce_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 
102 void
hce_disable_events(void)103 hce_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 
128 void
hce_launch_checks(int fd,short event,void * arg)129 hce_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 
193 void
hce_notify_done(struct host * host,enum host_error he)194 hce_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 
284 int
hce_dispatch_pfe(int fd,struct privsep_proc * p,struct imsg * imsg)285 hce_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 
339 int
hce_dispatch_parent(int fd,struct privsep_proc * p,struct imsg * imsg)340 hce_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 
372 int
hce_dispatch_relay(int fd,struct privsep_proc * p,struct imsg * imsg)373 hce_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