On Sat, Oct 13, 2012 at 08:40:35PM +0200, Claudio Jeker wrote:
> 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.
> 

No comments on this? Should I just commit it?

> -- 
> :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;
> 

-- 
:wq Claudio

Reply via email to