mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Extend the trie_walk_init api + test
The trie_walk_init() function now supports also searching whole trie subnet and all successor subnets (in lexicographic order). This behavior can be accomplished by setting @net, and @include_successors to subnet, and non-zero respectivelly.
This commit is contained in:
parent
972c717c56
commit
01e16f0e09
@ -127,7 +127,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);
|
||||||
@ -179,14 +179,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
|
||||||
|
@ -790,14 +790,22 @@ done:
|
|||||||
* @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
|
||||||
|
* removes the restriction for all subnets lexicographically succeeding the
|
||||||
|
* @net. In case of @net search fail the walk state starting position points to
|
||||||
|
* the nearest parent node availible. If you use @net and @include_successors,
|
||||||
|
* beware that the trie_walk_next() could return a net preceding the one
|
||||||
|
* specified in @net.
|
||||||
|
*
|
||||||
|
* If desired start position node was found in trie, 1 is returned, 0 otherwise.
|
||||||
*/
|
*/
|
||||||
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 +817,7 @@ trie_walk_init(struct f_trie_walk_state *s, const struct f_trie *t, const net_ad
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!net)
|
if (!net)
|
||||||
return;
|
return 1;
|
||||||
|
|
||||||
/* 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);
|
||||||
@ -840,16 +848,42 @@ trie_walk_init(struct f_trie_walk_state *s, const struct f_trie *t, const net_ad
|
|||||||
s->accept_length = net->pxlen;
|
s->accept_length = net->pxlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->stack[0] = n;
|
/* Set as root node the only searched subnet */
|
||||||
return;
|
if (!include_successors)
|
||||||
|
s->stack[0] = n;
|
||||||
|
/* Save the last node on the stack otherwise */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Found prefect match, no advancing */
|
||||||
|
s->stack[s->stack_pos] = n;
|
||||||
|
/* Search whole trie except skipped parts */
|
||||||
|
s->start_pos = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* Be careful about underflow */
|
||||||
|
else if (s->stack_pos > 0)
|
||||||
|
s->stack_pos--;
|
||||||
|
|
||||||
|
/* Search whole trie except skipped parts */
|
||||||
|
if (include_successors)
|
||||||
|
s->start_pos = 1;
|
||||||
|
|
||||||
|
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)))
|
||||||
|
@ -880,6 +880,137 @@ t_trie_walk_to_root(void)
|
|||||||
return 1;
|
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[]){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);
|
||||||
|
|
||||||
|
/* // print sorted prefixes
|
||||||
|
bt_debug("sorted prefixes\n");
|
||||||
|
for (struct f_prefix *px = pxset; px < pxset + num; px++)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
bt_format_net(buf, 64, &px->net);
|
||||||
|
bt_debug("%s{%d,%d}\n", buf, px->lo, px->hi);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Full walk */
|
||||||
|
bt_debug("Full walk inclusive (round %d, %d nets)\n", round, num);
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
uint pxc = 0;
|
||||||
|
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 */
|
||||||
|
pos = bt_random() % num;
|
||||||
|
end = pos + (int[]){2, 2, 3, 4}[level / 2];
|
||||||
|
end = MIN(end, num);
|
||||||
|
|
||||||
|
struct f_prefix from = pxset[pos];
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* Account for subnets before searched net from */
|
||||||
|
for (; pos < num; pos++)
|
||||||
|
if (net_compare(&pxset[pos].net, &from.net) >= 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)
|
||||||
|
{
|
||||||
|
log_networks(&net, &pxset[pos].net);
|
||||||
|
bt_assert(net_compare(&net, &pxset[pos].net) >= 0);
|
||||||
|
|
||||||
|
/* Skip possible duplicates */
|
||||||
|
while (net_equal(&pxset[pos].net, &pxset[pos + 1].net))
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
TRIE_WALK2_END;
|
||||||
|
|
||||||
|
bt_assert(pos == num);
|
||||||
|
bt_debug("Subnet walk done inclusive for %s (found %d nets)\n", buf0, pos - p0);
|
||||||
|
|
||||||
|
tmp_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_bird_cleanup();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@ -891,6 +1022,7 @@ 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_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");
|
||||||
|
@ -2057,7 +2057,7 @@ rt_table_export_start(struct rt_exporter *re, struct rt_export_request *req)
|
|||||||
{
|
{
|
||||||
hook->walk_state = mb_allocz(p, sizeof (struct f_trie_walk_state));
|
hook->walk_state = mb_allocz(p, sizeof (struct f_trie_walk_state));
|
||||||
hook->walk_lock = rt_lock_trie(tab);
|
hook->walk_lock = rt_lock_trie(tab);
|
||||||
trie_walk_init(hook->walk_state, tab->trie, req->addr);
|
trie_walk_init(hook->walk_state, tab->trie, req->addr, 0);
|
||||||
hook->event = ev_new_init(p, rt_feed_by_trie, hook);
|
hook->event = ev_new_init(p, rt_feed_by_trie, hook);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user