]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Filter: Improve prefix trie tests
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 21 Apr 2020 11:49:29 +0000 (13:49 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Sat, 25 Sep 2021 14:06:43 +0000 (16:06 +0200)
Add tests explicitly matching insides and outsides of trie and update
tests to do testing of both IPv4 and IPv6 tries.

filter/trie_test.c
test/birdtest.c
test/birdtest.h

index 5e931e4e7a3cefa7b354970a398c46035bfdd74d..6418427ece52c37aac6c0ae742efb424e2966408 100644 (file)
@@ -14,7 +14,7 @@
 #include "conf/conf.h"
 
 #define TESTS_NUM              10
-#define PREFIXES_NUM           10
+#define PREFIXES_NUM           32
 #define PREFIX_TESTS_NUM       10000
 
 #define BIG_BUFFER_SIZE                10000
@@ -31,106 +31,342 @@ xrandom(u32 max)
   return (bt_random() % max);
 }
 
+static inline uint
+get_exp_random(void)
+{
+  uint r, n = 0;
+
+  for (r = bt_random(); r & 1; r = r >> 1)
+    n++;
+
+  return n;
+}
+
+static inline int
+matching_ip4_nets(const net_addr_ip4 *a, const net_addr_ip4 *b)
+{
+  ip4_addr cmask = ip4_mkmask(MIN(a->pxlen, b->pxlen));
+  return ip4_compare(ip4_and(a->prefix, cmask), ip4_and(b->prefix, cmask)) == 0;
+}
+
+static inline int
+matching_ip6_nets(const net_addr_ip6 *a, const net_addr_ip6 *b)
+{
+  ip6_addr cmask = ip6_mkmask(MIN(a->pxlen, b->pxlen));
+  return ip6_compare(ip6_and(a->prefix, cmask), ip6_and(b->prefix, cmask)) == 0;
+}
+
+static inline int
+matching_nets(const net_addr *a, const net_addr *b)
+{
+  if (a->type != b->type)
+    return 0;
+
+  return (a->type == NET_IP4) ?
+    matching_ip4_nets((const net_addr_ip4 *) a, (const net_addr_ip4 *) b) :
+    matching_ip6_nets((const net_addr_ip6 *) a, (const net_addr_ip6 *) b);
+}
+
 static int
-is_prefix_included(list *prefixes, struct f_prefix *needle)
+is_prefix_included(list *prefixes, const net_addr *needle)
 {
   struct f_prefix_node *n;
   WALK_LIST(n, *prefixes)
-  {
-    ip6_addr cmask = ip6_mkmask(MIN(n->prefix.net.pxlen, needle->net.pxlen));
-
-    ip6_addr ip = net6_prefix(&n->prefix.net);
-    ip6_addr needle_ip = net6_prefix(&needle->net);
-
-    if ((ipa_compare(ipa_and(ip, cmask), ipa_and(needle_ip, cmask)) == 0) &&
-       (n->prefix.lo <= needle->net.pxlen) && (needle->net.pxlen <= n->prefix.hi))
+    if (matching_nets(&n->prefix.net, needle) &&
+       (n->prefix.lo <= needle->pxlen) && (needle->pxlen <= n->prefix.hi))
     {
-      bt_debug("FOUND\t" PRIip6 "/%d %d-%d\n", ARGip6(net6_prefix(&n->prefix.net)), n->prefix.net.pxlen, n->prefix.lo, n->prefix.hi);
+      char buf[64];
+      bt_format_net(buf, 64, &n->prefix.net);
+      bt_debug("FOUND %s %d-%d\n", buf, n->prefix.lo, n->prefix.hi);
+
       return 1; /* OK */
     }
-  }
+
   return 0; /* FAIL */
 }
 
-static struct f_prefix
-get_random_ip6_prefix(void)
+static void
+get_random_net(net_addr *net, int v6)
 {
-  struct f_prefix p;
-  u8 pxlen = xrandom(120)+8;
-  ip6_addr ip6 = ip6_build(bt_random(),bt_random(),bt_random(),bt_random());
-  ip6_addr mask = ip6_mkmask(pxlen);
-  net_addr_ip6 net6 = NET_ADDR_IP6(ip6_and(ip6, mask), pxlen);
+  if (!v6)
+  {
+    uint pxlen = xrandom(24)+8;
+    ip4_addr ip4 = ip4_from_u32((u32) bt_random());
+    net_fill_ip4(net, ip4_and(ip4, ip4_mkmask(pxlen)), pxlen);
+  }
+  else
+  {
+    uint pxlen = xrandom(120)+8;
+    ip6_addr ip6 = ip6_build(bt_random(), bt_random(), bt_random(), bt_random());
+    net_fill_ip6(net, ip6_and(ip6, ip6_mkmask(pxlen)), pxlen);
+  }
+}
 
-  p.net = *((net_addr*) &net6);
+static void
+get_random_prefix(struct f_prefix *px, int v6)
+{
+  get_random_net(&px->net, v6);
 
   if (bt_random() % 2)
   {
-    p.lo = 0;
-    p.hi = p.net.pxlen;
+    px->lo = 0;
+    px->hi = px->net.pxlen;
+  }
+  else
+  {
+    px->lo = px->net.pxlen;
+    px->hi = net_max_prefix_length[px->net.type];
+  }
+}
+
+static void
+get_random_ip4_subnet(net_addr_ip4 *net, const net_addr_ip4 *src, int pxlen)
+{
+  *net = NET_ADDR_IP4(ip4_and(src->prefix, ip4_mkmask(pxlen)), pxlen);
+
+  if (pxlen > src->pxlen)
+  {
+    ip4_addr rnd = ip4_from_u32((u32) bt_random());
+    ip4_addr mask = ip4_xor(ip4_mkmask(src->pxlen), ip4_mkmask(pxlen));
+    net->prefix = ip4_or(net->prefix, ip4_and(rnd, mask));
+  }
+}
+
+static void
+get_random_ip6_subnet(net_addr_ip6 *net, const net_addr_ip6 *src, int pxlen)
+{
+  *net = NET_ADDR_IP6(ip6_and(src->prefix, ip6_mkmask(pxlen)), pxlen);
+
+  if (pxlen > src->pxlen)
+  {
+    ip6_addr rnd = ip6_build(bt_random(), bt_random(), bt_random(), bt_random());
+    ip6_addr mask = ip6_xor(ip6_mkmask(src->pxlen), ip6_mkmask(pxlen));
+    net->prefix = ip6_or(net->prefix, ip6_and(rnd, mask));
   }
+}
+
+static void
+get_random_subnet(net_addr *net, const net_addr *src, int pxlen)
+{
+  if (src->type == NET_IP4)
+    get_random_ip4_subnet((net_addr_ip4 *) net, (const net_addr_ip4 *) src, pxlen);
   else
+    get_random_ip6_subnet((net_addr_ip6 *) net, (const net_addr_ip6 *) src, pxlen);
+}
+
+static void
+get_inner_net(net_addr *net, const struct f_prefix *src)
+{
+  int pxlen, step;
+
+  if (bt_random() % 2)
   {
-    p.lo = p.net.pxlen;
-    p.hi = net_max_prefix_length[p.net.type];
+    step = get_exp_random();
+    step = MIN(step, src->hi - src->lo);
+    pxlen = (bt_random() % 2) ? (src->lo + step) : (src->hi - step);
   }
+  else
+    pxlen = src->lo + bt_random() % (src->hi - src->lo + 1);
 
-  return p;
+  get_random_subnet(net, &src->net, pxlen);
 }
 
 static void
-generate_random_ipv6_prefixes(list *prefixes)
+swap_random_bits_ip4(net_addr_ip4 *net, int num)
 {
-  int i;
-  for (i = 0; i < PREFIXES_NUM; i++)
+  for (int i = 0; i < num; i++)
   {
-    struct f_prefix f = get_random_ip6_prefix();
+    ip4_addr swap = IP4_NONE;
+    ip4_setbit(&swap, bt_random() % net->pxlen);
+    net->prefix = ip4_xor(net->prefix, swap);
+  }
+}
 
-    struct f_prefix_node *px = calloc(1, sizeof(struct f_prefix_node));
-    px->prefix = f;
+static void
+swap_random_bits_ip6(net_addr_ip6 *net, int num)
+{
+  for (int i = 0; i < num; i++)
+  {
+    ip6_addr swap = IP6_NONE;
+    ip6_setbit(&swap, bt_random() % net->pxlen);
+    net->prefix = ip6_xor(net->prefix, swap);
+  }
+}
+
+static void
+swap_random_bits(net_addr *net, int num)
+{
+  if (net->type == NET_IP4)
+    swap_random_bits_ip4((net_addr_ip4 *) net, num);
+  else
+    swap_random_bits_ip6((net_addr_ip6 *) net, num);
+}
 
-    bt_debug("ADD\t" PRIip6 "/%d{%d,%d}\n", ARGip6(net6_prefix(&px->prefix.net)), px->prefix.net.pxlen, px->prefix.lo, px->prefix.hi);
+static void
+get_outer_net(net_addr *net, const struct f_prefix *src)
+{
+  int pxlen, step;
+  int inside = 0;
+  int max = net_max_prefix_length[src->net.type];
+
+  if ((src->lo > 0) && (bt_random() % 3))
+  {
+    step = 1 + get_exp_random();
+    step = MIN(step, src->lo);
+    pxlen = src->lo - step;
+  }
+  else if ((src->hi < max) && (bt_random() % 2))
+  {
+    step = 1 + get_exp_random();
+    step = MIN(step, max - src->hi);
+    pxlen = src->hi + step;
+  }
+  else
+  {
+    pxlen = src->lo + bt_random() % (src->hi - src->lo + 1);
+    inside = 1;
+  }
+
+  get_random_subnet(net, &src->net, pxlen);
+
+  /* Perhaps swap some bits in prefix */
+  if ((net->pxlen > 0) && (inside || (bt_random() % 4)))
+    swap_random_bits(net, 1 + get_exp_random());
+}
+
+static list *
+make_random_prefix_list(linpool *lp, int num, int v6)
+{
+  list *prefixes = lp_allocz(lp, sizeof(struct f_prefix_node));
+  init_list(prefixes);
+
+  for (int i = 0; i < num; i++)
+  {
+    struct f_prefix_node *px = lp_allocz(lp, sizeof(struct f_prefix_node));
+    get_random_prefix(&px->prefix, v6);
     add_tail(prefixes, &px->n);
+
+    char buf[64];
+    bt_format_net(buf, 64, &px->prefix.net);
+    bt_debug("ADD %s{%d,%d}\n", buf, px->prefix.lo, px->prefix.hi);
   }
+
+  return prefixes;
+}
+
+static struct f_trie *
+make_trie_from_prefix_list(linpool *lp, list *prefixes)
+{
+  struct f_trie *trie = f_new_trie(lp, 0);
+
+  struct f_prefix_node *n;
+  WALK_LIST(n, *prefixes)
+    trie_add_prefix(trie, &n->prefix.net, n->prefix.lo, n->prefix.hi);
+
+  return trie;
+}
+
+static void
+test_match_net(list *prefixes, struct f_trie *trie, const net_addr *net)
+{
+  char buf[64];
+  bt_format_net(buf, 64, net);
+  bt_debug("TEST %s\n", buf);
+
+  int should_be = is_prefix_included(prefixes, net);
+  int is_there  = trie_match_net(trie, net);
+
+  bt_assert_msg(should_be == is_there, "Prefix %s %s match", buf,
+               (should_be ? "should" : "should not"));
 }
 
 static int
-t_match_net(void)
+t_match_random_net(void)
 {
   bt_bird_init();
   bt_config_parse(BT_CONFIG_SIMPLE);
 
-  uint round;
-  for (round = 0; round < TESTS_NUM; round++)
+  int v6 = 0;
+  linpool *lp = lp_new_default(&root_pool);
+  for (int round = 0; round < TESTS_NUM; round++)
   {
-    list prefixes; /* of structs f_extended_prefix */
-    init_list(&prefixes);
-    struct f_trie *trie = f_new_trie(config->mem, 0);
+    list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6);
+    struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
 
-    generate_random_ipv6_prefixes(&prefixes);
-    struct f_prefix_node *n;
-    WALK_LIST(n, prefixes)
+    for (int i = 0; i < PREFIX_TESTS_NUM; i++)
     {
-      trie_add_prefix(trie, &n->prefix.net, n->prefix.lo, n->prefix.hi);
+      net_addr net;
+      get_random_net(&net, v6);
+      test_match_net(prefixes, trie, &net);
     }
 
-    int i;
-    for (i = 0; i < PREFIX_TESTS_NUM; i++)
+    v6 = !v6;
+    lp_flush(lp);
+  }
+
+  bt_bird_cleanup();
+  return 1;
+}
+
+static int
+t_match_inner_net(void)
+{
+  bt_bird_init();
+  bt_config_parse(BT_CONFIG_SIMPLE);
+
+  int v6 = 0;
+  linpool *lp = lp_new_default(&root_pool);
+  for (int round = 0; round < TESTS_NUM; round++)
+  {
+    list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6);
+    struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
+
+    struct f_prefix_node *n = HEAD(*prefixes);
+    for (int i = 0; i < PREFIX_TESTS_NUM; i++)
     {
-      struct f_prefix f = get_random_ip6_prefix();
-      bt_debug("TEST\t" PRIip6 "/%d\n", ARGip6(net6_prefix(&f.net)), f.net.pxlen);
+      net_addr net;
+      get_inner_net(&net, &n->prefix);
+      test_match_net(prefixes, trie, &net);
 
-      int should_be = is_prefix_included(&prefixes, &f);
-      int is_there  = trie_match_net(trie, &f.net);
-      bt_assert_msg(should_be == is_there, "Prefix " PRIip6 "/%d %s", ARGip6(net6_prefix(&f.net)), f.net.pxlen, (should_be ? "should be found in trie" : "should not be found in trie"));
+      n = NODE_VALID(NODE_NEXT(n)) ? NODE_NEXT(n) : HEAD(*prefixes);
     }
 
-    struct f_prefix_node *nxt;
-    WALK_LIST_DELSAFE(n, nxt, prefixes)
+    v6 = !v6;
+    lp_flush(lp);
+  }
+
+  bt_bird_cleanup();
+  return 1;
+}
+
+static int
+t_match_outer_net(void)
+{
+  bt_bird_init();
+  bt_config_parse(BT_CONFIG_SIMPLE);
+
+  int v6 = 0;
+  linpool *lp = lp_new_default(&root_pool);
+  for (int round = 0; round < TESTS_NUM; round++)
+  {
+    list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6);
+    struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
+
+    struct f_prefix_node *n = HEAD(*prefixes);
+    for (int i = 0; i < PREFIX_TESTS_NUM; i++)
     {
-      free(n);
+      net_addr net;
+      get_outer_net(&net, &n->prefix);
+      test_match_net(prefixes, trie, &net);
+
+      n = NODE_VALID(NODE_NEXT(n)) ? NODE_NEXT(n) : HEAD(*prefixes);
     }
+
+    v6 = !v6;
+    lp_flush(lp);
   }
 
+  v6 = !v6;
   bt_bird_cleanup();
   return 1;
 }
@@ -141,35 +377,25 @@ t_trie_same(void)
   bt_bird_init();
   bt_config_parse(BT_CONFIG_SIMPLE);
 
-  int round;
-  for (round = 0; round < TESTS_NUM*4; round++)
+  int v6 = 0;
+  linpool *lp = lp_new_default(&root_pool);
+  for (int round = 0; round < TESTS_NUM*4; round++)
   {
-    struct f_trie * trie1 = f_new_trie(config->mem, 0);
-    struct f_trie * trie2 = f_new_trie(config->mem, 0);
-
-    list prefixes; /* a list of f_extended_prefix structures */
-    init_list(&prefixes);
-    int i;
-    for (i = 0; i < 100; i++)
-      generate_random_ipv6_prefixes(&prefixes);
+    list *prefixes = make_random_prefix_list(lp, 100 * PREFIXES_NUM, v6);
+    struct f_trie *trie1 = f_new_trie(lp, 0);
+    struct f_trie *trie2 = f_new_trie(lp, 0);
 
     struct f_prefix_node *n;
-    WALK_LIST(n, prefixes)
-    {
+    WALK_LIST(n, *prefixes)
       trie_add_prefix(trie1, &n->prefix.net, n->prefix.lo, n->prefix.hi);
-    }
-    WALK_LIST_BACKWARDS(n, prefixes)
-    {
+
+    WALK_LIST_BACKWARDS(n, *prefixes)
       trie_add_prefix(trie2, &n->prefix.net, n->prefix.lo, n->prefix.hi);
-    }
 
     bt_assert(trie_same(trie1, trie2));
 
-    struct f_prefix_node *nxt;
-    WALK_LIST_DELSAFE(n, nxt, prefixes)
-    {
-      free(n);
-    }
+    v6 = !v6;
+    lp_flush(lp);
   }
 
   return 1;
@@ -180,7 +406,9 @@ main(int argc, char *argv[])
 {
   bt_init(argc, argv);
 
-  bt_test_suite(t_match_net, "Testing random prefix matching");
+  bt_test_suite(t_match_random_net, "Testing random prefix matching");
+  bt_test_suite(t_match_inner_net, "Testing random inner prefix matching");
+  bt_test_suite(t_match_outer_net, "Testing random outer prefix matching");
   bt_test_suite(t_trie_same, "A trie filled forward should be same with a trie filled backward.");
 
   return bt_exit_value();
index a1da078fcf2fb7aa30ad18f824b5479ef8c6b464..d739e78bbffdfc9f40640898b0b88bb6c3ff8a01 100644 (file)
@@ -501,6 +501,12 @@ bt_fmt_ipa(char *buf, size_t size, const void *data)
     bsnprintf(buf, size, "(null)");
 }
 
+void
+bt_format_net(char *buf, size_t size, const void *data)
+{
+  bsnprintf(buf, size, "%N", (const net_addr *) data);
+}
+
 int
 bt_is_char(byte c)
 {
index caec529b5bc90437ea1bde17a1cba9019d58e8c4..7a0c2fc4dbee2b427838e883ac0f88185aefcbcf 100644 (file)
@@ -165,6 +165,8 @@ struct bt_batch {
 void bt_fmt_str(char *buf, size_t size, const void *data);
 void bt_fmt_unsigned(char *buf, size_t size, const void *data);
 void bt_fmt_ipa(char *buf, size_t size, const void *data);
+void bt_format_net(char *buf, size_t size, const void *data);
+
 int bt_assert_batch__(struct bt_batch *opts);
 int bt_is_char(byte c);