Hi, this is the first step in making bgpd faster and non locking on reloads. The filters are now split into input and output chains. The input chains are also split per RIB. This should result in faster filtering and allows to only run softreconfig on those tables that need it. People with multiple RIBs will benefit the most at the moment.
I did some basic tests and I'm fairly confident that it should work but this is a huge change in the reload logic and needs a good broad testing and I don't mind some review of the code. -- :wq Claudio Index: bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.273 diff -u -p -r1.273 bgpd.h --- bgpd.h 18 Sep 2012 10:10:00 -0000 1.273 +++ bgpd.h 20 Sep 2012 08:06:06 -0000 @@ -103,6 +103,7 @@ enum reconf_action { RECONF_NONE, RECONF_KEEP, RECONF_REINIT, + RECONF_RELOAD, RECONF_DELETE }; Index: rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.321 diff -u -p -r1.321 rde.c --- rde.c 18 Sep 2012 10:10:00 -0000 1.321 +++ rde.c 12 Oct 2012 06:27:52 -0000 @@ -85,12 +85,11 @@ void rde_dump_mrt_new(struct mrt *, pi 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 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); @@ -645,11 +644,11 @@ rde_dispatch_imsg_parent(struct imsgbuf struct rde_rib rn; struct rde_peer *peer; struct peer_config *pconf; + 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) @@ -693,10 +692,10 @@ rde_dispatch_imsg_parent(struct imsgbuf 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); @@ -746,7 +745,19 @@ rde_dispatch_imsg_parent(struct imsgbuf TAILQ_INIT(&r->set); r->peer.ribid = rib_find(r->rib); 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 != @@ -781,110 +792,9 @@ rde_dispatch_imsg_parent(struct imsgbuf 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); @@ -1377,8 +1287,8 @@ rde_update_update(struct rde_peer *peer, for (i = 1; i < rib_size; i++) { /* 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; @@ -2310,8 +2220,8 @@ rde_dump_filterout(struct rde_peer *peer 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 @@ -2647,196 +2557,228 @@ rde_send_nexthop(struct bgpd_addr *next, * 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); + 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 (oa == ACTION_ALLOW && na == ACTION_ALLOW) { - if (path_compare(nasp, oasp) == 0) - goto done; - /* send update */ - up_generate(peer, nasp, &addr, pt->prefixlen); + if (peer->conf.softreconfig_out && + !rde_filter_equal(out_rules, out_rules_tmp, peer)) { + peer->reconf_out = 1; + } + } + /* bring ribs in sync */ + for (rid = 0; rid < rib_size; rid++) { + /* 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: + case RECONF_NONE: + log_warnx("Bad rib reload state"); + 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++) { + 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); } @@ -2858,8 +2800,8 @@ rde_softreconfig_unload_peer(struct rib_ 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) @@ -2887,7 +2829,7 @@ rde_up_dump_upcall(struct rib_entry *re, 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 @@ -2910,7 +2852,7 @@ rde_generate_updates(u_int16_t ribid, st continue; if (peer->state != PEER_UP) continue; - up_generate_updates(rules_l, peer, new, old); + up_generate_updates(out_rules, peer, new, old); } } @@ -3342,7 +3284,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) @@ -3543,7 +3485,6 @@ void rde_shutdown(void) { struct rde_peer *p; - struct filter_rule *r; u_int32_t i; /* @@ -3559,12 +3500,10 @@ 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++) { + rde_free_filter(ribs[i].in_rules); } - free(rules_l); nexthop_shutdown(); path_shutdown(); Index: rde.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v retrieving revision 1.144 diff -u -p -r1.144 rde.h --- rde.h 12 Sep 2012 05:56:22 -0000 1.144 +++ rde.h 12 Oct 2012 06:28:07 -0000 @@ -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_e 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) Index: rde_filter.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_filter.c,v retrieving revision 1.67 diff -u -p -r1.67 rde_filter.c --- rde_filter.c 20 Sep 2011 21:19:06 -0000 1.67 +++ rde_filter.c 12 Oct 2012 06:25:28 -0000 @@ -30,9 +30,9 @@ int rde_filter_match(struct filter_rule 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_a */ 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; @@ -391,42 +390,32 @@ rde_filter_match(struct filter_rule *f, 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; @@ -450,6 +439,22 @@ rde_filter_equal(struct filter_head *a, fb = TAILQ_NEXT(fb, entry); } 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 */ Index: rde_rib.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v retrieving revision 1.134 diff -u -p -r1.134 rde_rib.c --- rde_rib.c 12 Sep 2012 05:56:22 -0000 1.134 +++ rde_rib.c 19 Sep 2012 10:48:42 -0000 @@ -82,6 +82,11 @@ rib_new(char *name, u_int rtableid, u_in 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); } Index: rde_update.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v retrieving revision 1.80 diff -u -p -r1.80 rde_update.c --- rde_update.c 12 Apr 2012 17:31:05 -0000 1.80 +++ rde_update.c 19 Sep 2012 10:58:21 -0000 @@ -411,9 +411,8 @@ up_generate_updates(struct filter_head * 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 * } 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 * 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;