xref: /openbsd-src/usr.sbin/relayd/pfe.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: pfe.c,v 1.54 2009/04/01 15:05: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;
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 			break;
404 		case IMSG_RECONF_RDR:
405 			if ((rdr = calloc(1, sizeof(*rdr))) == NULL)
406 				fatal(NULL);
407 			memcpy(&rdr->conf, imsg.data,
408 			    sizeof(rdr->conf));
409 			rdr->table = table_find(env,
410 			     rdr->conf.table_id);
411 			if (rdr->conf.backup_id == EMPTY_TABLE)
412 				rdr->backup = &env->sc_empty_table;
413 			else
414 				rdr->backup = table_find(env,
415 				    rdr->conf.backup_id);
416 			if (rdr->table == NULL || rdr->backup == NULL)
417 				fatal("pfe_dispatch_parent:"
418 				    " corrupted configuration");
419 			log_debug("pfe_dispatch_parent: rdr->table: %s",
420 			    rdr->table->conf.name);
421 			log_debug("pfe_dispatch_parent: rdr->backup: %s",
422 			    rdr->backup->conf.name);
423 			TAILQ_INIT(&rdr->virts);
424 			TAILQ_INSERT_TAIL(env->sc_rdrs, rdr, entry);
425 			break;
426 		case IMSG_RECONF_VIRT:
427 			if ((virt = calloc(1, sizeof(*virt))) == NULL)
428 				fatal(NULL);
429 			memcpy(virt, imsg.data, sizeof(*virt));
430 			TAILQ_INSERT_TAIL(&rdr->virts, virt, entry);
431 			break;
432 		case IMSG_RECONF_END:
433 			log_warnx("pfe: configuration reloaded");
434 			init_tables(env);
435 			pfe_setup_events();
436 			pfe_sync();
437 			break;
438 		default:
439 			log_debug("pfe_dispatch_parent: unexpected imsg %d",
440 			    imsg.hdr.type);
441 			break;
442 		}
443 		imsg_free(&imsg);
444 	}
445 	imsg_event_add(ibuf);
446 }
447 
448 void
449 pfe_dispatch_relay(int fd, short event, void * ptr)
450 {
451 	struct imsgbuf		*ibuf;
452 	struct imsg		 imsg;
453 	ssize_t			 n;
454 	struct ctl_natlook	 cnl;
455 	struct ctl_stats	 crs;
456 	struct relay		*rlay;
457 
458 	ibuf = ptr;
459 	switch (event) {
460 	case EV_READ:
461 		if ((n = imsg_read(ibuf)) == -1)
462 			fatal("imsg_read error");
463 		if (n == 0) {
464 			/* this pipe is dead, so remove the event handler */
465 			event_del(&ibuf->ev);
466 			event_loopexit(NULL);
467 			return;
468 		}
469 		break;
470 	case EV_WRITE:
471 		if (msgbuf_write(&ibuf->w) == -1)
472 			fatal("msgbuf_write");
473 		imsg_event_add(ibuf);
474 		return;
475 	default:
476 		fatalx("unknown event");
477 	}
478 
479 	for (;;) {
480 		if ((n = imsg_get(ibuf, &imsg)) == -1)
481 			fatal("pfe_dispatch_relay: imsg_read error");
482 		if (n == 0)
483 			break;
484 
485 		switch (imsg.hdr.type) {
486 		case IMSG_NATLOOK:
487 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(cnl))
488 				fatalx("invalid imsg header len");
489 			bcopy(imsg.data, &cnl, sizeof(cnl));
490 			if (cnl.proc > env->sc_prefork_relay)
491 				fatalx("pfe_dispatch_relay: "
492 				    "invalid relay proc");
493 			if (natlook(env, &cnl) != 0)
494 				cnl.in = -1;
495 			imsg_compose(&ibuf_relay[cnl.proc], IMSG_NATLOOK, 0, 0,
496 			    -1, &cnl, sizeof(cnl));
497 			break;
498 		case IMSG_STATISTICS:
499 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(crs))
500 				fatalx("invalid imsg header len");
501 			bcopy(imsg.data, &crs, sizeof(crs));
502 			if (crs.proc > env->sc_prefork_relay)
503 				fatalx("pfe_dispatch_relay: "
504 				    "invalid relay proc");
505 			if ((rlay = relay_find(env, crs.id)) == NULL)
506 				fatalx("pfe_dispatch_relay: invalid relay id");
507 			bcopy(&crs, &rlay->rl_stats[crs.proc], sizeof(crs));
508 			rlay->rl_stats[crs.proc].interval =
509 			    env->sc_statinterval.tv_sec;
510 			break;
511 		default:
512 			log_debug("pfe_dispatch_relay: unexpected imsg %d",
513 			    imsg.hdr.type);
514 			break;
515 		}
516 		imsg_free(&imsg);
517 	}
518 	imsg_event_add(ibuf);
519 }
520 
521 void
522 show(struct ctl_conn *c)
523 {
524 	struct rdr	*rdr;
525 	struct host	*host;
526 	struct relay	*rlay;
527 
528 	if (env->sc_rdrs == NULL)
529 		goto relays;
530 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
531 		imsg_compose(&c->ibuf, IMSG_CTL_RDR, 0, 0, -1,
532 		    rdr, sizeof(*rdr));
533 		if (rdr->conf.flags & F_DISABLE)
534 			continue;
535 
536 		imsg_compose(&c->ibuf, IMSG_CTL_RDR_STATS, 0, 0, -1,
537 		    &rdr->stats, sizeof(rdr->stats));
538 
539 		imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1,
540 		    rdr->table, sizeof(*rdr->table));
541 		if (!(rdr->table->conf.flags & F_DISABLE))
542 			TAILQ_FOREACH(host, &rdr->table->hosts, entry)
543 				imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1,
544 				    host, sizeof(*host));
545 
546 		if (rdr->backup->conf.id == EMPTY_TABLE)
547 			continue;
548 		imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1,
549 		    rdr->backup, sizeof(*rdr->backup));
550 		if (!(rdr->backup->conf.flags & F_DISABLE))
551 			TAILQ_FOREACH(host, &rdr->backup->hosts, entry)
552 				imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1,
553 				    host, sizeof(*host));
554 	}
555 relays:
556 	if (env->sc_relays == NULL)
557 		goto end;
558 	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
559 		rlay->rl_stats[env->sc_prefork_relay].id = EMPTY_ID;
560 		imsg_compose(&c->ibuf, IMSG_CTL_RELAY, 0, 0, -1,
561 		    rlay, sizeof(*rlay));
562 		imsg_compose(&c->ibuf, IMSG_CTL_RELAY_STATS, 0, 0, -1,
563 		    &rlay->rl_stats, sizeof(rlay->rl_stats));
564 
565 		if (rlay->rl_dsttable == NULL)
566 			continue;
567 		imsg_compose(&c->ibuf, IMSG_CTL_TABLE, 0, 0, -1,
568 		    rlay->rl_dsttable, sizeof(*rlay->rl_dsttable));
569 		if (!(rlay->rl_dsttable->conf.flags & F_DISABLE))
570 			TAILQ_FOREACH(host, &rlay->rl_dsttable->hosts, entry)
571 				imsg_compose(&c->ibuf, IMSG_CTL_HOST, 0, 0, -1,
572 				    host, sizeof(*host));
573 	}
574 end:
575 	imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
576 }
577 
578 void
579 show_sessions(struct ctl_conn *c)
580 {
581 	int		 n, proc, done;
582 	struct imsg	 imsg;
583 
584 	for (proc = 0; proc < env->sc_prefork_relay; proc++) {
585 		/*
586 		 * Request all the running sessions from the process
587 		 */
588 		imsg_compose(&ibuf_relay[proc],
589 		    IMSG_CTL_SESSION, 0, 0, -1, NULL, 0);
590 		while (ibuf_relay[proc].w.queued)
591 			if (msgbuf_write(&ibuf_relay[proc].w) < 0)
592 				fatalx("write error");
593 
594 		/*
595 		 * Wait for the reply and forward the messages to the
596 		 * control connection.
597 		 */
598 		done = 0;
599 		while (!done) {
600 			do {
601 				if ((n = imsg_read(&ibuf_relay[proc])) == -1)
602 					fatalx("imsg_read error");
603 			} while (n == -2); /* handle non-blocking I/O */
604 			while (!done) {
605 				if ((n = imsg_get(&ibuf_relay[proc],
606 				    &imsg)) == -1)
607 					fatalx("imsg_get error");
608 				if (n == 0)
609 					break;
610 				switch (imsg.hdr.type) {
611 				case IMSG_CTL_SESSION:
612 					imsg_compose(&c->ibuf,
613 					    IMSG_CTL_SESSION, proc, 0, -1,
614 					    imsg.data, sizeof(struct session));
615 					break;
616 				case IMSG_CTL_END:
617 					done = 1;
618 					break;
619 				default:
620 					fatalx("wrong message for session");
621 					break;
622 				}
623 				imsg_free(&imsg);
624 			}
625 		}
626 	}
627 
628 	imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
629 }
630 
631 int
632 disable_rdr(struct ctl_conn *c, struct ctl_id *id)
633 {
634 	struct rdr	*rdr;
635 
636 	if (id->id == EMPTY_ID)
637 		rdr = rdr_findbyname(env, id->name);
638 	else
639 		rdr = rdr_find(env, id->id);
640 	if (rdr == NULL)
641 		return (-1);
642 	id->id = rdr->conf.id;
643 
644 	if (rdr->conf.flags & F_DISABLE)
645 		return (0);
646 
647 	rdr->conf.flags |= F_DISABLE;
648 	rdr->conf.flags &= ~(F_ADD);
649 	rdr->conf.flags |= F_DEL;
650 	rdr->table->conf.flags |= F_DISABLE;
651 	log_debug("disable_rdr: disabled rdr %d", rdr->conf.id);
652 	pfe_sync();
653 	return (0);
654 }
655 
656 int
657 enable_rdr(struct ctl_conn *c, struct ctl_id *id)
658 {
659 	struct rdr	*rdr;
660 	struct ctl_id	 eid;
661 
662 	if (id->id == EMPTY_ID)
663 		rdr = rdr_findbyname(env, id->name);
664 	else
665 		rdr = rdr_find(env, id->id);
666 	if (rdr == NULL)
667 		return (-1);
668 	id->id = rdr->conf.id;
669 
670 	if (!(rdr->conf.flags & F_DISABLE))
671 		return (0);
672 
673 	rdr->conf.flags &= ~(F_DISABLE);
674 	rdr->conf.flags &= ~(F_DEL);
675 	rdr->conf.flags |= F_ADD;
676 	log_debug("enable_rdr: enabled rdr %d", rdr->conf.id);
677 
678 	bzero(&eid, sizeof(eid));
679 
680 	/* XXX: we're syncing twice */
681 	eid.id = rdr->table->conf.id;
682 	if (enable_table(c, &eid) == -1)
683 		return (-1);
684 	if (rdr->backup->conf.id == EMPTY_ID)
685 		return (0);
686 	eid.id = rdr->backup->conf.id;
687 	if (enable_table(c, &eid) == -1)
688 		return (-1);
689 	return (0);
690 }
691 
692 int
693 disable_table(struct ctl_conn *c, struct ctl_id *id)
694 {
695 	struct table	*table;
696 	struct rdr	*rdr;
697 	struct host	*host;
698 
699 	if (id->id == EMPTY_ID)
700 		table = table_findbyname(env, id->name);
701 	else
702 		table = table_find(env, id->id);
703 	if (table == NULL)
704 		return (-1);
705 	id->id = table->conf.id;
706 	if ((rdr = rdr_find(env, table->conf.rdrid)) == NULL)
707 		fatalx("disable_table: desynchronised");
708 
709 	if (table->conf.flags & F_DISABLE)
710 		return (0);
711 	table->conf.flags |= (F_DISABLE|F_CHANGED);
712 	table->up = 0;
713 	TAILQ_FOREACH(host, &table->hosts, entry)
714 		host->up = HOST_UNKNOWN;
715 	imsg_compose(ibuf_hce, IMSG_TABLE_DISABLE, 0, 0, -1,
716 	    &table->conf.id, sizeof(table->conf.id));
717 	log_debug("disable_table: disabled table %d", table->conf.id);
718 	pfe_sync();
719 	return (0);
720 }
721 
722 int
723 enable_table(struct ctl_conn *c, struct ctl_id *id)
724 {
725 	struct rdr	*rdr;
726 	struct table	*table;
727 	struct host	*host;
728 
729 	if (id->id == EMPTY_ID)
730 		table = table_findbyname(env, id->name);
731 	else
732 		table = table_find(env, id->id);
733 	if (table == NULL)
734 		return (-1);
735 	id->id = table->conf.id;
736 
737 	if ((rdr = rdr_find(env, table->conf.rdrid)) == NULL)
738 		fatalx("enable_table: desynchronised");
739 
740 	if (!(table->conf.flags & F_DISABLE))
741 		return (0);
742 	table->conf.flags &= ~(F_DISABLE);
743 	table->conf.flags |= F_CHANGED;
744 	table->up = 0;
745 	TAILQ_FOREACH(host, &table->hosts, entry)
746 		host->up = HOST_UNKNOWN;
747 	imsg_compose(ibuf_hce, IMSG_TABLE_ENABLE, 0, 0, -1,
748 	    &table->conf.id, sizeof(table->conf.id));
749 	log_debug("enable_table: enabled table %d", table->conf.id);
750 	pfe_sync();
751 	return (0);
752 }
753 
754 int
755 disable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
756 {
757 	struct host	*h;
758 	struct table	*table;
759 	int		 n;
760 
761 	if (host == NULL) {
762 		if (id->id == EMPTY_ID)
763 			host = host_findbyname(env, id->name);
764 		else
765 			host = host_find(env, id->id);
766 		if (host == NULL || host->conf.parentid)
767 			return (-1);
768 	}
769 	id->id = host->conf.id;
770 
771 	if (host->flags & F_DISABLE)
772 		return (0);
773 
774 	if (host->up == HOST_UP) {
775 		if ((table = table_find(env, host->conf.tableid)) == NULL)
776 			fatalx("disable_host: invalid table id");
777 		table->up--;
778 		table->conf.flags |= F_CHANGED;
779 	}
780 
781 	host->up = HOST_UNKNOWN;
782 	host->flags |= F_DISABLE;
783 	host->flags |= F_DEL;
784 	host->flags &= ~(F_ADD);
785 	host->check_cnt = 0;
786 	host->up_cnt = 0;
787 
788 	imsg_compose(ibuf_hce, IMSG_HOST_DISABLE, 0, 0, -1,
789 	    &host->conf.id, sizeof(host->conf.id));
790 	/* Forward to relay engine(s) */
791 	for (n = 0; n < env->sc_prefork_relay; n++)
792 		imsg_compose(&ibuf_relay[n],
793 		    IMSG_HOST_DISABLE, 0, 0, -1,
794 		    &host->conf.id, sizeof(host->conf.id));
795 	log_debug("disable_host: disabled host %d", host->conf.id);
796 
797 	if (!host->conf.parentid) {
798 		/* Disable all children */
799 		SLIST_FOREACH(h, &host->children, child)
800 			disable_host(c, id, h);
801 		pfe_sync();
802 	}
803 	return (0);
804 }
805 
806 int
807 enable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
808 {
809 	struct host	*h;
810 	int		 n;
811 
812 	if (host == NULL) {
813 		if (id->id == EMPTY_ID)
814 			host = host_findbyname(env, id->name);
815 		else
816 			host = host_find(env, id->id);
817 		if (host == NULL || host->conf.parentid)
818 			return (-1);
819 	}
820 	id->id = host->conf.id;
821 
822 	if (!(host->flags & F_DISABLE))
823 		return (0);
824 
825 	host->up = HOST_UNKNOWN;
826 	host->flags &= ~(F_DISABLE);
827 	host->flags &= ~(F_DEL);
828 	host->flags &= ~(F_ADD);
829 
830 	imsg_compose(ibuf_hce, IMSG_HOST_ENABLE, 0, 0, -1,
831 	    &host->conf.id, sizeof (host->conf.id));
832 	/* Forward to relay engine(s) */
833 	for (n = 0; n < env->sc_prefork_relay; n++)
834 		imsg_compose(&ibuf_relay[n],
835 		    IMSG_HOST_ENABLE, 0, 0, -1,
836 		    &host->conf.id, sizeof(host->conf.id));
837 	log_debug("enable_host: enabled host %d", host->conf.id);
838 
839 	if (!host->conf.parentid) {
840 		/* Enable all children */
841 		SLIST_FOREACH(h, &host->children, child)
842 			enable_host(c, id, h);
843 		pfe_sync();
844 	}
845 	return (0);
846 }
847 
848 void
849 pfe_sync(void)
850 {
851 	struct rdr		*rdr;
852 	struct table		*active;
853 	struct table		*table;
854 	struct ctl_id		 id;
855 	struct imsg		 imsg;
856 	struct ctl_demote	 demote;
857 
858 	bzero(&id, sizeof(id));
859 	bzero(&imsg, sizeof(imsg));
860 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
861 		rdr->conf.flags &= ~(F_BACKUP);
862 		rdr->conf.flags &= ~(F_DOWN);
863 
864 		if (rdr->conf.flags & F_DISABLE ||
865 		    (rdr->table->up == 0 && rdr->backup->up == 0)) {
866 			rdr->conf.flags |= F_DOWN;
867 			active = NULL;
868 		} else if (rdr->table->up == 0 && rdr->backup->up > 0) {
869 			rdr->conf.flags |= F_BACKUP;
870 			active = rdr->backup;
871 			active->conf.flags |=
872 			    rdr->table->conf.flags & F_CHANGED;
873 			active->conf.flags |=
874 			    rdr->backup->conf.flags & F_CHANGED;
875 		} else
876 			active = rdr->table;
877 
878 		if (active != NULL && active->conf.flags & F_CHANGED) {
879 			id.id = active->conf.id;
880 			imsg.hdr.type = IMSG_CTL_TABLE_CHANGED;
881 			imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
882 			imsg.data = &id;
883 			sync_table(env, rdr, active);
884 			control_imsg_forward(&imsg);
885 		}
886 
887 		if (rdr->conf.flags & F_DOWN) {
888 			if (rdr->conf.flags & F_ACTIVE_RULESET) {
889 				flush_table(env, rdr);
890 				log_debug("pfe_sync: disabling ruleset");
891 				rdr->conf.flags &= ~(F_ACTIVE_RULESET);
892 				id.id = rdr->conf.id;
893 				imsg.hdr.type = IMSG_CTL_PULL_RULESET;
894 				imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
895 				imsg.data = &id;
896 				sync_ruleset(env, rdr, 0);
897 				control_imsg_forward(&imsg);
898 			}
899 		} else if (!(rdr->conf.flags & F_ACTIVE_RULESET)) {
900 			log_debug("pfe_sync: enabling ruleset");
901 			rdr->conf.flags |= F_ACTIVE_RULESET;
902 			id.id = rdr->conf.id;
903 			imsg.hdr.type = IMSG_CTL_PUSH_RULESET;
904 			imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
905 			imsg.data = &id;
906 			sync_ruleset(env, rdr, 1);
907 			control_imsg_forward(&imsg);
908 		}
909 	}
910 
911 	TAILQ_FOREACH(table, env->sc_tables, entry) {
912 		/*
913 		 * clean up change flag.
914 		 */
915 		table->conf.flags &= ~(F_CHANGED);
916 
917 		/*
918 		 * handle demotion.
919 		 */
920 		if ((table->conf.flags & F_DEMOTE) == 0)
921 			continue;
922 		demote.level = 0;
923 		if (table->up && table->conf.flags & F_DEMOTED) {
924 			demote.level = -1;
925 			table->conf.flags &= ~F_DEMOTED;
926 		}
927 		else if (!table->up && !(table->conf.flags & F_DEMOTED)) {
928 			demote.level = 1;
929 			table->conf.flags |= F_DEMOTED;
930 		}
931 		if (demote.level == 0)
932 			continue;
933 		log_debug("pfe_sync: demote %d table '%s' group '%s'",
934 		    demote.level, table->conf.name, table->conf.demote_group);
935 		(void)strlcpy(demote.group, table->conf.demote_group,
936 		    sizeof(demote.group));
937 		imsg_compose(ibuf_main, IMSG_DEMOTE, 0, 0, -1,
938 		    &demote, sizeof(demote));
939 	}
940 }
941 
942 void
943 pfe_statistics(int fd, short events, void *arg)
944 {
945 	struct rdr		*rdr;
946 	struct ctl_stats	*cur;
947 	struct timeval		 tv, tv_now;
948 	int			 resethour, resetday;
949 	u_long			 cnt;
950 
951 	timerclear(&tv);
952 	if (gettimeofday(&tv_now, NULL) == -1)
953 		fatal("pfe_statistics: gettimeofday");
954 
955 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
956 		cnt = check_table(env, rdr, rdr->table);
957 		if (rdr->conf.backup_id != EMPTY_TABLE)
958 			cnt += check_table(env, rdr, rdr->backup);
959 
960 		resethour = resetday = 0;
961 
962 		cur = &rdr->stats;
963 		cur->last = cnt > cur->cnt ? cnt - cur->cnt : 0;
964 
965 		cur->cnt = cnt;
966 		cur->tick++;
967 		cur->avg = (cur->last + cur->avg) / 2;
968 		cur->last_hour += cur->last;
969 		if ((cur->tick % (3600 / env->sc_statinterval.tv_sec)) == 0) {
970 			cur->avg_hour = (cur->last_hour + cur->avg_hour) / 2;
971 			resethour++;
972 		}
973 		cur->last_day += cur->last;
974 		if ((cur->tick % (86400 / env->sc_statinterval.tv_sec)) == 0) {
975 			cur->avg_day = (cur->last_day + cur->avg_day) / 2;
976 			resethour++;
977 		}
978 		if (resethour)
979 			cur->last_hour = 0;
980 		if (resetday)
981 			cur->last_day = 0;
982 
983 		rdr->stats.interval = env->sc_statinterval.tv_sec;
984 	}
985 
986 	/* Schedule statistics timer */
987 	evtimer_set(&env->sc_statev, pfe_statistics, NULL);
988 	bcopy(&env->sc_statinterval, &tv, sizeof(tv));
989 	evtimer_add(&env->sc_statev, &tv);
990 }
991