Commit e709dc09 authored by Ondrej Zajicek (work)'s avatar Ondrej Zajicek (work)
Browse files

Filter: Improve prefix trie tests

Add tests explicitly matching insides and outsides of trie and update
tests to do testing of both IPv4 and IPv6 tries.
parent dd61278c
Loading
Loading
Loading
Loading
+302 −74
Original line number Diff line number Diff line
@@ -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)
    if (matching_nets(&n->prefix.net, needle) &&
	(n->prefix.lo <= needle->pxlen) && (needle->pxlen <= n->prefix.hi))
    {
    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);
      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);

    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))
    {
      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);
      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)
{
  if (!v6)
  {
  struct f_prefix p;
  u8 pxlen = xrandom(120)+8;
    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());
  ip6_addr mask = ip6_mkmask(pxlen);
  net_addr_ip6 net6 = NET_ADDR_IP6(ip6_and(ip6, mask), pxlen);
    net_fill_ip6(net, ip6_and(ip6, ip6_mkmask(pxlen)), pxlen);
  }
}

static void
get_random_prefix(struct f_prefix *px, int v6)
{
  get_random_net(&px->net, v6);

  if (bt_random() % 2)
  {
    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));
  }
}

  p.net = *((net_addr*) &net6);
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 = 0;
    p.hi = p.net.pxlen;
    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);

  get_random_subnet(net, &src->net, pxlen);
}

static void
swap_random_bits_ip4(net_addr_ip4 *net, int num)
{
    p.lo = p.net.pxlen;
    p.hi = net_max_prefix_length[p.net.type];
  for (int i = 0; i < num; i++)
  {
    ip4_addr swap = IP4_NONE;
    ip4_setbit(&swap, bt_random() % net->pxlen);
    net->prefix = ip4_xor(net->prefix, swap);
  }
}

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

  return p;
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);
}

static void
generate_random_ipv6_prefixes(list *prefixes)
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))
  {
  int i;
  for (i = 0; i < PREFIXES_NUM; i++)
    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
  {
    struct f_prefix f = get_random_ip6_prefix();
    pxlen = src->lo + bt_random() % (src->hi - src->lo + 1);
    inside = 1;
  }

  get_random_subnet(net, &src->net, pxlen);

    struct f_prefix_node *px = calloc(1, sizeof(struct f_prefix_node));
    px->prefix = f;
  /* 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);

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

    v6 = !v6;
    lp_flush(lp);
  }

    int i;
    for (i = 0; i < PREFIX_TESTS_NUM; i++)
  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++)
  {
      struct f_prefix f = get_random_ip6_prefix();
      bt_debug("TEST\t" PRIip6 "/%d\n", ARGip6(net6_prefix(&f.net)), f.net.pxlen);
    list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6);
    struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);

      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"));
    struct f_prefix_node *n = HEAD(*prefixes);
    for (int i = 0; i < PREFIX_TESTS_NUM; i++)
    {
      net_addr net;
      get_inner_net(&net, &n->prefix);
      test_match_net(prefixes, trie, &net);

      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)
{
      free(n);
  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++)
    {
      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();
+6 −0
Original line number Diff line number Diff line
@@ -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)
{
+2 −0
Original line number Diff line number Diff line
@@ -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);