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