xref: /openbsd/usr.sbin/relayd/pfe.c (revision 4cfece93)
1 /*	$OpenBSD: pfe.c,v 1.89 2017/05/28 10:39:15 benno 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/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/time.h>
23 #include <sys/uio.h>
24 #include <sys/ioctl.h>
25 #include <net/if.h>
26 #include <net/pfvar.h>
27 
28 #include <event.h>
29 #include <fcntl.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <imsg.h>
34 
35 #include "relayd.h"
36 
37 void	 pfe_init(struct privsep *, struct privsep_proc *p, void *);
38 void	 pfe_shutdown(void);
39 void	 pfe_setup_events(void);
40 void	 pfe_disable_events(void);
41 void	 pfe_sync(void);
42 void	 pfe_statistics(int, short, void *);
43 
44 int	 pfe_dispatch_parent(int, struct privsep_proc *, struct imsg *);
45 int	 pfe_dispatch_hce(int, struct privsep_proc *, struct imsg *);
46 int	 pfe_dispatch_relay(int, struct privsep_proc *, struct imsg *);
47 
48 static struct relayd		*env = NULL;
49 
50 static struct privsep_proc procs[] = {
51 	{ "parent",	PROC_PARENT,	pfe_dispatch_parent },
52 	{ "relay",	PROC_RELAY,	pfe_dispatch_relay },
53 	{ "hce",	PROC_HCE,	pfe_dispatch_hce }
54 };
55 
56 void
57 pfe(struct privsep *ps, struct privsep_proc *p)
58 {
59 	int			s;
60 	struct pf_status	status;
61 
62 	env = ps->ps_env;
63 
64 	if ((s = open(PF_SOCKET, O_RDWR)) == -1) {
65 		fatal("%s: cannot open pf socket", __func__);
66 	}
67 	if (env->sc_pf == NULL) {
68 		if ((env->sc_pf = calloc(1, sizeof(*(env->sc_pf)))) == NULL)
69 			fatal("calloc");
70 		env->sc_pf->dev = s;
71 	}
72 	if (ioctl(env->sc_pf->dev, DIOCGETSTATUS, &status) == -1)
73 		fatal("%s: DIOCGETSTATUS", __func__);
74 	if (!status.running)
75 		fatalx("%s: pf is disabled", __func__);
76 	log_debug("%s: filter init done", __func__);
77 
78 	proc_run(ps, p, procs, nitems(procs), pfe_init, NULL);
79 }
80 
81 void
82 pfe_init(struct privsep *ps, struct privsep_proc *p, void *arg)
83 {
84 	if (config_init(ps->ps_env) == -1)
85 		fatal("failed to initialize configuration");
86 
87 	if (pledge("stdio recvfd unix pf", NULL) == -1)
88 		fatal("pledge");
89 
90 	p->p_shutdown = pfe_shutdown;
91 }
92 
93 void
94 pfe_shutdown(void)
95 {
96 	flush_rulesets(env);
97 	config_purge(env, CONFIG_ALL);
98 }
99 
100 void
101 pfe_setup_events(void)
102 {
103 	struct timeval	 tv;
104 
105 	/* Schedule statistics timer */
106 	if (!event_initialized(&env->sc_statev)) {
107 		evtimer_set(&env->sc_statev, pfe_statistics, NULL);
108 		bcopy(&env->sc_conf.statinterval, &tv, sizeof(tv));
109 		evtimer_add(&env->sc_statev, &tv);
110 	}
111 }
112 
113 void
114 pfe_disable_events(void)
115 {
116 	event_del(&env->sc_statev);
117 }
118 
119 int
120 pfe_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg)
121 {
122 	struct host		*host;
123 	struct table		*table;
124 	struct ctl_status	 st;
125 
126 	control_imsg_forward(p->p_ps, imsg);
127 
128 	switch (imsg->hdr.type) {
129 	case IMSG_HOST_STATUS:
130 		IMSG_SIZE_CHECK(imsg, &st);
131 		memcpy(&st, imsg->data, sizeof(st));
132 		if ((host = host_find(env, st.id)) == NULL)
133 			fatalx("%s: invalid host id", __func__);
134 		host->he = st.he;
135 		if (host->flags & F_DISABLE)
136 			break;
137 		host->retry_cnt = st.retry_cnt;
138 		if (st.up != HOST_UNKNOWN) {
139 			host->check_cnt++;
140 			if (st.up == HOST_UP)
141 				host->up_cnt++;
142 		}
143 		if (host->check_cnt != st.check_cnt) {
144 			log_debug("%s: host %d => %d", __func__,
145 			    host->conf.id, host->up);
146 			fatalx("%s: desynchronized", __func__);
147 		}
148 
149 		if (host->up == st.up)
150 			break;
151 
152 		/* Forward to relay engine(s) */
153 		proc_compose(env->sc_ps, PROC_RELAY,
154 		    IMSG_HOST_STATUS, &st, sizeof(st));
155 
156 		if ((table = table_find(env, host->conf.tableid))
157 		    == NULL)
158 			fatalx("%s: invalid table id", __func__);
159 
160 		log_debug("%s: state %d for host %u %s", __func__,
161 		    st.up, host->conf.id, host->conf.name);
162 
163 		snmp_hosttrap(env, table, host);
164 
165 		/*
166 		 * Do not change the table state when the host
167 		 * state switches between UNKNOWN and DOWN.
168 		 */
169 		if (HOST_ISUP(st.up)) {
170 			table->conf.flags |= F_CHANGED;
171 			table->up++;
172 			host->flags |= F_ADD;
173 			host->flags &= ~(F_DEL);
174 		} else if (HOST_ISUP(host->up)) {
175 			table->up--;
176 			table->conf.flags |= F_CHANGED;
177 			host->flags |= F_DEL;
178 			host->flags &= ~(F_ADD);
179 			host->up = st.up;
180 			pfe_sync();
181 		}
182 
183 		host->up = st.up;
184 		break;
185 	case IMSG_SYNC:
186 		pfe_sync();
187 		break;
188 	default:
189 		return (-1);
190 	}
191 
192 	return (0);
193 }
194 
195 int
196 pfe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
197 {
198 	switch (imsg->hdr.type) {
199 	case IMSG_CFG_TABLE:
200 		config_gettable(env, imsg);
201 		break;
202 	case IMSG_CFG_HOST:
203 		config_gethost(env, imsg);
204 		break;
205 	case IMSG_CFG_RDR:
206 		config_getrdr(env, imsg);
207 		break;
208 	case IMSG_CFG_VIRT:
209 		config_getvirt(env, imsg);
210 		break;
211 	case IMSG_CFG_ROUTER:
212 		config_getrt(env, imsg);
213 		break;
214 	case IMSG_CFG_ROUTE:
215 		config_getroute(env, imsg);
216 		break;
217 	case IMSG_CFG_PROTO:
218 		config_getproto(env, imsg);
219 		break;
220 	case IMSG_CFG_RELAY:
221 		config_getrelay(env, imsg);
222 		break;
223 	case IMSG_CFG_RELAY_TABLE:
224 		config_getrelaytable(env, imsg);
225 		break;
226 	case IMSG_CFG_DONE:
227 		config_getcfg(env, imsg);
228 		init_tables(env);
229 		snmp_init(env, PROC_PARENT);
230 		break;
231 	case IMSG_CTL_START:
232 		pfe_setup_events();
233 		pfe_sync();
234 		break;
235 	case IMSG_CTL_RESET:
236 		config_getreset(env, imsg);
237 		break;
238 	case IMSG_SNMPSOCK:
239 		snmp_getsock(env, imsg);
240 		break;
241 	default:
242 		return (-1);
243 	}
244 
245 	return (0);
246 }
247 
248 int
249 pfe_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
250 {
251 	struct ctl_natlook	 cnl;
252 	struct ctl_stats	 crs;
253 	struct relay		*rlay;
254 	struct ctl_conn		*c;
255 	struct rsession		 con, *s, *t;
256 	int			 cid;
257 	objid_t			 sid;
258 
259 	switch (imsg->hdr.type) {
260 	case IMSG_NATLOOK:
261 		IMSG_SIZE_CHECK(imsg, &cnl);
262 		bcopy(imsg->data, &cnl, sizeof(cnl));
263 		if (cnl.proc > env->sc_conf.prefork_relay)
264 			fatalx("%s: invalid relay proc", __func__);
265 		if (natlook(env, &cnl) != 0)
266 			cnl.in = -1;
267 		proc_compose_imsg(env->sc_ps, PROC_RELAY, cnl.proc,
268 		    IMSG_NATLOOK, -1, -1, &cnl, sizeof(cnl));
269 		break;
270 	case IMSG_STATISTICS:
271 		IMSG_SIZE_CHECK(imsg, &crs);
272 		bcopy(imsg->data, &crs, sizeof(crs));
273 		if (crs.proc > env->sc_conf.prefork_relay)
274 			fatalx("%s: invalid relay proc", __func__);
275 		if ((rlay = relay_find(env, crs.id)) == NULL)
276 			fatalx("%s: invalid relay id", __func__);
277 		bcopy(&crs, &rlay->rl_stats[crs.proc], sizeof(crs));
278 		rlay->rl_stats[crs.proc].interval =
279 		    env->sc_conf.statinterval.tv_sec;
280 		break;
281 	case IMSG_CTL_SESSION:
282 		IMSG_SIZE_CHECK(imsg, &con);
283 		memcpy(&con, imsg->data, sizeof(con));
284 		if ((c = control_connbyfd(con.se_cid)) == NULL) {
285 			log_debug("%s: control connection %d not found",
286 			    __func__, con.se_cid);
287 			return (0);
288 		}
289 		imsg_compose_event(&c->iev,
290 		    IMSG_CTL_SESSION, 0, 0, -1,
291 		    &con, sizeof(con));
292 		break;
293 	case IMSG_CTL_END:
294 		IMSG_SIZE_CHECK(imsg, &cid);
295 		memcpy(&cid, imsg->data, sizeof(cid));
296 		if ((c = control_connbyfd(cid)) == NULL) {
297 			log_debug("%s: control connection %d not found",
298 			    __func__, cid);
299 			return (0);
300 		}
301 		if (c->waiting == 0) {
302 			log_debug("%s: no pending control requests", __func__);
303 			return (0);
304 		} else if (--c->waiting == 0) {
305 			/* Last ack for a previous request */
306 			imsg_compose_event(&c->iev, IMSG_CTL_END,
307 			    0, 0, -1, NULL, 0);
308 		}
309 		break;
310 	case IMSG_SESS_PUBLISH:
311 		IMSG_SIZE_CHECK(imsg, s);
312 		if ((s = calloc(1, sizeof(*s))) == NULL)
313 			return (0);		/* XXX */
314 		memcpy(s, imsg->data, sizeof(*s));
315 		TAILQ_FOREACH(t, &env->sc_sessions, se_entry) {
316 			/* duplicate registration */
317 			if (t->se_id == s->se_id) {
318 				free(s);
319 				return (0);
320 			}
321 			if (t->se_id > s->se_id)
322 				break;
323 		}
324 		if (t)
325 			TAILQ_INSERT_BEFORE(t, s, se_entry);
326 		else
327 			TAILQ_INSERT_TAIL(&env->sc_sessions, s, se_entry);
328 		break;
329 	case IMSG_SESS_UNPUBLISH:
330 		IMSG_SIZE_CHECK(imsg, &sid);
331 		memcpy(&sid, imsg->data, sizeof(sid));
332 		TAILQ_FOREACH(s, &env->sc_sessions, se_entry)
333 			if (s->se_id == sid)
334 				break;
335 		if (s) {
336 			TAILQ_REMOVE(&env->sc_sessions, s, se_entry);
337 			free(s);
338 		} else {
339 			DPRINTF("removal of unpublished session %i", sid);
340 		}
341 		break;
342 	default:
343 		return (-1);
344 	}
345 
346 	return (0);
347 }
348 
349 void
350 show(struct ctl_conn *c)
351 {
352 	struct rdr		*rdr;
353 	struct host		*host;
354 	struct relay		*rlay;
355 	struct router		*rt;
356 	struct netroute		*nr;
357 	struct relay_table	*rlt;
358 
359 	if (env->sc_rdrs == NULL)
360 		goto relays;
361 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
362 		imsg_compose_event(&c->iev, IMSG_CTL_RDR, 0, 0, -1,
363 		    rdr, sizeof(*rdr));
364 		if (rdr->conf.flags & F_DISABLE)
365 			continue;
366 
367 		imsg_compose_event(&c->iev, IMSG_CTL_RDR_STATS, 0, 0, -1,
368 		    &rdr->stats, sizeof(rdr->stats));
369 
370 		imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
371 		    rdr->table, sizeof(*rdr->table));
372 		if (!(rdr->table->conf.flags & F_DISABLE))
373 			TAILQ_FOREACH(host, &rdr->table->hosts, entry)
374 				imsg_compose_event(&c->iev, IMSG_CTL_HOST,
375 				    0, 0, -1, host, sizeof(*host));
376 
377 		if (rdr->backup->conf.id == EMPTY_TABLE)
378 			continue;
379 		imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
380 		    rdr->backup, sizeof(*rdr->backup));
381 		if (!(rdr->backup->conf.flags & F_DISABLE))
382 			TAILQ_FOREACH(host, &rdr->backup->hosts, entry)
383 				imsg_compose_event(&c->iev, IMSG_CTL_HOST,
384 				    0, 0, -1, host, sizeof(*host));
385 	}
386 relays:
387 	if (env->sc_relays == NULL)
388 		goto routers;
389 	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
390 		rlay->rl_stats[env->sc_conf.prefork_relay].id = EMPTY_ID;
391 		imsg_compose_event(&c->iev, IMSG_CTL_RELAY, 0, 0, -1,
392 		    rlay, sizeof(*rlay));
393 		imsg_compose_event(&c->iev, IMSG_CTL_RELAY_STATS, 0, 0, -1,
394 		    &rlay->rl_stats, sizeof(rlay->rl_stats));
395 
396 		TAILQ_FOREACH(rlt, &rlay->rl_tables, rlt_entry) {
397 			imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
398 			    rlt->rlt_table, sizeof(*rlt->rlt_table));
399 			if (!(rlt->rlt_table->conf.flags & F_DISABLE))
400 				TAILQ_FOREACH(host,
401 				    &rlt->rlt_table->hosts, entry)
402 					imsg_compose_event(&c->iev,
403 					    IMSG_CTL_HOST, 0, 0, -1,
404 					    host, sizeof(*host));
405 		}
406 	}
407 
408 routers:
409 	if (env->sc_rts == NULL)
410 		goto end;
411 	TAILQ_FOREACH(rt, env->sc_rts, rt_entry) {
412 		imsg_compose_event(&c->iev, IMSG_CTL_ROUTER, 0, 0, -1,
413 		    rt, sizeof(*rt));
414 		if (rt->rt_conf.flags & F_DISABLE)
415 			continue;
416 
417 		TAILQ_FOREACH(nr, &rt->rt_netroutes, nr_entry)
418 			imsg_compose_event(&c->iev, IMSG_CTL_NETROUTE,
419 			    0, 0, -1, nr, sizeof(*nr));
420 		imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
421 		    rt->rt_gwtable, sizeof(*rt->rt_gwtable));
422 		if (!(rt->rt_gwtable->conf.flags & F_DISABLE))
423 			TAILQ_FOREACH(host, &rt->rt_gwtable->hosts, entry)
424 				imsg_compose_event(&c->iev, IMSG_CTL_HOST,
425 				    0, 0, -1, host, sizeof(*host));
426 	}
427 
428 end:
429 	imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
430 }
431 
432 void
433 show_sessions(struct ctl_conn *c)
434 {
435 	int			 proc, cid;
436 
437 	for (proc = 0; proc < env->sc_conf.prefork_relay; proc++) {
438 		cid = c->iev.ibuf.fd;
439 
440 		/*
441 		 * Request all the running sessions from the process
442 		 */
443 		proc_compose_imsg(env->sc_ps, PROC_RELAY, proc,
444 		    IMSG_CTL_SESSION, -1, -1, &cid, sizeof(cid));
445 		c->waiting++;
446 	}
447 }
448 
449 int
450 disable_rdr(struct ctl_conn *c, struct ctl_id *id)
451 {
452 	struct rdr	*rdr;
453 
454 	if (id->id == EMPTY_ID)
455 		rdr = rdr_findbyname(env, id->name);
456 	else
457 		rdr = rdr_find(env, id->id);
458 	if (rdr == NULL)
459 		return (-1);
460 	id->id = rdr->conf.id;
461 
462 	if (rdr->conf.flags & F_DISABLE)
463 		return (0);
464 
465 	rdr->conf.flags |= F_DISABLE;
466 	rdr->conf.flags &= ~(F_ADD);
467 	rdr->conf.flags |= F_DEL;
468 	rdr->table->conf.flags |= F_DISABLE;
469 	log_debug("%s: redirect %d", __func__, rdr->conf.id);
470 	pfe_sync();
471 	return (0);
472 }
473 
474 int
475 enable_rdr(struct ctl_conn *c, struct ctl_id *id)
476 {
477 	struct rdr	*rdr;
478 	struct ctl_id	 eid;
479 
480 	if (id->id == EMPTY_ID)
481 		rdr = rdr_findbyname(env, id->name);
482 	else
483 		rdr = rdr_find(env, id->id);
484 	if (rdr == NULL)
485 		return (-1);
486 	id->id = rdr->conf.id;
487 
488 	if (!(rdr->conf.flags & F_DISABLE))
489 		return (0);
490 
491 	rdr->conf.flags &= ~(F_DISABLE);
492 	rdr->conf.flags &= ~(F_DEL);
493 	rdr->conf.flags |= F_ADD;
494 	log_debug("%s: redirect %d", __func__, rdr->conf.id);
495 
496 	bzero(&eid, sizeof(eid));
497 
498 	/* XXX: we're syncing twice */
499 	eid.id = rdr->table->conf.id;
500 	if (enable_table(c, &eid) == -1)
501 		return (-1);
502 	if (rdr->backup->conf.id == EMPTY_ID)
503 		return (0);
504 	eid.id = rdr->backup->conf.id;
505 	if (enable_table(c, &eid) == -1)
506 		return (-1);
507 	return (0);
508 }
509 
510 int
511 disable_table(struct ctl_conn *c, struct ctl_id *id)
512 {
513 	struct table	*table;
514 	struct host	*host;
515 
516 	if (id->id == EMPTY_ID)
517 		table = table_findbyname(env, id->name);
518 	else
519 		table = table_find(env, id->id);
520 	if (table == NULL)
521 		return (-1);
522 	id->id = table->conf.id;
523 	if (table->conf.rdrid > 0 && rdr_find(env, table->conf.rdrid) == NULL)
524 		fatalx("%s: desynchronised", __func__);
525 
526 	if (table->conf.flags & F_DISABLE)
527 		return (0);
528 	table->conf.flags |= (F_DISABLE|F_CHANGED);
529 	table->up = 0;
530 	TAILQ_FOREACH(host, &table->hosts, entry)
531 		host->up = HOST_UNKNOWN;
532 	proc_compose(env->sc_ps, PROC_HCE, IMSG_TABLE_DISABLE,
533 	    &table->conf.id, sizeof(table->conf.id));
534 
535 	/* Forward to relay engine(s) */
536 	proc_compose(env->sc_ps, PROC_RELAY, IMSG_TABLE_DISABLE,
537 	    &table->conf.id, sizeof(table->conf.id));
538 
539 	log_debug("%s: table %d", __func__, table->conf.id);
540 	pfe_sync();
541 	return (0);
542 }
543 
544 int
545 enable_table(struct ctl_conn *c, struct ctl_id *id)
546 {
547 	struct table	*table;
548 	struct host	*host;
549 
550 	if (id->id == EMPTY_ID)
551 		table = table_findbyname(env, id->name);
552 	else
553 		table = table_find(env, id->id);
554 	if (table == NULL)
555 		return (-1);
556 	id->id = table->conf.id;
557 
558 	if (table->conf.rdrid > 0 && rdr_find(env, table->conf.rdrid) == NULL)
559 		fatalx("%s: desynchronised", __func__);
560 
561 	if (!(table->conf.flags & F_DISABLE))
562 		return (0);
563 	table->conf.flags &= ~(F_DISABLE);
564 	table->conf.flags |= F_CHANGED;
565 	table->up = 0;
566 	TAILQ_FOREACH(host, &table->hosts, entry)
567 		host->up = HOST_UNKNOWN;
568 	proc_compose(env->sc_ps, PROC_HCE, IMSG_TABLE_ENABLE,
569 	    &table->conf.id, sizeof(table->conf.id));
570 
571 	/* Forward to relay engine(s) */
572 	proc_compose(env->sc_ps, PROC_RELAY, IMSG_TABLE_ENABLE,
573 	    &table->conf.id, sizeof(table->conf.id));
574 
575 	log_debug("%s: table %d", __func__, table->conf.id);
576 	pfe_sync();
577 	return (0);
578 }
579 
580 int
581 disable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
582 {
583 	struct host	*h;
584 	struct table	*table;
585 
586 	if (host == NULL) {
587 		if (id->id == EMPTY_ID)
588 			host = host_findbyname(env, id->name);
589 		else
590 			host = host_find(env, id->id);
591 		if (host == NULL || host->conf.parentid)
592 			return (-1);
593 	}
594 	id->id = host->conf.id;
595 
596 	if (host->flags & F_DISABLE)
597 		return (0);
598 
599 	if (host->up == HOST_UP) {
600 		if ((table = table_find(env, host->conf.tableid)) == NULL)
601 			fatalx("%s: invalid table id", __func__);
602 		table->up--;
603 		table->conf.flags |= F_CHANGED;
604 	}
605 
606 	host->up = HOST_UNKNOWN;
607 	host->flags |= F_DISABLE;
608 	host->flags |= F_DEL;
609 	host->flags &= ~(F_ADD);
610 	host->check_cnt = 0;
611 	host->up_cnt = 0;
612 
613 	proc_compose(env->sc_ps, PROC_HCE, IMSG_HOST_DISABLE,
614 	    &host->conf.id, sizeof(host->conf.id));
615 
616 	/* Forward to relay engine(s) */
617 	proc_compose(env->sc_ps, PROC_RELAY, IMSG_HOST_DISABLE,
618 	    &host->conf.id, sizeof(host->conf.id));
619 	log_debug("%s: host %d", __func__, host->conf.id);
620 
621 	if (!host->conf.parentid) {
622 		/* Disable all children */
623 		SLIST_FOREACH(h, &host->children, child)
624 			disable_host(c, id, h);
625 		pfe_sync();
626 	}
627 	return (0);
628 }
629 
630 int
631 enable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
632 {
633 	struct host	*h;
634 
635 	if (host == NULL) {
636 		if (id->id == EMPTY_ID)
637 			host = host_findbyname(env, id->name);
638 		else
639 			host = host_find(env, id->id);
640 		if (host == NULL || host->conf.parentid)
641 			return (-1);
642 	}
643 	id->id = host->conf.id;
644 
645 	if (!(host->flags & F_DISABLE))
646 		return (0);
647 
648 	host->up = HOST_UNKNOWN;
649 	host->flags &= ~(F_DISABLE);
650 	host->flags &= ~(F_DEL);
651 	host->flags &= ~(F_ADD);
652 
653 	proc_compose(env->sc_ps, PROC_HCE, IMSG_HOST_ENABLE,
654 	    &host->conf.id, sizeof (host->conf.id));
655 
656 	/* Forward to relay engine(s) */
657 	proc_compose(env->sc_ps, PROC_RELAY, IMSG_HOST_ENABLE,
658 	    &host->conf.id, sizeof(host->conf.id));
659 
660 	log_debug("%s: host %d", __func__, host->conf.id);
661 
662 	if (!host->conf.parentid) {
663 		/* Enable all children */
664 		SLIST_FOREACH(h, &host->children, child)
665 			enable_host(c, id, h);
666 		pfe_sync();
667 	}
668 	return (0);
669 }
670 
671 void
672 pfe_sync(void)
673 {
674 	struct rdr		*rdr;
675 	struct table		*active;
676 	struct table		*table;
677 	struct ctl_id		 id;
678 	struct imsg		 imsg;
679 	struct ctl_demote	 demote;
680 	struct router		*rt;
681 
682 	bzero(&id, sizeof(id));
683 	bzero(&imsg, sizeof(imsg));
684 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
685 		rdr->conf.flags &= ~(F_BACKUP);
686 		rdr->conf.flags &= ~(F_DOWN);
687 
688 		if (rdr->conf.flags & F_DISABLE ||
689 		    (rdr->table->up == 0 && rdr->backup->up == 0)) {
690 			rdr->conf.flags |= F_DOWN;
691 			active = NULL;
692 		} else if (rdr->table->up == 0 && rdr->backup->up > 0) {
693 			rdr->conf.flags |= F_BACKUP;
694 			active = rdr->backup;
695 			active->conf.flags |=
696 			    rdr->table->conf.flags & F_CHANGED;
697 			active->conf.flags |=
698 			    rdr->backup->conf.flags & F_CHANGED;
699 		} else
700 			active = rdr->table;
701 
702 		if (active != NULL && active->conf.flags & F_CHANGED) {
703 			id.id = active->conf.id;
704 			imsg.hdr.type = IMSG_CTL_TABLE_CHANGED;
705 			imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
706 			imsg.data = &id;
707 			sync_table(env, rdr, active);
708 			control_imsg_forward(env->sc_ps, &imsg);
709 		}
710 
711 		if (rdr->conf.flags & F_DOWN) {
712 			if (rdr->conf.flags & F_ACTIVE_RULESET) {
713 				flush_table(env, rdr);
714 				log_debug("%s: disabling ruleset", __func__);
715 				rdr->conf.flags &= ~(F_ACTIVE_RULESET);
716 				id.id = rdr->conf.id;
717 				imsg.hdr.type = IMSG_CTL_PULL_RULESET;
718 				imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
719 				imsg.data = &id;
720 				sync_ruleset(env, rdr, 0);
721 				control_imsg_forward(env->sc_ps, &imsg);
722 			}
723 		} else if (!(rdr->conf.flags & F_ACTIVE_RULESET)) {
724 			log_debug("%s: enabling ruleset", __func__);
725 			rdr->conf.flags |= F_ACTIVE_RULESET;
726 			id.id = rdr->conf.id;
727 			imsg.hdr.type = IMSG_CTL_PUSH_RULESET;
728 			imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
729 			imsg.data = &id;
730 			sync_ruleset(env, rdr, 1);
731 			control_imsg_forward(env->sc_ps, &imsg);
732 		}
733 	}
734 
735 	TAILQ_FOREACH(rt, env->sc_rts, rt_entry) {
736 		rt->rt_conf.flags &= ~(F_BACKUP);
737 		rt->rt_conf.flags &= ~(F_DOWN);
738 
739 		if ((rt->rt_gwtable->conf.flags & F_CHANGED))
740 			sync_routes(env, rt);
741 	}
742 
743 	TAILQ_FOREACH(table, env->sc_tables, entry) {
744 		if (table->conf.check == CHECK_NOCHECK)
745 			continue;
746 
747 		/*
748 		 * clean up change flag.
749 		 */
750 		table->conf.flags &= ~(F_CHANGED);
751 
752 		/*
753 		 * handle demotion.
754 		 */
755 		if ((table->conf.flags & F_DEMOTE) == 0)
756 			continue;
757 		demote.level = 0;
758 		if (table->up && table->conf.flags & F_DEMOTED) {
759 			demote.level = -1;
760 			table->conf.flags &= ~F_DEMOTED;
761 		}
762 		else if (!table->up && !(table->conf.flags & F_DEMOTED)) {
763 			demote.level = 1;
764 			table->conf.flags |= F_DEMOTED;
765 		}
766 		if (demote.level == 0)
767 			continue;
768 		log_debug("%s: demote %d table '%s' group '%s'", __func__,
769 		    demote.level, table->conf.name, table->conf.demote_group);
770 		(void)strlcpy(demote.group, table->conf.demote_group,
771 		    sizeof(demote.group));
772 		proc_compose(env->sc_ps, PROC_PARENT, IMSG_DEMOTE,
773 		    &demote, sizeof(demote));
774 	}
775 }
776 
777 void
778 pfe_statistics(int fd, short events, void *arg)
779 {
780 	struct rdr		*rdr;
781 	struct ctl_stats	*cur;
782 	struct timeval		 tv, tv_now;
783 	int			 resethour, resetday;
784 	u_long			 cnt;
785 
786 	timerclear(&tv);
787 	getmonotime(&tv_now);
788 
789 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
790 		cnt = check_table(env, rdr, rdr->table);
791 		if (rdr->conf.backup_id != EMPTY_TABLE)
792 			cnt += check_table(env, rdr, rdr->backup);
793 
794 		resethour = resetday = 0;
795 
796 		cur = &rdr->stats;
797 		cur->last = cnt > cur->cnt ? cnt - cur->cnt : 0;
798 
799 		cur->cnt = cnt;
800 		cur->tick++;
801 		cur->avg = (cur->last + cur->avg) / 2;
802 		cur->last_hour += cur->last;
803 		if ((cur->tick %
804 		    (3600 / env->sc_conf.statinterval.tv_sec)) == 0) {
805 			cur->avg_hour = (cur->last_hour + cur->avg_hour) / 2;
806 			resethour++;
807 		}
808 		cur->last_day += cur->last;
809 		if ((cur->tick %
810 		    (86400 / env->sc_conf.statinterval.tv_sec)) == 0) {
811 			cur->avg_day = (cur->last_day + cur->avg_day) / 2;
812 			resethour++;
813 		}
814 		if (resethour)
815 			cur->last_hour = 0;
816 		if (resetday)
817 			cur->last_day = 0;
818 
819 		rdr->stats.interval = env->sc_conf.statinterval.tv_sec;
820 	}
821 
822 	/* Schedule statistics timer */
823 	evtimer_set(&env->sc_statev, pfe_statistics, NULL);
824 	bcopy(&env->sc_conf.statinterval, &tv, sizeof(tv));
825 	evtimer_add(&env->sc_statev, &tv);
826 }
827