]>
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 event_loop
*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_and_free(&test
->log
, log_free
);
125 XFREE(MTYPE_TMP
, test
);
128 static void test_state_add_route(struct test_state
*test
,
129 struct prefix_ipv6
*dst_p
,
130 struct prefix_ipv6
*src_p
)
132 struct route_node
*rn
=
133 srcdest_rnode_get(test
->table
, (struct prefix
*)dst_p
, src_p
);
134 struct prefix hash_entry
[2];
136 memset(hash_entry
, 0, sizeof(hash_entry
));
137 memcpy(&hash_entry
[0], dst_p
, sizeof(*dst_p
));
138 memcpy(&hash_entry
[1], src_p
, sizeof(*src_p
));
141 route_unlock_node(rn
);
142 assert(hash_lookup(test
->log
, hash_entry
) != NULL
);
145 assert(hash_lookup(test
->log
, hash_entry
) == NULL
);
148 rn
->info
= (void *)0xdeadbeef;
149 (void)hash_get(test
->log
, hash_entry
, log_alloc
);
152 static void test_state_del_route(struct test_state
*test
,
153 struct prefix_ipv6
*dst_p
,
154 struct prefix_ipv6
*src_p
)
156 struct route_node
*rn
= srcdest_rnode_lookup(
157 test
->table
, (struct prefix
*)dst_p
, src_p
);
158 struct prefix hash_entry
[2];
160 memset(hash_entry
, 0, sizeof(hash_entry
));
161 memcpy(&hash_entry
[0], dst_p
, sizeof(*dst_p
));
162 memcpy(&hash_entry
[1], src_p
, sizeof(*src_p
));
165 assert(!hash_lookup(test
->log
, hash_entry
));
169 assert(rn
->info
== (void *)0xdeadbeef);
171 route_unlock_node(rn
);
172 route_unlock_node(rn
);
174 struct prefix
*hash_entry_intern
= hash_release(test
->log
, hash_entry
);
175 assert(hash_entry_intern
!= NULL
);
176 XFREE(MTYPE_TMP
, hash_entry_intern
);
179 static void verify_log(struct hash_bucket
*bucket
, void *arg
)
181 struct test_state
*test
= arg
;
182 struct prefix
*hash_entry
= bucket
->data
;
183 struct prefix
*dst_p
= &hash_entry
[0];
184 struct prefix_ipv6
*src_p
= (struct prefix_ipv6
*)&hash_entry
[1];
185 struct route_node
*rn
= srcdest_rnode_lookup(test
->table
, dst_p
, src_p
);
188 assert(rn
->info
== (void *)0xdeadbeef);
190 route_unlock_node(rn
);
193 static void dump_log(struct hash_bucket
*bucket
, void *arg
)
195 struct prefix
*hash_entry
= bucket
->data
;
196 struct prefix_ipv6
*dst_p
= (struct prefix_ipv6
*)&hash_entry
[0];
197 struct prefix_ipv6
*src_p
= (struct prefix_ipv6
*)&hash_entry
[1];
198 char *route_id
= format_srcdest(dst_p
, src_p
);
200 fprintf(stderr
, " %s\n", route_id
);
204 static void test_dump(struct test_state
*test
)
206 fprintf(stderr
, "Contents of hash table:\n");
207 hash_iterate(test
->log
, dump_log
, test
);
208 fprintf(stderr
, "\n");
211 static void test_failed(struct test_state
*test
, const char *message
,
212 const struct prefix_ipv6
*dst_p
,
213 const struct prefix_ipv6
*src_p
)
215 char *route_id
= format_srcdest(dst_p
, src_p
);
217 fprintf(stderr
, "Test failed. Error: %s\n", message
);
218 fprintf(stderr
, "Route in question: %s\n", route_id
);
225 static void test_state_verify(struct test_state
*test
)
227 struct route_node
*rn
;
228 struct prefix hash_entry
[2];
230 memset(hash_entry
, 0, sizeof(hash_entry
));
232 /* Verify that there are no elements in the table which have never
234 for (rn
= route_top(test
->table
); rn
; rn
= srcdest_route_next(rn
)) {
235 const struct prefix_ipv6
*dst_p
, *src_p
;
237 /* While we are iterating, we hold a lock on the current
239 * so all the lock counts we check for take that into account;
241 * state all the numbers will be exactly one less.
243 * Also this makes quite some assumptions based on the current
244 * implementation details of route_table and srcdest_table -
246 * valid implementation might trigger assertions here.
249 if (rnode_is_dstnode(rn
)) {
250 struct srcdest_rnode
*srn
= (struct srcdest_rnode
*)rn
;
251 unsigned int expected_lock
= 1; /* We are in the loop */
254 != NULL
) /* The route node is not internal */
256 if (srn
->src_table
!= NULL
) /* There's a source table
257 associated with rn */
260 if (route_node_get_lock_count(rn
) != expected_lock
)
263 "Dest rnode lock count doesn't match expected count!",
264 (struct prefix_ipv6
*)&rn
->p
, NULL
);
266 unsigned int expected_lock
= 1; /* We are in the loop */
269 != NULL
) /* The route node is not internal */
272 if (route_node_get_lock_count(rn
) != expected_lock
) {
273 srcdest_rnode_prefixes(
274 rn
, (const struct prefix
**)&dst_p
,
275 (const struct prefix
**)&src_p
);
279 "Src rnode lock count doesn't match expected count!",
287 assert(rn
->info
== (void *)0xdeadbeef);
289 srcdest_rnode_prefixes(rn
, (const struct prefix
**)&dst_p
,
290 (const struct prefix
**)&src_p
);
291 memcpy(&hash_entry
[0], dst_p
, sizeof(*dst_p
));
293 memcpy(&hash_entry
[1], src_p
, sizeof(*src_p
));
295 memset(&hash_entry
[1], 0, sizeof(hash_entry
[1]));
297 if (hash_lookup(test
->log
, hash_entry
) == NULL
)
298 test_failed(test
, "Route is missing in hash", dst_p
,
302 /* Verify that all added elements are still in the table */
303 hash_iterate(test
->log
, verify_log
, test
);
306 static void get_rand_prefix(struct prng
*prng
, struct prefix_ipv6
*p
)
310 memset(p
, 0, sizeof(*p
));
312 for (i
= 0; i
< 4; i
++)
313 p
->prefix
.s6_addr32
[i
] = prng_rand(prng
);
314 p
->prefixlen
= prng_rand(prng
) % 129;
315 p
->family
= AF_INET6
;
320 static void get_rand_prefix_pair(struct prng
*prng
, struct prefix_ipv6
*dst_p
,
321 struct prefix_ipv6
*src_p
)
323 get_rand_prefix(prng
, dst_p
);
324 if ((prng_rand(prng
) % 4) == 0) {
325 get_rand_prefix(prng
, src_p
);
326 if (src_p
->prefixlen
)
330 memset(src_p
, 0, sizeof(*src_p
));
333 static void test_state_add_rand_route(struct test_state
*test
,
336 struct prefix_ipv6 dst_p
, src_p
;
338 get_rand_prefix_pair(prng
, &dst_p
, &src_p
);
339 test_state_add_route(test
, &dst_p
, &src_p
);
342 static void test_state_del_rand_route(struct test_state
*test
,
345 struct prefix_ipv6 dst_p
, src_p
;
347 get_rand_prefix_pair(prng
, &dst_p
, &src_p
);
348 test_state_del_route(test
, &dst_p
, &src_p
);
351 static void test_state_del_one_route(struct test_state
*test
, struct prng
*prng
)
353 unsigned int which_route
;
355 if (test
->log
->count
== 0)
358 which_route
= prng_rand(prng
) % test
->log
->count
;
360 struct route_node
*rn
;
361 const struct prefix
*dst_p
, *src_p
;
362 struct prefix_ipv6 dst6_p
, src6_p
;
364 for (rn
= route_top(test
->table
); rn
; rn
= srcdest_route_next(rn
)) {
368 route_unlock_node(rn
);
375 srcdest_rnode_prefixes(rn
, &dst_p
, &src_p
);
376 memcpy(&dst6_p
, dst_p
, sizeof(dst6_p
));
378 memcpy(&src6_p
, src_p
, sizeof(src6_p
));
380 memset(&src6_p
, 0, sizeof(src6_p
));
382 test_state_del_route(test
, &dst6_p
, &src6_p
);
385 static void run_prng_test(void)
387 struct test_state
*test
= test_state_new();
388 struct prng
*prng
= prng_new(0);
391 for (i
= 0; i
< 1000; i
++) {
392 switch (prng_rand(prng
) % 10) {
398 test_state_add_rand_route(test
, prng
);
403 test_state_del_one_route(test
, prng
);
407 test_state_del_rand_route(test
, prng
);
410 test_state_verify(test
);
414 test_state_free(test
);
417 int main(int argc
, char *argv
[])
420 printf("PRNG Test successful.\n");