File: [local] / src / usr.sbin / relayd / pfe.c (download)
Revision 1.90, Mon Sep 14 11:30:25 2020 UTC (3 years, 8 months ago) by martijn
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, HEAD Changes since 1.89: +7 -4 lines
Rewrite the agentx code of relayd. This new framework should allow us
to add new objects easier if so desired and should handle a lot more
corner-cases.
This commit should also fix the following:
- On most (all) tables it omits the *Entry elements, making it not map to
OPENBSD-RELAYD-MIB.txt.
- sstolen returns the size of the sockaddr_in{,6}, instead of the
sin{,6}_addr resulting in garbage data to be put in the ip-field.
- relaydSessionPortIn and relaydSessionPortOut are swapped
- relaydSessions only uses relaydSessionIndex, while
OPENBSD-RELAYD-MIB.txt says it should have 2 indices
- miscellaneous minor things related to the AGENTX-protocol, like wonky
index handeling and returning NOSUCHINSTANCE where NOSUCHOBJECT
should be returned, etc.
This commit does remove traps, but it's large enough as is and I intent
on adding it soon(tm). It also deprecates the snmp keyword in favour of
an agentx keyword. The snmp keyword is still available, but will be
removed in the future.
Tweaks and OK denis@ on the relayd parts
Tweaks and OK claudio@ on the agentx parts
"Get it in" deraadt@
|
/* $OpenBSD: pfe.c,v 1.90 2020/09/14 11:30:25 martijn Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <event.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <imsg.h>
#include "relayd.h"
void pfe_init(struct privsep *, struct privsep_proc *p, void *);
void pfe_shutdown(void);
void pfe_setup_events(void);
void pfe_disable_events(void);
void pfe_sync(void);
void pfe_statistics(int, short, void *);
int pfe_dispatch_parent(int, struct privsep_proc *, struct imsg *);
int pfe_dispatch_hce(int, struct privsep_proc *, struct imsg *);
int pfe_dispatch_relay(int, struct privsep_proc *, struct imsg *);
static struct relayd *env = NULL;
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT, pfe_dispatch_parent },
{ "relay", PROC_RELAY, pfe_dispatch_relay },
{ "hce", PROC_HCE, pfe_dispatch_hce }
};
void
pfe(struct privsep *ps, struct privsep_proc *p)
{
int s;
struct pf_status status;
env = ps->ps_env;
if ((s = open(PF_SOCKET, O_RDWR)) == -1) {
fatal("%s: cannot open pf socket", __func__);
}
if (env->sc_pf == NULL) {
if ((env->sc_pf = calloc(1, sizeof(*(env->sc_pf)))) == NULL)
fatal("calloc");
env->sc_pf->dev = s;
}
if (ioctl(env->sc_pf->dev, DIOCGETSTATUS, &status) == -1)
fatal("%s: DIOCGETSTATUS", __func__);
if (!status.running)
fatalx("%s: pf is disabled", __func__);
log_debug("%s: filter init done", __func__);
proc_run(ps, p, procs, nitems(procs), pfe_init, NULL);
}
void
pfe_init(struct privsep *ps, struct privsep_proc *p, void *arg)
{
if (config_init(ps->ps_env) == -1)
fatal("failed to initialize configuration");
if (pledge("stdio recvfd unix pf", NULL) == -1)
fatal("pledge");
p->p_shutdown = pfe_shutdown;
}
void
pfe_shutdown(void)
{
flush_rulesets(env);
config_purge(env, CONFIG_ALL);
}
void
pfe_setup_events(void)
{
struct timeval tv;
/* Schedule statistics timer */
if (!event_initialized(&env->sc_statev)) {
evtimer_set(&env->sc_statev, pfe_statistics, NULL);
bcopy(&env->sc_conf.statinterval, &tv, sizeof(tv));
evtimer_add(&env->sc_statev, &tv);
}
}
void
pfe_disable_events(void)
{
event_del(&env->sc_statev);
}
int
pfe_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct host *host;
struct table *table;
struct ctl_status st;
control_imsg_forward(p->p_ps, imsg);
switch (imsg->hdr.type) {
case IMSG_HOST_STATUS:
IMSG_SIZE_CHECK(imsg, &st);
memcpy(&st, imsg->data, sizeof(st));
if ((host = host_find(env, st.id)) == NULL)
fatalx("%s: invalid host id", __func__);
host->he = st.he;
if (host->flags & F_DISABLE)
break;
host->retry_cnt = st.retry_cnt;
if (st.up != HOST_UNKNOWN) {
host->check_cnt++;
if (st.up == HOST_UP)
host->up_cnt++;
}
if (host->check_cnt != st.check_cnt) {
log_debug("%s: host %d => %d", __func__,
host->conf.id, host->up);
fatalx("%s: desynchronized", __func__);
}
if (host->up == st.up)
break;
/* Forward to relay engine(s) */
proc_compose(env->sc_ps, PROC_RELAY,
IMSG_HOST_STATUS, &st, sizeof(st));
if ((table = table_find(env, host->conf.tableid))
== NULL)
fatalx("%s: invalid table id", __func__);
log_debug("%s: state %d for host %u %s", __func__,
st.up, host->conf.id, host->conf.name);
/* XXX Readd hosttrap code later */
#if 0
snmp_hosttrap(env, table, host);
#endif
/*
* Do not change the table state when the host
* state switches between UNKNOWN and DOWN.
*/
if (HOST_ISUP(st.up)) {
table->conf.flags |= F_CHANGED;
table->up++;
host->flags |= F_ADD;
host->flags &= ~(F_DEL);
} else if (HOST_ISUP(host->up)) {
table->up--;
table->conf.flags |= F_CHANGED;
host->flags |= F_DEL;
host->flags &= ~(F_ADD);
host->up = st.up;
pfe_sync();
}
host->up = st.up;
break;
case IMSG_SYNC:
pfe_sync();
break;
default:
return (-1);
}
return (0);
}
int
pfe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{
switch (imsg->hdr.type) {
case IMSG_CFG_TABLE:
config_gettable(env, imsg);
break;
case IMSG_CFG_HOST:
config_gethost(env, imsg);
break;
case IMSG_CFG_RDR:
config_getrdr(env, imsg);
break;
case IMSG_CFG_VIRT:
config_getvirt(env, imsg);
break;
case IMSG_CFG_ROUTER:
config_getrt(env, imsg);
break;
case IMSG_CFG_ROUTE:
config_getroute(env, imsg);
break;
case IMSG_CFG_PROTO:
config_getproto(env, imsg);
break;
case IMSG_CFG_RELAY:
config_getrelay(env, imsg);
break;
case IMSG_CFG_RELAY_TABLE:
config_getrelaytable(env, imsg);
break;
case IMSG_CFG_DONE:
config_getcfg(env, imsg);
init_tables(env);
agentx_init(env);
break;
case IMSG_CTL_START:
pfe_setup_events();
pfe_sync();
break;
case IMSG_CTL_RESET:
config_getreset(env, imsg);
break;
case IMSG_AGENTXSOCK:
agentx_getsock(imsg);
break;
default:
return (-1);
}
return (0);
}
int
pfe_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct ctl_natlook cnl;
struct ctl_stats crs;
struct relay *rlay;
struct ctl_conn *c;
struct rsession con, *s, *t;
int cid;
objid_t sid;
switch (imsg->hdr.type) {
case IMSG_NATLOOK:
IMSG_SIZE_CHECK(imsg, &cnl);
bcopy(imsg->data, &cnl, sizeof(cnl));
if (cnl.proc > env->sc_conf.prefork_relay)
fatalx("%s: invalid relay proc", __func__);
if (natlook(env, &cnl) != 0)
cnl.in = -1;
proc_compose_imsg(env->sc_ps, PROC_RELAY, cnl.proc,
IMSG_NATLOOK, -1, -1, &cnl, sizeof(cnl));
break;
case IMSG_STATISTICS:
IMSG_SIZE_CHECK(imsg, &crs);
bcopy(imsg->data, &crs, sizeof(crs));
if (crs.proc > env->sc_conf.prefork_relay)
fatalx("%s: invalid relay proc", __func__);
if ((rlay = relay_find(env, crs.id)) == NULL)
fatalx("%s: invalid relay id", __func__);
bcopy(&crs, &rlay->rl_stats[crs.proc], sizeof(crs));
rlay->rl_stats[crs.proc].interval =
env->sc_conf.statinterval.tv_sec;
break;
case IMSG_CTL_SESSION:
IMSG_SIZE_CHECK(imsg, &con);
memcpy(&con, imsg->data, sizeof(con));
if ((c = control_connbyfd(con.se_cid)) == NULL) {
log_debug("%s: control connection %d not found",
__func__, con.se_cid);
return (0);
}
imsg_compose_event(&c->iev,
IMSG_CTL_SESSION, 0, 0, -1,
&con, sizeof(con));
break;
case IMSG_CTL_END:
IMSG_SIZE_CHECK(imsg, &cid);
memcpy(&cid, imsg->data, sizeof(cid));
if ((c = control_connbyfd(cid)) == NULL) {
log_debug("%s: control connection %d not found",
__func__, cid);
return (0);
}
if (c->waiting == 0) {
log_debug("%s: no pending control requests", __func__);
return (0);
} else if (--c->waiting == 0) {
/* Last ack for a previous request */
imsg_compose_event(&c->iev, IMSG_CTL_END,
0, 0, -1, NULL, 0);
}
break;
case IMSG_SESS_PUBLISH:
IMSG_SIZE_CHECK(imsg, s);
if ((s = calloc(1, sizeof(*s))) == NULL)
return (0); /* XXX */
memcpy(s, imsg->data, sizeof(*s));
TAILQ_FOREACH(t, &env->sc_sessions, se_entry) {
/* duplicate registration */
if (t->se_id == s->se_id) {
free(s);
return (0);
}
if (t->se_id > s->se_id)
break;
}
if (t)
TAILQ_INSERT_BEFORE(t, s, se_entry);
else
TAILQ_INSERT_TAIL(&env->sc_sessions, s, se_entry);
break;
case IMSG_SESS_UNPUBLISH:
IMSG_SIZE_CHECK(imsg, &sid);
memcpy(&sid, imsg->data, sizeof(sid));
TAILQ_FOREACH(s, &env->sc_sessions, se_entry)
if (s->se_id == sid)
break;
if (s) {
TAILQ_REMOVE(&env->sc_sessions, s, se_entry);
free(s);
} else {
DPRINTF("removal of unpublished session %i", sid);
}
break;
default:
return (-1);
}
return (0);
}
void
show(struct ctl_conn *c)
{
struct rdr *rdr;
struct host *host;
struct relay *rlay;
struct router *rt;
struct netroute *nr;
struct relay_table *rlt;
if (env->sc_rdrs == NULL)
goto relays;
TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
imsg_compose_event(&c->iev, IMSG_CTL_RDR, 0, 0, -1,
rdr, sizeof(*rdr));
if (rdr->conf.flags & F_DISABLE)
continue;
imsg_compose_event(&c->iev, IMSG_CTL_RDR_STATS, 0, 0, -1,
&rdr->stats, sizeof(rdr->stats));
imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
rdr->table, sizeof(*rdr->table));
if (!(rdr->table->conf.flags & F_DISABLE))
TAILQ_FOREACH(host, &rdr->table->hosts, entry)
imsg_compose_event(&c->iev, IMSG_CTL_HOST,
0, 0, -1, host, sizeof(*host));
if (rdr->backup->conf.id == EMPTY_TABLE)
continue;
imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
rdr->backup, sizeof(*rdr->backup));
if (!(rdr->backup->conf.flags & F_DISABLE))
TAILQ_FOREACH(host, &rdr->backup->hosts, entry)
imsg_compose_event(&c->iev, IMSG_CTL_HOST,
0, 0, -1, host, sizeof(*host));
}
relays:
if (env->sc_relays == NULL)
goto routers;
TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
rlay->rl_stats[env->sc_conf.prefork_relay].id = EMPTY_ID;
imsg_compose_event(&c->iev, IMSG_CTL_RELAY, 0, 0, -1,
rlay, sizeof(*rlay));
imsg_compose_event(&c->iev, IMSG_CTL_RELAY_STATS, 0, 0, -1,
&rlay->rl_stats, sizeof(rlay->rl_stats));
TAILQ_FOREACH(rlt, &rlay->rl_tables, rlt_entry) {
imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
rlt->rlt_table, sizeof(*rlt->rlt_table));
if (!(rlt->rlt_table->conf.flags & F_DISABLE))
TAILQ_FOREACH(host,
&rlt->rlt_table->hosts, entry)
imsg_compose_event(&c->iev,
IMSG_CTL_HOST, 0, 0, -1,
host, sizeof(*host));
}
}
routers:
if (env->sc_rts == NULL)
goto end;
TAILQ_FOREACH(rt, env->sc_rts, rt_entry) {
imsg_compose_event(&c->iev, IMSG_CTL_ROUTER, 0, 0, -1,
rt, sizeof(*rt));
if (rt->rt_conf.flags & F_DISABLE)
continue;
TAILQ_FOREACH(nr, &rt->rt_netroutes, nr_entry)
imsg_compose_event(&c->iev, IMSG_CTL_NETROUTE,
0, 0, -1, nr, sizeof(*nr));
imsg_compose_event(&c->iev, IMSG_CTL_TABLE, 0, 0, -1,
rt->rt_gwtable, sizeof(*rt->rt_gwtable));
if (!(rt->rt_gwtable->conf.flags & F_DISABLE))
TAILQ_FOREACH(host, &rt->rt_gwtable->hosts, entry)
imsg_compose_event(&c->iev, IMSG_CTL_HOST,
0, 0, -1, host, sizeof(*host));
}
end:
imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
}
void
show_sessions(struct ctl_conn *c)
{
int proc, cid;
for (proc = 0; proc < env->sc_conf.prefork_relay; proc++) {
cid = c->iev.ibuf.fd;
/*
* Request all the running sessions from the process
*/
proc_compose_imsg(env->sc_ps, PROC_RELAY, proc,
IMSG_CTL_SESSION, -1, -1, &cid, sizeof(cid));
c->waiting++;
}
}
int
disable_rdr(struct ctl_conn *c, struct ctl_id *id)
{
struct rdr *rdr;
if (id->id == EMPTY_ID)
rdr = rdr_findbyname(env, id->name);
else
rdr = rdr_find(env, id->id);
if (rdr == NULL)
return (-1);
id->id = rdr->conf.id;
if (rdr->conf.flags & F_DISABLE)
return (0);
rdr->conf.flags |= F_DISABLE;
rdr->conf.flags &= ~(F_ADD);
rdr->conf.flags |= F_DEL;
rdr->table->conf.flags |= F_DISABLE;
log_debug("%s: redirect %d", __func__, rdr->conf.id);
pfe_sync();
return (0);
}
int
enable_rdr(struct ctl_conn *c, struct ctl_id *id)
{
struct rdr *rdr;
struct ctl_id eid;
if (id->id == EMPTY_ID)
rdr = rdr_findbyname(env, id->name);
else
rdr = rdr_find(env, id->id);
if (rdr == NULL)
return (-1);
id->id = rdr->conf.id;
if (!(rdr->conf.flags & F_DISABLE))
return (0);
rdr->conf.flags &= ~(F_DISABLE);
rdr->conf.flags &= ~(F_DEL);
rdr->conf.flags |= F_ADD;
log_debug("%s: redirect %d", __func__, rdr->conf.id);
bzero(&eid, sizeof(eid));
/* XXX: we're syncing twice */
eid.id = rdr->table->conf.id;
if (enable_table(c, &eid) == -1)
return (-1);
if (rdr->backup->conf.id == EMPTY_ID)
return (0);
eid.id = rdr->backup->conf.id;
if (enable_table(c, &eid) == -1)
return (-1);
return (0);
}
int
disable_table(struct ctl_conn *c, struct ctl_id *id)
{
struct table *table;
struct host *host;
if (id->id == EMPTY_ID)
table = table_findbyname(env, id->name);
else
table = table_find(env, id->id);
if (table == NULL)
return (-1);
id->id = table->conf.id;
if (table->conf.rdrid > 0 && rdr_find(env, table->conf.rdrid) == NULL)
fatalx("%s: desynchronised", __func__);
if (table->conf.flags & F_DISABLE)
return (0);
table->conf.flags |= (F_DISABLE|F_CHANGED);
table->up = 0;
TAILQ_FOREACH(host, &table->hosts, entry)
host->up = HOST_UNKNOWN;
proc_compose(env->sc_ps, PROC_HCE, IMSG_TABLE_DISABLE,
&table->conf.id, sizeof(table->conf.id));
/* Forward to relay engine(s) */
proc_compose(env->sc_ps, PROC_RELAY, IMSG_TABLE_DISABLE,
&table->conf.id, sizeof(table->conf.id));
log_debug("%s: table %d", __func__, table->conf.id);
pfe_sync();
return (0);
}
int
enable_table(struct ctl_conn *c, struct ctl_id *id)
{
struct table *table;
struct host *host;
if (id->id == EMPTY_ID)
table = table_findbyname(env, id->name);
else
table = table_find(env, id->id);
if (table == NULL)
return (-1);
id->id = table->conf.id;
if (table->conf.rdrid > 0 && rdr_find(env, table->conf.rdrid) == NULL)
fatalx("%s: desynchronised", __func__);
if (!(table->conf.flags & F_DISABLE))
return (0);
table->conf.flags &= ~(F_DISABLE);
table->conf.flags |= F_CHANGED;
table->up = 0;
TAILQ_FOREACH(host, &table->hosts, entry)
host->up = HOST_UNKNOWN;
proc_compose(env->sc_ps, PROC_HCE, IMSG_TABLE_ENABLE,
&table->conf.id, sizeof(table->conf.id));
/* Forward to relay engine(s) */
proc_compose(env->sc_ps, PROC_RELAY, IMSG_TABLE_ENABLE,
&table->conf.id, sizeof(table->conf.id));
log_debug("%s: table %d", __func__, table->conf.id);
pfe_sync();
return (0);
}
int
disable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
{
struct host *h;
struct table *table;
if (host == NULL) {
if (id->id == EMPTY_ID)
host = host_findbyname(env, id->name);
else
host = host_find(env, id->id);
if (host == NULL || host->conf.parentid)
return (-1);
}
id->id = host->conf.id;
if (host->flags & F_DISABLE)
return (0);
if (host->up == HOST_UP) {
if ((table = table_find(env, host->conf.tableid)) == NULL)
fatalx("%s: invalid table id", __func__);
table->up--;
table->conf.flags |= F_CHANGED;
}
host->up = HOST_UNKNOWN;
host->flags |= F_DISABLE;
host->flags |= F_DEL;
host->flags &= ~(F_ADD);
host->check_cnt = 0;
host->up_cnt = 0;
proc_compose(env->sc_ps, PROC_HCE, IMSG_HOST_DISABLE,
&host->conf.id, sizeof(host->conf.id));
/* Forward to relay engine(s) */
proc_compose(env->sc_ps, PROC_RELAY, IMSG_HOST_DISABLE,
&host->conf.id, sizeof(host->conf.id));
log_debug("%s: host %d", __func__, host->conf.id);
if (!host->conf.parentid) {
/* Disable all children */
SLIST_FOREACH(h, &host->children, child)
disable_host(c, id, h);
pfe_sync();
}
return (0);
}
int
enable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
{
struct host *h;
if (host == NULL) {
if (id->id == EMPTY_ID)
host = host_findbyname(env, id->name);
else
host = host_find(env, id->id);
if (host == NULL || host->conf.parentid)
return (-1);
}
id->id = host->conf.id;
if (!(host->flags & F_DISABLE))
return (0);
host->up = HOST_UNKNOWN;
host->flags &= ~(F_DISABLE);
host->flags &= ~(F_DEL);
host->flags &= ~(F_ADD);
proc_compose(env->sc_ps, PROC_HCE, IMSG_HOST_ENABLE,
&host->conf.id, sizeof (host->conf.id));
/* Forward to relay engine(s) */
proc_compose(env->sc_ps, PROC_RELAY, IMSG_HOST_ENABLE,
&host->conf.id, sizeof(host->conf.id));
log_debug("%s: host %d", __func__, host->conf.id);
if (!host->conf.parentid) {
/* Enable all children */
SLIST_FOREACH(h, &host->children, child)
enable_host(c, id, h);
pfe_sync();
}
return (0);
}
void
pfe_sync(void)
{
struct rdr *rdr;
struct table *active;
struct table *table;
struct ctl_id id;
struct imsg imsg;
struct ctl_demote demote;
struct router *rt;
bzero(&id, sizeof(id));
bzero(&imsg, sizeof(imsg));
TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
rdr->conf.flags &= ~(F_BACKUP);
rdr->conf.flags &= ~(F_DOWN);
if (rdr->conf.flags & F_DISABLE ||
(rdr->table->up == 0 && rdr->backup->up == 0)) {
rdr->conf.flags |= F_DOWN;
active = NULL;
} else if (rdr->table->up == 0 && rdr->backup->up > 0) {
rdr->conf.flags |= F_BACKUP;
active = rdr->backup;
active->conf.flags |=
rdr->table->conf.flags & F_CHANGED;
active->conf.flags |=
rdr->backup->conf.flags & F_CHANGED;
} else
active = rdr->table;
if (active != NULL && active->conf.flags & F_CHANGED) {
id.id = active->conf.id;
imsg.hdr.type = IMSG_CTL_TABLE_CHANGED;
imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
imsg.data = &id;
sync_table(env, rdr, active);
control_imsg_forward(env->sc_ps, &imsg);
}
if (rdr->conf.flags & F_DOWN) {
if (rdr->conf.flags & F_ACTIVE_RULESET) {
flush_table(env, rdr);
log_debug("%s: disabling ruleset", __func__);
rdr->conf.flags &= ~(F_ACTIVE_RULESET);
id.id = rdr->conf.id;
imsg.hdr.type = IMSG_CTL_PULL_RULESET;
imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
imsg.data = &id;
sync_ruleset(env, rdr, 0);
control_imsg_forward(env->sc_ps, &imsg);
}
} else if (!(rdr->conf.flags & F_ACTIVE_RULESET)) {
log_debug("%s: enabling ruleset", __func__);
rdr->conf.flags |= F_ACTIVE_RULESET;
id.id = rdr->conf.id;
imsg.hdr.type = IMSG_CTL_PUSH_RULESET;
imsg.hdr.len = sizeof(id) + IMSG_HEADER_SIZE;
imsg.data = &id;
sync_ruleset(env, rdr, 1);
control_imsg_forward(env->sc_ps, &imsg);
}
}
TAILQ_FOREACH(rt, env->sc_rts, rt_entry) {
rt->rt_conf.flags &= ~(F_BACKUP);
rt->rt_conf.flags &= ~(F_DOWN);
if ((rt->rt_gwtable->conf.flags & F_CHANGED))
sync_routes(env, rt);
}
TAILQ_FOREACH(table, env->sc_tables, entry) {
if (table->conf.check == CHECK_NOCHECK)
continue;
/*
* clean up change flag.
*/
table->conf.flags &= ~(F_CHANGED);
/*
* handle demotion.
*/
if ((table->conf.flags & F_DEMOTE) == 0)
continue;
demote.level = 0;
if (table->up && table->conf.flags & F_DEMOTED) {
demote.level = -1;
table->conf.flags &= ~F_DEMOTED;
}
else if (!table->up && !(table->conf.flags & F_DEMOTED)) {
demote.level = 1;
table->conf.flags |= F_DEMOTED;
}
if (demote.level == 0)
continue;
log_debug("%s: demote %d table '%s' group '%s'", __func__,
demote.level, table->conf.name, table->conf.demote_group);
(void)strlcpy(demote.group, table->conf.demote_group,
sizeof(demote.group));
proc_compose(env->sc_ps, PROC_PARENT, IMSG_DEMOTE,
&demote, sizeof(demote));
}
}
void
pfe_statistics(int fd, short events, void *arg)
{
struct rdr *rdr;
struct ctl_stats *cur;
struct timeval tv, tv_now;
int resethour, resetday;
u_long cnt;
timerclear(&tv);
getmonotime(&tv_now);
TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
cnt = check_table(env, rdr, rdr->table);
if (rdr->conf.backup_id != EMPTY_TABLE)
cnt += check_table(env, rdr, rdr->backup);
resethour = resetday = 0;
cur = &rdr->stats;
cur->last = cnt > cur->cnt ? cnt - cur->cnt : 0;
cur->cnt = cnt;
cur->tick++;
cur->avg = (cur->last + cur->avg) / 2;
cur->last_hour += cur->last;
if ((cur->tick %
(3600 / env->sc_conf.statinterval.tv_sec)) == 0) {
cur->avg_hour = (cur->last_hour + cur->avg_hour) / 2;
resethour++;
}
cur->last_day += cur->last;
if ((cur->tick %
(86400 / env->sc_conf.statinterval.tv_sec)) == 0) {
cur->avg_day = (cur->last_day + cur->avg_day) / 2;
resethour++;
}
if (resethour)
cur->last_hour = 0;
if (resetday)
cur->last_day = 0;
rdr->stats.interval = env->sc_conf.statinterval.tv_sec;
}
/* Schedule statistics timer */
evtimer_set(&env->sc_statev, pfe_statistics, NULL);
bcopy(&env->sc_conf.statinterval, &tv, sizeof(tv));
evtimer_add(&env->sc_statev, &tv);
}