1
0
mirror of https://github.com/openbsd/src.git synced 2024-12-22 07:27:59 -08:00
openbsd-src/usr.sbin/relayd/pfe.c
sashan 8663a10cc3 The fix comes from Giannis Kapetanakis (bilias _from_ edu.physics.uoc.gr).
When relayd(8) handles 'host disable/enable' command issued by relayctl(8),
it disables redirect it finds in tables for particular host.  However there can
be multiple redirect instances which use the same host in relayd(8) tables.
This change makes relayd(8) to walk through all tables and disable all redirects
which match the host.

OK giovanni@, OK sashan@
2024-06-17 08:36:56 +00:00

858 lines
21 KiB
C

/* $OpenBSD: pfe.c,v 1.91 2024/06/17 08:36:56 sashan 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, *t;
int host_byname = 0;
if (host == NULL) {
if (id->id == EMPTY_ID) {
host = host_findbyname(env, id->name);
host_byname = 1;
}
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);
/* Disable hosts with same name on all tables */
if (host_byname)
TAILQ_FOREACH(t, env->sc_tables, entry)
TAILQ_FOREACH(h, &t->hosts, entry)
if (strcmp(h->conf.name,
host->conf.name) == 0 &&
h->conf.id != host->conf.id &&
!h->conf.parentid)
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;
struct table *t;
int host_byname = 0;
if (host == NULL) {
if (id->id == EMPTY_ID) {
host = host_findbyname(env, id->name);
host_byname = 1;
}
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);
/* Enable hosts with same name on all tables */
if (host_byname)
TAILQ_FOREACH(t, env->sc_tables, entry)
TAILQ_FOREACH(h, &t->hosts, entry)
if (strcmp(h->conf.name,
host->conf.name) == 0 &&
h->conf.id != host->conf.id &&
!h->conf.parentid)
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);
}