xref: /openbsd-src/usr.sbin/relayd/hce.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: hce.c,v 1.60 2011/05/19 08:56:49 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 	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;
211 	int			 hostup;
212 	const char		*msg;
213 
214 	hostup = host->up;
215 	host->he = he;
216 
217 	if (host->up == HOST_DOWN && host->retry_cnt) {
218 		log_debug("%s: host %s retry %d", __func__,
219 		    host->conf.name, host->retry_cnt);
220 		host->up = host->last_up;
221 		host->retry_cnt--;
222 	} else
223 		host->retry_cnt = host->conf.retry;
224 	if (host->up != HOST_UNKNOWN) {
225 		host->check_cnt++;
226 		if (host->up == HOST_UP)
227 			host->up_cnt++;
228 	}
229 	st.id = host->conf.id;
230 	st.up = host->up;
231 	st.check_cnt = host->check_cnt;
232 	st.retry_cnt = host->retry_cnt;
233 	st.he = he;
234 	host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
235 	msg = host_error(he);
236 	if (msg)
237 		log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
238 
239 	proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_HOST_STATUS,
240 	    -1, &st, sizeof(st));
241 	if (host->up != host->last_up)
242 		logopt = RELAYD_OPT_LOGUPDATE;
243 	else
244 		logopt = RELAYD_OPT_LOGNOTIFY;
245 
246 	if (gettimeofday(&tv_now, NULL) == -1)
247 		fatal("hce_notify_done: gettimeofday");
248 	timersub(&tv_now, &host->cte.tv_start, &tv_dur);
249 	if (timercmp(&host->cte.tv_start, &tv_dur, >))
250 		duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
251 	else
252 		duration = 0;
253 
254 	if ((table = table_find(env, host->conf.tableid)) == NULL)
255 		fatalx("hce_notify_done: invalid table id");
256 
257 	if (env->sc_opts & logopt) {
258 		log_info("host %s, check %s%s (%lums), state %s -> %s, "
259 		    "availability %s",
260 		    host->conf.name, table_check(table->conf.check),
261 		    (table->conf.flags & F_SSL) ? " use ssl" : "", duration,
262 		    host_status(host->last_up), host_status(host->up),
263 		    print_availability(host->check_cnt, host->up_cnt));
264 	}
265 
266 	if (host->last_up != host->up)
267 		snmp_hosttrap(env, table, host);
268 
269 	host->last_up = host->up;
270 
271 	if (SLIST_EMPTY(&host->children))
272 		return;
273 
274 	/* Notify for all other hosts that inherit the state from this one */
275 	SLIST_FOREACH(h, &host->children, child) {
276 		h->up = hostup;
277 		hce_notify_done(h, he);
278 	}
279 }
280 
281 int
282 hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
283 {
284 	objid_t			 id;
285 	struct host		*host;
286 	struct table		*table;
287 
288 	switch (imsg->hdr.type) {
289 	case IMSG_HOST_DISABLE:
290 		memcpy(&id, imsg->data, sizeof(id));
291 		if ((host = host_find(env, id)) == NULL)
292 			fatalx("hce_dispatch_imsg: desynchronized");
293 		host->flags |= F_DISABLE;
294 		host->up = HOST_UNKNOWN;
295 		host->check_cnt = 0;
296 		host->up_cnt = 0;
297 		host->he = HCE_NONE;
298 		break;
299 	case IMSG_HOST_ENABLE:
300 		memcpy(&id, imsg->data, sizeof(id));
301 		if ((host = host_find(env, id)) == NULL)
302 			fatalx("hce_dispatch_imsg: desynchronized");
303 		host->flags &= ~(F_DISABLE);
304 		host->up = HOST_UNKNOWN;
305 		host->he = HCE_NONE;
306 		break;
307 	case IMSG_TABLE_DISABLE:
308 		memcpy(&id, imsg->data, sizeof(id));
309 		if ((table = table_find(env, id)) == NULL)
310 			fatalx("hce_dispatch_imsg: desynchronized");
311 		table->conf.flags |= F_DISABLE;
312 		TAILQ_FOREACH(host, &table->hosts, entry)
313 			host->up = HOST_UNKNOWN;
314 		break;
315 	case IMSG_TABLE_ENABLE:
316 		memcpy(&id, imsg->data, sizeof(id));
317 		if ((table = table_find(env, id)) == NULL)
318 			fatalx("hce_dispatch_imsg: desynchronized");
319 		table->conf.flags &= ~(F_DISABLE);
320 		TAILQ_FOREACH(host, &table->hosts, entry)
321 			host->up = HOST_UNKNOWN;
322 		break;
323 	case IMSG_CTL_POLL:
324 		evtimer_del(&env->sc_ev);
325 		TAILQ_FOREACH(table, env->sc_tables, entry)
326 			table->skipped = 0;
327 		hce_launch_checks(-1, EV_TIMEOUT, env);
328 		break;
329 	default:
330 		return (-1);
331 	}
332 
333 	return (0);
334 }
335 
336 int
337 hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
338 {
339 	struct ctl_script	 scr;
340 
341 	switch (imsg->hdr.type) {
342 	case IMSG_SCRIPT:
343 		IMSG_SIZE_CHECK(imsg, &scr);
344 		bcopy(imsg->data, &scr, sizeof(scr));
345 		script_done(env, &scr);
346 		break;
347 	case IMSG_CFG_TABLE:
348 		config_gettable(env, imsg);
349 		break;
350 	case IMSG_CFG_HOST:
351 		config_gethost(env, imsg);
352 		break;
353 	case IMSG_SNMPSOCK:
354 		snmp_getsock(env, imsg);
355 		break;
356 	case IMSG_CFG_DONE:
357 		config_getcfg(env, imsg);
358 		hce_setup_events();
359 		break;
360 	case IMSG_CTL_RESET:
361 		config_getreset(env, imsg);
362 		break;
363 	default:
364 		return (-1);
365 	}
366 
367 	return (0);
368 }
369