Line data Source code
1 : /*
2 : * Routing Table functions.
3 : * Copyright (C) 1998 Kunihiro Ishiguro
4 : *
5 : * This file is part of GNU Zebra.
6 : *
7 : * GNU Zebra is free software; you can redistribute it and/or modify it
8 : * under the terms of the GNU General Public License as published by the
9 : * Free Software Foundation; either version 2, or (at your option) any
10 : * later version.
11 : *
12 : * GNU Zebra is distributed in the hope that it will be useful, but
13 : * WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : * General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License along
18 : * with this program; see the file COPYING; if not, write to the Free Software
19 : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 : */
21 :
22 : #define FRR_COMPILING_TABLE_C
23 :
24 : #include <zebra.h>
25 :
26 : #include "prefix.h"
27 : #include "table.h"
28 : #include "memory.h"
29 : #include "sockunion.h"
30 : #include "libfrr_trace.h"
31 :
32 22 : DEFINE_MTYPE_STATIC(LIB, ROUTE_TABLE, "Route table");
33 22 : DEFINE_MTYPE(LIB, ROUTE_NODE, "Route node");
34 :
35 : static void route_table_free(struct route_table *);
36 :
37 190 : static int route_table_hash_cmp(const struct route_node *a,
38 : const struct route_node *b)
39 : {
40 190 : return prefix_cmp(&a->p, &b->p);
41 : }
42 :
43 1376 : DECLARE_HASH(rn_hash_node, struct route_node, nodehash, route_table_hash_cmp,
44 : prefix_hash_key);
45 : /*
46 : * route_table_init_with_delegate
47 : */
48 : struct route_table *
49 42 : route_table_init_with_delegate(route_table_delegate_t *delegate)
50 : {
51 42 : struct route_table *rt;
52 :
53 42 : rt = XCALLOC(MTYPE_ROUTE_TABLE, sizeof(struct route_table));
54 42 : rt->delegate = delegate;
55 42 : rn_hash_node_init(&rt->hash);
56 42 : return rt;
57 : }
58 :
59 76 : void route_table_finish(struct route_table *rt)
60 : {
61 76 : route_table_free(rt);
62 76 : }
63 :
64 : /* Allocate new route node. */
65 96 : static struct route_node *route_node_new(struct route_table *table)
66 : {
67 96 : return table->delegate->create_node(table->delegate, table);
68 : }
69 :
70 : /* Allocate new route node with prefix set. */
71 63 : static struct route_node *route_node_set(struct route_table *table,
72 : const struct prefix *prefix)
73 : {
74 63 : struct route_node *node;
75 :
76 63 : node = route_node_new(table);
77 :
78 63 : prefix_copy(&node->p, prefix);
79 63 : node->table = table;
80 :
81 63 : rn_hash_node_add(&node->table->hash, node);
82 :
83 63 : return node;
84 : }
85 :
86 : /* Free route node. */
87 96 : static void route_node_free(struct route_table *table, struct route_node *node)
88 : {
89 96 : if (table->cleanup)
90 74 : table->cleanup(table, node);
91 96 : table->delegate->destroy_node(table->delegate, table, node);
92 96 : }
93 :
94 : /* Free route table. */
95 76 : static void route_table_free(struct route_table *rt)
96 : {
97 76 : struct route_node *tmp_node;
98 76 : struct route_node *node;
99 :
100 76 : if (rt == NULL)
101 : return;
102 :
103 42 : node = rt->top;
104 :
105 : /* Bulk deletion of nodes remaining in this table. This function is not
106 : called until workers have completed their dependency on this table.
107 : A final route_unlock_node() will not be called for these nodes. */
108 130 : while (node) {
109 106 : if (node->l_left) {
110 22 : node = node->l_left;
111 22 : continue;
112 : }
113 :
114 84 : if (node->l_right) {
115 22 : node = node->l_right;
116 22 : continue;
117 : }
118 :
119 62 : tmp_node = node;
120 62 : node = node->parent;
121 :
122 62 : tmp_node->table->count--;
123 62 : tmp_node->lock =
124 : 0; /* to cause assert if unlocked after this */
125 62 : rn_hash_node_del(&rt->hash, tmp_node);
126 62 : route_node_free(rt, tmp_node);
127 :
128 62 : if (node != NULL) {
129 44 : if (node->l_left == tmp_node)
130 22 : node->l_left = NULL;
131 : else
132 22 : node->l_right = NULL;
133 : } else {
134 : break;
135 : }
136 : }
137 :
138 42 : assert(rt->count == 0);
139 :
140 42 : rn_hash_node_fini(&rt->hash);
141 42 : XFREE(MTYPE_ROUTE_TABLE, rt);
142 42 : return;
143 : }
144 :
145 : /* Utility mask array. */
146 : static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
147 : 0xf8, 0xfc, 0xfe, 0xff};
148 :
149 : /* Common prefix route genaration. */
150 33 : static void route_common(const struct prefix *n, const struct prefix *p,
151 : struct prefix *new)
152 : {
153 33 : int i;
154 33 : uint8_t diff;
155 33 : uint8_t mask;
156 33 : const uint8_t *np;
157 33 : const uint8_t *pp;
158 33 : uint8_t *newp;
159 :
160 33 : if (n->family == AF_FLOWSPEC)
161 0 : return prefix_copy(new, p);
162 33 : np = (const uint8_t *)&n->u.prefix;
163 33 : pp = (const uint8_t *)&p->u.prefix;
164 :
165 33 : newp = &new->u.prefix;
166 :
167 96 : for (i = 0; i < p->prefixlen / 8; i++) {
168 96 : if (np[i] == pp[i])
169 63 : newp[i] = np[i];
170 : else
171 : break;
172 : }
173 :
174 33 : new->prefixlen = i * 8;
175 :
176 33 : if (new->prefixlen != p->prefixlen) {
177 33 : diff = np[i] ^ pp[i];
178 33 : mask = 0x80;
179 137 : while (new->prefixlen < p->prefixlen && !(mask & diff)) {
180 104 : mask >>= 1;
181 104 : new->prefixlen++;
182 : }
183 33 : newp[i] = np[i] & maskbit[new->prefixlen % 8];
184 : }
185 : }
186 :
187 100 : static void set_link(struct route_node *node, struct route_node *new)
188 : {
189 100 : unsigned int bit = prefix_bit(&new->p.u.prefix, node->p.prefixlen);
190 :
191 100 : node->link[bit] = new;
192 100 : new->parent = node;
193 100 : }
194 :
195 : /* Find matched prefix. */
196 97 : struct route_node *route_node_match(struct route_table *table,
197 : union prefixconstptr pu)
198 : {
199 97 : const struct prefix *p = pu.p;
200 97 : struct route_node *node;
201 97 : struct route_node *matched;
202 :
203 97 : matched = NULL;
204 97 : node = table->top;
205 :
206 : /* Walk down tree. If there is matched route then store it to
207 : matched. */
208 352 : while (node && node->p.prefixlen <= p->prefixlen
209 533 : && prefix_match(&node->p, p)) {
210 246 : if (node->info)
211 145 : matched = node;
212 :
213 246 : if (node->p.prefixlen == p->prefixlen)
214 : break;
215 :
216 181 : node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)];
217 : }
218 :
219 : /* If matched route found, return it. */
220 97 : if (matched)
221 97 : return route_lock_node(matched);
222 :
223 : return NULL;
224 : }
225 :
226 0 : struct route_node *route_node_match_ipv4(struct route_table *table,
227 : const struct in_addr *addr)
228 : {
229 0 : struct prefix_ipv4 p;
230 :
231 0 : memset(&p, 0, sizeof(p));
232 0 : p.family = AF_INET;
233 0 : p.prefixlen = IPV4_MAX_BITLEN;
234 0 : p.prefix = *addr;
235 :
236 0 : return route_node_match(table, (struct prefix *)&p);
237 : }
238 :
239 0 : struct route_node *route_node_match_ipv6(struct route_table *table,
240 : const struct in6_addr *addr)
241 : {
242 0 : struct prefix_ipv6 p;
243 :
244 0 : memset(&p, 0, sizeof(p));
245 0 : p.family = AF_INET6;
246 0 : p.prefixlen = IPV6_MAX_BITLEN;
247 0 : p.prefix = *addr;
248 :
249 0 : return route_node_match(table, &p);
250 : }
251 :
252 : /* Lookup same prefix node. Return NULL when we can't find route. */
253 113 : struct route_node *route_node_lookup(struct route_table *table,
254 : union prefixconstptr pu)
255 : {
256 113 : struct route_node rn, *node;
257 113 : prefix_copy(&rn.p, pu.p);
258 113 : apply_mask(&rn.p);
259 :
260 113 : node = rn_hash_node_find(&table->hash, &rn);
261 113 : return (node && node->info) ? route_lock_node(node) : NULL;
262 : }
263 :
264 : /* Lookup same prefix node. Return NULL when we can't find route. */
265 20 : struct route_node *route_node_lookup_maynull(struct route_table *table,
266 : union prefixconstptr pu)
267 : {
268 20 : struct route_node rn, *node;
269 20 : prefix_copy(&rn.p, pu.p);
270 20 : apply_mask(&rn.p);
271 :
272 20 : node = rn_hash_node_find(&table->hash, &rn);
273 20 : return node ? route_lock_node(node) : NULL;
274 : }
275 :
276 : /* Add node to routing table. */
277 154 : struct route_node *route_node_get(struct route_table *table,
278 : union prefixconstptr pu)
279 : {
280 154 : if (frrtrace_enabled(frr_libfrr, route_node_get)) {
281 154 : char buf[PREFIX2STR_BUFFER];
282 154 : prefix2str(pu, buf, sizeof(buf));
283 154 : frrtrace(2, frr_libfrr, route_node_get, table, buf);
284 : }
285 :
286 154 : struct route_node search;
287 154 : struct prefix *p = &search.p;
288 :
289 154 : prefix_copy(p, pu.p);
290 154 : apply_mask(p);
291 :
292 154 : struct route_node *new;
293 154 : struct route_node *node;
294 154 : struct route_node *match;
295 154 : uint16_t prefixlen = p->prefixlen;
296 154 : const uint8_t *prefix = &p->u.prefix;
297 :
298 154 : node = rn_hash_node_find(&table->hash, &search);
299 154 : if (node && node->info)
300 71 : return route_lock_node(node);
301 :
302 83 : match = NULL;
303 83 : node = table->top;
304 239 : while (node && node->p.prefixlen <= prefixlen
305 329 : && prefix_match(&node->p, p)) {
306 123 : if (node->p.prefixlen == prefixlen)
307 20 : return route_lock_node(node);
308 :
309 103 : match = node;
310 103 : node = node->link[prefix_bit(prefix, node->p.prefixlen)];
311 : }
312 :
313 63 : if (node == NULL) {
314 30 : new = route_node_set(table, p);
315 30 : if (match)
316 8 : set_link(match, new);
317 : else
318 22 : table->top = new;
319 : } else {
320 33 : new = route_node_new(table);
321 33 : route_common(&node->p, p, &new->p);
322 33 : new->p.family = p->family;
323 33 : new->table = table;
324 33 : set_link(new, node);
325 33 : rn_hash_node_add(&table->hash, new);
326 :
327 33 : if (match)
328 26 : set_link(match, new);
329 : else
330 7 : table->top = new;
331 :
332 33 : if (new->p.prefixlen != p->prefixlen) {
333 33 : match = new;
334 33 : new = route_node_set(table, p);
335 33 : set_link(match, new);
336 33 : table->count++;
337 : }
338 : }
339 63 : table->count++;
340 63 : route_lock_node(new);
341 :
342 63 : return new;
343 : }
344 :
345 : /* Delete node from the routing table. */
346 168 : void route_node_delete(struct route_node *node)
347 : {
348 189 : struct route_node *child;
349 189 : struct route_node *parent;
350 :
351 189 : assert(node->lock == 0);
352 189 : assert(node->info == NULL);
353 :
354 189 : if (node->l_left && node->l_right)
355 : return;
356 :
357 34 : if (node->l_left)
358 : child = node->l_left;
359 : else
360 22 : child = node->l_right;
361 :
362 34 : parent = node->parent;
363 :
364 34 : if (child)
365 15 : child->parent = parent;
366 :
367 34 : if (parent) {
368 24 : if (parent->l_left == node)
369 9 : parent->l_left = child;
370 : else
371 15 : parent->l_right = child;
372 : } else
373 10 : node->table->top = child;
374 :
375 34 : node->table->count--;
376 :
377 34 : rn_hash_node_del(&node->table->hash, node);
378 :
379 : /* WARNING: FRAGILE CODE!
380 : * route_node_free may have the side effect of free'ing the entire
381 : * table.
382 : * this is permitted only if table->count got decremented to zero above,
383 : * because in that case parent will also be NULL, so that we won't try
384 : * to
385 : * delete a now-stale parent below.
386 : *
387 : * cf. srcdest_srcnode_destroy() in zebra/zebra_rib.c */
388 :
389 34 : route_node_free(node->table, node);
390 :
391 : /* If parent node is stub then delete it also. */
392 34 : if (parent && parent->lock == 0)
393 : route_node_delete(parent);
394 : }
395 :
396 : /* Get first node and lock it. This function is useful when one wants
397 : to lookup all the node exist in the routing table. */
398 243 : struct route_node *route_top(struct route_table *table)
399 : {
400 : /* If there is no node in the routing table return NULL. */
401 243 : if (table->top == NULL)
402 : return NULL;
403 :
404 : /* Lock the top node and return it. */
405 108 : route_lock_node(table->top);
406 108 : return table->top;
407 : }
408 :
409 : /* Unlock current node and lock next node then return it. */
410 423 : struct route_node *route_next(struct route_node *node)
411 : {
412 423 : struct route_node *next;
413 423 : struct route_node *start;
414 :
415 : /* Node may be deleted from route_unlock_node so we have to preserve
416 : next node's pointer. */
417 :
418 423 : if (node->l_left) {
419 167 : next = node->l_left;
420 167 : route_lock_node(next);
421 167 : route_unlock_node(node);
422 167 : return next;
423 : }
424 256 : if (node->l_right) {
425 20 : next = node->l_right;
426 20 : route_lock_node(next);
427 20 : route_unlock_node(node);
428 20 : return next;
429 : }
430 :
431 : start = node;
432 398 : while (node->parent) {
433 303 : if (node->parent->l_left == node && node->parent->l_right) {
434 141 : next = node->parent->l_right;
435 141 : route_lock_node(next);
436 141 : route_unlock_node(start);
437 141 : return next;
438 : }
439 : node = node->parent;
440 : }
441 95 : route_unlock_node(start);
442 95 : return NULL;
443 : }
444 :
445 : /* Unlock current node and lock next node until limit. */
446 0 : struct route_node *route_next_until(struct route_node *node,
447 : const struct route_node *limit)
448 : {
449 0 : struct route_node *next;
450 0 : struct route_node *start;
451 :
452 : /* Node may be deleted from route_unlock_node so we have to preserve
453 : next node's pointer. */
454 :
455 0 : if (node->l_left) {
456 0 : next = node->l_left;
457 0 : route_lock_node(next);
458 0 : route_unlock_node(node);
459 0 : return next;
460 : }
461 0 : if (node->l_right) {
462 0 : next = node->l_right;
463 0 : route_lock_node(next);
464 0 : route_unlock_node(node);
465 0 : return next;
466 : }
467 :
468 : start = node;
469 0 : while (node->parent && node != limit) {
470 0 : if (node->parent->l_left == node && node->parent->l_right) {
471 0 : next = node->parent->l_right;
472 0 : route_lock_node(next);
473 0 : route_unlock_node(start);
474 0 : return next;
475 : }
476 : node = node->parent;
477 : }
478 0 : route_unlock_node(start);
479 0 : return NULL;
480 : }
481 :
482 0 : unsigned long route_table_count(struct route_table *table)
483 : {
484 0 : return table->count;
485 : }
486 :
487 : /**
488 : * route_node_create
489 : *
490 : * Default function for creating a route node.
491 : */
492 62 : struct route_node *route_node_create(route_table_delegate_t *delegate,
493 : struct route_table *table)
494 : {
495 62 : struct route_node *node;
496 62 : node = XCALLOC(MTYPE_ROUTE_NODE, sizeof(struct route_node));
497 62 : return node;
498 : }
499 :
500 : /**
501 : * route_node_destroy
502 : *
503 : * Default function for destroying a route node.
504 : */
505 62 : void route_node_destroy(route_table_delegate_t *delegate,
506 : struct route_table *table, struct route_node *node)
507 : {
508 62 : XFREE(MTYPE_ROUTE_NODE, node);
509 62 : }
510 :
511 : /*
512 : * Default delegate.
513 : */
514 : static route_table_delegate_t default_delegate = {
515 : .create_node = route_node_create,
516 : .destroy_node = route_node_destroy};
517 :
518 0 : route_table_delegate_t *route_table_get_default_delegate(void)
519 : {
520 0 : return &default_delegate;
521 : }
522 :
523 : /*
524 : * route_table_init
525 : */
526 26 : struct route_table *route_table_init(void)
527 : {
528 26 : return route_table_init_with_delegate(&default_delegate);
529 : }
530 :
531 : /**
532 : * route_table_prefix_iter_cmp
533 : *
534 : * Compare two prefixes according to the order in which they appear in
535 : * an iteration over a tree.
536 : *
537 : * @return -1 if p1 occurs before p2 (p1 < p2)
538 : * 0 if the prefixes are identical (p1 == p2)
539 : * +1 if p1 occurs after p2 (p1 > p2)
540 : */
541 0 : int route_table_prefix_iter_cmp(const struct prefix *p1,
542 : const struct prefix *p2)
543 : {
544 0 : struct prefix common_space;
545 0 : struct prefix *common = &common_space;
546 :
547 0 : if (p1->prefixlen <= p2->prefixlen) {
548 0 : if (prefix_match(p1, p2)) {
549 :
550 : /*
551 : * p1 contains p2, or is equal to it.
552 : */
553 0 : return (p1->prefixlen == p2->prefixlen) ? 0 : -1;
554 : }
555 : } else {
556 :
557 : /*
558 : * Check if p2 contains p1.
559 : */
560 0 : if (prefix_match(p2, p1))
561 : return 1;
562 : }
563 :
564 0 : route_common(p1, p2, common);
565 0 : assert(common->prefixlen < p1->prefixlen);
566 0 : assert(common->prefixlen < p2->prefixlen);
567 :
568 : /*
569 : * Both prefixes are longer than the common prefix.
570 : *
571 : * We need to check the bit after the common prefixlen to determine
572 : * which one comes later.
573 : */
574 0 : if (prefix_bit(&p1->u.prefix, common->prefixlen)) {
575 :
576 : /*
577 : * We branch to the right to get to p1 from the common prefix.
578 : */
579 0 : assert(!prefix_bit(&p2->u.prefix, common->prefixlen));
580 : return 1;
581 : }
582 :
583 : /*
584 : * We branch to the right to get to p2 from the common prefix.
585 : */
586 0 : assert(prefix_bit(&p2->u.prefix, common->prefixlen));
587 : return -1;
588 : }
589 :
590 : /*
591 : * route_get_subtree_next
592 : *
593 : * Helper function that returns the first node that follows the nodes
594 : * in the sub-tree under 'node' in iteration order.
595 : */
596 : static struct route_node *route_get_subtree_next(struct route_node *node)
597 : {
598 0 : while (node->parent) {
599 0 : if (node->parent->l_left == node && node->parent->l_right)
600 : return node->parent->l_right;
601 :
602 : node = node->parent;
603 : }
604 :
605 : return NULL;
606 : }
607 :
608 : /**
609 : * route_table_get_next_internal
610 : *
611 : * Helper function to find the node that occurs after the given prefix in
612 : * order of iteration.
613 : *
614 : * @see route_table_get_next
615 : */
616 : static struct route_node *
617 0 : route_table_get_next_internal(struct route_table *table,
618 : const struct prefix *p)
619 : {
620 0 : struct route_node *node, *tmp_node;
621 0 : int cmp;
622 :
623 0 : node = table->top;
624 :
625 0 : while (node) {
626 0 : int match;
627 :
628 0 : if (node->p.prefixlen < p->prefixlen)
629 0 : match = prefix_match(&node->p, p);
630 : else
631 0 : match = prefix_match(p, &node->p);
632 :
633 0 : if (match) {
634 0 : if (node->p.prefixlen == p->prefixlen) {
635 :
636 : /*
637 : * The prefix p exists in the tree, just return
638 : * the next
639 : * node.
640 : */
641 0 : route_lock_node(node);
642 0 : node = route_next(node);
643 0 : if (node)
644 0 : route_unlock_node(node);
645 :
646 0 : return (node);
647 : }
648 :
649 0 : if (node->p.prefixlen > p->prefixlen) {
650 :
651 : /*
652 : * Node is in the subtree of p, and hence
653 : * greater than p.
654 : */
655 0 : return node;
656 : }
657 :
658 : /*
659 : * p is in the sub-tree under node.
660 : */
661 0 : tmp_node = node->link[prefix_bit(&p->u.prefix,
662 : node->p.prefixlen)];
663 :
664 0 : if (tmp_node) {
665 0 : node = tmp_node;
666 0 : continue;
667 : }
668 :
669 : /*
670 : * There are no nodes in the direction where p should
671 : * be. If
672 : * node has a right child, then it must be greater than
673 : * p.
674 : */
675 0 : if (node->l_right)
676 : return node->l_right;
677 :
678 : /*
679 : * No more children to follow, go upwards looking for
680 : * the next
681 : * node.
682 : */
683 0 : return route_get_subtree_next(node);
684 : }
685 :
686 : /*
687 : * Neither node prefix nor 'p' contains the other.
688 : */
689 0 : cmp = route_table_prefix_iter_cmp(&node->p, p);
690 0 : if (cmp > 0) {
691 :
692 : /*
693 : * Node follows p in iteration order. Return it.
694 : */
695 : return node;
696 : }
697 :
698 0 : assert(cmp < 0);
699 :
700 : /*
701 : * Node and the subtree under it come before prefix p in
702 : * iteration order. Prefix p and its sub-tree are not present in
703 : * the tree. Go upwards and find the first node that follows the
704 : * subtree. That node will also succeed p.
705 : */
706 0 : return route_get_subtree_next(node);
707 : }
708 :
709 : return NULL;
710 : }
711 :
712 : /**
713 : * route_table_get_next
714 : *
715 : * Find the node that occurs after the given prefix in order of
716 : * iteration.
717 : */
718 0 : struct route_node *route_table_get_next(struct route_table *table,
719 : union prefixconstptr pu)
720 : {
721 0 : const struct prefix *p = pu.p;
722 0 : struct route_node *node;
723 :
724 0 : node = route_table_get_next_internal(table, p);
725 0 : if (node) {
726 0 : assert(route_table_prefix_iter_cmp(&node->p, p) > 0);
727 0 : route_lock_node(node);
728 : }
729 0 : return node;
730 : }
731 :
732 : /*
733 : * route_table_iter_init
734 : */
735 0 : void route_table_iter_init(route_table_iter_t *iter, struct route_table *table)
736 : {
737 0 : memset(iter, 0, sizeof(*iter));
738 0 : iter->state = RT_ITER_STATE_INIT;
739 0 : iter->table = table;
740 0 : }
741 :
742 : /*
743 : * route_table_iter_pause
744 : *
745 : * Pause an iteration over the table. This allows the iteration to be
746 : * resumed point after arbitrary additions/deletions from the table.
747 : * An iteration can be resumed by just calling route_table_iter_next()
748 : * on the iterator.
749 : */
750 0 : void route_table_iter_pause(route_table_iter_t *iter)
751 : {
752 0 : switch (iter->state) {
753 :
754 : case RT_ITER_STATE_INIT:
755 : case RT_ITER_STATE_PAUSED:
756 : case RT_ITER_STATE_DONE:
757 : return;
758 :
759 0 : case RT_ITER_STATE_ITERATING:
760 :
761 : /*
762 : * Save the prefix that we are currently at. The next call to
763 : * route_table_iter_next() will return the node after this
764 : * prefix
765 : * in the tree.
766 : */
767 0 : prefix_copy(&iter->pause_prefix, &iter->current->p);
768 0 : route_unlock_node(iter->current);
769 0 : iter->current = NULL;
770 0 : iter->state = RT_ITER_STATE_PAUSED;
771 0 : return;
772 :
773 : default:
774 0 : assert(0);
775 : }
776 : }
777 :
778 : /*
779 : * route_table_iter_cleanup
780 : *
781 : * Release any resources held by the iterator.
782 : */
783 0 : void route_table_iter_cleanup(route_table_iter_t *iter)
784 : {
785 0 : if (iter->state == RT_ITER_STATE_ITERATING) {
786 0 : route_unlock_node(iter->current);
787 0 : iter->current = NULL;
788 : }
789 0 : assert(!iter->current);
790 :
791 : /*
792 : * Set the state to RT_ITER_STATE_DONE to make any
793 : * route_table_iter_next() calls on this iterator return NULL.
794 : */
795 0 : iter->state = RT_ITER_STATE_DONE;
796 0 : }
|