Coverage Report

Created: 2025-07-14 06:48

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