From ddbd97b2ea919ddfb49015fc6044daa960873138 Mon Sep 17 00:00:00 2001 From: "Alexander V. Chernikov" Date: Sat, 24 Jan 2015 21:10:01 +0300 Subject: [PATCH 1/6] Add LPM support for trie. --- filter/config.Y | 2 +- filter/filter.h | 7 +++- filter/trie.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++----- nest/rt-table.c | 4 +- 4 files changed, 113 insertions(+), 15 deletions(-) diff --git a/filter/config.Y b/filter/config.Y index e50e75c..7eb2c0a 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -592,7 +592,7 @@ fprefix: ; fprefix_set: - fprefix { $$ = f_new_trie(cfg_mem); trie_add_fprefix($$, &($1.val.px)); } + fprefix { $$ = f_new_trie(cfg_mem, sizeof(struct f_trie_node)); trie_add_fprefix($$, &($1.val.px)); } | fprefix_set ',' fprefix { $$ = $1; trie_add_fprefix($$, &($3.val.px)); } ; diff --git a/filter/filter.h b/filter/filter.h index 3a6b66d..522af9f 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -80,11 +80,13 @@ struct f_tree *find_tree(struct f_tree *t, struct f_val val); int same_tree(struct f_tree *t1, struct f_tree *t2); void tree_format(struct f_tree *t, buffer *buf); -struct f_trie *f_new_trie(linpool *lp); -void trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h); +struct f_trie *f_new_trie(linpool *lp, size_t node_size); +void *trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h); int trie_match_prefix(struct f_trie *t, ip_addr px, int plen); +void *trie_match_longest_prefix(struct f_trie *t, ip_addr px, int plen); int trie_same(struct f_trie *t1, struct f_trie *t2); void trie_format(struct f_trie *t, buffer *buf); +void trie_walk(struct f_trie *t, void *func, void *data); void fprefix_get_bounds(struct f_prefix *px, int *l, int *h); @@ -204,6 +206,7 @@ struct f_trie { linpool *lp; int zero; + int node_size; struct f_trie_node root; }; diff --git a/filter/trie.c b/filter/trie.c index 217d72c..9851877 100644 --- a/filter/trie.c +++ b/filter/trie.c @@ -75,23 +75,24 @@ #include "filter/filter.h" /** - * f_new_trie - * - * Allocates and returns a new empty trie. + * f_new_trie - Allocates and returns a new empty trie. + * @lp: linear pool to allocate items from + * @node_size: element size to allocate */ struct f_trie * -f_new_trie(linpool *lp) +f_new_trie(linpool *lp, size_t node_size) { struct f_trie * ret; - ret = lp_allocz(lp, sizeof(struct f_trie)); + ret = lp_allocz(lp, sizeof(struct f_trie) + node_size - sizeof(struct f_trie_node)); ret->lp = lp; + ret->node_size = node_size; return ret; } static inline struct f_trie_node * new_node(struct f_trie *t, int plen, ip_addr paddr, ip_addr pmask, ip_addr amask) { - struct f_trie_node *n = lp_allocz(t->lp, sizeof(struct f_trie_node)); + struct f_trie_node *n = lp_allocz(t->lp, t->node_size); n->plen = plen; n->addr = paddr; n->mask = pmask; @@ -116,9 +117,13 @@ attach_node(struct f_trie_node *parent, struct f_trie_node *child) * Adds prefix (prefix pattern) @px/@plen to trie @t. @l and @h are lower * and upper bounds on accepted prefix lengths, both inclusive. * 0 <= l, h <= 32 (128 for IPv6). + * + * Returns pointer to allocated node. Function can return pointer to + * existing node if @px and @plen are the same. If px/plen == 0/0 (or ::/0) + * pointer to root node is returned */ -void +void * trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h) { if (l == 0) @@ -156,7 +161,7 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h) attach_node(o, b); attach_node(b, n); attach_node(b, a); - return; + return a; } if (plen < n->plen) @@ -166,14 +171,14 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h) struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask); attach_node(o, a); attach_node(a, n); - return; + return a; } if (plen == n->plen) { /* We already found added node in trie. Just update accept mask */ n->accept = ipa_or(n->accept, amask); - return; + return n; } /* Update accept mask part M2 and go deeper */ @@ -187,6 +192,8 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h) /* We add new tail node 'a' after node 'o' */ struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask); attach_node(o, a); + + return a; } /** @@ -234,6 +241,94 @@ trie_match_prefix(struct f_trie *t, ip_addr px, int plen) return 0; } +#define NODE_IS_BRANCHING(x) (*((u32 *)(((struct f_trie_node *)(x)) + 1)) == 0) +/** + * trie_match_longest_prefix - find longest prefix match + * @t: trie + * @px: prefix address + * @plen: prefix length + * + * Tries to find a matching prefix pattern in the trie such that + * prefix @px/@plen matches that prefix pattern. Returns prefix pointer + * or NULL. + */ +void * +trie_match_longest_prefix(struct f_trie *t, ip_addr px, int plen) +{ + ip_addr pmask = ipa_mkmask(plen); + ip_addr paddr = ipa_and(px, pmask); + ip_addr cmask; + struct f_trie_node *n = &t->root, *parent = NULL; + + /* Return root node for 0/0 or :: */ + if ((plen == 0) && (t->zero)) + return n; + + /* Skip root node since it is cath-all node */ + n = n->c[(ipa_getbit(paddr, 0)) ? 1 : 0]; + + while (n) + { + cmask = ipa_and(n->mask, pmask); + + /* We are out of path */ + if (ipa_compare(ipa_and(paddr, cmask), ipa_and(n->addr, cmask))) + break; + + /* Mask is too specific */ + if (n->plen > plen) + break; + + /* Do not save pointer to branching nodes */ + if (!NODE_IS_BRANCHING(n)) + parent = n; + + /* Choose children */ + n = n->c[(ipa_getbit(paddr, n->plen)) ? 1 : 0]; + } + + /* + * parent is either + * 1) NULL (if the first non-null node does not exist oris out of path) + * or + * 2) points to the last entry that match + * + * In former case we check if catch-all prefix really exists and return + * either pointer to root node or NULL. In latter case we simply return parent. + */ + + return parent ? parent : (t->zero ? &t->root : NULL); +} + +static void +trie_walk_call(struct f_trie_node *n, void *func, void *data) +{ + void (*f)(struct f_trie_node *, void *) = func; + + if (n) + f(n, data); + + if (n->c[0]) + trie_walk_call(n->c[0], func, data); + + if (n->c[1]) + trie_walk_call(n->c[1], func, data); +} + +void +trie_walk(struct f_trie *t, void *func, void *data) +{ + void (*f)(struct f_trie_node *, void *) = func; + + if (t->zero) + f(&t->root, data); + + if (t->root.c[0]) + trie_walk_call(t->root.c[0], func, data); + if (t->root.c[1]) + trie_walk_call(t->root.c[1], func, data); +} + static int trie_node_same(struct f_trie_node *t1, struct f_trie_node *t2) { diff --git a/nest/rt-table.c b/nest/rt-table.c index 125f1d1..dbe0c50 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -1988,7 +1988,7 @@ rt_init_hostcache(rtable *tab) hc->slab = sl_new(rt_table_pool, sizeof(struct hostentry)); hc->lp = lp_new(rt_table_pool, 1008); - hc->trie = f_new_trie(hc->lp); + hc->trie = f_new_trie(hc->lp, sizeof(struct f_trie_node)); tab->hostcache = hc; } @@ -2136,7 +2136,7 @@ rt_update_hostcache(rtable *tab) /* Reset the trie */ lp_flush(hc->lp); - hc->trie = f_new_trie(hc->lp); + hc->trie = f_new_trie(hc->lp, sizeof(struct f_trie_node)); WALK_LIST_DELSAFE(n, x, hc->hostentries) { -- 2.1.2