xref: /openbsd-src/usr.sbin/relayd/pfe.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: pfe.c,v 1.55 2009/04/17 09:47:06 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/stat.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 
24 #include <net/if.h>
25 
26 #include <errno.h>
27 #include <event.h>
28 #include <fcntl.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <pwd.h>
33 
34 #include <openssl/ssl.h>
35 
36 #include "relayd.h"
37 
38 void	pfe_sig_handler(int sig, short, void *);
39 void	pfe_shutdown(void);
40 void	pfe_setup_events(void);
41 void	pfe_disable_events(void);
42 void	pfe_dispatch_imsg(int, short, void *);
43 void	pfe_dispatch_parent(int, short, void *);
44 void	pfe_dispatch_relay(int, short, void *);
45 void	pfe_sync(void);
46 void	pfe_statistics(int, short, void *);
47 
48 static struct relayd	*env = NULL;
49 
50 struct imsgbuf	*ibuf_main;
51 struct imsgbuf	*ibuf_hce;
52 struct imsgbuf	*ibuf_relay;
53 
54 void
55 pfe_sig_handler(int sig, short event, void *arg)
56 {
57 	switch (sig) {
58 	case SIGINT:
59 	case SIGTERM:
60 		pfe_shutdown();
61 		break;
62 	default:
63 		fatalx("pfe_sig_handler: unexpected signal");
64 	}
65 }
66 
67 pid_t
68 pfe(struct relayd *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
69     int pipe_parent2relay[RELAY_MAXPROC][2], int pipe_pfe2hce[2],
70     int pipe_pfe2relay[RELAY_MAXPROC][2])
71 {
72 	pid_t		 pid;
73 	struct passwd	*pw;
74 	struct event	 ev_sigint;
75 	struct event	 ev_sigterm;
76 	int		 i;
77 	size_t		 size;
78 
79 	switch (pid = fork()) {
80 	case -1:
81 		fatal("pfe: cannot fork");
82 	case 0:
83 		break;
84 	default:
85 		return (pid);
86 	}
87 
88 	env = x_env;
89 	purge_config(env, PURGE_PROTOS);
90 
91 	if (control_init() == -1)
92 		fatalx("pfe: control socket setup failed");
93 
94 	init_filter(env);
95 	init_tables(env);
96 
97 	if ((pw = getpwnam(RELAYD_USER)) == NULL)
98 		fatal("pfe: getpwnam");
99 
100 #ifndef DEBUG
101 	if (chroot(pw->pw_dir) == -1)
102 		fatal("pfe: chroot");
103 	if (chdir("/") == -1)
104 		fatal("pfe: chdir(\"/\")");
105 #else
106 #warning disabling privilege revocation and chroot in DEBUG mode
107 #endif
108 
109 	setproctitle("pf update engine");
110 	relayd_process = PROC_PFE;
111 
112 #ifndef DEBUG
113 	if (setgroups(1, &pw->pw_gid) ||
114 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
115 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
116 		fatal("pfe: cannot drop privileges");
117 #endif
118 
119 	event_init();
120 
121 	signal_set(&ev_sigint, SIGINT, pfe_sig_handler, NULL);
122 	signal_set(&ev_sigterm, SIGTERM, pfe_sig_handler, NULL);
123 	signal_add(&ev_sigint, NULL);
124 	signal_add(&ev_sigterm, NULL);
125 	signal(SIGPIPE, SIG_IGN);
126 	signal(SIGHUP, SIG_IGN);
127 
128 	/* setup pipes */
129 	close(pipe_pfe2hce[0]);
130 	close(pipe_parent2pfe[0]);
131 	close(pipe_parent2hce[0]);
132 	close(pipe_parent2hce[1]);
133 	for (i = 0; i < env->sc_prefork_relay; i++) {
134 		close(pipe_parent2relay[i][0]);
135 		close(pipe_parent2relay[i][1]);
136 		close(pipe_pfe2relay[i][0]);
137 	}
138 
139 	size = sizeof(struct imsgbuf);
140 	if ((ibuf_hce = calloc(1, size)) == NULL ||
141 	    (ibuf_relay = calloc(env->sc_prefork_relay, size)) == NULL ||
142 	    (ibuf_main = calloc(1, size)) == NULL)
143 		fatal("pfe");
144 
145 	imsg_init(ibuf_hce, pipe_pfe2hce[1], pfe_dispatch_imsg);
146 	imsg_init(ibuf_main, pipe_parent2pfe[1], pfe_dispatch_parent);
147 	for (i = 0; i < env->sc_prefork_relay; i++)
148 		imsg_init(&ibuf_relay[i], pipe_pfe2relay[i][1],
149 		    pfe_dispatch_relay);
150 
151 	ibuf_main->events = EV_READ;
152 	event_set(&ibuf_main->ev, ibuf_main->fd, ibuf_main->events,
153 	    ibuf_main->handler, ibuf_main);
154 	event_add(&ibuf_main->ev, NULL);
155 
156 	pfe_setup_events();
157 
158 	TAILQ_INIT(&ctl_conns);
159 
160 	if (control_listen(env, ibuf_main, ibuf_hce) == -1)
161 		fatalx("pfe: control socket listen failed");
162 
163 	/* Initial sync */
164 	pfe_sync();
165 
166 	event_dispatch();
167 	pfe_shutdown();
168 
169 	return (0);
170 }
171 
172 void
173 pfe_shutdown(void)
174 {
175 	flush_rulesets(env);
176 	log_info("pf update engine exiting");
177 	_exit(0);
178 }
179 
180 void
181 pfe_setup_events(void)
182 {
183 	int		 i;
184 	struct imsgbuf	*ibuf;
185 	struct timeval	 tv;
186 
187 	ibuf_hce->events = EV_READ;
188 	event_set(&ibuf_hce->ev, ibuf_hce->fd, ibuf_hce->events,
189 	    ibuf_hce->handler, ibuf_hce);
190 	event_add(&ibuf_hce->ev, NULL);
191 
192 	for (i = 0; i < env->sc_prefork_relay; i++) {
193 		ibuf = &ibuf_relay[i];
194 
195 		ibuf->events = EV_READ;
196 		event_set(&ibuf->ev, ibuf->fd, ibuf->events,
197 		    ibuf->handler, ibuf);
198 		event_add(&ibuf->ev, NULL);
199 	}
200 
201 	/* Schedule statistics timer */
202 	evtimer_set(&env->sc_statev, pfe_statistics, NULL);
203 	bcopy(&env->sc_statinterval, &tv, sizeof(tv));
204 	evtimer_add(&env->sc_statev, &tv);
205 }
206 
207 void
208 pfe_disable_events(void)
209 {
210 	int	i;
211 
212 	event_del(&ibuf_hce->ev);
213 
214 	for (i = 0; i < env->sc_prefork_relay; i++)
215 		event_del(&ibuf_relay[i].ev);
216 
217 	event_del(&env->sc_statev);
218 }
219 
220 void
221 pfe_dispatch_imsg(int fd, short event, void *ptr)
222 {
223 	struct imsgbuf		*ibuf;
224 	struct imsg		 imsg;
225 	ssize_t			 n;
226 
227 	struct host		*host;
228 	struct table		*table;
229 	struct ctl_status	 st;
230 
231 	ibuf = ptr;
232 	switch (event) {
233 	case EV_READ:
234 		if ((n = imsg_read(ibuf)) == -1)
235 			fatal("pfe_dispatch_imsg: imsg_read_error");
236 		if (n == 0) {
237 			/* this pipe is dead, so remove the event handler */
238 			event_del(&ibuf->ev);
239 			event_loopexit(NULL);
240 			return;
241 		}
242 		break;
243 	case EV_WRITE:
244 		if (msgbuf_write(&ibuf->w) == -1)
245 			fatal("pfe_dispatch_imsg: msgbuf_write");
246 		imsg_event_add(ibuf);
247 		return;
248 	default:
249 		fatalx("pfe_dispatch_imsg: unknown event");
250 	}
251 
252 	for (;;) {
253 		if ((n = imsg_get(ibuf, &imsg)) == -1)
254 			fatal("pfe_dispatch_imsg: imsg_read error");
255 		if (n == 0)
256 			break;
257 
258 		control_imsg_forward(&imsg);
259 		switch (imsg.hdr.type) {
260 		case IMSG_HOST_STATUS:
261 			if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(st))
262 				fatalx("pfe_dispatch_imsg: invalid request");
263 			memcpy(&st, imsg.data, sizeof(st));
264 			if ((host = host_find(env, st.id)) == NULL)
265 				fatalx("pfe_dispatch_imsg: invalid host id");
266 			host->he = st.he;
267 			if (host->flags & F_DISABLE)
268 				break;
269 			host->retry_cnt = st.retry_cnt;
270 			if (st.up != HOST_UNKNOWN) {
271 				host->check_cnt++;
272 				if (st.up == HOST_UP)
273 					host->up_cnt++;
274 			}
275 			if (host->check_cnt != st.check_cnt) {
276 				log_debug("pfe_dispatch_imsg: host %d => %d",
277 				    host->conf.id, host->up);
278 				fatalx("pfe_dispatch_imsg: desynchronized");
279 			}
280 
281 			if (host->up == st.up)
282 				break;
283 
284 			/* Forward to relay engine(s) */
285 			for (n = 0; n < env->sc_prefork_relay; n++)
286 				imsg_compose(&ibuf_relay[n],
287 				    IMSG_HOST_STATUS, 0, 0, -1, &st,
288 				    sizeof(st));
289 
290 			if ((table = table_find(env, host->conf.tableid))
291 			    == NULL)
292 				fatalx("pfe_dispatch_imsg: invalid table id");
293 
294 			log_debug("pfe_dispatch_imsg: state %d for host %u %s",
295 			    st.up, host->conf.id, host->conf.name);
296 
297 			/*
298 			 * Do not change the table state when the host
299 			 * state switches between UNKNOWN and DOWN.
300 			 */
301 			if (HOST_ISUP(st.up)) {
302 				table->conf.flags |= F_CHANGED;
303 				table->up++;
304 				host->flags |= F_ADD;
305 				host->flags &= ~(F_DEL);
306 			} else if (HOST_ISUP(host->up)) {
307 				table->up--;
308 				table->conf.flags |= F_CHANGED;
309 				host->flags |= F_DEL;
310 				host->flags &= ~(F_ADD);
311 			}
312 
313 			host->up = st.up;
314 			break;
315 		case IMSG_SYNC:
316 			pfe_sync();
317 			break;
318 		default:
319 			log_debug("pfe_dispatch_imsg: unexpected imsg %d",
320 			    imsg.hdr.type);
321 			break;
322 		}
323 		imsg_free(&imsg);
324 	}
325 	imsg_event_add(ibuf);
326 }
327 
328 void
329 pfe_dispatch_parent(int fd, short event, void * ptr)
330 {
331 	struct imsgbuf	*ibuf;
332 	struct imsg	 imsg;
333 	ssize_t		 n;
334 
335 	static struct rdr	*rdr = NULL;
336 	static struct table	*table = NULL;
337 	struct host		*host, *parent;
338 	struct address		*virt;
339 
340 	ibuf = ptr;
341 	switch (event) {
342 	case EV_READ:
343 		if ((n = imsg_read(ibuf)) == -1)
344 			fatal("imsg_read error");
345 		if (n == 0) {
346 			/* this pipe is dead, so remove the event handler */
347 			event_del(&ibuf->ev);
348 			event_loopexit(NULL);
349 			return;
350 		}
351 		break;
352 	case EV_WRITE:
353 		if (msgbuf_write(&ibuf->w) == -1)
354 			fatal("msgbuf_write");
355 		imsg_event_add(ibuf);
356 		return;
357 	default:
358 		fatalx("pfe_dispatch_parent: unknown event");
359 	}
360 
361 	for (;;) {
362 		if ((n = imsg_get(ibuf, &imsg)) == -1)
363 			fatal("pfe_dispatch_parent: imsg_read error");
364 		if (n == 0)
365 			break;
366 
367 		switch (imsg.hdr.type) {
368 		case IMSG_RECONF:
369 			log_debug("pfe: reloading configuration");
370 			if (imsg.hdr.len !=
371 			    sizeof(struct relayd) + IMSG_HEADER_SIZE)
372 				fatalx("corrupted reload data");
373 			pfe_disable_events();
374 			purge_config(env, PURGE_RDRS|PURGE_TABLES);
375 			merge_config(env, (struct relayd *)imsg.data);
376 			/*
377 			 * no relays when reconfiguring yet.
378 			 */
379 			env->sc_relays = NULL;
380 			env->sc_protos = NULL;
381 
382 			env->sc_tables = calloc(1, sizeof(*env->sc_tables));
383 			env->sc_rdrs = calloc(1, sizeof(*env->sc_rdrs));
384 			if (env->sc_tables == NULL || env->sc_rdrs == NULL)
385 				fatal(NULL);
386 
387 			TAILQ_INIT(env->sc_tables);
388 			TAILQ_INIT(env->sc_rdrs);
389 			break;
390 		case IMSG_RECONF_TABLE:
391 			if ((table = calloc(1, sizeof(*table))) == NULL)
392 				fatal(NULL);
393 			memcpy(&table->conf, imsg.data, sizeof(table->conf));
394 			TAILQ_INIT(&table->hosts);
395 			TAILQ_INSERT_TAIL(env->sc_tables, table, entry);
396 			break;
397 		case IMSG_RECONF_HOST:
398 			if ((host = calloc(1, sizeof(*host))) == NULL)
399 				fatal(NULL);
400 			memcpy(&host->conf, imsg.data, sizeof(host->conf));
401 			host->tablename = table->conf.name;
402 			TAILQ_INSERT_TAIL(&table->hosts, host, entry);
403 			if (host->conf.parentid) {
404 				parent = host_find(env, host->conf.parentid);
405 				SLIST_INSERT_HEAD(&parent->children,
406 				    host, child);
407 			}
408 			break;
409 		case IMSG_RECONF_RDR:
410 			if ((rdr = calloc(1, sizeof(*rdr))) == NULL)
411 				fatal(NULL);
412 			memcpy(&rdr->conf, imsg.data,
413 			    sizeof(rdr->conf));
414 			rdr->table = table_find(env,
415 			     rdr->conf.table_id);
416 			if (rdr->conf.backup_id == EMPTY_TABLE)
417 				rdr->backup = &env->sc_empty_table;
418 			else
419 				rdr->backup = table_find(env,
420 				    rdr->conf.backup_id);
421 			if (rdr->table == NULL || rdr->backup == NULL)
422 				fatal("pfe_dispatch_parent:"
423 				    " corrupted configuration");
424 			log_debug("pfe_dispatch_parent: rdr->table: %s",
425 			    rdr->table->conf.name);
426 			log_debug("pfe_dispatch_parent: rdr->backup: %s",
427 			    rdr->backup->conf.name);
428 			TAILQ_INIT(&rdr->virts);
429 			TAILQ_INSERT_TAIL(env->sc_rdrs, rdr, entry);
430 			break;
431 		case IMSG_RECONF_VIRT:
432 			if ((virt = calloc(1, sizeof(*virt))) == NULL)
433 				fatal(NULL);
434 			memcpy(virt, imsg.data, sizeof(*virt));
435 			TAILQ_INSERT_TAIL(&rdr->virts, virt, entry);
436 			break;
437 		case IMSG_RECONF_END:
438 			log_warnx("pfe: configuration reloaded");
439 			init_tables(env);
440 			pfe_setup_events();
441 			pfe_sync();
442 			break;
443 		default:
444 			log_debug("pfe_dispatch_parent: unexpected imsg %d",
445 			    imsg.hdr.type);
446 			break;
447 		}
448 		imsg_free(&imsg);
449 	}
450 	imsg_event_add(ibuf);
451 }
452 
453 void
454 pfe_dispatch_relay(int fd, short event, void * ptr)
455 {
456 	struct imsgbuf		*ibuf;
457 	struct imsg		 imsg;
458 	ssize_t			 n;
459 	struct ctl_natlook	 cnl;
460 	struct ctl_stats	 crs;
461 	struct relay		*rlay;
462 
463 	ibuf = ptr;
464 	switch (event) {
465 	case EV_READ:
466 		if ((n = imsg_read(ibuf)) == -1)
467 			fatal("imsg_read error");
468 		if (n == 0) {
469 			/* this pipe is dead, so remove the event handler */
470 			event_del(&ibuf->ev);
471 			event_loopexit(NULL);
472 			return;
473 		}
474 		break;
475 	case EV_WRITE:
476 		if (msgbuf_write(&ibuf->w) == -1)
477 			fatal("msgbuf_write");
478 		imsg_event_add(ibuf);
479 		return;
480 	default:
481 		fatalx("unknown event");
482 	}
483 
484 	for (;;) {
485 		if ((n = imsg_get(ibuf, &imsg)) == -1)
486 			fatal("pfe_dispatch_relay: imsg_read error");
487 		if (n == 0)
488 			break;
489 
490 		switch (imsg.hdr.type) {
491 		case IMSG_NATLOOK:
492 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(cnl))
493 				fatalx("invalid imsg header len");
494 			bcopy(imsg.data, &cnl, sizeof(cnl));
495 			if (cnl.proc > env->sc_prefork_relay)
496 				fatalx("pfe_dispatch_relay: "
497 				    "invalid relay proc");
498 			if (natlook(env, &cnl) != 0)
499 				cnl.in = -1;
500 			imsg_compose(&ibuf_relay[cnl.proc], IMSG_NATLOOK, 0, 0,
501 			    -1, &cnl, sizeof(cnl));
502 			break;
503 		case IMSG_STATISTICS:
504 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(crs))
505 				fatalx("invalid imsg header len");
506 			bcopy(imsg.data, &crs, sizeof(crs));
507 			if (crs.proc > env->sc_prefork_relay)
508 				fatalx("pfe_dispatch_relay: "
509 				    "invalid relay proc");
510 			if ((rlay = relay_find(env, crs.id)) == NULL)
511 				fatalx("pfe_dispatch_relay: invalid relay id");
512 			bcopy(&crs, &rlay->rl_stats[crs.proc], sizeof(crs));
513 			rlay->rl_stats[crs.proc].interval =
514 			    env->sc_statinterval.tv_sec;
515 			break;
516 		default:
517 			log_debug("pfe_dispatch_relay: unexpected imsg %d",
518 			    imsg.hdr.type);
519 			break;
520 		}
521 		imsg_free(&imsg);
522 	}
523 	imsg_event_add(ibuf);
524 }
525 
526 void
527 show(struct ctl_conn *c)
528 {
529 	struct rdr	*rdr;
530 	struct host	*host;
531 	struct relay	*rlay;
532 
533 	if (env->sc_rdrs == NULL)
534 		goto relays;
535 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
536 		imsg_compose(&c->ibuf, IMSG_CTL_RDR, 0, 0, -1,
537 		    rdr, sizeof(*rdr));
538 		if (rdr->conf.flags & F_DISABLE)
539 			continue;
540 
541 		imsg_compose(&c->ibuf, IMSG_CTL_RDR_STATS, 0, 0, -1,
542 		    &rdr->stats, sizeof(rdr->stats));
543 
544 		imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1,
545 		    rdr->table, sizeof(*rdr->table));
546 		if (!(rdr->table->conf.flags & F_DISABLE))
547 			TAILQ_FOREACH(host, &rdr->table->hosts, entry)
548 				imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1,
549 				    host, sizeof(*host));
550 
551 		if (rdr->backup->conf.id == EMPTY_TABLE)
552 			continue;
553 		imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1,
554 		    rdr->backup, sizeof(*rdr->backup));
555 		if (!(rdr->backup->conf.flags & F_DISABLE))
556 			TAILQ_FOREACH(host, &rdr->backup->hosts, entry)
557 				imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1,
558 				    host, sizeof(*host));
559 	}
560 relays:
561 	if (env->sc_relays == NULL)
562 		goto end;
563 	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
564 		rlay->rl_stats[env->sc_prefork_relay].id = EMPTY_ID;
565 		imsg_compose(&c->ibuf, IMSG_CTL_RELAY, 0, 0, -1,
566 		    rlay, sizeof(*rlay));
567 		imsg_compose(&c->ibuf, IMSG_CTL_RELAY_STATS, 0, 0, -1,
568 		    &rlay->rl_stats, sizeof(rlay->rl_stats));
569 
570 		if (rlay->rl_dsttable == NULL)
571 			continue;
572 		imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1,
573 		    rlay->rl_dsttable, sizeof(*rlay->rl_dsttable));
574 		if (!(rlay->rl_dsttable->conf.flags & F_DISABLE))
575 			TAILQ_FOREACH(host, &rlay->rl_dsttable->hosts, entry)
576 				imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1,
577 				    host, sizeof(*host));
578 	}
579 end:
580 	imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
581 }
582 
583 void
584 show_sessions(struct ctl_conn *c)
585 {
586 	int		 n, proc, done;
587 	struct imsg	 imsg;
588 
589 	for (proc = 0; proc < env->sc_prefork_relay; proc++) {
590 		/*
591 		 * Request all the running sessions from the process
592 		 */
593 		imsg_compose(&ibuf_relay[proc],
594 		    IMSG_CTL_SESSION, 0, 0, -1, NULL, 0);
595 		while (ibuf_relay[proc].w.queued)
596 			if (msgbuf_write(&ibuf_relay[proc].w) < 0)
597 				fatalx("write error");
598 
599 		/*
600 		 * Wait for the reply and forward the messages to the
601 		 * control connection.
602 		 */
603 		done = 0;
604 		while (!done) {
605 			do {
606 				if ((n = imsg_read(&ibuf_relay[proc])) == -1)
607 					fatalx("imsg_read error");
608 			} while (n == -2); /* handle non-blocking I/O */
609 			while (!done) {
610 				if ((n = imsg_get(&ibuf_relay[proc],
611 				    &imsg)) == -1)
612 					fatalx("imsg_get error");
613 				if (n == 0)
614 					break;
615 				switch (imsg.hdr.type) {
616 				case IMSG_CTL_SESSION:
617 					imsg_compose(&c->ibuf,
618 					    IMSG_CTL_SESSION, proc, 0, -1,
619 					    imsg.data, sizeof(struct session));
620 					break;
621 				case IMSG_CTL_END:
622 					done = 1;
623 					break;
624 				default:
625 					fatalx("wrong message for session");
626 					break;
627 				}
628 				imsg_free(&imsg);
629 			}
630 		}
631 	}
632 
633 	imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
634 }
635 
636 int
637 disable_rdr(struct ctl_conn *c, struct ctl_id *id)
638 {
639 	struct rdr	*rdr;
640 
641 	if (id->id == EMPTY_ID)
642 		rdr = rdr_findbyname(env, id->name);
643 	else
644 		rdr = rdr_find(env, id->id);
645 	if (rdr == NULL)
646 		return (-1);
647 	id->id = rdr->conf.id;
648 
649 	if (rdr->conf.flags & F_DISABLE)
650 		return (0);
651 
652 	rdr->conf.flags |= F_DISABLE;
653 	rdr->conf.flags &= ~(F_ADD);
654 	rdr->conf.flags |= F_DEL;
655 	rdr->table->conf.flags |= F_DISABLE;
656 	log_debug("disable_rdr: disabled rdr %d", rdr->conf.id);
657 	pfe_sync();
658 	return (0);
659 }
660 
661 int
662 enable_rdr(struct ctl_conn *c, struct ctl_id *id)
663 {
664 	struct rdr	*rdr;
665 	struct ctl_id	 eid;
666 
667 	if (id->id == EMPTY_ID)
668 		rdr = rdr_findbyname(env, id->name);
669 	else
670 		rdr = rdr_find(env, id->id);
671 	if (rdr == NULL)
672 		return (-1);
673 	id->id = rdr->conf.id;
674 
675 	if (!(rdr->conf.flags & F_DISABLE))
676 		return (0);
677 
678 	rdr->conf.flags &= ~(F_DISABLE);
679 	rdr->conf.flags &= ~(F_DEL);
680 	rdr->conf.flags |= F_ADD;
681 	log_debug("enable_rdr: enabled rdr %d", rdr->conf.id);
682 
683 	bzero(&eid, sizeof(eid));
684 
685 	/* XXX: we're syncing twice */
686 	eid.id = rdr->table->conf.id;
687 	if (enable_table(c, &eid) == -1)
688 		return (-1);
689 	if (rdr->backup->conf.id == EMPTY_ID)
690 		return (0);
691 	eid.id = rdr->backup->conf.id;
692 	if (enable_table(c, &eid) == -1)
693 		return (-1);
694 	return (0);
695 }
696 
697 int
698 disable_table(struct ctl_conn *c, struct ctl_id *id)
699 {
700 	struct table	*table;
701 	struct rdr	*rdr;
702 	struct host	*host;
703 
704 	if (id->id == EMPTY_ID)
705 		table = table_findbyname(env, id->name);
706 	else
707 		table = table_find(env, id->id);
708 	if (table == NULL)
709 		return (-1);
710 	id->id = table->conf.id;
711 	if ((rdr = rdr_find(env, table->conf.rdrid)) == NULL)
712 		fatalx("disable_table: desynchronised");
713 
714 	if (table->conf.flags & F_DISABLE)
715 		return (0);
716 	table->conf.flags |= (F_DISABLE|F_CHANGED);
717 	table->up = 0;
718 	TAILQ_FOREACH(host, &table->hosts, entry)
719 		host->up = HOST_UNKNOWN;
720 	imsg_compose(ibuf_hce, IMSG_TABLE_DISABLE, 0, 0, -1,
721 	    &table->conf.id, sizeof(table->conf.id));
722 	log_debug("disable_table: disabled table %d", table->conf.id);
723 	pfe_sync();
724 	return (0);
725 }
726 
727 int
728 enable_table(struct ctl_conn *c, struct ctl_id *id)
729 {
730 	struct rdr	*rdr;
731 	struct table	*table;
732 	struct host	*host;
733 
734 	if (id->id == EMPTY_ID)
735 		table = table_findbyname(env, id->name);
736 	else
737 		table = table_find(env, id->id);
738 	if (table == NULL)
739 		return (-1);
740 	id->id = table->conf.id;
741 
742 	if ((rdr = rdr_find(env, table->conf.rdrid)) == NULL)
743 		fatalx("enable_table: desynchronised");
744 
745 	if (!(table->conf.flags & F_DISABLE))
746 		return (0);
747 	table->conf.flags &= ~(F_DISABLE);
748 	table->conf.flags |= F_CHANGED;
749 	table->up = 0;
750 	TAILQ_FOREACH(host, &table->hosts, entry)
751 		host->up = HOST_UNKNOWN;
752 	imsg_compose(ibuf_hce, IMSG_TABLE_ENABLE, 0, 0, -1,
753 	    &table->conf.id, sizeof(table->conf.id));
754 	log_debug("enable_table: enabled table %d", table->conf.id);
755 	pfe_sync();
756 	return (0);
757 }
758 
759 int
760 disable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
761 {
762 	struct host	*h;
763 	struct table	*table;
764 	int		 n;
765 
766 	if (host == NULL) {
767 		if (id->id == EMPTY_ID)
768 			host = host_findbyname(env, id->name);
769 		else
770 			host = host_find(env, id->id);
771 		if (host == NULL || host->conf.parentid)
772 			return (-1);
773 	}
774 	id->id = host->conf.id;
775 
776 	if (host->flags & F_DISABLE)
777 		return (0);
778 
779 	if (host->up == HOST_UP) {
780 		if ((table = table_find(env, host->conf.tableid)) == NULL)
781 			fatalx("disable_host: invalid table id");
782 		table->up--;
783 		table->conf.flags |= F_CHANGED;
784 	}
785 
786 	host->up = HOST_UNKNOWN;
787 	host->flags |= F_DISABLE;
788 	host->flags |= F_DEL;
789 	host->flags &= ~(F_ADD);
790 	host->check_cnt = 0;
791 	host->up_cnt = 0;
792 
793 	imsg_compose(ibuf_hce, IMSG_HOST_DISABLE, 0, 0, -1,
794 	    &host->conf.id, sizeof(host->conf.id));
795 	/* Forward to relay engine(s) */
796 	for (n = 0; n < env->sc_prefork_relay; n++)
797 		imsg_compose(&ibuf_relay[n],
798 		    IMSG_HOST_DISABLE, 0, 0, -1,
799 		    &host->conf.id, sizeof(host->conf.id));
800 	log_debug("disable_host: disabled host %d", host->conf.id);
801 
802 	if (!host->conf.parentid) {
803 		/* Disable all children */
804 		SLIST_FOREACH(h, &host->children, child)
805 			disable_host(c, id, h);
806 		pfe_sync();
807 	}
808 	return (0);
809 }
810 
811 int
812 enable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
813 {
814 	struct host	*h;
815 	int		 n;
816 
817 	if (host == NULL) {
818 		if (id->id == EMPTY_ID)
819 			host = host_findbyname(env, id->name);
820 		else
821 			host = host_find(env, id->id);
822 		if (host == NULL || host->conf.parentid)
823 			return (-1);
824 	}
825 	id->id = host->conf.id;
826 
827 	if (!(host->flags & F_DISABLE))
828 		return (0);
829 
830 	host->up = HOST_UNKNOWN;
831 	host->flags &= ~(F_DISABLE);
832 	host->flags &= ~(F_DEL);
833 	host->flags &= ~(F_ADD);
834 
835 	imsg_compose(ibuf_hce, IMSG_HOST_ENABLE, 0, 0, -1,
836 	    &host->conf.id, sizeof (host->conf.id));
837 	/* Forward to relay engine(s) */
838 	for (n = 0; n < env->sc_prefork_relay; n++)
839 		imsg_compose(&ibuf_relay[n],
840 		    IMSG_HOST_ENABLE, 0, 0, -1,
841 		    &host->conf.id, sizeof(host->conf.id));
842 	log_debug("enable_host: enabled host %d", host->conf.id);
843 
844 	if (!host->conf.parentid) {
845 		/* Enable all children */
846 		SLIST_FOREACH(h, &host->children, child)
847 			enable_host(c, id, h);
848 		pfe_sync();
849 	}
850 	return (0);
851 }
852 
853 void
854 pfe_sync(void)
855 {
856 	struct rdr		*rdr;
857 	struct table		*active;
858 	struct table		*table;
859 	struct ctl_id		 id;
860 	struct imsg		 imsg;
861 	struct ctl_demote	 demote;
862 
863 	bzero(&id, sizeof(id));
864 	bzero(&imsg, sizeof(imsg));
865 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
866 		rdr->conf.flags &= ~(F_BACKUP);
867 		rdr->conf.flags &= ~(F_DOWN);
868 
869 		if (rdr->conf.flags & F_DISABLE ||
870 		    (rdr->table->up == 0 && rdr->backup->up == 0)) {
871 			rdr->conf.flags |= F_DOWN;
872 			active = NULL;
873 		} else if (rdr->table->up == 0 && rdr->backup->up > 0) {
874 			rdr->conf.flags |= F_BACKUP;
875 			active = rdr->backup;
876 			active->conf.flags |=
877 			    rdr->table->conf.flags & F_CHANGED;
878 			active->conf.flags |=
879 			    rdr->backup->conf.flags & F_CHANGED;
880 		} else
881 			active = rdr->table;
882 
883 		if (active != NULL && active->conf.flags & F_CHANGED) {
884 			id.id = active->conf.id;
885 			imsg.hdr.type = IMSG_CTL_TABLE_CHANGED;
886 			imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
887 			imsg.data = &id;
888 			sync_table(env, rdr, active);
889 			control_imsg_forward(&imsg);
890 		}
891 
892 		if (rdr->conf.flags & F_DOWN) {
893 			if (rdr->conf.flags & F_ACTIVE_RULESET) {
894 				flush_table(env, rdr);
895 				log_debug("pfe_sync: disabling ruleset");
896 				rdr->conf.flags &= ~(F_ACTIVE_RULESET);
897 				id.id = rdr->conf.id;
898 				imsg.hdr.type = IMSG_CTL_PULL_RULESET;
899 				imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
900 				imsg.data = &id;
901 				sync_ruleset(env, rdr, 0);
902 				control_imsg_forward(&imsg);
903 			}
904 		} else if (!(rdr->conf.flags & F_ACTIVE_RULESET)) {
905 			log_debug("pfe_sync: enabling ruleset");
906 			rdr->conf.flags |= F_ACTIVE_RULESET;
907 			id.id = rdr->conf.id;
908 			imsg.hdr.type = IMSG_CTL_PUSH_RULESET;
909 			imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
910 			imsg.data = &id;
911 			sync_ruleset(env, rdr, 1);
912 			control_imsg_forward(&imsg);
913 		}
914 	}
915 
916 	TAILQ_FOREACH(table, env->sc_tables, entry) {
917 		/*
918 		 * clean up change flag.
919 		 */
920 		table->conf.flags &= ~(F_CHANGED);
921 
922 		/*
923 		 * handle demotion.
924 		 */
925 		if ((table->conf.flags & F_DEMOTE) == 0)
926 			continue;
927 		demote.level = 0;
928 		if (table->up && table->conf.flags & F_DEMOTED) {
929 			demote.level = -1;
930 			table->conf.flags &= ~F_DEMOTED;
931 		}
932 		else if (!table->up && !(table->conf.flags & F_DEMOTED)) {
933 			demote.level = 1;
934 			table->conf.flags |= F_DEMOTED;
935 		}
936 		if (demote.level == 0)
937 			continue;
938 		log_debug("pfe_sync: demote %d table '%s' group '%s'",
939 		    demote.level, table->conf.name, table->conf.demote_group);
940 		(void)strlcpy(demote.group, table->conf.demote_group,
941 		    sizeof(demote.group));
942 		imsg_compose(ibuf_main, IMSG_DEMOTE, 0, 0, -1,
943 		    &demote, sizeof(demote));
944 	}
945 }
946 
947 void
948 pfe_statistics(int fd, short events, void *arg)
949 {
950 	struct rdr		*rdr;
951 	struct ctl_stats	*cur;
952 	struct timeval		 tv, tv_now;
953 	int			 resethour, resetday;
954 	u_long			 cnt;
955 
956 	timerclear(&tv);
957 	if (gettimeofday(&tv_now, NULL) == -1)
958 		fatal("pfe_statistics: gettimeofday");
959 
960 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
961 		cnt = check_table(env, rdr, rdr->table);
962 		if (rdr->conf.backup_id != EMPTY_TABLE)
963 			cnt += check_table(env, rdr, rdr->backup);
964 
965 		resethour = resetday = 0;
966 
967 		cur = &rdr->stats;
968 		cur->last = cnt > cur->cnt ? cnt - cur->cnt : 0;
969 
970 		cur->cnt = cnt;
971 		cur->tick++;
972 		cur->avg = (cur->last + cur->avg) / 2;
973 		cur->last_hour += cur->last;
974 		if ((cur->tick % (3600 / env->sc_statinterval.tv_sec)) == 0) {
975 			cur->avg_hour = (cur->last_hour + cur->avg_hour) / 2;
976 			resethour++;
977 		}
978 		cur->last_day += cur->last;
979 		if ((cur->tick % (86400 / env->sc_statinterval.tv_sec)) == 0) {
980 			cur->avg_day = (cur->last_day + cur->avg_day) / 2;
981 			resethour++;
982 		}
983 		if (resethour)
984 			cur->last_hour = 0;
985 		if (resetday)
986 			cur->last_day = 0;
987 
988 		rdr->stats.interval = env->sc_statinterval.tv_sec;
989 	}
990 
991 	/* Schedule statistics timer */
992 	evtimer_set(&env->sc_statev, pfe_statistics, NULL);
993 	bcopy(&env->sc_statinterval, &tv, sizeof(tv));
994 	evtimer_add(&env->sc_statev, &tv);
995 }
996