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);
> +}
>