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