summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorclaudio <claudio@openbsd.org>2013-08-14 20:34:25 +0000
committerclaudio <claudio@openbsd.org>2013-08-14 20:34:25 +0000
commit28b060e1f1df43bd7c1e56f85160a48baa1f326e (patch)
tree3baed04963b0906aa621158b44e66484adad092f
parentEnforce that the ls_id and the adv_rtr field in a type 1 router LSA are (diff)
downloadwireguard-openbsd-28b060e1f1df43bd7c1e56f85160a48baa1f326e.tar.xz
wireguard-openbsd-28b060e1f1df43bd7c1e56f85160a48baa1f326e.zip
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@
-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;