ok benno, one tab too much

Claudio Jeker([email protected]) on 2018.09.07 10:08:27 +0200:
> After bringing in as-set this diff makes prefix-set fast by using a
> bitwise search trie for fast lookups. It does not yet change the bad
> syntax of prefix-sets but benno@ and I will tackle that once this is
> hopefully in.
> The code was inspired by what bird is doing for their prefix-set
> lookups but the implementation is slighly different.
> 
> -- 
> :wq Claudio
> 
> Index: Makefile
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/Makefile,v
> retrieving revision 1.33
> diff -u -p -r1.33 Makefile
> --- Makefile  7 Sep 2018 05:43:33 -0000       1.33
> +++ Makefile  7 Sep 2018 05:50:17 -0000
> @@ -4,7 +4,8 @@ PROG= bgpd
>  SRCS=        bgpd.c session.c log.c logmsg.c parse.y config.c \
>       rde.c rde_rib.c rde_decide.c rde_prefix.c mrt.c kroute.c \
>       control.c pfkey.c rde_update.c rde_attr.c printconf.c \
> -     rde_filter.c rde_sets.c pftable.c name2id.c util.c carp.c timer.c
> +     rde_filter.c rde_sets.c rde_trie.c pftable.c name2id.c \
> +     util.c carp.c timer.c
>  CFLAGS+= -Wall -I${.CURDIR}
>  CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
>  CFLAGS+= -Wmissing-declarations
> Index: bgpd.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
> retrieving revision 1.195
> diff -u -p -r1.195 bgpd.c
> --- bgpd.c    7 Sep 2018 05:43:33 -0000       1.195
> +++ bgpd.c    7 Sep 2018 05:47:59 -0000
> @@ -507,7 +507,7 @@ reconfigure(char *conffile, struct bgpd_
>       while ((ps = SIMPLEQ_FIRST(conf->prefixsets)) != NULL) {
>               SIMPLEQ_REMOVE_HEAD(conf->prefixsets, entry);
>               if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIXSET, 0, 0, -1,
> -                 ps, sizeof(*ps)) == -1)
> +                 ps->name, sizeof(ps->name)) == -1)
>                       return (-1);
>               while ((psi = SIMPLEQ_FIRST(&ps->psitems)) != NULL) {
>                       SIMPLEQ_REMOVE_HEAD(&ps->psitems, entry);
> Index: bgpd.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
> retrieving revision 1.334
> diff -u -p -r1.334 bgpd.h
> --- bgpd.h    7 Sep 2018 05:43:33 -0000       1.334
> +++ bgpd.h    7 Sep 2018 07:48:20 -0000
> @@ -212,6 +212,8 @@ TAILQ_HEAD(network_head, network);
>  
>  struct prefixset;
>  SIMPLEQ_HEAD(prefixset_head, prefixset);
> +struct rde_prefixset_head;
> +struct rde_prefixset;
>  
>  struct as_set;
>  SIMPLEQ_HEAD(as_set_head, as_set);
> @@ -226,6 +228,7 @@ struct bgpd_config {
>       struct listen_addrs                     *listen_addrs;
>       struct mrt_head                         *mrt;
>       struct prefixset_head                   *prefixsets;
> +     struct rde_prefixset_head               *rde_prefixsets;
>       struct as_set_head                      *as_sets;
>       char                                    *csock;
>       char                                    *rcsock;
> @@ -680,7 +683,7 @@ struct filter_aslen {
>  struct filter_prefixset {
>       int                      flags;
>       char                     name[SET_NAME_LEN];
> -     struct prefixset        *ps;
> +     struct rde_prefixset    *ps;
>  };
>  
>  struct filter_community {
> Index: rde.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
> retrieving revision 1.419
> diff -u -p -r1.419 rde.c
> --- rde.c     7 Sep 2018 05:43:33 -0000       1.419
> +++ rde.c     7 Sep 2018 05:51:40 -0000
> @@ -100,8 +100,10 @@ static void       rde_softreconfig_unload_pee
>  void          rde_up_dump_upcall(struct rib_entry *, void *);
>  void          rde_update_queue_runner(void);
>  void          rde_update6_queue_runner(u_int8_t);
> -void          rde_mark_prefixsets_dirty(struct prefixset_head *,
> -                                             struct prefixset_head *);
> +struct rde_prefixset *rde_find_prefixset(char *, struct rde_prefixset_head 
> *);
> +void          rde_free_prefixsets(struct rde_prefixset_head *);
> +void          rde_mark_prefixsets_dirty(struct rde_prefixset_head *,
> +                     struct rde_prefixset_head *);
>  
>  void          peer_init(u_int32_t);
>  void          peer_shutdown(void);
> @@ -128,7 +130,7 @@ struct bgpd_config        *conf, *nconf;
>  time_t                        reloadtime;
>  struct rde_peer_head  peerlist;
>  struct rde_peer              *peerself;
> -struct prefixset_head        *prefixsets_tmp, *prefixsets_old;
> +struct rde_prefixset_head *prefixsets_tmp, *prefixsets_old;
>  struct as_set_head   *as_sets_tmp, *as_sets_old;
>  struct filter_head   *out_rules, *out_rules_tmp;
>  struct rdomain_head  *rdomains_l, *newdomains;
> @@ -686,9 +688,9 @@ badnetdel:
>  void
>  rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
>  {
> -     static struct rdomain   *rd;
> -     static struct prefixset *last_prefixset;
> +     static struct rde_prefixset     *last_prefixset;
>       static struct as_set    *last_as_set;
> +     static struct rdomain   *rd;
>       struct imsg              imsg;
>       struct mrt               xmrt;
>       struct rde_rib           rn;
> @@ -697,8 +699,8 @@ rde_dispatch_imsg_parent(struct imsgbuf 
>       struct filter_rule      *r;
>       struct filter_set       *s;
>       struct rib              *rib;
> -     struct prefixset        *ps;
> -     struct prefixset_item   *psi;
> +     struct rde_prefixset    *ps;
> +     struct prefixset_item    psi;
>       char                    *name;
>       size_t                   nmemb;
>       int                      n, fd;
> @@ -769,7 +771,7 @@ rde_dispatch_imsg_parent(struct imsgbuf 
>                               fatalx("IMSG_RECONF_CONF bad len");
>                       reloadtime = time(NULL);
>                       prefixsets_tmp = calloc(1,
> -                         sizeof(struct prefixset_head));
> +                         sizeof(struct rde_prefixset_head));
>                       if (prefixsets_tmp == NULL)
>                               fatal(NULL);
>                       SIMPLEQ_INIT(prefixsets_tmp);
> @@ -832,7 +834,7 @@ rde_dispatch_imsg_parent(struct imsgbuf 
>                       memcpy(r, imsg.data, sizeof(struct filter_rule));
>                       if (r->match.prefixset.flags != 0) {
>                               r->match.prefixset.ps =
> -                                 find_prefixset(r->match.prefixset.name,
> +                                 rde_find_prefixset(r->match.prefixset.name,
>                                       prefixsets_tmp);
>                               if (r->match.prefixset.ps == NULL)
>                                       log_warnx("%s: no prefixset for %s",
> @@ -877,27 +879,27 @@ rde_dispatch_imsg_parent(struct imsgbuf 
>                       break;
>               case IMSG_RECONF_PREFIXSET:
>                       if (imsg.hdr.len - IMSG_HEADER_SIZE !=
> -                         sizeof(struct prefixset))
> +                         sizeof(ps->name))
>                               fatalx("IMSG_RECONF_PREFIXSET bad len");
> -                     ps = malloc(sizeof(struct prefixset));
> +                     ps = calloc(1, sizeof(struct rde_prefixset));
>                       if (ps == NULL)
>                               fatal(NULL);
> -                     memcpy(ps, imsg.data, sizeof(struct prefixset));
> -                     SIMPLEQ_INIT(&ps->psitems);
> +                     memcpy(ps->name, imsg.data, sizeof(ps->name));
>                       SIMPLEQ_INSERT_TAIL(prefixsets_tmp, ps, entry);
>                       last_prefixset = ps;
>                       break;
>               case IMSG_RECONF_PREFIXSETITEM:
>                       if (imsg.hdr.len - IMSG_HEADER_SIZE !=
> -                         sizeof(struct prefixset_item))
> +                         sizeof(psi))
>                               fatalx("IMSG_RECONF_PREFIXSETITEM bad len");
> -                     psi = malloc(sizeof(struct prefixset_item));
> -                     if (psi == NULL)
> -                             fatal(NULL);
> -                     memcpy(psi, imsg.data, sizeof(struct prefixset_item));
> +                     memcpy(&psi, imsg.data, sizeof(psi));
>                       if (last_prefixset == NULL)
>                               fatalx("King Bula has no prefixset");
> -                     SIMPLEQ_INSERT_TAIL(&last_prefixset->psitems, psi, 
> entry);
> +                     if (trie_add(&last_prefixset->th, &psi.p.addr,
> +                         psi.p.len, psi.p.len_min, psi.p.len_max) == -1)
> +                             log_warnx("trie_add(%s) %s/%u, %u-%u) failed",
> +                                 last_prefixset->name, log_addr(&psi.p.addr),
> +                                 psi.p.len, psi.p.len_min, psi.p.len_max);
>                       break;
>               case IMSG_RECONF_AS_SET:
>                       if (imsg.hdr.len - IMSG_HEADER_SIZE !=
> @@ -2793,13 +2795,14 @@ rde_reload_done(void)
>                       nconf->flags &= ~BGPD_FLAG_NO_EVALUATE;
>       }
>  
> -     prefixsets_old = conf->prefixsets;
> +     prefixsets_old = conf->rde_prefixsets;
>       as_sets_old = conf->as_sets;
>  
>       memcpy(conf, nconf, sizeof(struct bgpd_config));
>       conf->listen_addrs = NULL;
>       conf->csock = NULL;
>       conf->rcsock = NULL;
> +     conf->prefixsets = NULL;
>       free(nconf);
>       nconf = NULL;
>  
> @@ -2827,7 +2830,7 @@ rde_reload_done(void)
>       as_sets_mark_dirty(as_sets_old, as_sets_tmp);
>  
>       /* swap the prefixsets */
> -     conf->prefixsets = prefixsets_tmp;
> +     conf->rde_prefixsets = prefixsets_tmp;
>       prefixsets_tmp = NULL;
>       /* and the as_sets */
>       conf->as_sets = as_sets_tmp;
> @@ -3018,7 +3021,7 @@ rde_softreconfig_done(void)
>               ribs[rid].state = RECONF_NONE;
>       }
>  
> -     free_prefixsets(prefixsets_old);
> +     rde_free_prefixsets(prefixsets_old);
>       prefixsets_old = NULL;
>       as_sets_free(as_sets_old);
>       as_sets_old = NULL;
> @@ -3809,25 +3812,47 @@ sa_cmp(struct bgpd_addr *a, struct socka
>       return (0);
>  }
>  
> +struct rde_prefixset *
> +rde_find_prefixset(char *name, struct rde_prefixset_head *p)
> +{
> +     struct rde_prefixset *ps;
> +
> +     SIMPLEQ_FOREACH(ps, p, entry) {
> +             if (!strcmp(ps->name, name))
> +                     return (ps);
> +     }
> +     return (NULL);
> +}
> +
> +void
> +rde_free_prefixsets(struct rde_prefixset_head *psh)
> +{
> +     struct rde_prefixset    *ps;
> +
> +     if (psh == NULL)
> +             return;
> +
> +     while (!SIMPLEQ_EMPTY(psh)) {
> +             ps = SIMPLEQ_FIRST(psh);
> +             trie_free(&ps->th);
> +             SIMPLEQ_REMOVE_HEAD(psh, entry);
> +             free(ps);
> +     }
> +}
> +
>  void
> -rde_mark_prefixsets_dirty(struct prefixset_head *psold,
> -    struct prefixset_head *psnew)
> +rde_mark_prefixsets_dirty(struct rde_prefixset_head *psold,
> +    struct rde_prefixset_head *psnew)
>  {
> -     struct prefixset *new, *fps;
> -     struct prefixset_item *item;
> +     struct rde_prefixset *new, *old;
>  
>       SIMPLEQ_FOREACH(new, psnew, entry) {
>               if ((psold == NULL) ||
> -                 (fps = find_prefixset(new->name, psold)) == NULL) {
> -                     new->sflags |= PREFIXSET_FLAG_DIRTY;
> +                 (old = rde_find_prefixset(new->name, psold)) == NULL) {
> +                     new->dirty = 1;
>               } else {
> -                     SIMPLEQ_FOREACH(item, &new->psitems, entry) {
> -                             if (find_prefixsetitem(item, &fps->psitems)
> -                                 == NULL) {
> -                                     new->sflags |= PREFIXSET_FLAG_DIRTY;
> -                                     break;
> -                             }
> -                     }
> +                     if (trie_equal(&new->th, &old->th) == 0)
> +                             new->dirty = 1;
>               }
>       }
>  }
> Index: rde.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
> retrieving revision 1.187
> diff -u -p -r1.187 rde.h
> --- rde.h     7 Sep 2018 05:43:33 -0000       1.187
> +++ rde.h     7 Sep 2018 05:52:09 -0000
> @@ -323,6 +323,23 @@ struct filterstate {
>       u_int8_t                 nhflags;
>  };
>  
> +struct tentry_v4;
> +struct tentry_v6;
> +struct trie_head {
> +     struct tentry_v4        *root_v4;
> +     struct tentry_v6        *root_v6;
> +     int                      match_default_v4;
> +     int                      match_default_v6;
> +};
> +
> +struct rde_prefixset {
> +     char                            name[SET_NAME_LEN];
> +     struct trie_head                th;
> +     SIMPLEQ_ENTRY(rde_prefixset)    entry;
> +     int                      dirty;
> +};
> +SIMPLEQ_HEAD(rde_prefixset_head, rde_prefixset);
> +
>  extern struct rde_memstats rdemem;
>  
>  /* prototypes */
> @@ -562,5 +579,13 @@ u_char           *up_dump_mp_unreach(u_char *, u_
>                    u_int8_t);
>  int           up_dump_mp_reach(u_char *, u_int16_t *, struct rde_peer *,
>                    u_int8_t);
> +
> +/* rde_trie.c */
> +int  trie_add(struct trie_head *, struct bgpd_addr *, u_int8_t,
> +         u_int8_t, u_int8_t);
> +void trie_free(struct trie_head *);
> +int  trie_match(struct trie_head *, struct bgpd_addr *, u_int8_t);
> +void trie_dump(struct trie_head *);
> +int  trie_equal(struct trie_head *, struct trie_head *);
>  
>  #endif /* __RDE_H__ */
> Index: rde_filter.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde_filter.c,v
> retrieving revision 1.102
> diff -u -p -r1.102 rde_filter.c
> --- rde_filter.c      7 Sep 2018 05:43:33 -0000       1.102
> +++ rde_filter.c      7 Sep 2018 05:51:53 -0000
> @@ -342,7 +342,6 @@ rde_filter_match(struct filter_rule *f, 
>  {
>       int             cas, type;
>       int64_t         las, ld1, ld2;
> -     struct prefixset_item   *psi;
>       struct rde_aspath       *asp = NULL;
>  
>       if (state != NULL)
> @@ -483,17 +482,15 @@ rde_filter_match(struct filter_rule *f, 
>        * prefixset and prefix filter rules are mutual exclusive
>        */
>       if (f->match.prefixset.flags != 0) {
> -             log_debug("%s: processing filter for prefixset %s",
> -                 __func__, f->match.prefixset.name);
> -             SIMPLEQ_FOREACH(psi, &f->match.prefixset.ps->psitems, entry) {
> -                     if (rde_prefix_match(&psi->p, p)) {
> -                             log_debug("%s: prefixset %s matched %s",
> -                                 __func__, f->match.prefixset.ps->name,
> -                                 log_addr(&psi->p.addr));
> -                             return (1);
> -                     }
> -             }
> -             return (0);
> +             struct bgpd_addr addr, *prefix = &addr;
> +             u_int8_t plen;
> +
> +             pt_getaddr(p->re->prefix, prefix);
> +             plen = p->re->prefix->prefixlen;
> +
> +             if (f->match.prefixset.ps == NULL ||
> +                 !trie_match(&f->match.prefixset.ps->th, prefix, plen))
> +                     return (0);
>       } else if (f->match.prefix.addr.aid != 0)
>               return (rde_prefix_match(&f->match.prefix, p));
>  
> @@ -574,7 +571,7 @@ rde_filter_equal(struct filter_head *a, 
>      struct rde_peer *peer)
>  {
>       struct filter_rule      *fa, *fb;
> -     struct prefixset        *psa, *psb;
> +     struct rde_prefixset    *psa, *psb;
>       struct as_set           *asa, *asb;
>  
>       fa = a ? TAILQ_FIRST(a) : NULL;
> @@ -615,10 +612,9 @@ rde_filter_equal(struct filter_head *a, 
>               fa->match.as.aset = asa;
>               fb->match.as.aset = asb;
>  
> -             if ((fa->match.prefixset.flags != 0) &&
> -                 (fa->match.prefixset.ps != NULL) &&
> -                 ((fa->match.prefixset.ps->sflags
> -                 & PREFIXSET_FLAG_DIRTY) != 0)) {
> +             if (fa->match.prefixset.flags != 0 &&
> +                 fa->match.prefixset.ps != NULL &&
> +                 fa->match.prefixset.ps->dirty) {
>                       log_debug("%s: prefixset %s has changed",
>                           __func__, fa->match.prefixset.name);
>                       return (0);
> Index: rde_trie.c
> ===================================================================
> RCS file: rde_trie.c
> diff -N rde_trie.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ rde_trie.c        7 Sep 2018 07:25:40 -0000
> @@ -0,0 +1,588 @@
> +/*   $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2018 Claudio Jeker <[email protected]>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +#include <sys/types.h>
> +#include <sys/queue.h>
> +
> +#include <netinet/in.h>
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +
> +#include "bgpd.h"
> +#include "rde.h"
> +
> +/*
> + * Bitwise compressed trie for prefix-sets.
> + * Since the trie is path compressed the traversing function needs to check
> + * that the lookup is still on path before taking a branch.
> + * There are two types of nodes in the trie. Internal branch nodes and real
> + * nodes. Internal nodes are added when needed because off path nodes are 
> being
> + * inserted and are just used for branching.
> + * During lookup every node defines which bit is checked for branching. This
> + * offset is strictly increasing. For IPv4 the maximum is therefor 32 levels.
> + * Every node checks the bit at position plen to decide which branch to take.
> + * The real nodes also include a prefixlen mask which represents the 
> prefixlen
> + * range that was defined. The prefixlen mask for IPv4 only has 32 bits but
> + * the prefixlen range is from 0 - 32 which needs 33bits. Now prefixlen 0 is
> + * special and only one element -- the default route -- can have prefixlen 0.
> + * So this default route is added as a flag to the trie_head and needs to be
> + * checked independent of the trie when doing a lookup.
> + * On insertion a prefix is added with its prefixlen plen and a min & max
> + * for the prefixlen range. The following precondition needs to hold
> + *     plen <= min <= max
> + * To only match the prefix itself min and max are set to plen.
> + * If a same prefix (addr/plen) is added to the trie but with different
> + * min & max values then the masks of both nodes are merged with a binary OR.
> + * The match function returns true the moment the first node is found which
> + * covers the looked up prefix and where the prefixlen mask matches for the
> + * looked up prefixlen. The moment the plen of a node is bigger is bigger 
> than
> + * the prefixlen of the looked up prefix the search can be stopped since 
> there
> + * will be no match.
> + */
> +struct tentry_v4 {
> +     struct tentry_v4        *trie[2];
> +     struct in_addr           addr;
> +     struct in_addr           plenmask;
> +     u_int8_t                 plen;
> +     u_int8_t                 node;
> +
> +     /* roa source-as list pointer */
> +};
> +
> +struct tentry_v6 {
> +     struct tentry_v6        *trie[2];
> +     struct in6_addr          addr;
> +     struct in6_addr          plenmask;
> +     u_int8_t                 plen;
> +     u_int8_t                 node;
> +
> +     /* roa source-as list pointer */
> +};
> +
> +/*
> + * Find first different bit between a & b starting from the MSB,
> + * a & b have to be different.
> + */
> +static int
> +inet4findmsb(struct in_addr *a, struct in_addr *b)
> +{
> +     int r = 0;
> +     u_int32_t v;
> +
> +     v = ntohl(a->s_addr ^ b->s_addr);
> +     if (v > 0xffff) { r += 16; v >>= 16; }
> +     if (v > 0x00ff) { r += 8; v >>= 8; }
> +     if (v > 0x000f) { r += 4; v >>= 4; }
> +     if (v > 0x0003) { r += 2; v >>= 2; }
> +     if (v > 0x0001) { r += 1; }
> +
> +     return 31 - r;
> +}
> +
> +/*
> + * Find first different bit between a & b starting from the MSB,
> + * a & b have to be different.
> + */
> +static int
> +inet6findmsb(struct in6_addr *a, struct in6_addr *b)
> +{
> +     int r = 0;
> +     u_int8_t i, x;
> +
> +     for (i = 0; i < sizeof(*a) && a->s6_addr[i] == b->s6_addr[i]; i++)
> +             ;
> +     /* first different octet */
> +     x = a->s6_addr[i] ^ b->s6_addr[i];
> +
> +     /* first different bit */
> +     if (x > 0xf) { r += 4; x >>= 4; }
> +     if (x > 0x3) { r += 2; x >>= 2; }
> +     if (x > 0x1) { r += 1; }
> +
> +     return i * 8 + 7 - r;
> +}
> +
> +static int
> +inet4isset(struct in_addr *addr, u_int8_t bit)
> +{
> +     return addr->s_addr & htonl(1 << (31 - bit));
> +}
> +
> +static int
> +inet6isset(struct in6_addr *addr, u_int8_t bit)
> +{
> +     return addr->s6_addr[bit / 8] & (0x80 >> (bit % 8));
> +}
> +
> +static void
> +inet4setbit(struct in_addr *addr, u_int8_t bit)
> +{
> +     /* bit 0 sets the MSB and 31 sets the LSB */
> +     addr->s_addr |= (1 << (31 - bit));
> +}
> +
> +static void
> +inet6setbit(struct in6_addr *addr, u_int8_t bit)
> +{
> +     addr->s6_addr[bit / 8] |= (0x80 >> (bit % 8));
> +}
> +
> +static int
> +trie_add_v4(struct trie_head *th, struct in_addr *prefix, u_int8_t plen,
> +    u_int8_t min, u_int8_t max)
> +{
> +     struct tentry_v4 *n, *new, *b, **prev;
> +     struct in_addr p, plenmask;
> +     u_int8_t i;
> +
> +     /*
> +      * check for default route, this is special cased since prefixlen 0
> +      * can't be checked in the prefixlen mask plenmask. Also there is only
> +      * one default route so using a flag for this works.
> +      */
> +     if (min == 0) {
> +             th->match_default_v4 = 1;
> +             if (max == 0)   /* just the default route */
> +                     return 0;
> +             min = 1;
> +     }
> +
> +     inet4applymask(&p, prefix, plen);
> +
> +     /*
> +      * The prefixlen mask goes from 1 to 32 but the bitmask
> +      * starts at 0 and so all bits are set with an offset of 1.
> +      * The default /0 route is handled specially above.
> +      */
> +     memset(&plenmask, 0, sizeof(plenmask));
> +     for (i = min; i <= max; i++)
> +             inet4setbit(&plenmask, i - 1);
> +
> +     /* walk tree finding spot to insert */
> +     prev = &th->root_v4;
> +     n = *prev;
> +     while (n) {
> +             struct in_addr mp;
> +             u_int8_t minlen;
> +
> +             minlen = n->plen > plen ? plen : n->plen;
> +             inet4applymask(&mp, &p, minlen);
> +             if (n->addr.s_addr !=  mp.s_addr) {
> +                     /*
> +                      * out of path, insert intermediary node between
> +                      * np and n, then insert n and new node there
> +                      */
> +                     if ((b = calloc(1, sizeof(*b))) == NULL)
> +                             return -1;
> +                     b->plen = inet4findmsb(&n->addr, &mp);
> +                     inet4applymask(&b->addr, &n->addr, b->plen);
> +
> +                     *prev = b;
> +                     if (inet4isset(&n->addr, b->plen)) {
> +                             b->trie[1] = n;
> +                             prev = &b->trie[0];
> +                     } else {
> +                             b->trie[0] = n;
> +                             prev = &b->trie[1];
> +                     }
> +                     n = NULL;
> +                     break;
> +             }
> +
> +             if (n->plen > plen) {
> +                     /* n is more specific, just insert new in between */
> +                     break;
> +             }
> +
> +             if (n->plen == plen) {
> +                     /* matching node, adjust */
> +                     n->node = 1;
> +                     n->plenmask.s_addr |= plenmask.s_addr;
> +                     return 0;
> +             }
> +
> +             /* no need to check for n->plen == 32 because of above if */
> +             if (inet4isset(&p, n->plen))
> +                     prev = &n->trie[1];
> +             else
> +                     prev = &n->trie[0];
> +             n = *prev;
> +     }
> +
> +     /* create new node */
> +     if ((new = calloc(1, sizeof(*new))) == NULL)
> +             return -1;
> +     new->addr = p;
> +     new->plen = plen;
> +     new->plenmask = plenmask;
> +     new->node = 1;
> +
> +     /* link node */
> +     *prev = new;
> +     if (n) {
> +             if (inet4isset(&n->addr, new->plen))
> +                     new->trie[1] = n;
> +             else
> +                     new->trie[0] = n;
> +     }
> +     return 0;
> +}
> +
> +static int
> +trie_add_v6(struct trie_head *th, struct in6_addr *prefix, u_int8_t plen,
> +    u_int8_t min, u_int8_t max)
> +{
> +     struct tentry_v6 *n, *new, *b, **prev;
> +     struct in6_addr p, plenmask;
> +     u_int8_t i;
> +
> +     /*
> +      * check for default route, this is special cased since prefixlen 0
> +      * can't be checked in the prefixlen mask plenmask. Also there is only
> +      * one default route so using a flag for this works.
> +      */
> +     if (min == 0) {
> +             th->match_default_v6 = 1;
> +             if (max == 0)   /* just the default route */
> +                     return 0;
> +             min = 1;
> +     }
> +
> +     inet6applymask(&p, prefix, plen);
> +
> +     /*
> +      * The prefixlen mask goes from 1 to 128 but the bitmask
> +      * starts at 0 and so all bits are set with an offset of 1.
> +      * The default /0 route is handled specially above.
> +      */
> +     memset(&plenmask, 0, sizeof(plenmask));
> +     for (i = min; i <= max; i++)
> +             inet6setbit(&plenmask, i - 1);
> +
> +     /* walk tree finding spot to insert */
> +     prev = &th->root_v6;
> +     n = *prev;
> +     while (n) {
> +             struct in6_addr mp;
> +             u_int8_t minlen;
> +
> +             minlen = n->plen > plen ? plen : n->plen;
> +             inet6applymask(&mp, &p, minlen);
> +             if (memcmp(&n->addr, &mp, sizeof(mp)) != 0) {
> +                     /*
> +                      * out of path, insert intermediary node between
> +                      * np and n, then insert n and new node there
> +                      */
> +                     if ((b = calloc(1, sizeof(*b))) == NULL)
> +                             return -1;
> +                     b->plen = inet6findmsb(&n->addr, &mp);
> +                     inet6applymask(&b->addr, &n->addr, b->plen);
> +
> +                     *prev = b;
> +                     if (inet6isset(&n->addr, b->plen)) {
> +                             b->trie[1] = n;
> +                             prev = &b->trie[0];
> +                     } else {
> +                             b->trie[0] = n;
> +                             prev = &b->trie[1];
> +                     }
> +                     n = NULL;
> +                     break;
> +             }
> +
> +             if (n->plen > plen) {
> +                     /* n is more specific, just insert new in between */
> +                     break;
> +             }
> +
> +             if (n->plen == plen) {
> +                     /* matching node, adjust */
> +                     n->node = 1;
> +                     for (i = 0; i < sizeof(plenmask); i++)
> +                             n->plenmask.s6_addr[i] |= plenmask.s6_addr[i];
> +                     return 0;
> +             }
> +
> +             /* no need to check for n->plen == 32 because of above if */
> +             if (inet6isset(&p, n->plen))
> +                     prev = &n->trie[1];
> +             else
> +                     prev = &n->trie[0];
> +             n = *prev;
> +     }
> +
> +     /* create new node */
> +     if ((new = calloc(1, sizeof(*new))) == NULL)
> +             return -1;
> +     new->addr = p;
> +     new->plen = plen;
> +     new->plenmask = plenmask;
> +     new->node = 1;
> +
> +     /* link node */
> +     *prev = new;
> +     if (n) {
> +             if (inet6isset(&n->addr, new->plen))
> +                     new->trie[1] = n;
> +             else
> +                     new->trie[0] = n;
> +     }
> +     return 0;
> +}
> +
> +int
> +trie_add(struct trie_head *th, struct bgpd_addr *prefix, u_int8_t plen,
> +    u_int8_t min, u_int8_t max)
> +{
> +     /* precondition plen <= min <= max */
> +     if (plen > min || min > max)
> +             return -1;
> +
> +     switch (prefix->aid) {
> +     case AID_INET:
> +             if (max > 32)
> +                     return -1;
> +             return trie_add_v4(th, &prefix->v4, plen, min, max);
> +     case AID_INET6:
> +             if (max > 128)
> +                     return -1;
> +             return trie_add_v6(th, &prefix->v6, plen, min, max);
> +     default:
> +             /* anything else fails */
> +             return -1;
> +     }
> +}
> +
> +static void
> +trie_free_v4(struct tentry_v4 *n)
> +{
> +     if (n == NULL)
> +             return;
> +     trie_free_v4(n->trie[0]);
> +     trie_free_v4(n->trie[1]);
> +     free(n);
> +}
> +
> +static void
> +trie_free_v6(struct tentry_v6 *n)
> +{
> +     if (n == NULL)
> +             return;
> +     trie_free_v6(n->trie[0]);
> +     trie_free_v6(n->trie[1]);
> +     free(n);
> +}
> +
> +void
> +trie_free(struct trie_head *th)
> +{
> +     trie_free_v4(th->root_v4);
> +     trie_free_v6(th->root_v6);
> +}
> +
> +static int
> +trie_match_v4(struct trie_head *th, struct in_addr *prefix, u_int8_t plen)
> +{
> +     struct tentry_v4 *n;
> +
> +     if (plen == 0) {
> +             /* special handling for default route */
> +             return th->match_default_v4;
> +     }
> +
> +     n = th->root_v4;
> +     while (n) {
> +             struct in_addr mp;
> +
> +             if (n->plen > plen)
> +                     break;  /* too specific, no match possible */
> +
> +             inet4applymask(&mp, prefix, n->plen);
> +             if (n->addr.s_addr !=  mp.s_addr)
> +                     break;  /* off path, no match possible */
> +
> +             /* plen is from 1 - 32 but the bitmask starts with 0 */
> +             if (n->node && inet4isset(&n->plenmask, plen - 1))
> +                     return 1;       /* prefixlen allowed, match */
> +
> +             if (n->plen == 32)
> +                     break;  /* can't go deeper */
> +             if (inet4isset(prefix, n->plen))
> +                     n = n->trie[1];
> +             else
> +                     n = n->trie[0];
> +     }
> +
> +     return 0;
> +}
> +
> +static int
> +trie_match_v6(struct trie_head *th, struct in6_addr *prefix, u_int8_t plen)
> +{
> +     struct tentry_v6 *n;
> +
> +     if (plen == 0) {
> +             /* special handling for default route */
> +             return th->match_default_v6;
> +     }
> +
> +     n = th->root_v6;
> +     while (n) {
> +             struct in6_addr mp;
> +
> +             if (n->plen > plen)
> +                     break;  /* too specific, no match possible */
> +
> +             inet6applymask(&mp, prefix, n->plen);
> +             if (memcmp(&n->addr, &mp, sizeof(mp)) != 0)
> +                     break;  /* off path, no match possible */
> +
> +             /* plen is from 1 - 128 but the bitmask starts with 0 */
> +             if (n->node && inet6isset(&n->plenmask, plen - 1))
> +                     return 1;       /* prefixlen allowed, match */
> +
> +             if (n->plen == 128)
> +                     break;  /* can't go deeper */
> +             if (inet6isset(prefix, n->plen))
> +                     n = n->trie[1];
> +             else
> +                     n = n->trie[0];
> +     }
> +     return 0;
> +}
> +
> +/* find first matching element in the trie for prefix "prefix/plen" */
> +int
> +trie_match(struct trie_head *th, struct bgpd_addr *prefix, u_int8_t plen)
> +{
> +     switch (prefix->aid) {
> +     case AID_INET:
> +             return trie_match_v4(th, &prefix->v4, plen);
> +     case AID_INET6:
> +             return trie_match_v6(th, &prefix->v6, plen);
> +     default:
> +             /* anything else is no match */
> +             return 0;
> +     }
> +}
> +
> +static int
> +trie_equal_v4(struct tentry_v4 *a, struct tentry_v4 *b)
> +{
> +     if (a == NULL && b == NULL)
> +             return 1;
> +     if (a == NULL || b == NULL)
> +             return 0;
> +     

remove tab ^

> +     if (a->addr.s_addr != b->addr.s_addr ||
> +         a->plen != b->plen ||
> +         a->node != b->node ||
> +         a->plenmask.s_addr != b->plenmask.s_addr)
> +             return 0;
> +
> +     if (trie_equal_v4(a->trie[0], b->trie[0]) == 0 ||
> +         trie_equal_v4(a->trie[1], b->trie[1]) == 0)
> +             return 0;
> +
> +     return 1;
> +}
> +
> +static int
> +trie_equal_v6(struct tentry_v6 *a, struct tentry_v6 *b)
> +{
> +     if (a == NULL && b == NULL)
> +             return 1;
> +     if (a == NULL || b == NULL)
> +             return 0;
> +
> +     if (memcmp(&a->addr, &b->addr, sizeof(a->addr)) != 0 ||
> +         a->plen != b->plen ||
> +         a->node != b->node ||
> +         memcmp(&a->plenmask, &b->plenmask, sizeof(a->plenmask)) != 0)
> +             return 0;
> +
> +     if (trie_equal_v6(a->trie[0], b->trie[0]) == 0 ||
> +         trie_equal_v6(a->trie[1], b->trie[1]) == 0)
> +             return 0;
> +
> +     return 1;
> +}
> +
> +/* Compare two tires and return 1 if they are the same else 0. */
> +int
> +trie_equal(struct trie_head *a, struct trie_head *b)
> +{
> +     if (a->match_default_v4 != b->match_default_v4 ||
> +         a->match_default_v6 != b->match_default_v6)
> +             return 0;
> +     if (trie_equal_v4(a->root_v4, b->root_v4) == 0)
> +             return 0;
> +     if (trie_equal_v6(a->root_v6, b->root_v6) == 0)
> +             return 0;
> +     return 1;
> +}
> +
> +/* debugging functions for printing the trie */
> +static void
> +trie_dump_v4(struct tentry_v4 *n)
> +{
> +     if (n == NULL)
> +             return;
> +     if (n->node)
> +             printf("%s/%u plenmask %08x\n", inet_ntoa(n->addr), n->plen,
> +                 n->plenmask.s_addr);
> +     else
> +             printf("   %s/%u\n", inet_ntoa(n->addr), n->plen);
> +
> +     trie_dump_v4(n->trie[0]);
> +     trie_dump_v4(n->trie[1]);
> +}
> +
> +static void
> +trie_dump_v6(struct tentry_v6 *n)
> +{
> +     char buf[48];
> +     unsigned int i;
> +
> +     if (n == NULL)
> +             return;
> +     if (n->node) {
> +             printf("%s/%u plenmask ",
> +                 inet_ntop(AF_INET6, &n->addr, buf, sizeof(buf)), n->plen);
> +             for (i = 0; i < sizeof(n->plenmask); i++)
> +                     printf("%02x", n->plenmask.s6_addr[i]);
> +             printf("\n");
> +     } else
> +             printf("   %s/%u\n",
> +                 inet_ntop(AF_INET6, &n->addr, buf, sizeof(buf)), n->plen);
> +
> +     trie_dump_v6(n->trie[0]);
> +     trie_dump_v6(n->trie[1]);
> +}
> +
> +void
> +trie_dump(struct trie_head *th)
> +{
> +     if (th->match_default_v4)
> +             printf("0.0.0.0/0 plenmask %08x\n", 0);
> +     trie_dump_v4(th->root_v4);
> +     if (th->match_default_v6)
> +             printf("::/0 plenmask 0\n");
> +     trie_dump_v6(th->root_v6);
> +}
> 

Reply via email to