Blob


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