blob: fc9cc3dd9d977add551dc8349d5c7368cde0782c [file] [log] [blame]
Avneesh Sachdev28971c82012-08-17 08:19:50 -07001/* $QuaggaId: Format:%an, %ai, %h$ $
2 *
3 * Routing table test
4 * Copyright (C) 2012 OSR.
5 *
6 * This file is part of Quagga
7 *
8 * Quagga is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * Quagga is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Quagga; see the file COPYING. If not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 * 02111-1307, USA.
22 */
23
24#include <zebra.h>
25
26#include "prefix.h"
27#include "table.h"
28
29/*
30 * test_node_t
31 *
32 * Information that is kept for each node in the radix tree.
33 */
34typedef struct test_node_t_
35{
36
37 /*
38 * Human readable representation of the string. Allocated using
39 * malloc()/dup().
40 */
41 char *prefix_str;
42} test_node_t;
43
44struct thread_master *master;
45
46/*
47 * add_node
48 *
49 * Add the given prefix (passed in as a string) to the given table.
50 */
51static void
52add_node (struct route_table *table, const char *prefix_str)
53{
54 struct prefix_ipv4 p;
55 test_node_t *node;
56 struct route_node *rn;
57
58 assert (prefix_str);
59
60 if (str2prefix_ipv4 (prefix_str, &p) <= 0)
61 {
62 assert (0);
63 }
64
65 rn = route_node_get (table, (struct prefix *) &p);
66 if (rn->info)
67 {
68 assert (0);
69 return;
70 }
71
72 node = malloc (sizeof (test_node_t));
73 assert (node);
74 node->prefix_str = strdup (prefix_str);
75 assert (node->prefix_str);
76 rn->info = node;
77}
78
79/*
80 * add_nodes
81 *
82 * Convenience function to add a bunch of nodes together.
83 *
84 * The arguments must be prefixes in string format, with a NULL as the
85 * last argument.
86 */
87static void
88add_nodes (struct route_table *table, ...)
89{
90 va_list arglist;
91 char *prefix;
92
93 va_start (arglist, table);
94
95 prefix = va_arg (arglist, char *);
96 while (prefix)
97 {
98 add_node (table, prefix);
99 prefix = va_arg (arglist, char *);
100 }
101
102 va_end (arglist);
103}
104
105/*
106 * print_subtree
107 *
108 * Recursive function to print a route node and its children.
109 *
110 * @see print_table
111 */
112static void
113print_subtree (struct route_node *rn, const char *legend, int indent_level)
114{
115 char buf[INET_ADDRSTRLEN + 4];
116 int i;
117
118 /*
119 * Print this node first.
120 */
121 for (i = 0; i < indent_level; i++)
122 {
123 printf (" ");
124 }
125
126 prefix2str (&rn->p, buf, sizeof (buf));
127 printf ("%s: %s", legend, buf);
128 if (!rn->info)
129 {
130 printf (" (internal)");
131 }
132 printf ("\n");
133 if (rn->l_left)
134 {
135 print_subtree (rn->l_left, "Left", indent_level + 1);
136 }
137 if (rn->l_right)
138 {
139 print_subtree (rn->l_right, "Right", indent_level + 1);
140 }
141}
142
143/*
144 * print_table
145 *
146 * Function that prints out the internal structure of a route table.
147 */
148static void
149print_table (struct route_table *table)
150{
151 struct route_node *rn;
152
153 rn = table->top;
154
155 if (!rn)
156 {
157 printf ("<Empty Table>\n");
158 return;
159 }
160
161 print_subtree (rn, "Top", 0);
162}
163
164/*
165 * clear_table
166 *
167 * Remove all nodes from the given table.
168 */
169static void
170clear_table (struct route_table *table)
171{
172 route_table_iter_t iter;
173 struct route_node *rn;
174 test_node_t *node;
175
176 route_table_iter_init (&iter, table);
177
178 while ((rn = route_table_iter_next (&iter)))
179 {
180 node = rn->info;
181 if (!node)
182 {
183 continue;
184 }
185 rn->info = NULL;
186 route_unlock_node (rn);
187 free (node->prefix_str);
188 free (node);
189 }
190
191 route_table_iter_cleanup (&iter);
192
193 assert (table->top == NULL);
194}
195
196/*
197 * verify_next_by_iterating
198 *
199 * Iterate over the tree to make sure that the first prefix after
200 * target_pfx is the expected one. Note that target_pfx may not be
201 * present in the tree.
202 */
203static void
204verify_next_by_iterating (struct route_table *table,
205 struct prefix *target_pfx, struct prefix *next_pfx)
206{
207 route_table_iter_t iter;
208 struct route_node *rn;
209
210 route_table_iter_init (&iter, table);
211 while ((rn = route_table_iter_next (&iter)))
212 {
213 if (route_table_prefix_iter_cmp (&rn->p, target_pfx) > 0)
214 {
215 assert (!prefix_cmp (&rn->p, next_pfx));
216 break;
217 }
218 }
219
220 if (!rn)
221 {
222 assert (!next_pfx);
223 }
224
225 route_table_iter_cleanup (&iter);
226}
227
228/*
229 * verify_next
230 *
231 * Verifies that route_table_get_next() returns the expected result
232 * (result) for the prefix string 'target'.
233 */
234static void
235verify_next (struct route_table *table, const char *target, const char *next)
236{
237 struct prefix_ipv4 target_pfx, next_pfx;
238 struct route_node *rn;
239 char result_buf[INET_ADDRSTRLEN + 4];
240
241 if (str2prefix_ipv4 (target, &target_pfx) <= 0)
242 {
243 assert (0);
244 }
245
246 rn = route_table_get_next (table, (struct prefix *) &target_pfx);
247 if (rn)
248 {
249 prefix2str (&rn->p, result_buf, sizeof (result_buf));
250 }
251 else
252 {
253 snprintf (result_buf, sizeof (result_buf), "(Null)");
254 }
255
256 printf ("\n");
257 print_table (table);
258 printf ("Verifying successor of %s. Expected: %s, Result: %s\n", target,
259 next ? next : "(Null)", result_buf);
260
261 if (!rn)
262 {
263 assert (!next);
264 verify_next_by_iterating (table, (struct prefix *) &target_pfx, NULL);
265 return;
266 }
267
268 assert (next);
269
270 if (str2prefix_ipv4 (next, &next_pfx) <= 0)
271 {
272 assert (0);
273 }
274
275 if (prefix_cmp (&rn->p, (struct prefix *) &next_pfx))
276 {
277 assert (0);
278 }
279 route_unlock_node (rn);
280
281 verify_next_by_iterating (table, (struct prefix *) &target_pfx,
282 (struct prefix *) &next_pfx);
283}
284
285/*
286 * test_get_next
287 */
288static void
289test_get_next (void)
290{
291 struct route_table *table;
292
293 printf ("\n\nTesting route_table_get_next()\n");
294 table = route_table_init ();
295
296 /*
297 * Target exists in tree, but has no successor.
298 */
299 add_nodes (table, "1.0.1.0/24", NULL);
300 verify_next (table, "1.0.1.0/24", NULL);
301 clear_table (table);
302
303 /*
304 * Target exists in tree, and there is a node in its left subtree.
305 */
306 add_nodes (table, "1.0.1.0/24", "1.0.1.0/25", NULL);
307 verify_next (table, "1.0.1.0/24", "1.0.1.0/25");
308 clear_table (table);
309
310 /*
311 * Target exists in tree, and there is a node in its right subtree.
312 */
313 add_nodes (table, "1.0.1.0/24", "1.0.1.128/25", NULL);
314 verify_next (table, "1.0.1.0/24", "1.0.1.128/25");
315 clear_table (table);
316
317 /*
318 * Target exists in the tree, next node is outside subtree.
319 */
320 add_nodes (table, "1.0.1.0/24", "1.1.0.0/16", NULL);
321 verify_next (table, "1.0.1.0/24", "1.1.0.0/16");
322 clear_table (table);
323
324 /*
325 * The target node does not exist in the tree for all the test cases
326 * below this point.
327 */
328
329 /*
330 * There is no successor in the tree.
331 */
332 add_nodes (table, "1.0.0.0/16", NULL);
333 verify_next (table, "1.0.1.0/24", NULL);
334 clear_table (table);
335
336 /*
337 * There exists a node that would be in the target's left subtree.
338 */
339 add_nodes (table, "1.0.0.0/16", "1.0.1.0/25", NULL);
340 verify_next (table, "1.0.1.0/24", "1.0.1.0/25");
341 clear_table (table);
342
343 /*
344 * There exists a node would be in the target's right subtree.
345 */
346 add_nodes (table, "1.0.0.0/16", "1.0.1.128/25", NULL);
347 verify_next (table, "1.0.1.0/24", "1.0.1.128/25");
348 clear_table (table);
349
350 /*
351 * A search for the target reaches a node where there are no child
352 * nodes in the direction of the target (left), but the node has a
353 * right child.
354 */
355 add_nodes (table, "1.0.0.0/16", "1.0.128.0/17", NULL);
356 verify_next (table, "1.0.0.0/17", "1.0.128.0/17");
357 clear_table (table);
358
359 /*
360 * A search for the target reaches a node with no children. We have
361 * to go upwards in the tree to find a successor.
362 */
363 add_nodes (table, "1.0.0.0/16", "1.0.0.0/24", "1.0.1.0/24",
364 "1.0.128.0/17", NULL);
365 verify_next (table, "1.0.1.0/25", "1.0.128.0/17");
366 clear_table (table);
367
368 /*
369 * A search for the target reaches a node where neither the node nor
370 * the target prefix contain each other.
371 *
372 * In first case below the node succeeds the target.
373 *
374 * In the second case, the node comes before the target, so we have
375 * to go up the tree looking for a successor.
376 */
377 add_nodes (table, "1.0.0.0/16", "1.0.1.0/24", NULL);
378 verify_next (table, "1.0.0.0/24", "1.0.1.0/24");
379 clear_table (table);
380
381 add_nodes (table, "1.0.0.0/16", "1.0.0.0/24", "1.0.1.0/25",
382 "1.0.128.0/17", NULL);
383 verify_next (table, "1.0.1.128/25", "1.0.128.0/17");
384 clear_table (table);
385
386 route_table_finish (table);
387}
388
389/*
390 * verify_prefix_iter_cmp
391 */
392static void
393verify_prefix_iter_cmp (const char *p1, const char *p2, int exp_result)
394{
395 struct prefix_ipv4 p1_pfx, p2_pfx;
396 int result;
397
398 if (str2prefix_ipv4 (p1, &p1_pfx) <= 0)
399 {
400 assert (0);
401 }
402
403 if (str2prefix_ipv4 (p2, &p2_pfx) <= 0)
404 {
405 assert (0);
406 }
407
408 result = route_table_prefix_iter_cmp ((struct prefix *) &p1_pfx,
409 (struct prefix *) &p2_pfx);
410
411 printf ("Verifying cmp(%s, %s) returns %d\n", p1, p2, exp_result);
412
413 assert (exp_result == result);
414
415 /*
416 * Also check the reverse comparision.
417 */
418 result = route_table_prefix_iter_cmp ((struct prefix *) &p2_pfx,
419 (struct prefix *) &p1_pfx);
420
421 if (exp_result)
422 {
423 exp_result = -exp_result;
424 }
425
426 printf ("Verifying cmp(%s, %s) returns %d\n", p1, p2, exp_result);
427 assert (result == exp_result);
428}
429
430/*
431 * test_prefix_iter_cmp
432 *
433 * Tests comparision of prefixes according to order of iteration.
434 */
435static void
436test_prefix_iter_cmp ()
437{
438 printf ("\n\nTesting route_table_prefix_iter_cmp()\n");
439
440 verify_prefix_iter_cmp ("1.0.0.0/8", "1.0.0.0/8", 0);
441
442 verify_prefix_iter_cmp ("1.0.0.0/8", "1.0.0.0/16", -1);
443
444 verify_prefix_iter_cmp ("1.0.0.0/16", "1.128.0.0/16", -1);
445}
446
447/*
448 * verify_iter_with_pause
449 *
450 * Iterates over a tree using two methods: 'normal' iteration, and an
451 * iterator that pauses at each node. Verifies that the two methods
452 * yield the same results.
453 */
454static void
455verify_iter_with_pause (struct route_table *table)
456{
457 unsigned long num_nodes;
458 struct route_node *rn, *iter_rn;
459 route_table_iter_t iter_space;
460 route_table_iter_t *iter = &iter_space;
461
462 route_table_iter_init (iter, table);
463 num_nodes = 0;
464
465 for (rn = route_top (table); rn; rn = route_next (rn))
466 {
467 num_nodes++;
468 route_table_iter_pause (iter);
469
470 assert (iter->current == NULL);
471 if (route_table_iter_started (iter))
472 {
473 assert (iter->state == RT_ITER_STATE_PAUSED);
474 }
475 else
476 {
477 assert (rn == table->top);
478 assert (iter->state == RT_ITER_STATE_INIT);
479 }
480
481 iter_rn = route_table_iter_next (iter);
482
483 /*
484 * Make sure both iterations return the same node.
485 */
486 assert (rn == iter_rn);
487 }
488
489 assert (num_nodes == route_table_count (table));
490
491 route_table_iter_pause (iter);
492 iter_rn = route_table_iter_next (iter);
493
494 assert (iter_rn == NULL);
495 assert (iter->state == RT_ITER_STATE_DONE);
496
497 assert (route_table_iter_next (iter) == NULL);
498 assert (iter->state == RT_ITER_STATE_DONE);
499
500 route_table_iter_cleanup (iter);
501
502 print_table (table);
503 printf ("Verified pausing iteration on tree with %lu nodes\n", num_nodes);
504}
505
506/*
507 * test_iter_pause
508 */
509static void
510test_iter_pause (void)
511{
512 struct route_table *table;
513 int i, num_prefixes;
514 const char *prefixes[] = {
515 "1.0.1.0/24",
516 "1.0.1.0/25",
517 "1.0.1.128/25",
518 "1.0.2.0/24",
519 "2.0.0.0/8"
520 };
521
522 num_prefixes = sizeof (prefixes) / sizeof (prefixes[0]);
523
524 printf ("\n\nTesting that route_table_iter_pause() works as expected\n");
525 table = route_table_init ();
526 for (i = 0; i < num_prefixes; i++)
527 {
528 add_nodes (table, prefixes[i], NULL);
529 }
530
531 verify_iter_with_pause (table);
532
533 clear_table (table);
534 route_table_finish (table);
535}
536
537/*
538 * run_tests
539 */
540static void
541run_tests (void)
542{
543 test_prefix_iter_cmp ();
544 test_get_next ();
545 test_iter_pause ();
546}
547
548/*
549 * main
550 */
551int
552main (void)
553{
554 run_tests ();
555}