summaryrefslogtreecommitdiff
path: root/usr.sbin/bgpd
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2013-08-14 20:34:28 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2013-08-14 20:34:28 +0000
commitf44bc7e917817f68ad0898d6e487f831a0a40e32 (patch)
treecbce1840219b3510d2dc61e7eded1b291e5ab078 /usr.sbin/bgpd
parent18cac51086626d75fff160fc5311f85de18d5562 (diff)
Rewrite the internals of the RDE reload logic.
This is the first step to make bgpd reload non blocking in the RDE. It also speeds up the reload time a fair bit in some cases (mainly if you run with multiple RIBs and have larger filtersets) and it should also fix a few edge cases on reloads. Testing done by benno@, florian@ and sthen@ OK henning@ and benno@
Diffstat (limited to 'usr.sbin/bgpd')
-rw-r--r--usr.sbin/bgpd/bgpd.h3
-rw-r--r--usr.sbin/bgpd/rde.c514
-rw-r--r--usr.sbin/bgpd/rde.h15
-rw-r--r--usr.sbin/bgpd/rde_filter.c53
-rw-r--r--usr.sbin/bgpd/rde_rib.c16
-rw-r--r--usr.sbin/bgpd/rde_update.c16
6 files changed, 302 insertions, 315 deletions
diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h
index 26f636a1bd8..01fd80e7afc 100644
--- a/usr.sbin/bgpd/bgpd.h
+++ b/usr.sbin/bgpd/bgpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: bgpd.h,v 1.277 2013/05/11 14:42:28 benno Exp $ */
+/* $OpenBSD: bgpd.h,v 1.278 2013/08/14 20:34:25 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -103,6 +103,7 @@ enum reconf_action {
RECONF_NONE,
RECONF_KEEP,
RECONF_REINIT,
+ RECONF_RELOAD,
RECONF_DELETE
};
diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c
index b454993e790..87c08136baa 100644
--- a/usr.sbin/bgpd/rde.c
+++ b/usr.sbin/bgpd/rde.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde.c,v 1.324 2013/07/17 14:09:13 benno Exp $ */
+/* $OpenBSD: rde.c,v 1.325 2013/08/14 20:34:26 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -85,12 +85,11 @@ void rde_dump_mrt_new(struct mrt *, pid_t, int);
void rde_dump_done(void *);
int rde_rdomain_import(struct rde_aspath *, struct rdomain *);
-void rde_up_dump_upcall(struct rib_entry *, void *);
+void rde_reload_done(void);
void rde_softreconfig_out(struct rib_entry *, void *);
void rde_softreconfig_in(struct rib_entry *, void *);
-void rde_softreconfig_load(struct rib_entry *, void *);
-void rde_softreconfig_load_peer(struct rib_entry *, void *);
void rde_softreconfig_unload_peer(struct rib_entry *, void *);
+void rde_up_dump_upcall(struct rib_entry *, void *);
void rde_update_queue_runner(void);
void rde_update6_queue_runner(u_int8_t);
@@ -119,7 +118,7 @@ struct bgpd_config *conf, *nconf;
time_t reloadtime;
struct rde_peer_head peerlist;
struct rde_peer *peerself;
-struct filter_head *rules_l, *newrules;
+struct filter_head *out_rules, *out_rules_tmp;
struct rdomain_head *rdomains_l, *newdomains;
struct imsgbuf *ibuf_se;
struct imsgbuf *ibuf_se_ctl;
@@ -224,10 +223,10 @@ rde_main(int pipe_m2r[2], int pipe_s2r[2], int pipe_m2s[2], int pipe_s2rctl[2],
nexthop_init(nexthophashsize);
peer_init(peerhashsize);
- rules_l = calloc(1, sizeof(struct filter_head));
- if (rules_l == NULL)
+ out_rules = calloc(1, sizeof(struct filter_head));
+ if (out_rules == NULL)
fatal(NULL);
- TAILQ_INIT(rules_l);
+ TAILQ_INIT(out_rules);
rdomains_l = calloc(1, sizeof(struct rdomain_head));
if (rdomains_l == NULL)
fatal(NULL);
@@ -637,12 +636,11 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
struct imsg imsg;
struct mrt xmrt;
struct rde_rib rn;
- struct rde_peer *peer;
+ struct filter_head *nr;
struct filter_rule *r;
struct filter_set *s;
struct nexthop *nh;
- int n, fd, reconf_in = 0, reconf_out = 0,
- reconf_rib = 0;
+ int n, fd;
u_int16_t rid;
if ((n = imsg_read(ibuf)) == -1)
@@ -686,10 +684,10 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
sizeof(struct bgpd_config))
fatalx("IMSG_RECONF_CONF bad len");
reloadtime = time(NULL);
- newrules = calloc(1, sizeof(struct filter_head));
- if (newrules == NULL)
+ out_rules_tmp = calloc(1, sizeof(struct filter_head));
+ if (out_rules_tmp == NULL)
fatal(NULL);
- TAILQ_INIT(newrules);
+ TAILQ_INIT(out_rules_tmp);
newdomains = calloc(1, sizeof(struct rdomain_head));
if (newdomains == NULL)
fatal(NULL);
@@ -698,8 +696,11 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
NULL)
fatal(NULL);
memcpy(nconf, imsg.data, sizeof(struct bgpd_config));
- for (rid = 0; rid < rib_size; rid++)
+ for (rid = 0; rid < rib_size; rid++) {
+ if (*ribs[rid].name == '\0')
+ break;
ribs[rid].state = RECONF_DELETE;
+ }
break;
case IMSG_RECONF_RIB:
if (imsg.hdr.len - IMSG_HEADER_SIZE !=
@@ -712,10 +713,18 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
else if (ribs[rid].rtableid != rn.rtableid ||
(ribs[rid].flags & F_RIB_HASNOFIB) !=
(rn.flags & F_RIB_HASNOFIB)) {
- /* Big hammer in the F_RIB_NOFIB case but
- * not often enough used to optimise it more. */
+ struct filter_head *in_rules;
+ /*
+ * Big hammer in the F_RIB_HASNOFIB case but
+ * not often enough used to optimise it more.
+ * Need to save the filters so that they're not
+ * lost.
+ */
+ in_rules = ribs[rid].in_rules;
+ ribs[rid].in_rules = NULL;
rib_free(&ribs[rid]);
rib_new(rn.name, rn.rtableid, rn.flags);
+ ribs[rid].in_rules = in_rules;
} else
ribs[rid].state = RECONF_KEEP;
break;
@@ -727,9 +736,27 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
fatal(NULL);
memcpy(r, imsg.data, sizeof(struct filter_rule));
TAILQ_INIT(&r->set);
- r->peer.ribid = rib_find(r->rib);
+ if ((r->peer.ribid = rib_find(r->rib)) == RIB_FAILED) {
+ log_warnx("IMSG_RECONF_FILTER: filter rule "
+ "for not existsing rib %s", r->rib);
+ parent_set = NULL;
+ free(r);
+ break;
+ }
parent_set = &r->set;
- TAILQ_INSERT_TAIL(newrules, r, entry);
+ if (r->dir == DIR_IN) {
+ nr = ribs[r->peer.ribid].in_rules_tmp;
+ if (nr == NULL) {
+ nr = calloc(1,
+ sizeof(struct filter_head));
+ if (nr == NULL)
+ fatal(NULL);
+ TAILQ_INIT(nr);
+ ribs[r->peer.ribid].in_rules_tmp = nr;
+ }
+ TAILQ_INSERT_TAIL(nr, r, entry);
+ } else
+ TAILQ_INSERT_TAIL(out_rules_tmp, r, entry);
break;
case IMSG_RECONF_RDOMAIN:
if (imsg.hdr.len - IMSG_HEADER_SIZE !=
@@ -764,110 +791,9 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
case IMSG_RECONF_DONE:
if (nconf == NULL)
fatalx("got IMSG_RECONF_DONE but no config");
- if ((nconf->flags & BGPD_FLAG_NO_EVALUATE)
- != (conf->flags & BGPD_FLAG_NO_EVALUATE)) {
- log_warnx("change to/from route-collector "
- "mode ignored");
- if (conf->flags & BGPD_FLAG_NO_EVALUATE)
- nconf->flags |= BGPD_FLAG_NO_EVALUATE;
- else
- nconf->flags &= ~BGPD_FLAG_NO_EVALUATE;
- }
- memcpy(conf, nconf, sizeof(struct bgpd_config));
- conf->listen_addrs = NULL;
- conf->csock = NULL;
- conf->rcsock = NULL;
- free(nconf);
- nconf = NULL;
parent_set = NULL;
- /* sync peerself with conf */
- peerself->remote_bgpid = ntohl(conf->bgpid);
- peerself->conf.local_as = conf->as;
- peerself->conf.remote_as = conf->as;
- peerself->short_as = conf->short_as;
-
- /* apply new set of rdomain, sync will be done later */
- while ((rd = SIMPLEQ_FIRST(rdomains_l)) != NULL) {
- SIMPLEQ_REMOVE_HEAD(rdomains_l, entry);
- filterset_free(&rd->import);
- filterset_free(&rd->export);
- free(rd);
- }
- free(rdomains_l);
- rdomains_l = newdomains;
-
- /* check if filter changed */
- LIST_FOREACH(peer, &peerlist, peer_l) {
- if (peer->conf.id == 0)
- continue;
- peer->reconf_out = 0;
- peer->reconf_in = 0;
- peer->reconf_rib = 0;
- if (peer->conf.softreconfig_in &&
- !rde_filter_equal(rules_l, newrules, peer,
- DIR_IN)) {
- peer->reconf_in = 1;
- reconf_in = 1;
- }
- if (peer->ribid != rib_find(peer->conf.rib)) {
- rib_dump(&ribs[peer->ribid],
- rde_softreconfig_unload_peer, peer,
- AID_UNSPEC);
- peer->ribid = rib_find(peer->conf.rib);
- peer->reconf_rib = 1;
- reconf_rib = 1;
- continue;
- }
- if (peer->conf.softreconfig_out &&
- !rde_filter_equal(rules_l, newrules, peer,
- DIR_OUT)) {
- peer->reconf_out = 1;
- reconf_out = 1;
- }
- }
- /* bring ribs in sync before softreconfig dance */
- for (rid = 0; rid < rib_size; rid++) {
- if (ribs[rid].state == RECONF_DELETE)
- rib_free(&ribs[rid]);
- else if (ribs[rid].state == RECONF_REINIT)
- rib_dump(&ribs[0],
- rde_softreconfig_load, &ribs[rid],
- AID_UNSPEC);
- }
- /* sync local-RIBs first */
- if (reconf_in)
- rib_dump(&ribs[0], rde_softreconfig_in, NULL,
- AID_UNSPEC);
- /* then sync peers */
- if (reconf_out) {
- int i;
- for (i = 1; i < rib_size; i++) {
- if (ribs[i].state == RECONF_REINIT)
- /* already synced by _load */
- continue;
- rib_dump(&ribs[i], rde_softreconfig_out,
- NULL, AID_UNSPEC);
- }
- }
- if (reconf_rib) {
- LIST_FOREACH(peer, &peerlist, peer_l) {
- rib_dump(&ribs[peer->ribid],
- rde_softreconfig_load_peer,
- peer, AID_UNSPEC);
- }
- }
- while ((r = TAILQ_FIRST(rules_l)) != NULL) {
- TAILQ_REMOVE(rules_l, r, entry);
- filterset_free(&r->set);
- free(r);
- }
- free(rules_l);
- rules_l = newrules;
-
- log_info("RDE reconfigured");
- imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0,
- -1, NULL, 0);
+ rde_reload_done();
break;
case IMSG_NEXTHOP_UPDATE:
nexthop_update(imsg.data);
@@ -1342,8 +1268,6 @@ done:
return (error);
}
-extern u_int16_t rib_size;
-
void
rde_update_update(struct rde_peer *peer, struct rde_aspath *asp,
struct bgpd_addr *prefix, u_int8_t prefixlen)
@@ -1359,9 +1283,11 @@ rde_update_update(struct rde_peer *peer, struct rde_aspath *asp,
r += path_update(&ribs[0], peer, asp, prefix, prefixlen);
for (i = 1; i < rib_size; i++) {
+ if (*ribs[i].name == '\0')
+ break;
/* input filter */
- action = rde_filter(i, &fasp, rules_l, peer, asp, prefix,
- prefixlen, peer, DIR_IN);
+ action = rde_filter(ribs[i].in_rules, &fasp, peer, asp, prefix,
+ prefixlen, peer);
if (fasp == NULL)
fasp = asp;
@@ -1399,6 +1325,8 @@ rde_update_withdraw(struct rde_peer *peer, struct bgpd_addr *prefix,
peer->prefix_rcvd_withdraw++;
for (i = rib_size - 1; ; i--) {
+ if (*ribs[i].name == '\0')
+ break;
if (prefix_remove(&ribs[i], peer, prefix, prefixlen, 0)) {
rde_update_log("withdraw", i, peer, NULL, prefix,
prefixlen);
@@ -2293,8 +2221,8 @@ rde_dump_filterout(struct rde_peer *peer, struct prefix *p,
return;
pt_getaddr(p->prefix, &addr);
- a = rde_filter(1 /* XXX */, &asp, rules_l, peer, p->aspath, &addr,
- p->prefix->prefixlen, p->aspath->peer, DIR_OUT);
+ a = rde_filter(out_rules, &asp, peer, p->aspath, &addr,
+ p->prefix->prefixlen, p->aspath->peer);
if (asp)
asp->peer = p->aspath->peer;
else
@@ -2630,196 +2558,235 @@ rde_send_nexthop(struct bgpd_addr *next, int valid)
* soft reconfig specific functions
*/
void
-rde_softreconfig_out(struct rib_entry *re, void *ptr)
+rde_reload_done(void)
{
- struct prefix *p = re->active;
- struct pt_entry *pt;
+ struct rdomain *rd;
struct rde_peer *peer;
- struct rde_aspath *oasp, *nasp;
- enum filter_actions oa, na;
- struct bgpd_addr addr;
+ struct filter_head *fh;
+ u_int16_t rid;
- if (p == NULL)
- return;
+ /* first merge the main config */
+ if ((nconf->flags & BGPD_FLAG_NO_EVALUATE)
+ != (conf->flags & BGPD_FLAG_NO_EVALUATE)) {
+ log_warnx("change to/from route-collector "
+ "mode ignored");
+ if (conf->flags & BGPD_FLAG_NO_EVALUATE)
+ nconf->flags |= BGPD_FLAG_NO_EVALUATE;
+ else
+ nconf->flags &= ~BGPD_FLAG_NO_EVALUATE;
+ }
+ memcpy(conf, nconf, sizeof(struct bgpd_config));
+ conf->listen_addrs = NULL;
+ conf->csock = NULL;
+ conf->rcsock = NULL;
+ free(nconf);
+ nconf = NULL;
+
+ /* sync peerself with conf */
+ peerself->remote_bgpid = ntohl(conf->bgpid);
+ peerself->conf.local_as = conf->as;
+ peerself->conf.remote_as = conf->as;
+ peerself->short_as = conf->short_as;
+
+ /* apply new set of rdomain, sync will be done later */
+ while ((rd = SIMPLEQ_FIRST(rdomains_l)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(rdomains_l, entry);
+ filterset_free(&rd->import);
+ filterset_free(&rd->export);
+ free(rd);
+ }
+ free(rdomains_l);
+ rdomains_l = newdomains;
+ /* XXX WHERE IS THE SYNC ??? */
- pt = re->prefix;
- pt_getaddr(pt, &addr);
+ /*
+ * make the new filter rules the active one but keep the old for
+ * softrconfig. This is needed so that changes happening are using
+ * the right filters.
+ */
+ fh = out_rules;
+ out_rules = out_rules_tmp;
+ out_rules_tmp = fh;
+
+ /* check if filter changed */
LIST_FOREACH(peer, &peerlist, peer_l) {
if (peer->conf.id == 0)
continue;
- if (peer->ribid != re->ribid)
- continue;
- if (peer->reconf_out == 0)
+ peer->reconf_out = 0;
+ peer->reconf_rib = 0;
+ if (peer->ribid != rib_find(peer->conf.rib)) {
+ rib_dump(&ribs[peer->ribid],
+ rde_softreconfig_unload_peer, peer, AID_UNSPEC);
+ peer->ribid = rib_find(peer->conf.rib);
+ if (peer->ribid == RIB_FAILED)
+ fatalx("King Bula's peer met an unknown RIB");
+ peer->reconf_rib = 1;
continue;
- if (up_test_update(peer, p) != 1)
- continue;
-
- oa = rde_filter(re->ribid, &oasp, rules_l, peer, p->aspath,
- &addr, pt->prefixlen, p->aspath->peer, DIR_OUT);
- na = rde_filter(re->ribid, &nasp, newrules, peer, p->aspath,
- &addr, pt->prefixlen, p->aspath->peer, DIR_OUT);
- oasp = oasp != NULL ? oasp : p->aspath;
- nasp = nasp != NULL ? nasp : p->aspath;
-
- if (oa == ACTION_DENY && na == ACTION_DENY)
- /* nothing todo */
- goto done;
- if (oa == ACTION_DENY && na == ACTION_ALLOW) {
- /* send update */
- up_generate(peer, nasp, &addr, pt->prefixlen);
- goto done;
}
- if (oa == ACTION_ALLOW && na == ACTION_DENY) {
- /* send withdraw */
- up_generate(peer, NULL, &addr, pt->prefixlen);
- goto done;
+ if (peer->conf.softreconfig_out &&
+ !rde_filter_equal(out_rules, out_rules_tmp, peer)) {
+ peer->reconf_out = 1;
}
- if (oa == ACTION_ALLOW && na == ACTION_ALLOW) {
- if (path_compare(nasp, oasp) == 0)
- goto done;
- /* send update */
- up_generate(peer, nasp, &addr, pt->prefixlen);
+ }
+ /* bring ribs in sync */
+ for (rid = 0; rid < rib_size; rid++) {
+ if (*ribs[rid].name == '\0')
+ continue;
+ /* flip rules, make new active */
+ fh = ribs[rid].in_rules;
+ ribs[rid].in_rules = ribs[rid].in_rules_tmp;
+ ribs[rid].in_rules_tmp = fh;
+
+ switch (ribs[rid].state) {
+ case RECONF_DELETE:
+ rib_free(&ribs[rid]);
+ break;
+ case RECONF_KEEP:
+ if (rde_filter_equal(ribs[rid].in_rules,
+ ribs[rid].in_rules_tmp, NULL))
+ /* rib is in sync */
+ break;
+ ribs[rid].state = RECONF_RELOAD;
+ /* FALLTHROUGH */
+ case RECONF_REINIT:
+ rib_dump(&ribs[0], rde_softreconfig_in, &ribs[rid],
+ AID_UNSPEC);
+ break;
+ case RECONF_RELOAD:
+ log_warnx("Bad rib reload state");
+ /* FALLTHROUGH */
+ case RECONF_NONE:
+ break;
}
-
-done:
- if (oasp != p->aspath)
- path_put(oasp);
- if (nasp != p->aspath)
- path_put(nasp);
}
+ LIST_FOREACH(peer, &peerlist, peer_l) {
+ if (peer->reconf_out)
+ rib_dump(&ribs[peer->ribid], rde_softreconfig_out,
+ peer, AID_UNSPEC);
+ else if (peer->reconf_rib)
+ /* dump the full table to neighbors that changed rib */
+ peer_dump(peer->conf.id, AID_UNSPEC);
+ }
+ rde_free_filter(out_rules_tmp);
+ out_rules_tmp = NULL;
+ for (rid = 0; rid < rib_size; rid++) {
+ if (*ribs[rid].name == '\0')
+ continue;
+ rde_free_filter(ribs[rid].in_rules_tmp);
+ ribs[rid].in_rules_tmp = NULL;
+ ribs[rid].state = RECONF_NONE;
+ }
+
+ log_info("RDE reconfigured");
+ imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0,
+ -1, NULL, 0);
}
void
rde_softreconfig_in(struct rib_entry *re, void *ptr)
{
+ struct rib *rib = ptr;
struct prefix *p, *np;
struct pt_entry *pt;
struct rde_peer *peer;
struct rde_aspath *asp, *oasp, *nasp;
enum filter_actions oa, na;
struct bgpd_addr addr;
- u_int16_t i;
pt = re->prefix;
pt_getaddr(pt, &addr);
for (p = LIST_FIRST(&re->prefix_h); p != NULL; p = np) {
+ /*
+ * prefix_remove() and path_update() may change the object
+ * so cache the values.
+ */
np = LIST_NEXT(p, rib_l);
-
- /* store aspath as prefix may change till we're done */
asp = p->aspath;
peer = asp->peer;
- /* XXX how can this happen ??? */
- if (peer->reconf_in == 0)
- continue;
-
- for (i = 1; i < rib_size; i++) {
- /* only active ribs need a softreconfig rerun */
- if (ribs[i].state != RECONF_KEEP)
- continue;
-
- /* check if prefix changed */
- oa = rde_filter(i, &oasp, rules_l, peer, asp, &addr,
- pt->prefixlen, peer, DIR_IN);
- na = rde_filter(i, &nasp, newrules, peer, asp, &addr,
- pt->prefixlen, peer, DIR_IN);
+ /* check if prefix changed */
+ if (rib->state == RECONF_RELOAD) {
+ oa = rde_filter(rib->in_rules_tmp, &oasp, peer,
+ asp, &addr, pt->prefixlen, peer);
oasp = oasp != NULL ? oasp : asp;
- nasp = nasp != NULL ? nasp : asp;
-
- if (oa == ACTION_DENY && na == ACTION_DENY)
- /* nothing todo */
- goto done;
- if (oa == ACTION_DENY && na == ACTION_ALLOW) {
- /* update Local-RIB */
- path_update(&ribs[i], peer, nasp, &addr,
- pt->prefixlen);
- goto done;
- }
- if (oa == ACTION_ALLOW && na == ACTION_DENY) {
- /* remove from Local-RIB */
- prefix_remove(&ribs[i], peer, &addr,
- pt->prefixlen, 0);
- goto done;
- }
- if (oa == ACTION_ALLOW && na == ACTION_ALLOW) {
- if (path_compare(nasp, oasp) == 0)
- goto done;
- /* send update */
- path_update(&ribs[i], peer, nasp, &addr,
- pt->prefixlen);
- }
-
-done:
- if (oasp != asp)
- path_put(oasp);
- if (nasp != asp)
- path_put(nasp);
+ } else {
+ /* make sure we update everything for RECONF_REINIT */
+ oa = ACTION_DENY;
+ oasp = asp;
}
- }
-}
-
-void
-rde_softreconfig_load(struct rib_entry *re, void *ptr)
-{
- struct rib *rib = ptr;
- struct prefix *p, *np;
- struct pt_entry *pt;
- struct rde_peer *peer;
- struct rde_aspath *asp, *nasp;
- enum filter_actions action;
- struct bgpd_addr addr;
-
- pt = re->prefix;
- pt_getaddr(pt, &addr);
- for (p = LIST_FIRST(&re->prefix_h); p != NULL; p = np) {
- np = LIST_NEXT(p, rib_l);
-
- /* store aspath as prefix may change till we're done */
- asp = p->aspath;
- peer = asp->peer;
-
- action = rde_filter(rib->id, &nasp, newrules, peer, asp, &addr,
- pt->prefixlen, peer, DIR_IN);
+ na = rde_filter(rib->in_rules, &nasp, peer, asp,
+ &addr, pt->prefixlen, peer);
nasp = nasp != NULL ? nasp : asp;
- if (action == ACTION_ALLOW) {
+ /* go through all 4 possible combinations */
+ /* if (oa == ACTION_DENY && na == ACTION_DENY) */
+ /* nothing todo */
+ if (oa == ACTION_DENY && na == ACTION_ALLOW) {
/* update Local-RIB */
path_update(rib, peer, nasp, &addr, pt->prefixlen);
+ } else if (oa == ACTION_ALLOW && na == ACTION_DENY) {
+ /* remove from Local-RIB */
+ prefix_remove(rib, peer, &addr, pt->prefixlen, 0);
+ } else if (oa == ACTION_ALLOW && na == ACTION_ALLOW) {
+ if (path_compare(nasp, oasp) != 0)
+ /* send update */
+ path_update(rib, peer, nasp, &addr,
+ pt->prefixlen);
}
+ if (oasp != asp)
+ path_put(oasp);
if (nasp != asp)
path_put(nasp);
}
}
void
-rde_softreconfig_load_peer(struct rib_entry *re, void *ptr)
+rde_softreconfig_out(struct rib_entry *re, void *ptr)
{
- struct rde_peer *peer = ptr;
struct prefix *p = re->active;
struct pt_entry *pt;
- struct rde_aspath *nasp;
- enum filter_actions na;
+ struct rde_peer *peer = ptr;
+ struct rde_aspath *oasp, *nasp;
+ enum filter_actions oa, na;
struct bgpd_addr addr;
+ if (peer->conf.id == 0)
+ fatalx("King Bula troubled by bad peer");
+
+ if (p == NULL)
+ return;
+
pt = re->prefix;
pt_getaddr(pt, &addr);
- /* check if prefix was announced */
if (up_test_update(peer, p) != 1)
return;
- na = rde_filter(re->ribid, &nasp, newrules, peer, p->aspath,
- &addr, pt->prefixlen, p->aspath->peer, DIR_OUT);
+ oa = rde_filter(out_rules_tmp, &oasp, peer, p->aspath,
+ &addr, pt->prefixlen, p->aspath->peer);
+ na = rde_filter(out_rules, &nasp, peer, p->aspath,
+ &addr, pt->prefixlen, p->aspath->peer);
+ oasp = oasp != NULL ? oasp : p->aspath;
nasp = nasp != NULL ? nasp : p->aspath;
- if (na == ACTION_DENY)
+ /* go through all 4 possible combinations */
+ /* if (oa == ACTION_DENY && na == ACTION_DENY) */
/* nothing todo */
- goto done;
+ if (oa == ACTION_DENY && na == ACTION_ALLOW) {
+ /* send update */
+ up_generate(peer, nasp, &addr, pt->prefixlen);
+ } else if (oa == ACTION_ALLOW && na == ACTION_DENY) {
+ /* send withdraw */
+ up_generate(peer, NULL, &addr, pt->prefixlen);
+ } else if (oa == ACTION_ALLOW && na == ACTION_ALLOW) {
+ /* send update if path attributes changed */
+ if (path_compare(nasp, oasp) != 0)
+ up_generate(peer, nasp, &addr, pt->prefixlen);
+ }
- /* send update */
- up_generate(peer, nasp, &addr, pt->prefixlen);
-done:
+ if (oasp != p->aspath)
+ path_put(oasp);
if (nasp != p->aspath)
path_put(nasp);
}
@@ -2841,8 +2808,8 @@ rde_softreconfig_unload_peer(struct rib_entry *re, void *ptr)
if (up_test_update(peer, p) != 1)
return;
- oa = rde_filter(re->ribid, &oasp, rules_l, peer, p->aspath,
- &addr, pt->prefixlen, p->aspath->peer, DIR_OUT);
+ oa = rde_filter(out_rules_tmp, &oasp, peer, p->aspath,
+ &addr, pt->prefixlen, p->aspath->peer);
oasp = oasp != NULL ? oasp : p->aspath;
if (oa == ACTION_DENY)
@@ -2870,7 +2837,7 @@ rde_up_dump_upcall(struct rib_entry *re, void *ptr)
fatalx("King Bula: monstrous evil horror.");
if (re->active == NULL)
return;
- up_generate_updates(rules_l, peer, re->active, NULL);
+ up_generate_updates(out_rules, peer, re->active, NULL);
}
void
@@ -2893,7 +2860,7 @@ rde_generate_updates(u_int16_t ribid, struct prefix *new, struct prefix *old)
continue;
if (peer->state != PEER_UP)
continue;
- up_generate_updates(rules_l, peer, new, old);
+ up_generate_updates(out_rules, peer, new, old);
}
}
@@ -3140,6 +3107,8 @@ peer_add(u_int32_t id, struct peer_config *p_conf)
memcpy(&peer->conf, p_conf, sizeof(struct peer_config));
peer->remote_bgpid = 0;
peer->ribid = rib_find(peer->conf.rib);
+ if (peer->ribid == RIB_FAILED)
+ fatalx("King Bula's new peer met an unknown RIB");
peer->state = PEER_NONE;
up_init(peer);
@@ -3334,7 +3303,7 @@ peer_dump(u_int32_t id, u_int8_t aid)
}
if (peer->conf.announce_type == ANNOUNCE_DEFAULT_ROUTE)
- up_generate_default(rules_l, peer, aid);
+ up_generate_default(out_rules, peer, aid);
else
rib_dump(&ribs[peer->ribid], rde_up_dump_upcall, peer, aid);
if (peer->capa.grestart.restart)
@@ -3455,9 +3424,12 @@ network_add(struct network_config *nc, int flagstatic)
rde_apply_set(asp, &nc->attrset, nc->prefix.aid, peerself, peerself);
if (vpnset)
rde_apply_set(asp, vpnset, nc->prefix.aid, peerself, peerself);
- for (i = 1; i < rib_size; i++)
+ for (i = 1; i < rib_size; i++) {
+ if (*ribs[i].name == '\0')
+ break;
path_update(&ribs[i], peerself, asp, &nc->prefix,
nc->prefixlen);
+ }
path_put(asp);
filterset_free(&nc->attrset);
}
@@ -3500,9 +3472,12 @@ network_delete(struct network_config *nc, int flagstatic)
}
}
- for (i = rib_size - 1; i > 0; i--)
+ for (i = rib_size - 1; i > 0; i--) {
+ if (*ribs[i].name == '\0')
+ break;
prefix_remove(&ribs[i], peerself, &nc->prefix, nc->prefixlen,
flags);
+ }
}
void
@@ -3542,7 +3517,6 @@ void
rde_shutdown(void)
{
struct rde_peer *p;
- struct filter_rule *r;
u_int32_t i;
/*
@@ -3558,12 +3532,12 @@ rde_shutdown(void)
peer_down(p->conf.id);
/* free filters */
- while ((r = TAILQ_FIRST(rules_l)) != NULL) {
- TAILQ_REMOVE(rules_l, r, entry);
- filterset_free(&r->set);
- free(r);
+ rde_free_filter(out_rules);
+ for (i = 0; i < rib_size; i++) {
+ if (*ribs[i].name == '\0')
+ break;
+ rde_free_filter(ribs[i].in_rules);
}
- free(rules_l);
nexthop_shutdown();
path_shutdown();
diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h
index 62d156d193e..abafc19b10f 100644
--- a/usr.sbin/bgpd/rde.h
+++ b/usr.sbin/bgpd/rde.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde.h,v 1.146 2013/07/17 14:09:13 benno Exp $ */
+/* $OpenBSD: rde.h,v 1.147 2013/08/14 20:34:26 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and
@@ -76,7 +76,6 @@ struct rde_peer {
u_int16_t ribid;
u_int16_t short_as;
u_int16_t mrt_idx;
- u_int8_t reconf_in; /* in filter changed */
u_int8_t reconf_out; /* out filter changed */
u_int8_t reconf_rib; /* rib changed */
};
@@ -287,6 +286,8 @@ struct rib_entry {
struct rib {
char name[PEER_DESCR_LEN];
struct rib_tree rib;
+ struct filter_head *in_rules;
+ struct filter_head *in_rules_tmp;
u_int rtableid;
u_int16_t flags;
u_int16_t id;
@@ -325,6 +326,7 @@ u_int32_t rde_local_as(void);
int rde_noevaluate(void);
int rde_decisionflags(void);
int rde_as4byte(struct rde_peer *);
+void rde_free_filter(struct filter_head *);
/* rde_attr.c */
int attr_write(void *, u_int16_t, u_int8_t, u_int8_t, void *,
@@ -379,14 +381,13 @@ int community_ext_conv(struct filter_extcommunity *, u_int16_t,
void prefix_evaluate(struct prefix *, struct rib_entry *);
/* rde_filter.c */
-enum filter_actions rde_filter(u_int16_t, struct rde_aspath **,
- struct filter_head *, struct rde_peer *,
- struct rde_aspath *, struct bgpd_addr *, u_int8_t,
- struct rde_peer *, enum directions);
+enum filter_actions rde_filter(struct filter_head *, struct rde_aspath **,
+ struct rde_peer *, struct rde_aspath *,
+ struct bgpd_addr *, u_int8_t, struct rde_peer *);
void rde_apply_set(struct rde_aspath *, struct filter_set_head *,
u_int8_t, struct rde_peer *, struct rde_peer *);
int rde_filter_equal(struct filter_head *, struct filter_head *,
- struct rde_peer *, enum directions);
+ struct rde_peer *);
/* rde_prefix.c */
#define pt_empty(pt) ((pt)->refcnt == 0)
diff --git a/usr.sbin/bgpd/rde_filter.c b/usr.sbin/bgpd/rde_filter.c
index f38284de83f..830702d5a03 100644
--- a/usr.sbin/bgpd/rde_filter.c
+++ b/usr.sbin/bgpd/rde_filter.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde_filter.c,v 1.68 2012/11/13 09:47:20 claudio Exp $ */
+/* $OpenBSD: rde_filter.c,v 1.69 2013/08/14 20:34:27 claudio Exp $ */
/*
* Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
@@ -30,9 +30,9 @@ int rde_filter_match(struct filter_rule *, struct rde_aspath *,
int filterset_equal(struct filter_set_head *, struct filter_set_head *);
enum filter_actions
-rde_filter(u_int16_t ribid, struct rde_aspath **new, struct filter_head *rules,
+rde_filter(struct filter_head *rules, struct rde_aspath **new,
struct rde_peer *peer, struct rde_aspath *asp, struct bgpd_addr *prefix,
- u_int8_t prefixlen, struct rde_peer *from, enum directions dir)
+ u_int8_t prefixlen, struct rde_peer *from)
{
struct filter_rule *f;
enum filter_actions action = ACTION_ALLOW; /* default allow */
@@ -47,11 +47,10 @@ rde_filter(u_int16_t ribid, struct rde_aspath **new, struct filter_head *rules,
*/
return (ACTION_DENY);
+ if (rules == NULL)
+ return (action);
+
TAILQ_FOREACH(f, rules, entry) {
- if (dir != f->dir)
- continue;
- if (dir == DIR_IN && f->peer.ribid != ribid)
- continue;
if (f->peer.groupid != 0 &&
f->peer.groupid != peer->conf.groupid)
continue;
@@ -420,42 +419,32 @@ rde_filter_match(struct filter_rule *f, struct rde_aspath *asp,
int
rde_filter_equal(struct filter_head *a, struct filter_head *b,
- struct rde_peer *peer, enum directions dir)
+ struct rde_peer *peer)
{
struct filter_rule *fa, *fb;
- fa = TAILQ_FIRST(a);
- fb = TAILQ_FIRST(b);
+ fa = a ? TAILQ_FIRST(a) : NULL;
+ fb = b ? TAILQ_FIRST(b) : NULL;
while (fa != NULL || fb != NULL) {
- /* skip all rules with wrong direction */
- if (fa != NULL && dir != fa->dir) {
- fa = TAILQ_NEXT(fa, entry);
- continue;
- }
- if (fb != NULL && dir != fb->dir) {
- fb = TAILQ_NEXT(fb, entry);
- continue;
- }
-
/* skip all rules with wrong peer */
- if (fa != NULL && fa->peer.groupid != 0 &&
+ if (peer != NULL && fa != NULL && fa->peer.groupid != 0 &&
fa->peer.groupid != peer->conf.groupid) {
fa = TAILQ_NEXT(fa, entry);
continue;
}
- if (fa != NULL && fa->peer.peerid != 0 &&
+ if (peer != NULL && fa != NULL && fa->peer.peerid != 0 &&
fa->peer.peerid != peer->conf.id) {
fa = TAILQ_NEXT(fa, entry);
continue;
}
- if (fb != NULL && fb->peer.groupid != 0 &&
+ if (peer != NULL && fb != NULL && fb->peer.groupid != 0 &&
fb->peer.groupid != peer->conf.groupid) {
fb = TAILQ_NEXT(fb, entry);
continue;
}
- if (fb != NULL && fb->peer.peerid != 0 &&
+ if (peer != NULL && fb != NULL && fb->peer.peerid != 0 &&
fb->peer.peerid != peer->conf.id) {
fb = TAILQ_NEXT(fb, entry);
continue;
@@ -481,6 +470,22 @@ rde_filter_equal(struct filter_head *a, struct filter_head *b,
return (1);
}
+void
+rde_free_filter(struct filter_head *fh)
+{
+ struct filter_rule *r;
+
+ if (fh == NULL)
+ return;
+
+ while ((r = TAILQ_FIRST(fh)) != NULL) {
+ TAILQ_REMOVE(fh, r, entry);
+ filterset_free(&r->set);
+ free(r);
+ }
+ free(fh);
+}
+
/* free a filterset and take care of possible name2id references */
void
filterset_free(struct filter_set_head *sh)
diff --git a/usr.sbin/bgpd/rde_rib.c b/usr.sbin/bgpd/rde_rib.c
index 24c418b4e27..859e09ad67d 100644
--- a/usr.sbin/bgpd/rde_rib.c
+++ b/usr.sbin/bgpd/rde_rib.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde_rib.c,v 1.137 2013/07/17 14:09:13 benno Exp $ */
+/* $OpenBSD: rde_rib.c,v 1.138 2013/08/14 20:34:27 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
@@ -82,6 +82,11 @@ rib_new(char *name, u_int rtableid, u_int16_t flags)
ribs[id].flags = flags;
ribs[id].rtableid = rtableid;
+ ribs[id].in_rules = calloc(1, sizeof(struct filter_head));
+ if (ribs[id].in_rules == NULL)
+ fatal(NULL);
+ TAILQ_INIT(ribs[id].in_rules);
+
return (id);
}
@@ -91,7 +96,7 @@ rib_find(char *name)
u_int16_t id;
if (name == NULL || *name == '\0')
- return (1); /* XXX */
+ return (1); /* no name returns the Loc-RIB */
for (id = 0; id < rib_size; id++) {
if (!strcmp(ribs[id].name, name))
@@ -108,6 +113,7 @@ rib_free(struct rib *rib)
struct rib_entry *re, *xre;
struct prefix *p, *np;
+ /* abort pending rib_dumps */
for (ctx = LIST_FIRST(&rib_dump_h); ctx != NULL; ctx = next) {
next = LIST_NEXT(ctx, entry);
if (ctx->ctx_rib == rib) {
@@ -126,8 +132,8 @@ rib_free(struct rib *rib)
/*
* Removing the prefixes is tricky because the last one
- * will remove the rib_entry as well and at because we do
- * a empty check in prefix_destroy() it is not possible to
+ * will remove the rib_entry as well and because we do
+ * an empty check in prefix_destroy() it is not possible to
* use the default for loop.
*/
while ((p = LIST_FIRST(&re->prefix_h))) {
@@ -145,6 +151,8 @@ rib_free(struct rib *rib)
break;
}
}
+ rde_free_filter(rib->in_rules_tmp);
+ rde_free_filter(rib->in_rules);
bzero(rib, sizeof(struct rib));
}
diff --git a/usr.sbin/bgpd/rde_update.c b/usr.sbin/bgpd/rde_update.c
index afb6e8d357a..911d08180a5 100644
--- a/usr.sbin/bgpd/rde_update.c
+++ b/usr.sbin/bgpd/rde_update.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde_update.c,v 1.80 2012/04/12 17:31:05 claudio Exp $ */
+/* $OpenBSD: rde_update.c,v 1.81 2013/08/14 20:34:27 claudio Exp $ */
/*
* Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
@@ -411,9 +411,8 @@ up_generate_updates(struct filter_head *rules, struct rde_peer *peer,
return;
pt_getaddr(old->prefix, &addr);
- if (rde_filter(peer->ribid, NULL, rules, peer, old->aspath,
- &addr, old->prefix->prefixlen, old->aspath->peer,
- DIR_OUT) == ACTION_DENY)
+ if (rde_filter(rules, NULL, peer, old->aspath, &addr,
+ old->prefix->prefixlen, old->aspath->peer) == ACTION_DENY)
return;
/* withdraw prefix */
@@ -430,9 +429,8 @@ up_generate_updates(struct filter_head *rules, struct rde_peer *peer,
}
pt_getaddr(new->prefix, &addr);
- if (rde_filter(peer->ribid, &asp, rules, peer, new->aspath,
- &addr, new->prefix->prefixlen, new->aspath->peer,
- DIR_OUT) == ACTION_DENY) {
+ if (rde_filter(rules, &asp, peer, new->aspath, &addr,
+ new->prefix->prefixlen, new->aspath->peer) == ACTION_DENY) {
path_put(asp);
up_generate_updates(rules, peer, NULL, old);
return;
@@ -474,8 +472,8 @@ up_generate_default(struct filter_head *rules, struct rde_peer *peer,
bzero(&addr, sizeof(addr));
addr.aid = aid;
- if (rde_filter(peer->ribid, &fasp, rules, peer, asp, &addr, 0, NULL,
- DIR_OUT) == ACTION_DENY) {
+ if (rde_filter(rules, &fasp, peer, asp, &addr, 0, NULL) ==
+ ACTION_DENY) {
path_put(fasp);
path_put(asp);
return;