xref: /openbsd/usr.sbin/relayd/hce.c (revision e8fb3979)
1*e8fb3979Spyr /*	$OpenBSD: hce.c,v 1.12 2007/01/29 14:23:31 pyr Exp $	*/
2feb9ff76Sreyk 
3feb9ff76Sreyk /*
4feb9ff76Sreyk  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
5feb9ff76Sreyk  *
6feb9ff76Sreyk  * Permission to use, copy, modify, and distribute this software for any
7feb9ff76Sreyk  * purpose with or without fee is hereby granted, provided that the above
8feb9ff76Sreyk  * copyright notice and this permission notice appear in all copies.
9feb9ff76Sreyk  *
10feb9ff76Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11feb9ff76Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12feb9ff76Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13feb9ff76Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14feb9ff76Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15feb9ff76Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16feb9ff76Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17feb9ff76Sreyk  */
18feb9ff76Sreyk 
19feb9ff76Sreyk #include <sys/queue.h>
20feb9ff76Sreyk #include <sys/param.h>
21feb9ff76Sreyk #include <sys/types.h>
22feb9ff76Sreyk #include <sys/time.h>
23feb9ff76Sreyk #include <sys/stat.h>
24feb9ff76Sreyk #include <sys/socket.h>
25feb9ff76Sreyk #include <sys/un.h>
26feb9ff76Sreyk #include <netinet/in_systm.h>
27feb9ff76Sreyk #include <netinet/in.h>
28feb9ff76Sreyk #include <netinet/ip.h>
29feb9ff76Sreyk #include <net/if.h>
30feb9ff76Sreyk #include <errno.h>
31feb9ff76Sreyk #include <event.h>
32feb9ff76Sreyk #include <fcntl.h>
33feb9ff76Sreyk #include <stdlib.h>
34feb9ff76Sreyk #include <string.h>
35feb9ff76Sreyk #include <unistd.h>
36feb9ff76Sreyk #include <err.h>
37feb9ff76Sreyk #include <pwd.h>
38feb9ff76Sreyk 
39*e8fb3979Spyr #include <openssl/ssl.h>
40*e8fb3979Spyr 
41ee9f3836Sderaadt #include "hoststated.h"
42feb9ff76Sreyk 
43feb9ff76Sreyk void	hce_sig_handler(int sig, short, void *);
44feb9ff76Sreyk void	hce_shutdown(void);
45feb9ff76Sreyk void	hce_dispatch_imsg(int, short, void *);
46feb9ff76Sreyk void	hce_dispatch_parent(int, short, void *);
47feb9ff76Sreyk void	hce_launch_checks(int, short, void *);
487e351ffdSreyk int	hce_checks_done(void);
49feb9ff76Sreyk 
508f79868bSpyr static struct hoststated	*env = NULL;
51feb9ff76Sreyk struct imsgbuf		*ibuf_pfe;
52feb9ff76Sreyk struct imsgbuf		*ibuf_main;
53feb9ff76Sreyk 
54feb9ff76Sreyk void
55feb9ff76Sreyk hce_sig_handler(int sig, short event, void *arg)
56feb9ff76Sreyk {
57feb9ff76Sreyk 	switch (sig) {
58feb9ff76Sreyk 	case SIGINT:
59feb9ff76Sreyk 	case SIGTERM:
60feb9ff76Sreyk 		hce_shutdown();
61feb9ff76Sreyk 	default:
62feb9ff76Sreyk 		fatalx("hce_sig_handler: unexpected signal");
63feb9ff76Sreyk 	}
64feb9ff76Sreyk }
65feb9ff76Sreyk 
66feb9ff76Sreyk pid_t
678f79868bSpyr hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
68feb9ff76Sreyk 	int pipe_pfe2hce[2])
69feb9ff76Sreyk {
70feb9ff76Sreyk 	pid_t		 pid;
71feb9ff76Sreyk 	struct passwd	*pw;
72feb9ff76Sreyk 	struct timeval	 tv;
73feb9ff76Sreyk 	struct event	 ev_sigint;
74feb9ff76Sreyk 	struct event	 ev_sigterm;
75*e8fb3979Spyr 	struct table	*table;
76feb9ff76Sreyk 
77feb9ff76Sreyk 	switch (pid = fork()) {
78feb9ff76Sreyk 	case -1:
79feb9ff76Sreyk 		fatal("hce: cannot fork");
80feb9ff76Sreyk 	case 0:
81feb9ff76Sreyk 		break;
82feb9ff76Sreyk 	default:
83feb9ff76Sreyk 		return (pid);
84feb9ff76Sreyk 	}
85feb9ff76Sreyk 
86feb9ff76Sreyk 	env = x_env;
87feb9ff76Sreyk 
888f79868bSpyr 	if ((pw = getpwnam(HOSTSTATED_USER)) == NULL)
89feb9ff76Sreyk 		fatal("hce: getpwnam");
90feb9ff76Sreyk 
91feb9ff76Sreyk 	if (chroot(pw->pw_dir) == -1)
92feb9ff76Sreyk 		fatal("hce: chroot");
93feb9ff76Sreyk 	if (chdir("/") == -1)
94feb9ff76Sreyk 		fatal("hce: chdir(\"/\")");
95feb9ff76Sreyk 
96feb9ff76Sreyk 	setproctitle("host check engine");
978f79868bSpyr 	hoststated_process = PROC_HCE;
98feb9ff76Sreyk 
9901d85ec5Sreyk 	/* this is needed for icmp tests */
10001d85ec5Sreyk 	icmp_init(env);
10101d85ec5Sreyk 
102feb9ff76Sreyk 	if (setgroups(1, &pw->pw_gid) ||
103feb9ff76Sreyk 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
104feb9ff76Sreyk 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
105feb9ff76Sreyk 		fatal("hce: can't drop privileges");
106feb9ff76Sreyk 
107feb9ff76Sreyk 	event_init();
108feb9ff76Sreyk 
109feb9ff76Sreyk 	signal_set(&ev_sigint, SIGINT, hce_sig_handler, NULL);
110feb9ff76Sreyk 	signal_set(&ev_sigterm, SIGTERM, hce_sig_handler, NULL);
111feb9ff76Sreyk 	signal_add(&ev_sigint, NULL);
112feb9ff76Sreyk 	signal_add(&ev_sigterm, NULL);
113d043a2b1Sclaudio 	signal(SIGPIPE, SIG_IGN);
114feb9ff76Sreyk 
115feb9ff76Sreyk 	/* setup pipes */
116feb9ff76Sreyk 	close(pipe_pfe2hce[1]);
117feb9ff76Sreyk 	close(pipe_parent2hce[0]);
118feb9ff76Sreyk 	close(pipe_parent2pfe[0]);
119feb9ff76Sreyk 	close(pipe_parent2pfe[1]);
120feb9ff76Sreyk 
121feb9ff76Sreyk 	if ((ibuf_pfe = calloc(1, sizeof(struct imsgbuf))) == NULL ||
122feb9ff76Sreyk 	    (ibuf_main = calloc(1, sizeof(struct imsgbuf))) == NULL)
123feb9ff76Sreyk 		fatal("hce");
124feb9ff76Sreyk 	imsg_init(ibuf_pfe, pipe_pfe2hce[0], hce_dispatch_imsg);
125feb9ff76Sreyk 	imsg_init(ibuf_main, pipe_parent2hce[1], hce_dispatch_parent);
126feb9ff76Sreyk 
127feb9ff76Sreyk 	ibuf_pfe->events = EV_READ;
128feb9ff76Sreyk 	event_set(&ibuf_pfe->ev, ibuf_pfe->fd, ibuf_pfe->events,
129feb9ff76Sreyk 	    ibuf_pfe->handler, ibuf_pfe);
130feb9ff76Sreyk 	event_add(&ibuf_pfe->ev, NULL);
131feb9ff76Sreyk 
132feb9ff76Sreyk 	ibuf_main->events = EV_READ;
133feb9ff76Sreyk 	event_set(&ibuf_main->ev, ibuf_main->fd, ibuf_main->events,
134feb9ff76Sreyk 	    ibuf_main->handler, ibuf_main);
135feb9ff76Sreyk 	event_add(&ibuf_main->ev, NULL);
136feb9ff76Sreyk 
13701d85ec5Sreyk 	evtimer_set(&env->ev, hce_launch_checks, env);
13801d85ec5Sreyk 	bzero(&tv, sizeof(tv));
139feb9ff76Sreyk 	evtimer_add(&env->ev, &tv);
140feb9ff76Sreyk 
141*e8fb3979Spyr 	if (env->flags & F_SSL) {
142*e8fb3979Spyr 		ssl_init(env);
143*e8fb3979Spyr 		TAILQ_FOREACH(table, &env->tables, entry) {
144*e8fb3979Spyr 			if (!(table->flags & F_SSL))
145*e8fb3979Spyr 				continue;
146*e8fb3979Spyr 			table->ssl_ctx = ssl_ctx_create(env);
147*e8fb3979Spyr 		}
148*e8fb3979Spyr 	}
149*e8fb3979Spyr 
150feb9ff76Sreyk 	event_dispatch();
151feb9ff76Sreyk 
152feb9ff76Sreyk 	hce_shutdown();
153feb9ff76Sreyk 
154feb9ff76Sreyk 	return (0);
155feb9ff76Sreyk }
156feb9ff76Sreyk 
157feb9ff76Sreyk void
158feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg)
159feb9ff76Sreyk {
160feb9ff76Sreyk 	struct host		*host;
161feb9ff76Sreyk 	struct table		*table;
16201d85ec5Sreyk 	struct timeval		 tv;
16301d85ec5Sreyk 
16401d85ec5Sreyk 	log_debug("hce_launch_checks: scheduled");
16501d85ec5Sreyk 
16601d85ec5Sreyk 	/*
16701d85ec5Sreyk 	 * notify pfe checks are done and schedule next check
16801d85ec5Sreyk 	 */
16901d85ec5Sreyk 	imsg_compose(ibuf_pfe, IMSG_SYNC, 0, 0, NULL, 0);
17001d85ec5Sreyk 	TAILQ_FOREACH(table, &env->tables, entry) {
17101d85ec5Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
17201d85ec5Sreyk 			host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
17301d85ec5Sreyk 			event_del(&host->cte.ev);
17401d85ec5Sreyk 		}
17501d85ec5Sreyk 	}
17601d85ec5Sreyk 
17701d85ec5Sreyk 	if (gettimeofday(&tv, NULL))
17801d85ec5Sreyk 		fatal("hce_launch_checks: gettimeofday");
179feb9ff76Sreyk 
1807e351ffdSreyk 	TAILQ_FOREACH(table, &env->tables, entry) {
1817e351ffdSreyk 		if (table->flags & F_DISABLE)
1827e351ffdSreyk 			continue;
1837e351ffdSreyk 		if (table->check == CHECK_NOCHECK)
1847e351ffdSreyk 			fatalx("hce_launch_checks: unknown check type");
18501d85ec5Sreyk 
1867e351ffdSreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
1877e351ffdSreyk 			if (host->flags & F_DISABLE)
1887e351ffdSreyk 				continue;
18901d85ec5Sreyk 			if (table->check == CHECK_ICMP) {
19001d85ec5Sreyk 				schedule_icmp(env, host);
19101d85ec5Sreyk 				continue;
19201d85ec5Sreyk 			}
19301d85ec5Sreyk 
19401d85ec5Sreyk 			/* Any other TCP-style checks */
1957e351ffdSreyk 			bzero(&host->cte, sizeof(host->cte));
1967e351ffdSreyk 			host->last_up = host->up;
1977e351ffdSreyk 			host->cte.host = host;
1987e351ffdSreyk 			host->cte.table = table;
19901d85ec5Sreyk 			bcopy(&tv, &host->cte.tv_start,
20001d85ec5Sreyk 			    sizeof(host->cte.tv_start));
2017e351ffdSreyk 			check_tcp(&host->cte);
2027e351ffdSreyk 		}
2037e351ffdSreyk 	}
20401d85ec5Sreyk 	check_icmp(env, &tv);
20501d85ec5Sreyk 
20601d85ec5Sreyk 	bcopy(&env->interval, &tv, sizeof(tv));
20701d85ec5Sreyk 	evtimer_add(&env->ev, &tv);
2087e351ffdSreyk }
2097e351ffdSreyk 
2107e351ffdSreyk int
2117e351ffdSreyk hce_checks_done()
2127e351ffdSreyk {
2137e351ffdSreyk 	struct table		*table;
2147e351ffdSreyk 	struct host		*host;
2157e351ffdSreyk 
216feb9ff76Sreyk 	TAILQ_FOREACH(table, &env->tables, entry) {
217feb9ff76Sreyk 		if (table->flags & F_DISABLE)
218feb9ff76Sreyk 			continue;
219feb9ff76Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
220feb9ff76Sreyk 			if (host->flags & F_DISABLE)
221feb9ff76Sreyk 				continue;
2227e351ffdSreyk 			if (!(host->flags & F_CHECK_DONE))
2237e351ffdSreyk 				return (0);
224feb9ff76Sreyk 		}
2257e351ffdSreyk 	}
2267e351ffdSreyk 	return (1);
2277e351ffdSreyk }
2287e351ffdSreyk 
2297e351ffdSreyk void
2307e351ffdSreyk hce_notify_done(struct host *host, const char *msg)
2317e351ffdSreyk {
2327e351ffdSreyk 	struct ctl_status	 st;
2337e351ffdSreyk 
234feb9ff76Sreyk 	st.id = host->id;
235feb9ff76Sreyk 	st.up = host->up;
23601d85ec5Sreyk 	host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
2377e351ffdSreyk 	if (msg)
23801d85ec5Sreyk 		log_debug("hce_notify_done: %s (%s)", host->name, msg);
2397e351ffdSreyk 	if (host->up != host->last_up) {
2407e351ffdSreyk 		imsg_compose(ibuf_pfe, IMSG_HOST_STATUS, 0, 0, &st, sizeof(st));
2417e351ffdSreyk 		host->last_up = host->up;
242feb9ff76Sreyk 	}
243feb9ff76Sreyk }
244feb9ff76Sreyk 
245feb9ff76Sreyk void
246feb9ff76Sreyk hce_shutdown(void)
247feb9ff76Sreyk {
248feb9ff76Sreyk 	log_info("host check engine exiting");
249feb9ff76Sreyk 	_exit(0);
250feb9ff76Sreyk }
251feb9ff76Sreyk 
252feb9ff76Sreyk void
253feb9ff76Sreyk hce_dispatch_imsg(int fd, short event, void *ptr)
254feb9ff76Sreyk {
255feb9ff76Sreyk 	struct imsgbuf		*ibuf;
256feb9ff76Sreyk 	struct imsg		 imsg;
257feb9ff76Sreyk 	ssize_t			 n;
258feb9ff76Sreyk 	objid_t			 id;
259feb9ff76Sreyk 	struct host		*host;
260feb9ff76Sreyk 	struct table		*table;
261feb9ff76Sreyk 
262feb9ff76Sreyk 	ibuf = ptr;
263feb9ff76Sreyk 	switch (event) {
264feb9ff76Sreyk 	case EV_READ:
265feb9ff76Sreyk 		if ((n = imsg_read(ibuf)) == -1)
266feb9ff76Sreyk 			fatal("hce_dispatch_imsg: imsg_read_error");
267feb9ff76Sreyk 		if (n == 0)
268feb9ff76Sreyk 			fatalx("hce_dispatch_imsg: pipe closed");
269feb9ff76Sreyk 		break;
270feb9ff76Sreyk 	case EV_WRITE:
271feb9ff76Sreyk 		if (msgbuf_write(&ibuf->w) == -1)
272feb9ff76Sreyk 			fatal("hce_dispatch_imsg: msgbuf_write");
273feb9ff76Sreyk 		imsg_event_add(ibuf);
274feb9ff76Sreyk 		return;
275feb9ff76Sreyk 	default:
276feb9ff76Sreyk 		fatalx("hce_dispatch_imsg: unknown event");
277feb9ff76Sreyk 	}
278feb9ff76Sreyk 
279feb9ff76Sreyk 	for (;;) {
280feb9ff76Sreyk 		if ((n = imsg_get(ibuf, &imsg)) == -1)
281feb9ff76Sreyk 			fatal("hce_dispatch_imsg: imsg_read error");
282feb9ff76Sreyk 		if (n == 0)
283feb9ff76Sreyk 			break;
284feb9ff76Sreyk 
285feb9ff76Sreyk 		switch (imsg.hdr.type) {
286feb9ff76Sreyk 		case IMSG_HOST_DISABLE:
287feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
288feb9ff76Sreyk 			if ((host = host_find(env, id)) == NULL)
289feb9ff76Sreyk 				fatalx("hce_dispatch_imsg: desynchronized");
290feb9ff76Sreyk 			host->flags |= F_DISABLE;
291feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
292feb9ff76Sreyk 			break;
293feb9ff76Sreyk 		case IMSG_HOST_ENABLE:
294feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
295feb9ff76Sreyk 			if ((host = host_find(env, id)) == NULL)
296feb9ff76Sreyk 				fatalx("hce_dispatch_imsg: desynchronized");
297feb9ff76Sreyk 			host->flags &= ~(F_DISABLE);
298feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
299feb9ff76Sreyk 			break;
300feb9ff76Sreyk 		case IMSG_TABLE_DISABLE:
301feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
302feb9ff76Sreyk 			if ((table = table_find(env, id)) == NULL)
303feb9ff76Sreyk 				fatalx("hce_dispatch_imsg: desynchronized");
304feb9ff76Sreyk 			table->flags |= F_DISABLE;
305feb9ff76Sreyk 			TAILQ_FOREACH(host, &table->hosts, entry)
306feb9ff76Sreyk 				host->up = HOST_UNKNOWN;
307feb9ff76Sreyk 			break;
308feb9ff76Sreyk 		case IMSG_TABLE_ENABLE:
309feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
310feb9ff76Sreyk 			if ((table = table_find(env, id)) == NULL)
311feb9ff76Sreyk 				fatalx("hce_dispatch_imsg: desynchronized");
312feb9ff76Sreyk 			table->flags &= ~(F_DISABLE);
313feb9ff76Sreyk 			TAILQ_FOREACH(host, &table->hosts, entry)
314feb9ff76Sreyk 				host->up = HOST_UNKNOWN;
315feb9ff76Sreyk 			break;
316feb9ff76Sreyk 		default:
317feb9ff76Sreyk 			log_debug("hce_dispatch_msg: unexpected imsg %d",
318feb9ff76Sreyk 			    imsg.hdr.type);
319feb9ff76Sreyk 			break;
320feb9ff76Sreyk 		}
321feb9ff76Sreyk 		imsg_free(&imsg);
322feb9ff76Sreyk 	}
323feb9ff76Sreyk 	imsg_event_add(ibuf);
324feb9ff76Sreyk }
325feb9ff76Sreyk 
326feb9ff76Sreyk void
327feb9ff76Sreyk hce_dispatch_parent(int fd, short event, void * ptr)
328feb9ff76Sreyk {
329feb9ff76Sreyk 	struct imsgbuf	*ibuf;
330feb9ff76Sreyk 	struct imsg	 imsg;
331feb9ff76Sreyk 	ssize_t		 n;
332feb9ff76Sreyk 
333feb9ff76Sreyk 	ibuf = ptr;
334feb9ff76Sreyk 	switch (event) {
335feb9ff76Sreyk 	case EV_READ:
336feb9ff76Sreyk 		if ((n = imsg_read(ibuf)) == -1)
337feb9ff76Sreyk 			fatal("hce_dispatch_parent: imsg_read error");
338feb9ff76Sreyk 		if (n == 0)	/* connection closed */
339feb9ff76Sreyk 			fatalx("hce_dispatch_parent: pipe closed");
340feb9ff76Sreyk 		break;
341feb9ff76Sreyk 	case EV_WRITE:
342feb9ff76Sreyk 		if (msgbuf_write(&ibuf->w) == -1)
343feb9ff76Sreyk 			fatal("hce_dispatch_parent: msgbuf_write");
344feb9ff76Sreyk 		imsg_event_add(ibuf);
345feb9ff76Sreyk 		return;
346feb9ff76Sreyk 	default:
347feb9ff76Sreyk 		fatalx("hce_dispatch_parent: unknown event");
348feb9ff76Sreyk 	}
349feb9ff76Sreyk 
350feb9ff76Sreyk 	for (;;) {
351feb9ff76Sreyk 		if ((n = imsg_get(ibuf, &imsg)) == -1)
352feb9ff76Sreyk 			fatal("hce_dispatch_parent: imsg_read error");
353feb9ff76Sreyk 		if (n == 0)
354feb9ff76Sreyk 			break;
355feb9ff76Sreyk 
356feb9ff76Sreyk 		switch (imsg.hdr.type) {
357feb9ff76Sreyk 		default:
358feb9ff76Sreyk 			log_debug("hce_dispatch_parent: unexpected imsg %d",
359feb9ff76Sreyk 			    imsg.hdr.type);
360feb9ff76Sreyk 			break;
361feb9ff76Sreyk 		}
362feb9ff76Sreyk 		imsg_free(&imsg);
363feb9ff76Sreyk 	}
364feb9ff76Sreyk }
365