xref: /openbsd/usr.sbin/relayd/hce.c (revision cca36db2)
1 /*	$OpenBSD: hce.c,v 1.63 2012/05/09 12:54:13 giovanni 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 	if (gettimeofday(&tv, NULL) == -1)
160 		fatal("hce_launch_checks: gettimeofday");
161 
162 	TAILQ_FOREACH(table, env->sc_tables, entry) {
163 		if (table->conf.flags & F_DISABLE)
164 			continue;
165 		if (table->conf.skip_cnt) {
166 			if (table->skipped++ > table->conf.skip_cnt)
167 				table->skipped = 0;
168 			if (table->skipped != 1)
169 				continue;
170 		}
171 		if (table->conf.check == CHECK_NOCHECK)
172 			fatalx("hce_launch_checks: unknown check type");
173 
174 		TAILQ_FOREACH(host, &table->hosts, entry) {
175 			if (host->flags & F_DISABLE || host->conf.parentid)
176 				continue;
177 			bcopy(&tv, &host->cte.tv_start,
178 			    sizeof(host->cte.tv_start));
179 			switch (table->conf.check) {
180 			case CHECK_ICMP:
181 				schedule_icmp(env, host);
182 				break;
183 			case CHECK_SCRIPT:
184 				check_script(env, host);
185 				break;
186 			default:
187 				/* Any other TCP-style checks */
188 				host->last_up = host->up;
189 				host->cte.host = host;
190 				host->cte.table = table;
191 				check_tcp(&host->cte);
192 				break;
193 			}
194 		}
195 	}
196 	check_icmp(env, &tv);
197 
198 	bcopy(&env->sc_interval, &tv, sizeof(tv));
199 	evtimer_add(&env->sc_ev, &tv);
200 }
201 
202 void
203 hce_notify_done(struct host *host, enum host_error he)
204 {
205 	struct table		*table;
206 	struct ctl_status	 st;
207 	struct timeval		 tv_now, tv_dur;
208 	u_long			 duration;
209 	u_int			 logopt;
210 	struct host		*h, *hostnst;
211 	int			 hostup;
212 	const char		*msg;
213 
214 	if ((hostnst = host_find(env, host->conf.id)) == NULL)
215 		fatalx("hce_notify_done: desynchronized");
216 
217 	if ((table = table_find(env, host->conf.tableid)) == NULL)
218 		fatalx("hce_notify_done: invalid table id");
219 
220 	if (hostnst->flags & F_DISABLE) {
221 		if (env->sc_opts & RELAYD_OPT_LOGUPDATE) {
222 			log_info("host %s, check %s%s (ignoring result, "
223 			    "host disabled)",
224 			    host->conf.name, table_check(table->conf.check),
225 			    (table->conf.flags & F_SSL) ? " use ssl" : "");
226 		}
227 		host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
228 		return;
229 	}
230 
231 	hostup = host->up;
232 	host->he = he;
233 
234 	if (host->up == HOST_DOWN && host->retry_cnt) {
235 		log_debug("%s: host %s retry %d", __func__,
236 		    host->conf.name, host->retry_cnt);
237 		host->up = host->last_up;
238 		host->retry_cnt--;
239 	} else
240 		host->retry_cnt = host->conf.retry;
241 	if (host->up != HOST_UNKNOWN) {
242 		host->check_cnt++;
243 		if (host->up == HOST_UP)
244 			host->up_cnt++;
245 	}
246 	st.id = host->conf.id;
247 	st.up = host->up;
248 	st.check_cnt = host->check_cnt;
249 	st.retry_cnt = host->retry_cnt;
250 	st.he = he;
251 	host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
252 	msg = host_error(he);
253 	if (msg)
254 		log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
255 
256 	proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_HOST_STATUS,
257 	    -1, &st, sizeof(st));
258 	if (host->up != host->last_up)
259 		logopt = RELAYD_OPT_LOGUPDATE;
260 	else
261 		logopt = RELAYD_OPT_LOGNOTIFY;
262 
263 	if (gettimeofday(&tv_now, NULL) == -1)
264 		fatal("hce_notify_done: gettimeofday");
265 	timersub(&tv_now, &host->cte.tv_start, &tv_dur);
266 	if (timercmp(&host->cte.tv_start, &tv_dur, >))
267 		duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
268 	else
269 		duration = 0;
270 
271 	if (env->sc_opts & logopt) {
272 		log_info("host %s, check %s%s (%lums), state %s -> %s, "
273 		    "availability %s",
274 		    host->conf.name, table_check(table->conf.check),
275 		    (table->conf.flags & F_SSL) ? " use ssl" : "", duration,
276 		    host_status(host->last_up), host_status(host->up),
277 		    print_availability(host->check_cnt, host->up_cnt));
278 	}
279 
280 	if (host->last_up != host->up)
281 		snmp_hosttrap(env, table, host);
282 
283 	host->last_up = host->up;
284 
285 	if (SLIST_EMPTY(&host->children))
286 		return;
287 
288 	/* Notify for all other hosts that inherit the state from this one */
289 	SLIST_FOREACH(h, &host->children, child) {
290 		h->up = hostup;
291 		hce_notify_done(h, he);
292 	}
293 }
294 
295 int
296 hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
297 {
298 	objid_t			 id;
299 	struct host		*host;
300 	struct table		*table;
301 
302 	switch (imsg->hdr.type) {
303 	case IMSG_HOST_DISABLE:
304 		memcpy(&id, imsg->data, sizeof(id));
305 		if ((host = host_find(env, id)) == NULL)
306 			fatalx("hce_dispatch_pfe: desynchronized");
307 		host->flags |= F_DISABLE;
308 		host->up = HOST_UNKNOWN;
309 		host->check_cnt = 0;
310 		host->up_cnt = 0;
311 		host->he = HCE_NONE;
312 		break;
313 	case IMSG_HOST_ENABLE:
314 		memcpy(&id, imsg->data, sizeof(id));
315 		if ((host = host_find(env, id)) == NULL)
316 			fatalx("hce_dispatch_pfe: desynchronized");
317 		host->flags &= ~(F_DISABLE);
318 		host->up = HOST_UNKNOWN;
319 		host->he = HCE_NONE;
320 		break;
321 	case IMSG_TABLE_DISABLE:
322 		memcpy(&id, imsg->data, sizeof(id));
323 		if ((table = table_find(env, id)) == NULL)
324 			fatalx("hce_dispatch_pfe: desynchronized");
325 		table->conf.flags |= F_DISABLE;
326 		TAILQ_FOREACH(host, &table->hosts, entry)
327 			host->up = HOST_UNKNOWN;
328 		break;
329 	case IMSG_TABLE_ENABLE:
330 		memcpy(&id, imsg->data, sizeof(id));
331 		if ((table = table_find(env, id)) == NULL)
332 			fatalx("hce_dispatch_pfe: desynchronized");
333 		table->conf.flags &= ~(F_DISABLE);
334 		TAILQ_FOREACH(host, &table->hosts, entry)
335 			host->up = HOST_UNKNOWN;
336 		break;
337 	case IMSG_CTL_POLL:
338 		evtimer_del(&env->sc_ev);
339 		TAILQ_FOREACH(table, env->sc_tables, entry)
340 			table->skipped = 0;
341 		hce_launch_checks(-1, EV_TIMEOUT, env);
342 		break;
343 	default:
344 		return (-1);
345 	}
346 
347 	return (0);
348 }
349 
350 int
351 hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
352 {
353 	struct ctl_script	 scr;
354 
355 	switch (imsg->hdr.type) {
356 	case IMSG_SCRIPT:
357 		IMSG_SIZE_CHECK(imsg, &scr);
358 		bcopy(imsg->data, &scr, sizeof(scr));
359 		script_done(env, &scr);
360 		break;
361 	case IMSG_CFG_TABLE:
362 		config_gettable(env, imsg);
363 		break;
364 	case IMSG_CFG_HOST:
365 		config_gethost(env, imsg);
366 		break;
367 	case IMSG_SNMPSOCK:
368 		snmp_getsock(env, imsg);
369 		break;
370 	case IMSG_CFG_DONE:
371 		config_getcfg(env, imsg);
372 		break;
373 	case IMSG_CTL_START:
374 		hce_setup_events();
375 		break;
376 	case IMSG_CTL_RESET:
377 		config_getreset(env, imsg);
378 		break;
379 	default:
380 		return (-1);
381 	}
382 
383 	return (0);
384 }
385