0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-09-19 11:55:21 +00:00

Trie: Add support for unbound subnet walks

Trie walk supports adding restriction on walk bounds by supplying option net
during walk initialization. Currently, walk initialized this way couldn't escape
specified subnet. By additional flag, we add support to drop to subnet's upper
bound for subnet started walks. Therefore it is now possible to start a walk in
some subnet and continue walking all lexicagraphical successors present in the
trie.
This commit is contained in:
Vojtech Vilimek 2024-08-21 11:05:39 +02:00
parent c8835cc193
commit 6dd986587a
5 changed files with 742 additions and 21 deletions

View File

@ -12,6 +12,7 @@
#include "nest/bird.h" #include "nest/bird.h"
#include "nest/route.h" #include "nest/route.h"
#include "nest/attrs.h"
/* Type numbers must be in 0..0xff range */ /* Type numbers must be in 0..0xff range */
#define T_MASK 0xff #define T_MASK 0xff
@ -231,7 +232,7 @@ void *trie_add_prefix(struct f_trie *t, const net_addr *n, uint l, uint h);
int trie_match_net(const struct f_trie *t, const net_addr *n); int trie_match_net(const struct f_trie *t, const net_addr *n);
int trie_match_longest_ip4(const struct f_trie *t, const net_addr_ip4 *net, net_addr_ip4 *dst, ip4_addr *found0); int trie_match_longest_ip4(const struct f_trie *t, const net_addr_ip4 *net, net_addr_ip4 *dst, ip4_addr *found0);
int trie_match_longest_ip6(const struct f_trie *t, const net_addr_ip6 *net, net_addr_ip6 *dst, ip6_addr *found0); int trie_match_longest_ip6(const struct f_trie *t, const net_addr_ip6 *net, net_addr_ip6 *dst, ip6_addr *found0);
void trie_walk_init(struct f_trie_walk_state *s, const struct f_trie *t, const net_addr *from); int trie_walk_init(struct f_trie_walk_state *s, const struct f_trie *t, const net_addr *from, u8 include_successors);
int trie_walk_next(struct f_trie_walk_state *s, net_addr *net); int trie_walk_next(struct f_trie_walk_state *s, net_addr *net);
int trie_same(const struct f_trie *t1, const struct f_trie *t2); int trie_same(const struct f_trie *t1, const struct f_trie *t2);
void trie_format(const struct f_trie *t, buffer *buf); void trie_format(const struct f_trie *t, buffer *buf);
@ -283,14 +284,17 @@ trie_match_next_longest_ip6(net_addr_ip6 *n, ip6_addr *found)
#define TRIE_WALK_TO_ROOT_END }) #define TRIE_WALK_TO_ROOT_END })
#define TRIE_WALK2(trie, net, from, include) ({ \
#define TRIE_WALK(trie, net, from) ({ \
net_addr net; \ net_addr net; \
struct f_trie_walk_state tws_; \ struct f_trie_walk_state tws_; \
trie_walk_init(&tws_, trie, from); \ trie_walk_init(&tws_, trie, from, include); \
while (trie_walk_next(&tws_, &net)) while (trie_walk_next(&tws_, &net))
#define TRIE_WALK_END }) #define TRIE_WALK2_END })
#define TRIE_WALK(trie, net, from) TRIE_WALK2(trie, net, from, 0) \
#define TRIE_WALK_END TRIE_WALK2_END
#define F_CMP_ERROR 999 #define F_CMP_ERROR 999

74
filter/trie-data-edge Normal file
View File

@ -0,0 +1,74 @@
17.192.0.0/12
17.192.64.0/18
17.192.64.16/28
17.192.64.96/28
17.192.64.240/28
17.192.64.144/28
17.192.192.0/18
17.201.0.0/18
17.216.0.0/18
17.201.0.16/28
101.80.0.0/12
101.192.192.0/18
101.201.0.0/18
101.201.0.80/28
101.216.0.0/18
180.201.0.0/18
180.216.0.0/18
180.48.0.0/12
251.16.0.0/12
1.0.0.0/12
1.1.0.0/18
1.1.1.0/28
1.1.3.0/28
1.1.5.0/28
1.1.7.0/28
1.3.0.0/18
1.5.0.0/18
1.5.1.0/28
1.5.3.0/28
1.7.0.0/18
3.0.0.0/12
3.1.0.0/18
3.3.0.0/18
3.5.0.0/18
5.0.0.0/12
5.1.0.0/28
7.0.0.0/12
1.0.0.0/12
3.0.0.0/12
3.1.0.0/18
5.0.0.0/12
5.1.0.0/18
5.3.0.0/18
5.3.1.0/28
5.3.3.0/28
5.5.0.0/18
7.0.0.0/12
7.1.0.0/18
7.3.0.0/18
7.5.0.0/18
7.7.0.0/18
7.7.1.0/28
7.7.3.0/28
7.7.5.0/28
7.7.7.0/28
1.0.0.0/8
1.1.0.0/16
1.1.255.0/24
1.32.0.0/12
1.34.0.0/16
1.64.0.0/12
0.0.0.0/0
1.1.1.1/32
255.255.255.255/32
0.0.0.0/0
123.45.0.0/16
0.0.0.0/0

View File

@ -784,20 +784,47 @@ done:
#define SAME_PREFIX(A,B,X,L) ((X) ? ip4_prefix_equal((A)->v4.addr, net4_prefix(B), (L)) : ip6_prefix_equal((A)->v6.addr, net6_prefix(B), (L))) #define SAME_PREFIX(A,B,X,L) ((X) ? ip4_prefix_equal((A)->v4.addr, net4_prefix(B), (L)) : ip6_prefix_equal((A)->v6.addr, net6_prefix(B), (L)))
#define GET_NET_BITS(N,X,A,B) ((X) ? ip4_getbits(net4_prefix(N), (A), (B)) : ip6_getbits(net6_prefix(N), (A), (B))) #define GET_NET_BITS(N,X,A,B) ((X) ? ip4_getbits(net4_prefix(N), (A), (B)) : ip6_getbits(net6_prefix(N), (A), (B)))
#define GET_NODE_BITS(N,X,A,B) ((X) ? ip4_getbits((N)->v4.addr, (A), (B)) : ip6_getbits((N)->v6.addr, (A), (B)))
#define NEXT_PREFIX(A,B,X) ((X) ? ip4_compare((A)->v4.addr, net4_prefix(B)) < 0 : ip6_compare((A)->v6.addr, net6_prefix(B)) < 0)
#define CHECK_LOCAL_MASK(A,B,L,X) \
((X) ? (A)->v4.local >= trie_local_mask4(net4_prefix(B), (B)->pxlen, (L)) : (A)->v6.local >= trie_local_mask6(net6_prefix(B), (B)->pxlen, (L)))
#define SELECT_CHILD(pos,step) ((1u << (step)) + (pos))
#define MATCH_LOCAL_MASK(A,B,L,X) \
(!!((X) ? (A)->v4.local & trie_local_mask4(net4_prefix(B), (B)->pxlen, (L)) : (A)->v6.local & trie_local_mask6(net6_prefix(B), (B)->pxlen, (L))))
/*
* We want to select a specific subtrie base on it's index in child array c.
*
* 1
* 2 3
* 4 5 6 7
* 8 9 A B C D E F
* -----------------------------------------------------
* 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
*
*/
/** /**
* trie_walk_init * trie_walk_init
* @s: walk state * @s: walk state
* @t: trie * @t: trie
* @net: optional subnet for walk * @net: optional subnet for walk
* @include_successors: optional flag for continue walking beyond subnet @net
* *
* Initialize walk state for subsequent walk through nodes of the trie @t by * Initialize walk state for subsequent walk through nodes of the trie @t by
* trie_walk_next(). The argument @net allows to restrict walk to given subnet, * trie_walk_next(). The argument @net allows to restrict walk to given subnet,
* otherwise full walk over all nodes is used. This is done by finding node at * otherwise full walk over all nodes is used. This is done by finding node at
* or below @net and starting position in it. * or below @net and starting position in it. The argument @include_successors,
* which requeries specifing the @net argument, removes the restriction for all
* nets lexicographically succeeding the @net. This is done by storing the walken
* nodes on the state's stack.
*
* For set @include_successors the return value is 1 if the @net is present in
* the trie @t, in all other cases 0.
*/ */
void int
trie_walk_init(struct f_trie_walk_state *s, const struct f_trie *t, const net_addr *net) trie_walk_init(struct f_trie_walk_state *s, const struct f_trie *t, const net_addr *net, u8 include_successors)
{ {
*s = (struct f_trie_walk_state) { *s = (struct f_trie_walk_state) {
.ipv4 = t->ipv4, .ipv4 = t->ipv4,
@ -809,7 +836,7 @@ trie_walk_init(struct f_trie_walk_state *s, const struct f_trie *t, const net_ad
}; };
if (!net) if (!net)
return; return 0;
/* We want to find node of level at least plen */ /* We want to find node of level at least plen */
int plen = ROUND_DOWN_POW2(net->pxlen, TRIE_STEP); int plen = ROUND_DOWN_POW2(net->pxlen, TRIE_STEP);
@ -827,29 +854,157 @@ trie_walk_init(struct f_trie_walk_state *s, const struct f_trie *t, const net_ad
/* We found final node */ /* We found final node */
if (nlen >= plen) if (nlen >= plen)
{ {
if (!include_successors)
s->stack[0] = n;
else
s->stack[s->stack_pos] = n;
if (nlen == plen) if (nlen == plen)
{ {
/* Find proper local_pos, while accept_length is not used */ /* Find proper local_pos, while accept_length is not used */
int step = net->pxlen - plen; int step = net->pxlen - plen;
s->start_pos = s->local_pos = (1u << step) + GET_NET_BITS(net, v4, plen, step); s->local_pos = (1u << step) + GET_NET_BITS(net, v4, plen, step);
s->accept_length = plen; s->accept_length = plen;
if (!include_successors)
{
s->start_pos = s->local_pos;
return MATCH_LOCAL_MASK(n, net, nlen, v4);
}
if (GET_LOCAL(n, v4) != 0 && !CHECK_LOCAL_MASK(n, net, nlen, v4))
{
s->local_pos = (1u << TRIE_STEP) + GET_NET_BITS(net, v4, nlen, TRIE_STEP);
return 0;
}
int pos = 1;
int bits = GET_NET_BITS(net, v4, nlen, TRIE_STEP);
for (int i = 0; i < net->pxlen - plen; i++)
{
if (bits & (1u << (TRIE_STEP - i - 1)))
pos = 2 * pos + 1;
else
pos = 2 * pos;
}
s->local_pos = pos;
return MATCH_LOCAL_MASK(n, net, nlen, v4);
} }
else else
{ {
/* Start from pos 1 in local node, but first try accept mask */ /* Start from pos 1 in current node, but first try accept mask */
s->accept_length = net->pxlen; s->accept_length = net->pxlen;
return 0;
} }
s->stack[0] = n;
return;
} }
/* We store node in stack before moving on */
if (include_successors)
s->stack[s->stack_pos++] = n;
/* Choose child */ /* Choose child */
n = GET_CHILD(n, v4, GET_NET_BITS(net, v4, nlen, TRIE_STEP)); n = GET_CHILD(n, v4, GET_NET_BITS(net, v4, nlen, TRIE_STEP));
} }
s->stack[0] = NULL; /* We do not override the trie root in case of inclusive search */
return; if (!include_successors)
{
s->stack[0] = NULL;
return 0;
}
/* We are out of path, find nearest successor */
if (s->stack_pos == 0)
return 0;
/*
* If we end up on node that has compressed path, we need to step up node
* for better decision making
*/
s->stack_pos--;
n = s->stack[s->stack_pos];
ASSERT(n != NULL);
int nlen = v4 ? n->v4.plen : n->v6.plen;
struct net_addr_ip4 *net4 = NULL;
struct net_addr_ip6 *net6 = NULL;
if (v4) net4 = (net_addr_ip4 *) net;
else net6 = (net_addr_ip6 *) net;
/* We known for sure that the searched prefix is not in the trie */
int cmp;
int bits = GET_NET_BITS(net, v4, nlen, TRIE_STEP);
const struct f_trie_node *child = GET_CHILD(n, v4, bits);
while (child)
{
cmp = v4 ? ip4_compare(child->v4.addr, net4->prefix)
: ip6_compare(child->v6.addr, net6->prefix);
if (cmp == 0)
{
if (v4 ? child->v4.plen <= net4->pxlen : child->v6.plen <= net6->pxlen)
bits = 0;
else
bits = 15;
}
else if (cmp < 0)
{
for (int i = bits; i < (1 << TRIE_STEP); i++)
{
const struct f_trie_node *tmp = GET_CHILD(n, v4, i);
if (!tmp) continue;
if (v4 ? ip4_compare(tmp->v4.addr, net4->prefix) > 0
: ip6_compare(tmp->v6.addr, net6->prefix) > 0)
break;
child = tmp;
}
bits = 15;
}
else /* cmp > 0 */
bits = 0;
s->stack_pos++;
s->stack[s->stack_pos] = n = child;
child = GET_CHILD(child, v4, bits);
}
nlen = (v4) ? n->v4.plen : n->v6.plen;
cmp = v4 ? ip4_compare(n->v4.addr, net4->prefix)
: ip6_compare(n->v6.addr, net6->prefix);
s->accept_length = nlen;
if (cmp == 0)
{
bits = GET_NET_BITS(net, v4, nlen, TRIE_STEP);
if (plen - nlen >= TRIE_STEP)
{
s->local_pos = SELECT_CHILD(bits, TRIE_STEP);
return 0;
}
int pos = 1;
for (int i = 0; i < net->pxlen - plen; i++)
{
if (bits & (1u << (TRIE_STEP - i - 1)))
pos = 2 * pos + 1;
else
pos = 2 * pos;
}
s->local_pos = pos;
}
else if (cmp < 0)
{
s->local_pos = SELECT_CHILD(bits, TRIE_STEP);
}
else /* cmp > 0 */
{
/* Everything already set */
}
return 0;
} }
#define GET_ACCEPT_BIT(N,X,B) ((X) ? ip4_getbit((N)->v4.accept, (B)) : ip6_getbit((N)->v6.accept, (B))) #define GET_ACCEPT_BIT(N,X,B) ((X) ? ip4_getbit((N)->v4.accept, (B)) : ip6_getbit((N)->v6.accept, (B)))

View File

@ -626,6 +626,72 @@ log_networks(const net_addr *a, const net_addr *b)
} }
} }
static void
test_walk_return_val(struct f_trie *trie, struct f_prefix pxset[], uint count, int include)
{
if (count == 0)
{
net_addr net;
bt_random_net(&net, trie->ipv4 ? NET_IP4 : NET_IP6);
struct f_trie_walk_state tws;
bt_assert(!trie_walk_init(&tws, trie, &net, include));
net_addr res;
bt_assert(!trie_walk_next(&tws, &res));
return;
}
u32 index = bt_random_n(count);
net_addr *tested = &pxset[index].net;
net_addr res;
struct f_trie_walk_state tws;
bt_assert(trie_walk_init(&tws, trie, tested, include)); /* return true */
bt_assert(trie_walk_next(&tws, &res));
bt_assert(net_equal(tested, &res));
net_addr rand_net;
bt_random_net(&rand_net, trie->ipv4 ? NET_IP4 : NET_IP6);
for (u32 i = 0; i < count; i++)
{
if (net_equal(&pxset[i].net, &rand_net))
return;
}
memset(&res, 0, sizeof(res));
memset(&tws, 0, sizeof(tws));
bt_assert(!trie_walk_init(&tws, trie, &rand_net, include)); /* return false */
if (include)
{
if (net_compare(&pxset[count - 1].net, &rand_net) < 0)
bt_assert(!trie_walk_next(&tws, &res));
else
{
bt_assert(trie_walk_next(&tws, &res));
bt_assert(net_compare(&rand_net, &res) < 0);
}
}
else
{
u32 pos;
for (pos = 0; pos < count; pos++)
if (net_compare(&pxset[pos].net, &rand_net) > 0)
break;
if (pos < count && net_in_netX(&pxset[pos].net, &rand_net))
{
bt_assert(trie_walk_next(&tws, &res));
bt_assert(net_equal(&pxset[pos].net, &res));
}
else
{
bt_assert(!trie_walk_next(&tws, &res));
}
}
}
static int static int
t_trie_walk(void) t_trie_walk(void)
{ {
@ -636,7 +702,7 @@ t_trie_walk(void)
{ {
int level = round / TESTS_NUM; int level = round / TESTS_NUM;
int v6 = level % 2; int v6 = level % 2;
int num = PREFIXES_NUM * (int[]){1, 10, 100, 1000}[level / 2]; int num = PREFIXES_NUM * (int[]){0, 1, 10, 100, 1000}[level / 2];
int pos = 0, end = 0; int pos = 0, end = 0;
list *prefixes = make_random_prefix_list(num, v6, 1); list *prefixes = make_random_prefix_list(num, v6, 1);
struct f_trie *trie = make_trie_from_prefix_list(prefixes); struct f_trie *trie = make_trie_from_prefix_list(prefixes);
@ -649,6 +715,8 @@ t_trie_walk(void)
qsort(pxset, num, sizeof(struct f_prefix), compare_prefixes); qsort(pxset, num, sizeof(struct f_prefix), compare_prefixes);
/* Test trie_walk_init() return value */
test_walk_return_val(trie, pxset, num, 0);
/* Full walk */ /* Full walk */
bt_debug("Full walk (round %d, %d nets)\n", round, num); bt_debug("Full walk (round %d, %d nets)\n", round, num);
@ -673,13 +741,19 @@ t_trie_walk(void)
bt_assert(pxc == trie->prefix_count); bt_assert(pxc == trie->prefix_count);
bt_debug("Full walk done\n"); bt_debug("Full walk done\n");
/* Prepare net for subnet walk - start with random prefix */ /* Prepare net for subnet walk - start with random prefix */
pos = bt_random() % num; if (num)
pos = bt_random_n(num);
else
pos = 0;
end = pos + (int[]){2, 2, 3, 4}[level / 2]; end = pos + (int[]){2, 2, 3, 4}[level / 2];
end = MIN(end, num); end = MIN(end, num);
struct f_prefix from = pxset[pos]; struct f_prefix from;
if (num)
from = pxset[pos];
else
get_random_prefix(&from, v6, 1);
/* Find a common superprefix to several subsequent prefixes */ /* Find a common superprefix to several subsequent prefixes */
for (; pos < end; pos++) for (; pos < end; pos++)
@ -857,6 +931,418 @@ t_trie_walk_to_root(void)
return 1; return 1;
} }
static inline void
test_walk_init(struct f_trie *trie, u32 in_px, u32 in_plen, u32 res_px, u32 res_plen, int has_next)
{
net_addr_ip4 net = NET_ADDR_IP4(ip4_from_u32(in_px), in_plen);
struct f_trie_walk_state tws;
/* return value of trie_walk_init() is tested elsewhere */
trie_walk_init(&tws, trie, (struct net_addr *) &net, 1);
net_addr res;
int b = trie_walk_next(&tws, &res);
bt_assert(b == has_next);
if (has_next)
{
net_addr_ip4 expected = NET_ADDR_IP4(ip4_from_u32(res_px), res_plen);
bt_assert(net_equal_ip4((struct net_addr_ip4 *) &res, &expected));
}
}
/*
* a very simplistic and deterministic test suite to test all reasoable code paths in
* trie_walk_init()
*/
static int
t_trie_walk_determ(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
#define EDGE_CASE_COUNT 7
list *prefixes[EDGE_CASE_COUNT] = { 0 };
struct f_trie *tries[EDGE_CASE_COUNT] = { 0 };
bt_debug("Reading data from 'filter/trie-data-edge'\n");
int n = read_prefix_file("filter/trie-data-edge", 0, prefixes, tries);
bt_debug("Read data from 'trie-data-edge' %d lists\n", n);
if (n < EDGE_CASE_COUNT)
{
bt_debug("Loaded less lists than expected!\n");
return 0;
}
test_walk_init(tries[0], 100663296, 7 /* 6.0.0.0/7 */, 297795584, 12 /* 17.192.0.0/12 */, 1);
test_walk_init(tries[0], 201326592, 12 /* 12.0.0.0/12 */, 297795584, 12 /* 17.192.0.0/12 */, 1);
test_walk_init(tries[0], 297795584, 14 /* 17.192.0.0/14 */, 297811968, 18 /* 17.192.64.00/18 */, 1);
test_walk_init(tries[0], 297795584, 18 /* 17.192.0.0/24 */, 297811968, 18 /* 17.192.64.00/18 */, 1);
test_walk_init(tries[0], 297798400, 24 /* 17.192.11.0/24 */, 297811968, 18 /* 17.192.64.0/18 */, 1);
test_walk_init(tries[0], 297811968, 28 /* 17.192.64.0/28 */, 297811984, 28 /* 17.192.64.16/28 */, 1);
test_walk_init(tries[0], 297811980, 31 /* 17.192.64.12/31 */, 297811984, 28 /* 17.192.64.16/28 */, 1);
/*
* ==============================
* Tests on left leaning trie
*/
struct f_trie *left = tries[1];
test_walk_init(left, 0, 4 /* 0.0.0.0/4 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 0, 12 /* 0.0.0.0/12 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 0, 24 /* 0.0.0.0/24 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 0, 32 /* 0.0.0.0/32 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 16777216, 12 /* 1.0.0.0/12 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 16777216, 14 /* 1.0.0.0/14 */, 16842752, 18 /* 1.1.0.0/18 */, 1);
test_walk_init(left, 16777216, 16 /* 1.0.0.0/16 */, 16842752, 18 /* 1.1.0.0/18 */, 1);
test_walk_init(left, 16777216, 18 /* 1.0.0.0/18 */, 16842752, 18 /* 1.1.0.0/18 */, 1);
test_walk_init(left, 16842753, 18 /* 1.1.0.0/18 */, 16842752, 18 /* 1.1.0.0/18 */, 1);
test_walk_init(left, 16842753, 20 /* 1.1.0.0/20 */, 16843008, 28 /* 1.1.1.0/28 */, 1);
test_walk_init(left, 16842753, 24 /* 1.1.0.0/24 */, 16843008, 28 /* 1.1.1.0/28 */, 1);
test_walk_init(left, 16842753, 28 /* 1.1.0.0/28 */, 16843008, 28 /* 1.1.1.0/28 */, 1);
test_walk_init(left, 16842753, 30 /* 1.1.0.0/30 */, 16843008, 28 /* 1.1.1.0/28 */, 1);
test_walk_init(left, 16842753, 32 /* 1.1.0.0/32 */, 16843008, 28 /* 1.1.1.0/28 */, 1);
/* longest path or it's extension */
test_walk_init(left, 16843009, 28 /* 1.1.1.0/28 */, 16843008, 28 /* 1.1.1.0/28 */, 1);
test_walk_init(left, 16843009, 30 /* 1.1.1.0/30 */, 16843520, 28 /* 1.1.3.0/28 */, 1);
test_walk_init(left, 16843009, 32 /* 1.1.1.0/32 */, 16843520, 28 /* 1.1.3.0/28 */, 1);
/* prefixes `after' longest path */
test_walk_init(left, 16843264, 23 /* 1.1.2.0/23 */, 16843520, 28 /* 1.1.3.0/28 */, 1);
test_walk_init(left, 16843264, 24 /* 1.1.2.0/24 */, 16843520, 28 /* 1.1.3.0/28 */, 1);
test_walk_init(left, 16843264, 26 /* 1.1.2.0/26 */, 16843520, 28 /* 1.1.3.0/28 */, 1);
test_walk_init(left, 16843264, 28 /* 1.1.2.0/28 */, 16843520, 28 /* 1.1.3.0/28 */, 1);
test_walk_init(left, 16843264, 32 /* 1.1.2.0/32 */, 16843520, 28 /* 1.1.3.0/28 */, 1);
test_walk_init(left, 16843521, 28 /* 1.1.3.0/28 */, 16843520, 28 /* 1.1.3.0/28 */, 1);
/* extension of longest path */
test_walk_init(left, 16843521, 30 /* 1.1.3.0/30 */, 16844032, 28 /* 1.1.5.0/28 */, 1);
test_walk_init(left, 16843521, 32 /* 1.1.3.0/32 */, 16844032, 28 /* 1.1.5.0/28 */, 1);
test_walk_init(left, 16844544, 28 /* 1.1.7.0/28 */, 16844544, 28 /* 1.1.7.0/28 */, 1);
test_walk_init(left, 16844544, 30 /* 1.1.7.0/30 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16844544, 32 /* 1.1.7.0/32 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16844801, 24 /* 1.1.8.0/24 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16844801, 28 /* 1.1.8.0/28 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16844801, 32 /* 1.1.8.0/32 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16908290, 16 /* 1.2.0.0/16 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16908290, 18 /* 1.2.0.0/18 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16908290, 22 /* 1.2.0.0/22 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16908290, 24 /* 1.2.0.0/24 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16908290, 28 /* 1.2.0.0/28 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16908290, 30 /* 1.2.0.0/30 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16908290, 32 /* 1.2.0.0/32 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16973827, 18 /* 1.3.0.0/18 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16973827, 20 /* 1.3.0.0/20 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 16973827, 22 /* 1.3.0.0/22 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 16973827, 24 /* 1.3.0.0/24 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 16973827, 28 /* 1.3.0.0/28 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 16973827, 30 /* 1.3.0.0/30 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 16973827, 32 /* 1.3.0.0/32 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 33554432, 9 /* 2.0.0.0/9 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 33554432, 12 /* 2.0.0.0/12 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 33554432, 14 /* 2.0.0.0/14 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 33554432, 18 /* 2.0.0.0/18 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 33554432, 22 /* 2.0.0.0/22 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 33554432, 28 /* 2.0.0.0/28 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 33554432, 30 /* 2.0.0.0/30 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 33554432, 32 /* 2.0.0.0/32 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 50659333, 18 /* 3.5.0.0/18 */, 50659328, 18 /* 3.5.0.0/18 */, 1);
test_walk_init(left, 50659333, 19 /* 3.5.0.0/19 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 50659333, 32 /* 3.5.0.0/32 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 50659589, 30 /* 3.5.1.0/30 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 50659845, 32 /* 3.5.2.0/32 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 50724870, 16 /* 3.6.0.0/16 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 50724870, 18 /* 3.6.0.0/18 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 67108864, 6 /* 4.0.0.0/6 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 67108864, 12 /* 4.0.0.0/12 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 67108864, 14 /* 4.0.0.0/14 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 67108864, 18 /* 4.0.0.0/18 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 67108864, 20 /* 4.0.0.0/20 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 67108864, 28 /* 4.0.0.0/28 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 67108864, 30 /* 4.0.0.0/30 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 67108864, 32 /* 4.0.0.0/32 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 83886080, 10 /* 5.0.0.0/10 */, 83886080, 12 /* 5.0.0.0/12 */, 1);
test_walk_init(left, 83886080, 14 /* 5.0.0.0/14 */, 83951616, 28 /* 5.1.0.0/28 */, 1);
test_walk_init(left, 83886080, 28 /* 5.0.0.0/28 */, 83951616, 28 /* 5.1.0.0/28 */, 1);
test_walk_init(left, 83886080, 30 /* 5.0.0.0/30 */, 83951616, 28 /* 5.1.0.0/28 */, 1);
test_walk_init(left, 83886080, 32 /* 5.0.0.0/32 */, 83951616, 28 /* 5.1.0.0/28 */, 1);
test_walk_init(left, 83951617, 30 /* 5.1.0.0/30 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 83951617, 32 /* 5.1.0.0/32 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 84017154, 32 /* 5.2.0.0/32 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 100663296, 8 /* 6.0.0.0/8 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 100663296, 12 /* 6.0.0.0/12 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 100663296, 16 /* 6.0.0.0/16 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 100663296, 18 /* 6.0.0.0/18 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 100663296, 22 /* 6.0.0.0/22 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 100663296, 32 /* 6.0.0.0/32 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 117440512, 9 /* 7.0.0.0/9 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 117440512, 12 /* 7.0.0.0/12 */, 117440512, 12 /* 7.0.0.0/12 */, 1);
test_walk_init(left, 117440512, 13 /* 7.0.0.0/13 */, 0, 0 /* N/A */, 0);
test_walk_init(left, 117440512, 23 /* 7.0.0.0/23 */, 0, 0 /* N/A */, 0);
test_walk_init(left, 117440512, 32 /* 7.0.0.0/32 */, 0, 0 /* N/A */, 0);
test_walk_init(left, 117506049, 20 /* 7.1.0.0/20 */, 0, 0 /* N/A */, 0);
test_walk_init(left, 117506049, 32 /* 7.1.0.0/32 */, 0, 0 /* N/A */, 0);
test_walk_init(left, 134217728, 9 /* 8.0.0.0/9 */, 0, 0 /* N/A */, 0);
test_walk_init(left, 134217728, 14 /* 8.0.0.0/14 */, 0, 0 /* N/A */, 0);
test_walk_init(left, 134283265, 32 /* 8.1.0.0/32 */, 0, 0 /* N/A */, 0);
test_walk_init(left, 4194368, 24 /* 0.64.0.0/24 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 4196160, 24 /* 0.64.7.0/24 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 4229952, 25 /* 0.64.139.0/25 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 4259648, 26 /* 0.64.255.0/26 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 8388736, 24 /* 0.128.0.0/24 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 8390528, 25 /* 0.128.7.0/25 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 8450176, 22 /* 0.128.240.0/22 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left, 8453760, 23 /* 0.128.254.0/23 */, 16777216, 12 /* 1.0.0.0/12 */, 1);
test_walk_init(left,2147483648, 3 /* 128.0.0.0/3 */, 0, 0 /* N/A */, 0);
test_walk_init(left,2147483648, 9 /* 128.0.0.0/9 */, 0, 0 /* N/A */, 0);
test_walk_init(left,2148597777, 25 /* 128.17.0.0/25 */, 0, 0 /* N/A */, 0);
test_walk_init(left, 20971584, 23 /* 1.64.0.0/23 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 20973376, 24 /* 1.64.7.0/24 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 21007168, 24 /* 1.64.139.0/24 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 25230976, 23 /* 1.128.254.0/23 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 16859137, 30 /* 1.1.64.0/30 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16859137, 32 /* 1.1.64.7/32 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16859137, 32 /* 1.1.64.138/32 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16859137, 31 /* 1.1.64.240/31 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16875521, 29 /* 1.1.128.0/29 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16875521, 32 /* 1.1.128.7/32 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16875521, 29 /* 1.1.128.240/29 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16875521, 31 /* 1.1.128.254/31 */, 16973824, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 16990211, 25 /* 1.3.64.0/25 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 16990211, 32 /* 1.3.64.7/32 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 16990211, 29 /* 1.3.64.240/29 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 16990211, 31 /* 1.3.64.254/31 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 17006595, 26 /* 1.3.128.0/26 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 17006595, 32 /* 1.3.128.7/32 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 17006595, 29 /* 1.3.128.240/29 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 17006595, 31 /* 1.3.128.254/31 */, 17104896, 18 /* 1.5.0.0/18 */, 1);
test_walk_init(left, 17252359, 26 /* 1.7.64.0/26 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 17252359, 32 /* 1.7.64.7/32 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 17252359, 28 /* 1.7.64.240/28 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 17252359, 31 /* 1.7.64.254/31 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 17268743, 25 /* 1.7.128.0/25 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 17268743, 32 /* 1.7.128.7/32 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 17268743, 29 /* 1.7.128.240/29 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 17268743, 31 /* 1.7.128.254/31 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 125829120, 9 /* 7.128.0.0/9 */, 0, 0 /* N/A */, 0);
test_walk_init(left, 125829120, 12 /* 7.128.0.0/12 */, 0, 0 /* N/A */, 0);
/* corner cases */
test_walk_init(left, 17825792, 16 /* 1.16.0.0/16 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 17825792, 24 /* 1.16.0.0/24 */, 50331648, 12 /* 3.0.0.0/12 */, 1);
test_walk_init(left, 16846848, 24 /* 1.1.16.0/24 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16846848, 32 /* 1.1.16.0/32 */, 16973824, 18 /* 1.3.0.0/18 */, 1);
test_walk_init(left, 16777472, 24 /* 1.0.1.0/24 */, 16842752, 18 /* 1.1.0.0/18 */, 1);
test_walk_init(left, 16781312, 19 /* 1.0.16.0/19 */, 16842752, 18 /* 1.1.0.0/18 */, 1);
struct f_trie *corner = tries[3];
test_walk_init(corner,16777216, 12 /* 1.0.0.0/12 */, 16842752, 16 /* 1.1.0.0/16 */, 1);
test_walk_init(corner,16908288, 32 /* 1.2.0.0/32 */, 18874368, 12 /* 1.32.0.0/12 */, 1);
struct f_trie *with_zero = tries[4];
test_walk_init(with_zero, 50529027, 32 /* 3.3.3.3/32 */, 4294967295, 32 /* 255.255.255.255/32 */, 1);
struct f_trie *two_px = tries[5];
test_walk_init(two_px, 0, 0 /* 0.0.0.0/0 */, 0, 0 /* 0.0.0.0/0 */, 1);
test_walk_init(two_px, 1, 32 /* 0.0.0.1/32 */, 2066546688, 16 /* 123.45.0.0/16 */, 1);
test_walk_init(two_px, 3355443200, 8 /* 200.0.0.0/8 */, 0, 0 /* N/A */, 0);
test_walk_init(two_px, 50529027, 32 /* 3.3.3.3/32 */, 2066546688, 16 /* 123.45.0.0/16 */, 1);
struct f_trie *root_only = tries[6];
test_walk_init(root_only, 50529027, 32 /* 3.3.3.3/32 */, 0, 0 /* N/A */, 0);
bt_bird_cleanup();
return 1;
}
static int
t_trie_walk_inclusive(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
for (int round = 0; round < TESTS_NUM*8; round++)
{
int level = round / TESTS_NUM;
int v6 = level % 2;
int num = PREFIXES_NUM * (int[]){0, 1, 10, 100, 1000}[level / 2];
int pos = 0, end = 0;
list *prefixes = make_random_prefix_list(num, v6, 1);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
struct f_prefix_node *n;
WALK_LIST(n, *prefixes)
pxset[pos++] = n->prefix;
memset(&pxset[pos], 0, sizeof (struct f_prefix));
qsort(pxset, num, sizeof(struct f_prefix), compare_prefixes);
/* Test trie_walk_init() return value */
test_walk_return_val(trie, pxset, num, 1);
/* Full walk */
bt_debug("Full walk inclusive (round %d, %d nets)\n", round, num);
pos = 0;
uint pxc = 0;
/* Last argument should have no effect on the walk */
TRIE_WALK2(trie, net, NULL, 1)
{
log_networks(&net, &pxset[pos].net);
bt_assert(net_equal(&net, &pxset[pos].net));
/* Skip possible duplicates */
while (net_equal(&pxset[pos].net, &pxset[pos + 1].net))
pos++;
pos++;
pxc++;
}
TRIE_WALK2_END;
bt_assert(pos == num);
bt_assert(pxc == trie->prefix_count);
bt_debug("Full walk inclusive done\n");
/* Prepare net for subnet walk - start with random prefix from trie */
if (num)
pos = bt_random_n(num);
else
pos = 0;
end = pos + (int[]){2, 2, 3, 4}[level / 2];
end = MIN(end, num);
struct f_prefix from;
if (num)
from = pxset[pos];
else
get_random_prefix(&from, v6, 1);
/* Find a common superprefix to several subsequent prefixes */
for (; pos < end; pos++)
{
if (net_equal(&from.net, &pxset[pos].net))
continue;
int common = !v6 ?
ip4_pxlen(net4_prefix(&from.net), net4_prefix(&pxset[pos].net)) :
ip6_pxlen(net6_prefix(&from.net), net6_prefix(&pxset[pos].net));
from.net.pxlen = MIN(from.net.pxlen, common);
if (!v6)
((net_addr_ip4 *) &from.net)->prefix =
ip4_and(net4_prefix(&from.net), net4_prefix(&pxset[pos].net));
else
((net_addr_ip6 *) &from.net)->prefix =
ip6_and(net6_prefix(&from.net), net6_prefix(&pxset[pos].net));
}
/* Fix irrelevant bits */
if (!v6)
((net_addr_ip4 *) &from.net)->prefix =
ip4_and(net4_prefix(&from.net), ip4_mkmask(net4_pxlen(&from.net)));
else
((net_addr_ip6 *) &from.net)->prefix =
ip6_and(net6_prefix(&from.net), ip6_mkmask(net6_pxlen(&from.net)));
/* Find initial position for final prefix */
for (pos = 0; pos < num; pos++)
if (compare_prefixes(&pxset[pos], &from) >= 0)
break;
int p0 = pos;
char buf0[64];
bt_format_net(buf0, 64, &from.net);
bt_debug("Subnet walk inclusive for %s (round %d, %d nets)\n", buf0, round, num);
/* Subnet walk */
TRIE_WALK2(trie, net, &from.net, 1)
{
bt_assert(net_compare(&net, &pxset[pos].net) >= 0);
bt_assert(net_compare(&net, &from.net) >= 0);
if (!net_equal(&net, &pxset[pos + 1].net) || !(net_compare(&net, &from.net) >= 0))
{
/* Make sure that net is from inserted prefixes */
bt_format_net(buf0, 64, &net);
bt_debug("got: %s", buf0);
bt_format_net(buf0, 64, &pxset[pos].net);
bt_debug(" expected %s", buf0);
if (pos + 1 < num)
{
bt_format_net(buf0, 64, &pxset[pos + 1].net);
bt_debug(" (next: %s)\n", buf0);
}
else
bt_debug("\n");
}
bt_assert(net_equal(&net, &pxset[pos].net));
bt_assert(net_compare(&net, &from.net) >= 0);
/* Skip possible duplicates */
while (net_equal(&pxset[pos].net, &pxset[pos + 1].net))
pos++;
pos++;
}
TRIE_WALK2_END;
bt_debug("pos == num %u %u; p0 %u \n", pos, num, p0);
bt_debug("Subnet walk done inclusive for %s (found %d nets)\n", buf0, pos - p0);
bt_assert(pos == num);
/* Prepare net for subnet walk - start with random prefix (likely not from trie) */
get_random_prefix(&from, v6, 1);
for (pos = 0; pos < num; pos++)
if (compare_prefixes(&pxset[pos], &from) >= 0)
break;
p0 = pos;
bt_format_net(buf0, 64, &from.net);
bt_debug("Subnet walk inclusive for random %s (round %d, %d nets)\n", buf0, round, num);
/* Subnet walk */
TRIE_WALK2(trie, net, &from.net, 1)
{
bt_assert(net_equal(&net, &pxset[pos].net));
bt_assert(net_compare(&net, &from.net) >= 0);
while (net_equal(&pxset[pos].net, &pxset[pos + 1].net))
pos++;
pos++;
}
TRIE_WALK2_END;
bt_debug("Subnet walk inclusive for random %s (found %d nets from %d)\n", buf0, pos - p0, num - p0);
bt_assert(pos == num);
tmp_flush();
}
bt_bird_cleanup();
return 1;
}
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
@ -868,6 +1354,8 @@ main(int argc, char *argv[])
bt_test_suite(t_trie_same, "A trie filled forward should be same with a trie filled backward."); bt_test_suite(t_trie_same, "A trie filled forward should be same with a trie filled backward.");
bt_test_suite(t_trie_walk, "Testing TRIE_WALK() on random tries"); bt_test_suite(t_trie_walk, "Testing TRIE_WALK() on random tries");
bt_test_suite(t_trie_walk_to_root, "Testing TRIE_WALK_TO_ROOT() on random tries"); bt_test_suite(t_trie_walk_to_root, "Testing TRIE_WALK_TO_ROOT() on random tries");
bt_test_suite(t_trie_walk_inclusive, "Testing TRIE_WALK2() on random tries");
bt_test_suite(t_trie_walk_determ, "Testing trie_walk_init() on edge case tries deterministically");
// bt_test_suite(t_bench_trie_datasets_subset, "Benchmark tries from datasets by random subset of nets"); // bt_test_suite(t_bench_trie_datasets_subset, "Benchmark tries from datasets by random subset of nets");
// bt_test_suite(t_bench_trie_datasets_random, "Benchmark tries from datasets by generated addresses"); // bt_test_suite(t_bench_trie_datasets_random, "Benchmark tries from datasets by generated addresses");

View File

@ -270,7 +270,7 @@ rt_show_cont(struct cli *c)
if (d->trie_walk) if (d->trie_walk)
{ {
d->walk_lock = rt_lock_trie(tab); d->walk_lock = rt_lock_trie(tab);
trie_walk_init(d->walk_state, tab->trie, d->addr); trie_walk_init(d->walk_state, tab->trie, d->addr, 0);
} }
else else
FIB_ITERATE_INIT(&d->fit, &tab->fib); FIB_ITERATE_INIT(&d->fit, &tab->fib);