From ddbd97b2ea919ddfb49015fc6044daa960873138 Mon Sep 17 00:00:00 2001
From: "Alexander V. Chernikov" <melifaro@yandex-team.ru>
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

