]>
git.proxmox.com Git - mirror_frr.git/blob - tests/lib/test_srcdest_table.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Test srcdest table for correctness.
5 * Copyright (C) 2017 by David Lamparter & Christian Franke,
6 * Open Source Routing / NetDEF Inc.
8 * This file is part of FRRouting (FRR)
17 #include "srcdest_table.h"
20 /* Copied from ripngd/ripng_nexthop.h - maybe the whole s6_addr32 thing
21 * should be added by autoconf if not present?
24 #define s6_addr32 __u6_addr.__u6_addr32
27 struct thread_master
*master
;
29 /* This structure is copied from lib/srcdest_table.c to which it is
30 * private as far as other parts of Quagga are concerned.
32 struct srcdest_rnode
{
33 /* must be first in structure for casting to/from route_node */
36 struct route_table
*src_table
;
40 struct route_table
*table
;
44 static char *format_srcdest(const struct prefix_ipv6
*dst_p
,
45 const struct prefix_ipv6
*src_p
)
52 prefix2str((const struct prefix
*)dst_p
, dst_str
, sizeof(dst_str
));
53 if (src_p
&& src_p
->prefixlen
)
54 prefix2str((const struct prefix
*)src_p
, src_str
,
59 ec
= asprintf(&rv
, "%s%s%s", dst_str
,
60 (src_str
[0] != '\0') ? " from " : "", src_str
);
66 static unsigned int log_key(const void *data
)
68 const struct prefix
*hash_entry
= data
;
69 struct prefix_ipv6
*dst_p
= (struct prefix_ipv6
*)&hash_entry
[0];
70 struct prefix_ipv6
*src_p
= (struct prefix_ipv6
*)&hash_entry
[1];
71 unsigned int hash
= 0;
74 hash
= (hash
* 33) ^ (unsigned int)dst_p
->prefixlen
;
75 for (i
= 0; i
< 4; i
++)
76 hash
= (hash
* 33) ^ (unsigned int)dst_p
->prefix
.s6_addr32
[i
];
78 hash
= (hash
* 33) ^ (unsigned int)src_p
->prefixlen
;
80 for (i
= 0; i
< 4; i
++)
82 ^ (unsigned int)src_p
->prefix
.s6_addr32
[i
];
87 static bool log_cmp(const void *a
, const void *b
)
89 if (a
== NULL
|| b
== NULL
)
92 return !memcmp(a
, b
, 2 * sizeof(struct prefix
));
95 static void log_free(void *data
)
97 XFREE(MTYPE_TMP
, data
);
100 static void *log_alloc(void *data
)
102 void *rv
= XMALLOC(MTYPE_TMP
, 2 * sizeof(struct prefix
));
103 memcpy(rv
, data
, 2 * sizeof(struct prefix
));
107 static struct test_state
*test_state_new(void)
109 struct test_state
*rv
;
111 rv
= XCALLOC(MTYPE_TMP
, sizeof(*rv
));
114 rv
->table
= srcdest_table_init();
117 rv
->log
= hash_create(log_key
, log_cmp
, NULL
);
121 static void test_state_free(struct test_state
*test
)
123 route_table_finish(test
->table
);
124 hash_clean(test
->log
, log_free
);
125 hash_free(test
->log
);
126 XFREE(MTYPE_TMP
, test
);
129 static void test_state_add_route(struct test_state
*test
,
130 struct prefix_ipv6
*dst_p
,
131 struct prefix_ipv6
*src_p
)
133 struct route_node
*rn
=
134 srcdest_rnode_get(test
->table
, (struct prefix
*)dst_p
, src_p
);
135 struct prefix hash_entry
[2];
137 memset(hash_entry
, 0, sizeof(hash_entry
));
138 memcpy(&hash_entry
[0], dst_p
, sizeof(*dst_p
));
139 memcpy(&hash_entry
[1], src_p
, sizeof(*src_p
));
142 route_unlock_node(rn
);
143 assert(hash_lookup(test
->log
, hash_entry
) != NULL
);
146 assert(hash_lookup(test
->log
, hash_entry
) == NULL
);
149 rn
->info
= (void *)0xdeadbeef;
150 (void)hash_get(test
->log
, hash_entry
, log_alloc
);
153 static void test_state_del_route(struct test_state
*test
,
154 struct prefix_ipv6
*dst_p
,
155 struct prefix_ipv6
*src_p
)
157 struct route_node
*rn
= srcdest_rnode_lookup(
158 test
->table
, (struct prefix
*)dst_p
, src_p
);
159 struct prefix hash_entry
[2];
161 memset(hash_entry
, 0, sizeof(hash_entry
));
162 memcpy(&hash_entry
[0], dst_p
, sizeof(*dst_p
));
163 memcpy(&hash_entry
[1], src_p
, sizeof(*src_p
));
166 assert(!hash_lookup(test
->log
, hash_entry
));
170 assert(rn
->info
== (void *)0xdeadbeef);
172 route_unlock_node(rn
);
173 route_unlock_node(rn
);
175 struct prefix
*hash_entry_intern
= hash_release(test
->log
, hash_entry
);
176 assert(hash_entry_intern
!= NULL
);
177 XFREE(MTYPE_TMP
, hash_entry_intern
);
180 static void verify_log(struct hash_bucket
*bucket
, void *arg
)
182 struct test_state
*test
= arg
;
183 struct prefix
*hash_entry
= bucket
->data
;
184 struct prefix
*dst_p
= &hash_entry
[0];
185 struct prefix_ipv6
*src_p
= (struct prefix_ipv6
*)&hash_entry
[1];
186 struct route_node
*rn
= srcdest_rnode_lookup(test
->table
, dst_p
, src_p
);
189 assert(rn
->info
== (void *)0xdeadbeef);
191 route_unlock_node(rn
);
194 static void dump_log(struct hash_bucket
*bucket
, void *arg
)
196 struct prefix
*hash_entry
= bucket
->data
;
197 struct prefix_ipv6
*dst_p
= (struct prefix_ipv6
*)&hash_entry
[0];
198 struct prefix_ipv6
*src_p
= (struct prefix_ipv6
*)&hash_entry
[1];
199 char *route_id
= format_srcdest(dst_p
, src_p
);
201 fprintf(stderr
, " %s\n", route_id
);
205 static void test_dump(struct test_state
*test
)
207 fprintf(stderr
, "Contents of hash table:\n");
208 hash_iterate(test
->log
, dump_log
, test
);
209 fprintf(stderr
, "\n");
212 static void test_failed(struct test_state
*test
, const char *message
,
213 const struct prefix_ipv6
*dst_p
,
214 const struct prefix_ipv6
*src_p
)
216 char *route_id
= format_srcdest(dst_p
, src_p
);
218 fprintf(stderr
, "Test failed. Error: %s\n", message
);
219 fprintf(stderr
, "Route in question: %s\n", route_id
);
226 static void test_state_verify(struct test_state
*test
)
228 struct route_node
*rn
;
229 struct prefix hash_entry
[2];
231 memset(hash_entry
, 0, sizeof(hash_entry
));
233 /* Verify that there are no elements in the table which have never
235 for (rn
= route_top(test
->table
); rn
; rn
= srcdest_route_next(rn
)) {
236 const struct prefix_ipv6
*dst_p
, *src_p
;
238 /* While we are iterating, we hold a lock on the current
240 * so all the lock counts we check for take that into account;
242 * state all the numbers will be exactly one less.
244 * Also this makes quite some assumptions based on the current
245 * implementation details of route_table and srcdest_table -
247 * valid implementation might trigger assertions here.
250 if (rnode_is_dstnode(rn
)) {
251 struct srcdest_rnode
*srn
= (struct srcdest_rnode
*)rn
;
252 unsigned int expected_lock
= 1; /* We are in the loop */
255 != NULL
) /* The route node is not internal */
257 if (srn
->src_table
!= NULL
) /* There's a source table
258 associated with rn */
261 if (route_node_get_lock_count(rn
) != expected_lock
)
264 "Dest rnode lock count doesn't match expected count!",
265 (struct prefix_ipv6
*)&rn
->p
, NULL
);
267 unsigned int expected_lock
= 1; /* We are in the loop */
270 != NULL
) /* The route node is not internal */
273 if (route_node_get_lock_count(rn
) != expected_lock
) {
274 srcdest_rnode_prefixes(
275 rn
, (const struct prefix
**)&dst_p
,
276 (const struct prefix
**)&src_p
);
280 "Src rnode lock count doesn't match expected count!",
288 assert(rn
->info
== (void *)0xdeadbeef);
290 srcdest_rnode_prefixes(rn
, (const struct prefix
**)&dst_p
,
291 (const struct prefix
**)&src_p
);
292 memcpy(&hash_entry
[0], dst_p
, sizeof(*dst_p
));
294 memcpy(&hash_entry
[1], src_p
, sizeof(*src_p
));
296 memset(&hash_entry
[1], 0, sizeof(hash_entry
[1]));
298 if (hash_lookup(test
->log
, hash_entry
) == NULL
)
299 test_failed(test
, "Route is missing in hash", dst_p
,
303 /* Verify that all added elements are still in the table */
304 hash_iterate(test
->log
, verify_log
, test
);
307 static void get_rand_prefix(struct prng
*prng
, struct prefix_ipv6
*p
)
311 memset(p
, 0, sizeof(*p
));
313 for (i
= 0; i
< 4; i
++)
314 p
->prefix
.s6_addr32
[i
] = prng_rand(prng
);
315 p
->prefixlen
= prng_rand(prng
) % 129;
316 p
->family
= AF_INET6
;
321 static void get_rand_prefix_pair(struct prng
*prng
, struct prefix_ipv6
*dst_p
,
322 struct prefix_ipv6
*src_p
)
324 get_rand_prefix(prng
, dst_p
);
325 if ((prng_rand(prng
) % 4) == 0) {
326 get_rand_prefix(prng
, src_p
);
327 if (src_p
->prefixlen
)
331 memset(src_p
, 0, sizeof(*src_p
));
334 static void test_state_add_rand_route(struct test_state
*test
,
337 struct prefix_ipv6 dst_p
, src_p
;
339 get_rand_prefix_pair(prng
, &dst_p
, &src_p
);
340 test_state_add_route(test
, &dst_p
, &src_p
);
343 static void test_state_del_rand_route(struct test_state
*test
,
346 struct prefix_ipv6 dst_p
, src_p
;
348 get_rand_prefix_pair(prng
, &dst_p
, &src_p
);
349 test_state_del_route(test
, &dst_p
, &src_p
);
352 static void test_state_del_one_route(struct test_state
*test
, struct prng
*prng
)
354 unsigned int which_route
;
356 if (test
->log
->count
== 0)
359 which_route
= prng_rand(prng
) % test
->log
->count
;
361 struct route_node
*rn
;
362 const struct prefix
*dst_p
, *src_p
;
363 struct prefix_ipv6 dst6_p
, src6_p
;
365 for (rn
= route_top(test
->table
); rn
; rn
= srcdest_route_next(rn
)) {
369 route_unlock_node(rn
);
376 srcdest_rnode_prefixes(rn
, &dst_p
, &src_p
);
377 memcpy(&dst6_p
, dst_p
, sizeof(dst6_p
));
379 memcpy(&src6_p
, src_p
, sizeof(src6_p
));
381 memset(&src6_p
, 0, sizeof(src6_p
));
383 test_state_del_route(test
, &dst6_p
, &src6_p
);
386 static void run_prng_test(void)
388 struct test_state
*test
= test_state_new();
389 struct prng
*prng
= prng_new(0);
392 for (i
= 0; i
< 1000; i
++) {
393 switch (prng_rand(prng
) % 10) {
399 test_state_add_rand_route(test
, prng
);
404 test_state_del_one_route(test
, prng
);
408 test_state_del_rand_route(test
, prng
);
411 test_state_verify(test
);
415 test_state_free(test
);
418 int main(int argc
, char *argv
[])
421 printf("PRNG Test successful.\n");