xref: /openbsd-src/usr.sbin/relayd/pfe.c (revision 8663a10cc3dd3c5d98e4158b4db2207a322eac52)
1*8663a10cSsashan /*	$OpenBSD: pfe.c,v 1.91 2024/06/17 08:36:56 sashan Exp $	*/
2feb9ff76Sreyk 
3feb9ff76Sreyk /*
436f5dc5eSpyr  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
5feb9ff76Sreyk  *
6feb9ff76Sreyk  * Permission to use, copy, modify, and distribute this software for any
7feb9ff76Sreyk  * purpose with or without fee is hereby granted, provided that the above
8feb9ff76Sreyk  * copyright notice and this permission notice appear in all copies.
9feb9ff76Sreyk  *
10feb9ff76Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11feb9ff76Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12feb9ff76Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13feb9ff76Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14feb9ff76Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15feb9ff76Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16feb9ff76Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17feb9ff76Sreyk  */
18feb9ff76Sreyk 
19f04ff968Sreyk #include <sys/types.h>
20f04ff968Sreyk #include <sys/queue.h>
2105b29ae0Sbenno #include <sys/socket.h>
22f04ff968Sreyk #include <sys/time.h>
23f04ff968Sreyk #include <sys/uio.h>
2405b29ae0Sbenno #include <sys/ioctl.h>
2505b29ae0Sbenno #include <net/if.h>
2605b29ae0Sbenno #include <net/pfvar.h>
270ca734d7Sreyk 
28feb9ff76Sreyk #include <event.h>
2905b29ae0Sbenno #include <fcntl.h>
30feb9ff76Sreyk #include <stdlib.h>
31feb9ff76Sreyk #include <string.h>
32005c709bSbenno #include <unistd.h>
33f04ff968Sreyk #include <imsg.h>
34e8fb3979Spyr 
35748ceb64Sreyk #include "relayd.h"
36feb9ff76Sreyk 
370325c666Sreyk void	 pfe_init(struct privsep *, struct privsep_proc *p, void *);
38feb9ff76Sreyk void	 pfe_shutdown(void);
39dd80bec5Spyr void	 pfe_setup_events(void);
40dd80bec5Spyr void	 pfe_disable_events(void);
41feb9ff76Sreyk void	 pfe_sync(void);
42dec6607bSreyk void	 pfe_statistics(int, short, void *);
43feb9ff76Sreyk 
440325c666Sreyk int	 pfe_dispatch_parent(int, struct privsep_proc *, struct imsg *);
450325c666Sreyk int	 pfe_dispatch_hce(int, struct privsep_proc *, struct imsg *);
460325c666Sreyk int	 pfe_dispatch_relay(int, struct privsep_proc *, struct imsg *);
470325c666Sreyk 
48748ceb64Sreyk static struct relayd		*env = NULL;
49feb9ff76Sreyk 
500325c666Sreyk static struct privsep_proc procs[] = {
510325c666Sreyk 	{ "parent",	PROC_PARENT,	pfe_dispatch_parent },
520325c666Sreyk 	{ "relay",	PROC_RELAY,	pfe_dispatch_relay },
530325c666Sreyk 	{ "hce",	PROC_HCE,	pfe_dispatch_hce }
540325c666Sreyk };
55feb9ff76Sreyk 
56f910ac11Sreyk void
pfe(struct privsep * ps,struct privsep_proc * p)570325c666Sreyk pfe(struct privsep *ps, struct privsep_proc *p)
58feb9ff76Sreyk {
5905b29ae0Sbenno 	int			s;
6005b29ae0Sbenno 	struct pf_status	status;
6105b29ae0Sbenno 
620325c666Sreyk 	env = ps->ps_env;
63feb9ff76Sreyk 
6405b29ae0Sbenno 	if ((s = open(PF_SOCKET, O_RDWR)) == -1) {
6505b29ae0Sbenno 		fatal("%s: cannot open pf socket", __func__);
6605b29ae0Sbenno 	}
6705b29ae0Sbenno 	if (env->sc_pf == NULL) {
6805b29ae0Sbenno 		if ((env->sc_pf = calloc(1, sizeof(*(env->sc_pf)))) == NULL)
6905b29ae0Sbenno 			fatal("calloc");
7005b29ae0Sbenno 		env->sc_pf->dev = s;
7105b29ae0Sbenno 	}
7205b29ae0Sbenno 	if (ioctl(env->sc_pf->dev, DIOCGETSTATUS, &status) == -1)
73efc39811Sbenno 		fatal("%s: DIOCGETSTATUS", __func__);
7405b29ae0Sbenno 	if (!status.running)
75efc39811Sbenno 		fatalx("%s: pf is disabled", __func__);
7605b29ae0Sbenno 	log_debug("%s: filter init done", __func__);
7705b29ae0Sbenno 
78f910ac11Sreyk 	proc_run(ps, p, procs, nitems(procs), pfe_init, NULL);
79053249cdSpyr }
80feb9ff76Sreyk 
810325c666Sreyk void
pfe_init(struct privsep * ps,struct privsep_proc * p,void * arg)820325c666Sreyk pfe_init(struct privsep *ps, struct privsep_proc *p, void *arg)
830325c666Sreyk {
84a2195becSreyk 	if (config_init(ps->ps_env) == -1)
85a2195becSreyk 		fatal("failed to initialize configuration");
86a2195becSreyk 
87005c709bSbenno 	if (pledge("stdio recvfd unix pf", NULL) == -1)
88005c709bSbenno 		fatal("pledge");
89005c709bSbenno 
900325c666Sreyk 	p->p_shutdown = pfe_shutdown;
91feb9ff76Sreyk }
92feb9ff76Sreyk 
93feb9ff76Sreyk void
pfe_shutdown(void)94feb9ff76Sreyk pfe_shutdown(void)
95feb9ff76Sreyk {
96feb9ff76Sreyk 	flush_rulesets(env);
97a2195becSreyk 	config_purge(env, CONFIG_ALL);
98feb9ff76Sreyk }
99feb9ff76Sreyk 
100feb9ff76Sreyk void
pfe_setup_events(void)101dd80bec5Spyr pfe_setup_events(void)
102dd80bec5Spyr {
103dec6607bSreyk 	struct timeval	 tv;
104dd80bec5Spyr 
105dec6607bSreyk 	/* Schedule statistics timer */
106a2195becSreyk 	if (!event_initialized(&env->sc_statev)) {
10735d10c30Sreyk 		evtimer_set(&env->sc_statev, pfe_statistics, NULL);
108586b5f8aSreyk 		bcopy(&env->sc_conf.statinterval, &tv, sizeof(tv));
10935d10c30Sreyk 		evtimer_add(&env->sc_statev, &tv);
110dd80bec5Spyr 	}
111a2195becSreyk }
112dd80bec5Spyr 
113dd80bec5Spyr void
pfe_disable_events(void)114dd80bec5Spyr pfe_disable_events(void)
115dd80bec5Spyr {
11635d10c30Sreyk 	event_del(&env->sc_statev);
117dd80bec5Spyr }
118dd80bec5Spyr 
1190325c666Sreyk int
pfe_dispatch_hce(int fd,struct privsep_proc * p,struct imsg * imsg)1200325c666Sreyk pfe_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg)
121feb9ff76Sreyk {
122feb9ff76Sreyk 	struct host		*host;
123feb9ff76Sreyk 	struct table		*table;
124feb9ff76Sreyk 	struct ctl_status	 st;
125feb9ff76Sreyk 
126c28c61ccSreyk 	control_imsg_forward(p->p_ps, imsg);
1272668107aSreyk 
1280325c666Sreyk 	switch (imsg->hdr.type) {
129feb9ff76Sreyk 	case IMSG_HOST_STATUS:
1300325c666Sreyk 		IMSG_SIZE_CHECK(imsg, &st);
1310325c666Sreyk 		memcpy(&st, imsg->data, sizeof(st));
132feb9ff76Sreyk 		if ((host = host_find(env, st.id)) == NULL)
133efc39811Sbenno 			fatalx("%s: invalid host id", __func__);
134c0dc99f6Sreyk 		host->he = st.he;
135253ca2ceSreyk 		if (host->flags & F_DISABLE)
136253ca2ceSreyk 			break;
1372edd718bSreyk 		host->retry_cnt = st.retry_cnt;
1382edd718bSreyk 		if (st.up != HOST_UNKNOWN) {
1392edd718bSreyk 			host->check_cnt++;
1402edd718bSreyk 			if (st.up == HOST_UP)
1412edd718bSreyk 				host->up_cnt++;
1422edd718bSreyk 		}
1432edd718bSreyk 		if (host->check_cnt != st.check_cnt) {
14485a8c65fSreyk 			log_debug("%s: host %d => %d", __func__,
14568b79041Spyr 			    host->conf.id, host->up);
146efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
147feb9ff76Sreyk 		}
148feb9ff76Sreyk 
1492edd718bSreyk 		if (host->up == st.up)
1502edd718bSreyk 			break;
1512edd718bSreyk 
1522edd718bSreyk 		/* Forward to relay engine(s) */
153c2c37c5dSreyk 		proc_compose(env->sc_ps, PROC_RELAY,
154c2c37c5dSreyk 		    IMSG_HOST_STATUS, &st, sizeof(st));
1552edd718bSreyk 
15668b79041Spyr 		if ((table = table_find(env, host->conf.tableid))
15768b79041Spyr 		    == NULL)
158efc39811Sbenno 			fatalx("%s: invalid table id", __func__);
159feb9ff76Sreyk 
16085a8c65fSreyk 		log_debug("%s: state %d for host %u %s", __func__,
16168b79041Spyr 		    st.up, host->conf.id, host->conf.name);
162feb9ff76Sreyk 
163c26b8e61Smartijn /* XXX Readd hosttrap code later */
164c26b8e61Smartijn #if 0
1656e07057bSblambert 		snmp_hosttrap(env, table, host);
166c26b8e61Smartijn #endif
1676e07057bSblambert 
168f123d28fSreyk 		/*
169f123d28fSreyk 		 * Do not change the table state when the host
170f123d28fSreyk 		 * state switches between UNKNOWN and DOWN.
171f123d28fSreyk 		 */
172f123d28fSreyk 		if (HOST_ISUP(st.up)) {
17368b79041Spyr 			table->conf.flags |= F_CHANGED;
174feb9ff76Sreyk 			table->up++;
175feb9ff76Sreyk 			host->flags |= F_ADD;
176feb9ff76Sreyk 			host->flags &= ~(F_DEL);
177f123d28fSreyk 		} else if (HOST_ISUP(host->up)) {
178feb9ff76Sreyk 			table->up--;
17968b79041Spyr 			table->conf.flags |= F_CHANGED;
180feb9ff76Sreyk 			host->flags |= F_DEL;
181feb9ff76Sreyk 			host->flags &= ~(F_ADD);
1822381fb0aSbenno 			host->up = st.up;
1832381fb0aSbenno 			pfe_sync();
184feb9ff76Sreyk 		}
185f123d28fSreyk 
186feb9ff76Sreyk 		host->up = st.up;
187feb9ff76Sreyk 		break;
188feb9ff76Sreyk 	case IMSG_SYNC:
189feb9ff76Sreyk 		pfe_sync();
190feb9ff76Sreyk 		break;
191feb9ff76Sreyk 	default:
1920325c666Sreyk 		return (-1);
193feb9ff76Sreyk 	}
194feb9ff76Sreyk 
1950325c666Sreyk 	return (0);
1960325c666Sreyk }
1970325c666Sreyk 
1980325c666Sreyk int
pfe_dispatch_parent(int fd,struct privsep_proc * p,struct imsg * imsg)1990325c666Sreyk pfe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
200feb9ff76Sreyk {
2010325c666Sreyk 	switch (imsg->hdr.type) {
202a2195becSreyk 	case IMSG_CFG_TABLE:
203a2195becSreyk 		config_gettable(env, imsg);
204adf98c11Spyr 		break;
205a2195becSreyk 	case IMSG_CFG_HOST:
206a2195becSreyk 		config_gethost(env, imsg);
207adf98c11Spyr 		break;
208a2195becSreyk 	case IMSG_CFG_RDR:
209a2195becSreyk 		config_getrdr(env, imsg);
210adf98c11Spyr 		break;
211a2195becSreyk 	case IMSG_CFG_VIRT:
212a2195becSreyk 		config_getvirt(env, imsg);
213adf98c11Spyr 		break;
214a2195becSreyk 	case IMSG_CFG_ROUTER:
215a2195becSreyk 		config_getrt(env, imsg);
216adf98c11Spyr 		break;
217a2195becSreyk 	case IMSG_CFG_ROUTE:
218a2195becSreyk 		config_getroute(env, imsg);
219a2195becSreyk 		break;
220a2195becSreyk 	case IMSG_CFG_PROTO:
221a2195becSreyk 		config_getproto(env, imsg);
222a2195becSreyk 		break;
223a2195becSreyk 	case IMSG_CFG_RELAY:
224a2195becSreyk 		config_getrelay(env, imsg);
225a2195becSreyk 		break;
226416fa9c0Sreyk 	case IMSG_CFG_RELAY_TABLE:
227416fa9c0Sreyk 		config_getrelaytable(env, imsg);
228416fa9c0Sreyk 		break;
229a2195becSreyk 	case IMSG_CFG_DONE:
230a2195becSreyk 		config_getcfg(env, imsg);
23164b0bc0cSreyk 		init_tables(env);
232c26b8e61Smartijn 		agentx_init(env);
2330dcba380Scamield 		break;
2340dcba380Scamield 	case IMSG_CTL_START:
235adf98c11Spyr 		pfe_setup_events();
236adf98c11Spyr 		pfe_sync();
237adf98c11Spyr 		break;
238a2195becSreyk 	case IMSG_CTL_RESET:
239a2195becSreyk 		config_getreset(env, imsg);
240a2195becSreyk 		break;
241c26b8e61Smartijn 	case IMSG_AGENTXSOCK:
242c26b8e61Smartijn 		agentx_getsock(imsg);
2436e07057bSblambert 		break;
244feb9ff76Sreyk 	default:
2450325c666Sreyk 		return (-1);
246feb9ff76Sreyk 	}
247feb9ff76Sreyk 
2480325c666Sreyk 	return (0);
2490325c666Sreyk }
2500325c666Sreyk 
2510325c666Sreyk int
pfe_dispatch_relay(int fd,struct privsep_proc * p,struct imsg * imsg)2520325c666Sreyk pfe_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
2532edd718bSreyk {
2542edd718bSreyk 	struct ctl_natlook	 cnl;
2552edd718bSreyk 	struct ctl_stats	 crs;
2562edd718bSreyk 	struct relay		*rlay;
25786b74329Sreyk 	struct ctl_conn		*c;
2586e07057bSblambert 	struct rsession		 con, *s, *t;
25986b74329Sreyk 	int			 cid;
2606e07057bSblambert 	objid_t			 sid;
2612edd718bSreyk 
2620325c666Sreyk 	switch (imsg->hdr.type) {
2632edd718bSreyk 	case IMSG_NATLOOK:
2640325c666Sreyk 		IMSG_SIZE_CHECK(imsg, &cnl);
2650325c666Sreyk 		bcopy(imsg->data, &cnl, sizeof(cnl));
266586b5f8aSreyk 		if (cnl.proc > env->sc_conf.prefork_relay)
267efc39811Sbenno 			fatalx("%s: invalid relay proc", __func__);
2682edd718bSreyk 		if (natlook(env, &cnl) != 0)
2692edd718bSreyk 			cnl.in = -1;
2700325c666Sreyk 		proc_compose_imsg(env->sc_ps, PROC_RELAY, cnl.proc,
271c2c37c5dSreyk 		    IMSG_NATLOOK, -1, -1, &cnl, sizeof(cnl));
2722edd718bSreyk 		break;
2732edd718bSreyk 	case IMSG_STATISTICS:
2740325c666Sreyk 		IMSG_SIZE_CHECK(imsg, &crs);
2750325c666Sreyk 		bcopy(imsg->data, &crs, sizeof(crs));
276586b5f8aSreyk 		if (crs.proc > env->sc_conf.prefork_relay)
277efc39811Sbenno 			fatalx("%s: invalid relay proc", __func__);
2782edd718bSreyk 		if ((rlay = relay_find(env, crs.id)) == NULL)
279efc39811Sbenno 			fatalx("%s: invalid relay id", __func__);
2804a5b9b3eSreyk 		bcopy(&crs, &rlay->rl_stats[crs.proc], sizeof(crs));
2814a5b9b3eSreyk 		rlay->rl_stats[crs.proc].interval =
282586b5f8aSreyk 		    env->sc_conf.statinterval.tv_sec;
2832edd718bSreyk 		break;
28486b74329Sreyk 	case IMSG_CTL_SESSION:
28586b74329Sreyk 		IMSG_SIZE_CHECK(imsg, &con);
28686b74329Sreyk 		memcpy(&con, imsg->data, sizeof(con));
28786b74329Sreyk 		if ((c = control_connbyfd(con.se_cid)) == NULL) {
28886b74329Sreyk 			log_debug("%s: control connection %d not found",
28986b74329Sreyk 			    __func__, con.se_cid);
29086b74329Sreyk 			return (0);
29186b74329Sreyk 		}
29286b74329Sreyk 		imsg_compose_event(&c->iev,
29386b74329Sreyk 		    IMSG_CTL_SESSION, 0, 0, -1,
29486b74329Sreyk 		    &con, sizeof(con));
29586b74329Sreyk 		break;
29686b74329Sreyk 	case IMSG_CTL_END:
29786b74329Sreyk 		IMSG_SIZE_CHECK(imsg, &cid);
29886b74329Sreyk 		memcpy(&cid, imsg->data, sizeof(cid));
29986b74329Sreyk 		if ((c = control_connbyfd(cid)) == NULL) {
30086b74329Sreyk 			log_debug("%s: control connection %d not found",
30186b74329Sreyk 			    __func__, cid);
30286b74329Sreyk 			return (0);
30386b74329Sreyk 		}
30486b74329Sreyk 		if (c->waiting == 0) {
30586b74329Sreyk 			log_debug("%s: no pending control requests", __func__);
30686b74329Sreyk 			return (0);
30786b74329Sreyk 		} else if (--c->waiting == 0) {
30886b74329Sreyk 			/* Last ack for a previous request */
30986b74329Sreyk 			imsg_compose_event(&c->iev, IMSG_CTL_END,
31086b74329Sreyk 			    0, 0, -1, NULL, 0);
31186b74329Sreyk 		}
31286b74329Sreyk 		break;
3136e07057bSblambert 	case IMSG_SESS_PUBLISH:
3146e07057bSblambert 		IMSG_SIZE_CHECK(imsg, s);
3156e07057bSblambert 		if ((s = calloc(1, sizeof(*s))) == NULL)
3166e07057bSblambert 			return (0);		/* XXX */
3176e07057bSblambert 		memcpy(s, imsg->data, sizeof(*s));
3186e07057bSblambert 		TAILQ_FOREACH(t, &env->sc_sessions, se_entry) {
319080b67b5Sjsg 			/* duplicate registration */
320080b67b5Sjsg 			if (t->se_id == s->se_id) {
321080b67b5Sjsg 				free(s);
3226e07057bSblambert 				return (0);
323080b67b5Sjsg 			}
3246e07057bSblambert 			if (t->se_id > s->se_id)
3256e07057bSblambert 				break;
3266e07057bSblambert 		}
3276e07057bSblambert 		if (t)
3286e07057bSblambert 			TAILQ_INSERT_BEFORE(t, s, se_entry);
3296e07057bSblambert 		else
3306e07057bSblambert 			TAILQ_INSERT_TAIL(&env->sc_sessions, s, se_entry);
3316e07057bSblambert 		break;
3326e07057bSblambert 	case IMSG_SESS_UNPUBLISH:
3336e07057bSblambert 		IMSG_SIZE_CHECK(imsg, &sid);
3346e07057bSblambert 		memcpy(&sid, imsg->data, sizeof(sid));
3356e07057bSblambert 		TAILQ_FOREACH(s, &env->sc_sessions, se_entry)
3366e07057bSblambert 			if (s->se_id == sid)
3376e07057bSblambert 				break;
3386e07057bSblambert 		if (s) {
3396e07057bSblambert 			TAILQ_REMOVE(&env->sc_sessions, s, se_entry);
3406e07057bSblambert 			free(s);
3418c50f9c1Sblambert 		} else {
3428c50f9c1Sblambert 			DPRINTF("removal of unpublished session %i", sid);
3438c50f9c1Sblambert 		}
3446e07057bSblambert 		break;
3452edd718bSreyk 	default:
3460325c666Sreyk 		return (-1);
3472edd718bSreyk 	}
3480325c666Sreyk 
3490325c666Sreyk 	return (0);
3502edd718bSreyk }
3512edd718bSreyk 
3522edd718bSreyk void
show(struct ctl_conn * c)353feb9ff76Sreyk show(struct ctl_conn *c)
354feb9ff76Sreyk {
3559591a9f7Spyr 	struct rdr		*rdr;
356feb9ff76Sreyk 	struct host		*host;
3572edd718bSreyk 	struct relay		*rlay;
358417c432fSreyk 	struct router		*rt;
359417c432fSreyk 	struct netroute		*nr;
360416fa9c0Sreyk 	struct relay_table	*rlt;
361feb9ff76Sreyk 
36235d10c30Sreyk 	if (env->sc_rdrs == NULL)
363f00ce691Spyr 		goto relays;
36435d10c30Sreyk 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
365f07d0e3bSpyr 		imsg_compose_event(&c->iev, IMSG_CTL_RDR, 0, 0, -1,
3669591a9f7Spyr 		    rdr, sizeof(*rdr));
3679591a9f7Spyr 		if (rdr->conf.flags & F_DISABLE)
368feb9ff76Sreyk 			continue;
369feb9ff76Sreyk 
370f07d0e3bSpyr 		imsg_compose_event(&c->iev, IMSG_CTL_RDR_STATS, 0, 0, -1,
371dec6607bSreyk 		    &rdr->stats, sizeof(rdr->stats));
372dec6607bSreyk 
373f07d0e3bSpyr 		imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
3749591a9f7Spyr 		    rdr->table, sizeof(*rdr->table));
3759591a9f7Spyr 		if (!(rdr->table->conf.flags & F_DISABLE))
3769591a9f7Spyr 			TAILQ_FOREACH(host, &rdr->table->hosts, entry)
377f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_HOST,
378069bf5e4Spyr 				    0, 0, -1, host, sizeof(*host));
379feb9ff76Sreyk 
3809591a9f7Spyr 		if (rdr->backup->conf.id == EMPTY_TABLE)
381feb9ff76Sreyk 			continue;
382f07d0e3bSpyr 		imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
3839591a9f7Spyr 		    rdr->backup, sizeof(*rdr->backup));
3849591a9f7Spyr 		if (!(rdr->backup->conf.flags & F_DISABLE))
3859591a9f7Spyr 			TAILQ_FOREACH(host, &rdr->backup->hosts, entry)
386f07d0e3bSpyr 				imsg_compose_event(&c->iev, IMSG_CTL_HOST,
387069bf5e4Spyr 				    0, 0, -1, host, sizeof(*host));
388feb9ff76Sreyk 	}
389f00ce691Spyr relays:
39035d10c30Sreyk 	if (env->sc_relays == NULL)
391417c432fSreyk 		goto routers;
3924a5b9b3eSreyk 	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
393586b5f8aSreyk 		rlay->rl_stats[env->sc_conf.prefork_relay].id = EMPTY_ID;
394f07d0e3bSpyr 		imsg_compose_event(&c->iev, IMSG_CTL_RELAY, 0, 0, -1,
3952edd718bSreyk 		    rlay, sizeof(*rlay));
396f07d0e3bSpyr 		imsg_compose_event(&c->iev, IMSG_CTL_RELAY_STATS, 0, 0, -1,
3974a5b9b3eSreyk 		    &rlay->rl_stats, sizeof(rlay->rl_stats));
398253ca2ceSreyk 
399416fa9c0Sreyk 		TAILQ_FOREACH(rlt, &rlay->rl_tables, rlt_entry) {
400f07d0e3bSpyr 			imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
401416fa9c0Sreyk 			    rlt->rlt_table, sizeof(*rlt->rlt_table));
402416fa9c0Sreyk 			if (!(rlt->rlt_table->conf.flags & F_DISABLE))
403416fa9c0Sreyk 				TAILQ_FOREACH(host,
404416fa9c0Sreyk 				    &rlt->rlt_table->hosts, entry)
405416fa9c0Sreyk 					imsg_compose_event(&c->iev,
406416fa9c0Sreyk 					    IMSG_CTL_HOST, 0, 0, -1,
407416fa9c0Sreyk 					    host, sizeof(*host));
408416fa9c0Sreyk 		}
4092edd718bSreyk 	}
410417c432fSreyk 
411417c432fSreyk routers:
412417c432fSreyk 	if (env->sc_rts == NULL)
413417c432fSreyk 		goto end;
414417c432fSreyk 	TAILQ_FOREACH(rt, env->sc_rts, rt_entry) {
415417c432fSreyk 		imsg_compose_event(&c->iev, IMSG_CTL_ROUTER, 0, 0, -1,
416417c432fSreyk 		    rt, sizeof(*rt));
417417c432fSreyk 		if (rt->rt_conf.flags & F_DISABLE)
418417c432fSreyk 			continue;
419417c432fSreyk 
420417c432fSreyk 		TAILQ_FOREACH(nr, &rt->rt_netroutes, nr_entry)
421417c432fSreyk 			imsg_compose_event(&c->iev, IMSG_CTL_NETROUTE,
422417c432fSreyk 			    0, 0, -1, nr, sizeof(*nr));
423417c432fSreyk 		imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
424417c432fSreyk 		    rt->rt_gwtable, sizeof(*rt->rt_gwtable));
425417c432fSreyk 		if (!(rt->rt_gwtable->conf.flags & F_DISABLE))
426417c432fSreyk 			TAILQ_FOREACH(host, &rt->rt_gwtable->hosts, entry)
427417c432fSreyk 				imsg_compose_event(&c->iev, IMSG_CTL_HOST,
428417c432fSreyk 				    0, 0, -1, host, sizeof(*host));
429417c432fSreyk 	}
430417c432fSreyk 
431f00ce691Spyr end:
432f07d0e3bSpyr 	imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
433feb9ff76Sreyk }
434feb9ff76Sreyk 
4359d421a7aSreyk void
show_sessions(struct ctl_conn * c)4369d421a7aSreyk show_sessions(struct ctl_conn *c)
4379d421a7aSreyk {
43886b74329Sreyk 	int			 proc, cid;
4399d421a7aSreyk 
440586b5f8aSreyk 	for (proc = 0; proc < env->sc_conf.prefork_relay; proc++) {
44186b74329Sreyk 		cid = c->iev.ibuf.fd;
4420325c666Sreyk 
4439d421a7aSreyk 		/*
4449d421a7aSreyk 		 * Request all the running sessions from the process
4459d421a7aSreyk 		 */
4460325c666Sreyk 		proc_compose_imsg(env->sc_ps, PROC_RELAY, proc,
447c2c37c5dSreyk 		    IMSG_CTL_SESSION, -1, -1, &cid, sizeof(cid));
44886b74329Sreyk 		c->waiting++;
4499d421a7aSreyk 	}
4509d421a7aSreyk }
451feb9ff76Sreyk 
452feb9ff76Sreyk int
disable_rdr(struct ctl_conn * c,struct ctl_id * id)4539591a9f7Spyr disable_rdr(struct ctl_conn *c, struct ctl_id *id)
454feb9ff76Sreyk {
4559591a9f7Spyr 	struct rdr	*rdr;
456feb9ff76Sreyk 
457ef1f2334Sreyk 	if (id->id == EMPTY_ID)
4589591a9f7Spyr 		rdr = rdr_findbyname(env, id->name);
459ef1f2334Sreyk 	else
4609591a9f7Spyr 		rdr = rdr_find(env, id->id);
4619591a9f7Spyr 	if (rdr == NULL)
462feb9ff76Sreyk 		return (-1);
4639591a9f7Spyr 	id->id = rdr->conf.id;
464feb9ff76Sreyk 
4659591a9f7Spyr 	if (rdr->conf.flags & F_DISABLE)
466feb9ff76Sreyk 		return (0);
467feb9ff76Sreyk 
4689591a9f7Spyr 	rdr->conf.flags |= F_DISABLE;
4699591a9f7Spyr 	rdr->conf.flags &= ~(F_ADD);
4709591a9f7Spyr 	rdr->conf.flags |= F_DEL;
4719591a9f7Spyr 	rdr->table->conf.flags |= F_DISABLE;
47285a8c65fSreyk 	log_debug("%s: redirect %d", __func__, rdr->conf.id);
473feb9ff76Sreyk 	pfe_sync();
474feb9ff76Sreyk 	return (0);
475feb9ff76Sreyk }
476feb9ff76Sreyk 
477feb9ff76Sreyk int
enable_rdr(struct ctl_conn * c,struct ctl_id * id)4789591a9f7Spyr enable_rdr(struct ctl_conn *c, struct ctl_id *id)
479feb9ff76Sreyk {
4809591a9f7Spyr 	struct rdr	*rdr;
481ef1f2334Sreyk 	struct ctl_id	 eid;
482feb9ff76Sreyk 
483ef1f2334Sreyk 	if (id->id == EMPTY_ID)
4849591a9f7Spyr 		rdr = rdr_findbyname(env, id->name);
485ef1f2334Sreyk 	else
4869591a9f7Spyr 		rdr = rdr_find(env, id->id);
4879591a9f7Spyr 	if (rdr == NULL)
488feb9ff76Sreyk 		return (-1);
4899591a9f7Spyr 	id->id = rdr->conf.id;
490feb9ff76Sreyk 
4919591a9f7Spyr 	if (!(rdr->conf.flags & F_DISABLE))
492feb9ff76Sreyk 		return (0);
493feb9ff76Sreyk 
4949591a9f7Spyr 	rdr->conf.flags &= ~(F_DISABLE);
4959591a9f7Spyr 	rdr->conf.flags &= ~(F_DEL);
4969591a9f7Spyr 	rdr->conf.flags |= F_ADD;
49785a8c65fSreyk 	log_debug("%s: redirect %d", __func__, rdr->conf.id);
498feb9ff76Sreyk 
499ef1f2334Sreyk 	bzero(&eid, sizeof(eid));
500ef1f2334Sreyk 
501feb9ff76Sreyk 	/* XXX: we're syncing twice */
5029591a9f7Spyr 	eid.id = rdr->table->conf.id;
503ef1f2334Sreyk 	if (enable_table(c, &eid) == -1)
504feb9ff76Sreyk 		return (-1);
5059591a9f7Spyr 	if (rdr->backup->conf.id == EMPTY_ID)
506eac15771Spyr 		return (0);
5079591a9f7Spyr 	eid.id = rdr->backup->conf.id;
508ef1f2334Sreyk 	if (enable_table(c, &eid) == -1)
509feb9ff76Sreyk 		return (-1);
510feb9ff76Sreyk 	return (0);
511feb9ff76Sreyk }
512feb9ff76Sreyk 
513feb9ff76Sreyk int
disable_table(struct ctl_conn * c,struct ctl_id * id)514ef1f2334Sreyk disable_table(struct ctl_conn *c, struct ctl_id *id)
515feb9ff76Sreyk {
516feb9ff76Sreyk 	struct table	*table;
517feb9ff76Sreyk 	struct host	*host;
518feb9ff76Sreyk 
519ef1f2334Sreyk 	if (id->id == EMPTY_ID)
520ef1f2334Sreyk 		table = table_findbyname(env, id->name);
521ef1f2334Sreyk 	else
522ef1f2334Sreyk 		table = table_find(env, id->id);
523ef1f2334Sreyk 	if (table == NULL)
524feb9ff76Sreyk 		return (-1);
52568b79041Spyr 	id->id = table->conf.id;
526b850e197Sjsg 	if (table->conf.rdrid > 0 && rdr_find(env, table->conf.rdrid) == NULL)
527efc39811Sbenno 		fatalx("%s: desynchronised", __func__);
528feb9ff76Sreyk 
52968b79041Spyr 	if (table->conf.flags & F_DISABLE)
530feb9ff76Sreyk 		return (0);
53168b79041Spyr 	table->conf.flags |= (F_DISABLE|F_CHANGED);
532feb9ff76Sreyk 	table->up = 0;
533feb9ff76Sreyk 	TAILQ_FOREACH(host, &table->hosts, entry)
534feb9ff76Sreyk 		host->up = HOST_UNKNOWN;
535c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_HCE, IMSG_TABLE_DISABLE,
53668b79041Spyr 	    &table->conf.id, sizeof(table->conf.id));
5370325c666Sreyk 
538b850e197Sjsg 	/* Forward to relay engine(s) */
539c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_RELAY, IMSG_TABLE_DISABLE,
540b850e197Sjsg 	    &table->conf.id, sizeof(table->conf.id));
5410325c666Sreyk 
54285a8c65fSreyk 	log_debug("%s: table %d", __func__, table->conf.id);
543feb9ff76Sreyk 	pfe_sync();
544feb9ff76Sreyk 	return (0);
545feb9ff76Sreyk }
546feb9ff76Sreyk 
547feb9ff76Sreyk int
enable_table(struct ctl_conn * c,struct ctl_id * id)548ef1f2334Sreyk enable_table(struct ctl_conn *c, struct ctl_id *id)
549feb9ff76Sreyk {
550feb9ff76Sreyk 	struct table	*table;
551feb9ff76Sreyk 	struct host	*host;
552feb9ff76Sreyk 
553ef1f2334Sreyk 	if (id->id == EMPTY_ID)
554ef1f2334Sreyk 		table = table_findbyname(env, id->name);
555ef1f2334Sreyk 	else
556ef1f2334Sreyk 		table = table_find(env, id->id);
557ef1f2334Sreyk 	if (table == NULL)
558feb9ff76Sreyk 		return (-1);
55968b79041Spyr 	id->id = table->conf.id;
560ef1f2334Sreyk 
561b850e197Sjsg 	if (table->conf.rdrid > 0 && rdr_find(env, table->conf.rdrid) == NULL)
562efc39811Sbenno 		fatalx("%s: desynchronised", __func__);
563feb9ff76Sreyk 
56468b79041Spyr 	if (!(table->conf.flags & F_DISABLE))
565feb9ff76Sreyk 		return (0);
56668b79041Spyr 	table->conf.flags &= ~(F_DISABLE);
56768b79041Spyr 	table->conf.flags |= F_CHANGED;
568feb9ff76Sreyk 	table->up = 0;
569feb9ff76Sreyk 	TAILQ_FOREACH(host, &table->hosts, entry)
570feb9ff76Sreyk 		host->up = HOST_UNKNOWN;
571c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_HCE, IMSG_TABLE_ENABLE,
57268b79041Spyr 	    &table->conf.id, sizeof(table->conf.id));
5730325c666Sreyk 
574b850e197Sjsg 	/* Forward to relay engine(s) */
575c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_RELAY, IMSG_TABLE_ENABLE,
576b850e197Sjsg 	    &table->conf.id, sizeof(table->conf.id));
5770325c666Sreyk 
57885a8c65fSreyk 	log_debug("%s: table %d", __func__, table->conf.id);
579feb9ff76Sreyk 	pfe_sync();
580feb9ff76Sreyk 	return (0);
581feb9ff76Sreyk }
582feb9ff76Sreyk 
583feb9ff76Sreyk int
disable_host(struct ctl_conn * c,struct ctl_id * id,struct host * host)584c723f8edSreyk disable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
585feb9ff76Sreyk {
586c723f8edSreyk 	struct host	*h;
587*8663a10cSsashan 	struct table	*table, *t;
588*8663a10cSsashan 	int	 host_byname = 0;
589feb9ff76Sreyk 
590c723f8edSreyk 	if (host == NULL) {
591*8663a10cSsashan 		if (id->id == EMPTY_ID) {
592ef1f2334Sreyk 			host = host_findbyname(env, id->name);
593*8663a10cSsashan 			host_byname = 1;
594*8663a10cSsashan 		}
595ef1f2334Sreyk 		else
596ef1f2334Sreyk 			host = host_find(env, id->id);
597c723f8edSreyk 		if (host == NULL || host->conf.parentid)
598feb9ff76Sreyk 			return (-1);
599c723f8edSreyk 	}
60068b79041Spyr 	id->id = host->conf.id;
601feb9ff76Sreyk 
602feb9ff76Sreyk 	if (host->flags & F_DISABLE)
603feb9ff76Sreyk 		return (0);
604feb9ff76Sreyk 
605feb9ff76Sreyk 	if (host->up == HOST_UP) {
60668b79041Spyr 		if ((table = table_find(env, host->conf.tableid)) == NULL)
607efc39811Sbenno 			fatalx("%s: invalid table id", __func__);
608feb9ff76Sreyk 		table->up--;
60968b79041Spyr 		table->conf.flags |= F_CHANGED;
610feb9ff76Sreyk 	}
611feb9ff76Sreyk 
612feb9ff76Sreyk 	host->up = HOST_UNKNOWN;
613feb9ff76Sreyk 	host->flags |= F_DISABLE;
614feb9ff76Sreyk 	host->flags |= F_DEL;
615feb9ff76Sreyk 	host->flags &= ~(F_ADD);
6162edd718bSreyk 	host->check_cnt = 0;
6172edd718bSreyk 	host->up_cnt = 0;
618feb9ff76Sreyk 
619c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_HCE, IMSG_HOST_DISABLE,
62068b79041Spyr 	    &host->conf.id, sizeof(host->conf.id));
6210325c666Sreyk 
622253ca2ceSreyk 	/* Forward to relay engine(s) */
623c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_RELAY, IMSG_HOST_DISABLE,
62468b79041Spyr 	    &host->conf.id, sizeof(host->conf.id));
62585a8c65fSreyk 	log_debug("%s: host %d", __func__, host->conf.id);
626c723f8edSreyk 
6271584e35dSreyk 	if (!host->conf.parentid) {
628c723f8edSreyk 		/* Disable all children */
6291584e35dSreyk 		SLIST_FOREACH(h, &host->children, child)
630c723f8edSreyk 			disable_host(c, id, h);
631*8663a10cSsashan 
632*8663a10cSsashan 		/* Disable hosts with same name on all tables */
633*8663a10cSsashan 		if (host_byname)
634*8663a10cSsashan 			TAILQ_FOREACH(t, env->sc_tables, entry)
635*8663a10cSsashan 				TAILQ_FOREACH(h, &t->hosts, entry)
636*8663a10cSsashan 					if (strcmp(h->conf.name,
637*8663a10cSsashan 					    host->conf.name) == 0 &&
638*8663a10cSsashan 					    h->conf.id != host->conf.id &&
639*8663a10cSsashan 					    !h->conf.parentid)
640*8663a10cSsashan 						disable_host(c, id, h);
641feb9ff76Sreyk 		pfe_sync();
6421584e35dSreyk 	}
643feb9ff76Sreyk 	return (0);
644feb9ff76Sreyk }
645feb9ff76Sreyk 
646feb9ff76Sreyk int
enable_host(struct ctl_conn * c,struct ctl_id * id,struct host * host)647c723f8edSreyk enable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
648feb9ff76Sreyk {
649c723f8edSreyk 	struct host	*h;
650*8663a10cSsashan 	struct table	*t;
651*8663a10cSsashan 	int	 host_byname = 0;
652*8663a10cSsashan 
653feb9ff76Sreyk 
654c723f8edSreyk 	if (host == NULL) {
655*8663a10cSsashan 		if (id->id == EMPTY_ID) {
656ef1f2334Sreyk 			host = host_findbyname(env, id->name);
657*8663a10cSsashan 			host_byname = 1;
658*8663a10cSsashan 		}
659ef1f2334Sreyk 		else
660ef1f2334Sreyk 			host = host_find(env, id->id);
661c723f8edSreyk 		if (host == NULL || host->conf.parentid)
662feb9ff76Sreyk 			return (-1);
663c723f8edSreyk 	}
66468b79041Spyr 	id->id = host->conf.id;
665feb9ff76Sreyk 
666feb9ff76Sreyk 	if (!(host->flags & F_DISABLE))
667feb9ff76Sreyk 		return (0);
668feb9ff76Sreyk 
669feb9ff76Sreyk 	host->up = HOST_UNKNOWN;
670feb9ff76Sreyk 	host->flags &= ~(F_DISABLE);
671feb9ff76Sreyk 	host->flags &= ~(F_DEL);
672feb9ff76Sreyk 	host->flags &= ~(F_ADD);
673feb9ff76Sreyk 
674c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_HCE, IMSG_HOST_ENABLE,
67568b79041Spyr 	    &host->conf.id, sizeof (host->conf.id));
6760325c666Sreyk 
677253ca2ceSreyk 	/* Forward to relay engine(s) */
678c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_RELAY, IMSG_HOST_ENABLE,
67968b79041Spyr 	    &host->conf.id, sizeof(host->conf.id));
6800325c666Sreyk 
68185a8c65fSreyk 	log_debug("%s: host %d", __func__, host->conf.id);
682c723f8edSreyk 
6831584e35dSreyk 	if (!host->conf.parentid) {
684c723f8edSreyk 		/* Enable all children */
6851584e35dSreyk 		SLIST_FOREACH(h, &host->children, child)
686c723f8edSreyk 			enable_host(c, id, h);
687*8663a10cSsashan 
688*8663a10cSsashan 		/* Enable hosts with same name on all tables */
689*8663a10cSsashan 		if (host_byname)
690*8663a10cSsashan 			TAILQ_FOREACH(t, env->sc_tables, entry)
691*8663a10cSsashan 				TAILQ_FOREACH(h, &t->hosts, entry)
692*8663a10cSsashan 					if (strcmp(h->conf.name,
693*8663a10cSsashan 					    host->conf.name) == 0 &&
694*8663a10cSsashan 					    h->conf.id != host->conf.id &&
695*8663a10cSsashan 					    !h->conf.parentid)
696*8663a10cSsashan 						enable_host(c, id, h);
697feb9ff76Sreyk 		pfe_sync();
6981584e35dSreyk 	}
699feb9ff76Sreyk 	return (0);
700feb9ff76Sreyk }
701feb9ff76Sreyk 
702feb9ff76Sreyk void
pfe_sync(void)703feb9ff76Sreyk pfe_sync(void)
704feb9ff76Sreyk {
7059591a9f7Spyr 	struct rdr		*rdr;
706feb9ff76Sreyk 	struct table		*active;
7072edd718bSreyk 	struct table		*table;
7089ea8c990Spyr 	struct ctl_id		 id;
7099ea8c990Spyr 	struct imsg		 imsg;
7102edd718bSreyk 	struct ctl_demote	 demote;
71134438db4Sreyk 	struct router		*rt;
712feb9ff76Sreyk 
7139ea8c990Spyr 	bzero(&id, sizeof(id));
7149ea8c990Spyr 	bzero(&imsg, sizeof(imsg));
71535d10c30Sreyk 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
7169591a9f7Spyr 		rdr->conf.flags &= ~(F_BACKUP);
7179591a9f7Spyr 		rdr->conf.flags &= ~(F_DOWN);
718feb9ff76Sreyk 
7199591a9f7Spyr 		if (rdr->conf.flags & F_DISABLE ||
7209591a9f7Spyr 		    (rdr->table->up == 0 && rdr->backup->up == 0)) {
7219591a9f7Spyr 			rdr->conf.flags |= F_DOWN;
722feb9ff76Sreyk 			active = NULL;
7239591a9f7Spyr 		} else if (rdr->table->up == 0 && rdr->backup->up > 0) {
7249591a9f7Spyr 			rdr->conf.flags |= F_BACKUP;
7259591a9f7Spyr 			active = rdr->backup;
72668b79041Spyr 			active->conf.flags |=
7279591a9f7Spyr 			    rdr->table->conf.flags & F_CHANGED;
72868b79041Spyr 			active->conf.flags |=
7299591a9f7Spyr 			    rdr->backup->conf.flags & F_CHANGED;
730feb9ff76Sreyk 		} else
7319591a9f7Spyr 			active = rdr->table;
732feb9ff76Sreyk 
73368b79041Spyr 		if (active != NULL && active->conf.flags & F_CHANGED) {
73468b79041Spyr 			id.id = active->conf.id;
7359ea8c990Spyr 			imsg.hdr.type = IMSG_CTL_TABLE_CHANGED;
7369ea8c990Spyr 			imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
7379ea8c990Spyr 			imsg.data = &id;
7389591a9f7Spyr 			sync_table(env, rdr, active);
739c28c61ccSreyk 			control_imsg_forward(env->sc_ps, &imsg);
7409ea8c990Spyr 		}
741feb9ff76Sreyk 
7429591a9f7Spyr 		if (rdr->conf.flags & F_DOWN) {
7439591a9f7Spyr 			if (rdr->conf.flags & F_ACTIVE_RULESET) {
7449591a9f7Spyr 				flush_table(env, rdr);
74585a8c65fSreyk 				log_debug("%s: disabling ruleset", __func__);
7469591a9f7Spyr 				rdr->conf.flags &= ~(F_ACTIVE_RULESET);
7479591a9f7Spyr 				id.id = rdr->conf.id;
7489ea8c990Spyr 				imsg.hdr.type = IMSG_CTL_PULL_RULESET;
7499ea8c990Spyr 				imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
7509ea8c990Spyr 				imsg.data = &id;
7519591a9f7Spyr 				sync_ruleset(env, rdr, 0);
752c28c61ccSreyk 				control_imsg_forward(env->sc_ps, &imsg);
753feb9ff76Sreyk 			}
7549591a9f7Spyr 		} else if (!(rdr->conf.flags & F_ACTIVE_RULESET)) {
75585a8c65fSreyk 			log_debug("%s: enabling ruleset", __func__);
7569591a9f7Spyr 			rdr->conf.flags |= F_ACTIVE_RULESET;
7579591a9f7Spyr 			id.id = rdr->conf.id;
7589ea8c990Spyr 			imsg.hdr.type = IMSG_CTL_PUSH_RULESET;
7599ea8c990Spyr 			imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
7609ea8c990Spyr 			imsg.data = &id;
7619591a9f7Spyr 			sync_ruleset(env, rdr, 1);
762c28c61ccSreyk 			control_imsg_forward(env->sc_ps, &imsg);
763feb9ff76Sreyk 		}
764feb9ff76Sreyk 	}
7652edd718bSreyk 
76634438db4Sreyk 	TAILQ_FOREACH(rt, env->sc_rts, rt_entry) {
76734438db4Sreyk 		rt->rt_conf.flags &= ~(F_BACKUP);
76834438db4Sreyk 		rt->rt_conf.flags &= ~(F_DOWN);
76934438db4Sreyk 
77034438db4Sreyk 		if ((rt->rt_gwtable->conf.flags & F_CHANGED))
77134438db4Sreyk 			sync_routes(env, rt);
77234438db4Sreyk 	}
77334438db4Sreyk 
77435d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
775a2195becSreyk 		if (table->conf.check == CHECK_NOCHECK)
776a2195becSreyk 			continue;
777a2195becSreyk 
778228f7fe4Spyr 		/*
779228f7fe4Spyr 		 * clean up change flag.
780228f7fe4Spyr 		 */
781228f7fe4Spyr 		table->conf.flags &= ~(F_CHANGED);
782228f7fe4Spyr 
783228f7fe4Spyr 		/*
784228f7fe4Spyr 		 * handle demotion.
785228f7fe4Spyr 		 */
78668b79041Spyr 		if ((table->conf.flags & F_DEMOTE) == 0)
7872edd718bSreyk 			continue;
7882edd718bSreyk 		demote.level = 0;
78968b79041Spyr 		if (table->up && table->conf.flags & F_DEMOTED) {
7902edd718bSreyk 			demote.level = -1;
79168b79041Spyr 			table->conf.flags &= ~F_DEMOTED;
7922edd718bSreyk 		}
79368b79041Spyr 		else if (!table->up && !(table->conf.flags & F_DEMOTED)) {
7942edd718bSreyk 			demote.level = 1;
79568b79041Spyr 			table->conf.flags |= F_DEMOTED;
7962edd718bSreyk 		}
7972edd718bSreyk 		if (demote.level == 0)
7982edd718bSreyk 			continue;
79985a8c65fSreyk 		log_debug("%s: demote %d table '%s' group '%s'", __func__,
80068b79041Spyr 		    demote.level, table->conf.name, table->conf.demote_group);
80168b79041Spyr 		(void)strlcpy(demote.group, table->conf.demote_group,
8022edd718bSreyk 		    sizeof(demote.group));
803c2c37c5dSreyk 		proc_compose(env->sc_ps, PROC_PARENT, IMSG_DEMOTE,
8042edd718bSreyk 		    &demote, sizeof(demote));
8052edd718bSreyk 	}
806feb9ff76Sreyk }
807dec6607bSreyk 
808dec6607bSreyk void
pfe_statistics(int fd,short events,void * arg)809dec6607bSreyk pfe_statistics(int fd, short events, void *arg)
810dec6607bSreyk {
811dec6607bSreyk 	struct rdr		*rdr;
812dec6607bSreyk 	struct ctl_stats	*cur;
813dec6607bSreyk 	struct timeval		 tv, tv_now;
814dec6607bSreyk 	int			 resethour, resetday;
815dec6607bSreyk 	u_long			 cnt;
816dec6607bSreyk 
817dec6607bSreyk 	timerclear(&tv);
818fd1841a3Sreyk 	getmonotime(&tv_now);
819dec6607bSreyk 
82035d10c30Sreyk 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
821dec6607bSreyk 		cnt = check_table(env, rdr, rdr->table);
822dec6607bSreyk 		if (rdr->conf.backup_id != EMPTY_TABLE)
823dec6607bSreyk 			cnt += check_table(env, rdr, rdr->backup);
824dec6607bSreyk 
825dec6607bSreyk 		resethour = resetday = 0;
826dec6607bSreyk 
827dec6607bSreyk 		cur = &rdr->stats;
828dec6607bSreyk 		cur->last = cnt > cur->cnt ? cnt - cur->cnt : 0;
829dec6607bSreyk 
830dec6607bSreyk 		cur->cnt = cnt;
831dec6607bSreyk 		cur->tick++;
832dec6607bSreyk 		cur->avg = (cur->last + cur->avg) / 2;
833dec6607bSreyk 		cur->last_hour += cur->last;
834586b5f8aSreyk 		if ((cur->tick %
835586b5f8aSreyk 		    (3600 / env->sc_conf.statinterval.tv_sec)) == 0) {
836dec6607bSreyk 			cur->avg_hour = (cur->last_hour + cur->avg_hour) / 2;
837dec6607bSreyk 			resethour++;
838dec6607bSreyk 		}
839dec6607bSreyk 		cur->last_day += cur->last;
840586b5f8aSreyk 		if ((cur->tick %
841586b5f8aSreyk 		    (86400 / env->sc_conf.statinterval.tv_sec)) == 0) {
842dec6607bSreyk 			cur->avg_day = (cur->last_day + cur->avg_day) / 2;
843dec6607bSreyk 			resethour++;
844dec6607bSreyk 		}
845dec6607bSreyk 		if (resethour)
846dec6607bSreyk 			cur->last_hour = 0;
847dec6607bSreyk 		if (resetday)
848dec6607bSreyk 			cur->last_day = 0;
849dec6607bSreyk 
850586b5f8aSreyk 		rdr->stats.interval = env->sc_conf.statinterval.tv_sec;
851dec6607bSreyk 	}
852dec6607bSreyk 
853dec6607bSreyk 	/* Schedule statistics timer */
85435d10c30Sreyk 	evtimer_set(&env->sc_statev, pfe_statistics, NULL);
855586b5f8aSreyk 	bcopy(&env->sc_conf.statinterval, &tv, sizeof(tv));
85635d10c30Sreyk 	evtimer_add(&env->sc_statev, &tv);
857dec6607bSreyk }
858