version 1.1.1.1, 2012/02/21 17:26:12
|
version 1.1.1.2, 2013/07/21 23:54:39
|
Line 27
|
Line 27
|
#include "memory.h" |
#include "memory.h" |
#include "sockunion.h" |
#include "sockunion.h" |
|
|
void route_node_delete (struct route_node *); | static void route_node_delete (struct route_node *); |
void route_table_free (struct route_table *); | static void route_table_free (struct route_table *); |
|
|
|
|
|
/* |
|
* route_table_init_with_delegate |
|
*/ |
struct route_table * |
struct route_table * |
route_table_init (void) | route_table_init_with_delegate (route_table_delegate_t *delegate) |
{ |
{ |
struct route_table *rt; |
struct route_table *rt; |
|
|
rt = XCALLOC (MTYPE_ROUTE_TABLE, sizeof (struct route_table)); |
rt = XCALLOC (MTYPE_ROUTE_TABLE, sizeof (struct route_table)); |
|
rt->delegate = delegate; |
return rt; |
return rt; |
} |
} |
|
|
Line 47 route_table_finish (struct route_table *rt)
|
Line 52 route_table_finish (struct route_table *rt)
|
|
|
/* Allocate new route node. */ |
/* Allocate new route node. */ |
static struct route_node * |
static struct route_node * |
route_node_new (void) | route_node_new (struct route_table *table) |
{ |
{ |
struct route_node *node; | return table->delegate->create_node (table->delegate, table); |
node = XCALLOC (MTYPE_ROUTE_NODE, sizeof (struct route_node)); | |
return node; | |
} |
} |
|
|
/* Allocate new route node with prefix set. */ |
/* Allocate new route node with prefix set. */ |
Line 60 route_node_set (struct route_table *table, struct pref
|
Line 63 route_node_set (struct route_table *table, struct pref
|
{ |
{ |
struct route_node *node; |
struct route_node *node; |
|
|
node = route_node_new (); | node = route_node_new (table); |
|
|
prefix_copy (&node->p, prefix); |
prefix_copy (&node->p, prefix); |
node->table = table; |
node->table = table; |
Line 70 route_node_set (struct route_table *table, struct pref
|
Line 73 route_node_set (struct route_table *table, struct pref
|
|
|
/* Free route node. */ |
/* Free route node. */ |
static void |
static void |
route_node_free (struct route_node *node) | route_node_free (struct route_table *table, struct route_node *node) |
{ |
{ |
XFREE (MTYPE_ROUTE_NODE, node); | table->delegate->destroy_node (table->delegate, table, node); |
} |
} |
|
|
/* Free route table. */ |
/* Free route table. */ |
void | static void |
route_table_free (struct route_table *rt) |
route_table_free (struct route_table *rt) |
{ |
{ |
struct route_node *tmp_node; |
struct route_node *tmp_node; |
Line 87 route_table_free (struct route_table *rt)
|
Line 90 route_table_free (struct route_table *rt)
|
|
|
node = rt->top; |
node = rt->top; |
|
|
|
/* Bulk deletion of nodes remaining in this table. This function is not |
|
called until workers have completed their dependency on this table. |
|
A final route_unlock_node() will not be called for these nodes. */ |
while (node) |
while (node) |
{ |
{ |
if (node->l_left) |
if (node->l_left) |
Line 104 route_table_free (struct route_table *rt)
|
Line 110 route_table_free (struct route_table *rt)
|
tmp_node = node; |
tmp_node = node; |
node = node->parent; |
node = node->parent; |
|
|
|
tmp_node->table->count--; |
|
tmp_node->lock = 0; /* to cause assert if unlocked after this */ |
|
route_node_free (rt, tmp_node); |
|
|
if (node != NULL) |
if (node != NULL) |
{ |
{ |
if (node->l_left == tmp_node) |
if (node->l_left == tmp_node) |
node->l_left = NULL; |
node->l_left = NULL; |
else |
else |
node->l_right = NULL; |
node->l_right = NULL; |
|
|
route_node_free (tmp_node); |
|
} |
} |
else |
else |
{ |
{ |
route_node_free (tmp_node); |
|
break; |
break; |
} |
} |
} |
} |
|
|
|
assert (rt->count == 0); |
|
|
XFREE (MTYPE_ROUTE_TABLE, rt); |
XFREE (MTYPE_ROUTE_TABLE, rt); |
return; |
return; |
} |
} |
Line 186 route_lock_node (struct route_node *node)
|
Line 195 route_lock_node (struct route_node *node)
|
void |
void |
route_unlock_node (struct route_node *node) |
route_unlock_node (struct route_node *node) |
{ |
{ |
|
assert (node->lock > 0); |
node->lock--; |
node->lock--; |
|
|
if (node->lock == 0) |
if (node->lock == 0) |
Line 255 route_node_match_ipv6 (const struct route_table *table
|
Line 265 route_node_match_ipv6 (const struct route_table *table
|
|
|
/* Lookup same prefix node. Return NULL when we can't find route. */ |
/* Lookup same prefix node. Return NULL when we can't find route. */ |
struct route_node * |
struct route_node * |
route_node_lookup (struct route_table *table, struct prefix *p) | route_node_lookup (const struct route_table *table, struct prefix *p) |
{ |
{ |
struct route_node *node; |
struct route_node *node; |
|
u_char prefixlen = p->prefixlen; |
|
const u_char *prefix = &p->u.prefix; |
|
|
node = table->top; |
node = table->top; |
|
|
while (node && node->p.prefixlen <= p->prefixlen && | while (node && node->p.prefixlen <= prefixlen && |
prefix_match (&node->p, p)) |
prefix_match (&node->p, p)) |
{ |
{ |
if (node->p.prefixlen == p->prefixlen) | if (node->p.prefixlen == prefixlen) |
return node->info ? route_lock_node (node) : NULL; |
return node->info ? route_lock_node (node) : NULL; |
|
|
node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; | node = node->link[prefix_bit(prefix, node->p.prefixlen)]; |
} |
} |
|
|
return NULL; |
return NULL; |
Line 275 route_node_lookup (struct route_table *table, struct p
|
Line 287 route_node_lookup (struct route_table *table, struct p
|
|
|
/* Add node to routing table. */ |
/* Add node to routing table. */ |
struct route_node * |
struct route_node * |
route_node_get (struct route_table *table, struct prefix *p) | route_node_get (struct route_table *const table, struct prefix *p) |
{ |
{ |
struct route_node *new; |
struct route_node *new; |
struct route_node *node; |
struct route_node *node; |
struct route_node *match; |
struct route_node *match; |
|
u_char prefixlen = p->prefixlen; |
|
const u_char *prefix = &p->u.prefix; |
|
|
match = NULL; |
match = NULL; |
node = table->top; |
node = table->top; |
while (node && node->p.prefixlen <= p->prefixlen && | while (node && node->p.prefixlen <= prefixlen && |
prefix_match (&node->p, p)) |
prefix_match (&node->p, p)) |
{ |
{ |
if (node->p.prefixlen == p->prefixlen) | if (node->p.prefixlen == prefixlen) |
return route_lock_node (node); |
return route_lock_node (node); |
| |
match = node; |
match = node; |
node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; | node = node->link[prefix_bit(prefix, node->p.prefixlen)]; |
} |
} |
|
|
if (node == NULL) |
if (node == NULL) |
Line 303 route_node_get (struct route_table *table, struct pref
|
Line 317 route_node_get (struct route_table *table, struct pref
|
} |
} |
else |
else |
{ |
{ |
new = route_node_new (); | new = route_node_new (table); |
route_common (&node->p, p, &new->p); |
route_common (&node->p, p, &new->p); |
new->p.family = p->family; |
new->p.family = p->family; |
new->table = table; |
new->table = table; |
Line 319 route_node_get (struct route_table *table, struct pref
|
Line 333 route_node_get (struct route_table *table, struct pref
|
match = new; |
match = new; |
new = route_node_set (table, p); |
new = route_node_set (table, p); |
set_link (match, new); |
set_link (match, new); |
|
table->count++; |
} |
} |
} |
} |
|
table->count++; |
route_lock_node (new); |
route_lock_node (new); |
|
|
return new; |
return new; |
} |
} |
|
|
/* Delete node from the routing table. */ |
/* Delete node from the routing table. */ |
void | static void |
route_node_delete (struct route_node *node) |
route_node_delete (struct route_node *node) |
{ |
{ |
struct route_node *child; |
struct route_node *child; |
Line 359 route_node_delete (struct route_node *node)
|
Line 375 route_node_delete (struct route_node *node)
|
else |
else |
node->table->top = child; |
node->table->top = child; |
|
|
route_node_free (node); | node->table->count--; |
|
|
|
route_node_free (node->table, node); |
|
|
/* If parent node is stub then delete it also. */ |
/* If parent node is stub then delete it also. */ |
if (parent && parent->lock == 0) |
if (parent && parent->lock == 0) |
route_node_delete (parent); |
route_node_delete (parent); |
Line 460 route_next_until (struct route_node *node, struct rout
|
Line 478 route_next_until (struct route_node *node, struct rout
|
} |
} |
route_unlock_node (start); |
route_unlock_node (start); |
return NULL; |
return NULL; |
|
} |
|
|
|
unsigned long |
|
route_table_count (const struct route_table *table) |
|
{ |
|
return table->count; |
|
} |
|
|
|
/** |
|
* route_node_create |
|
* |
|
* Default function for creating a route node. |
|
*/ |
|
static struct route_node * |
|
route_node_create (route_table_delegate_t *delegate, |
|
struct route_table *table) |
|
{ |
|
struct route_node *node; |
|
node = XCALLOC (MTYPE_ROUTE_NODE, sizeof (struct route_node)); |
|
return node; |
|
} |
|
|
|
/** |
|
* route_node_destroy |
|
* |
|
* Default function for destroying a route node. |
|
*/ |
|
static void |
|
route_node_destroy (route_table_delegate_t *delegate, |
|
struct route_table *table, struct route_node *node) |
|
{ |
|
XFREE (MTYPE_ROUTE_NODE, node); |
|
} |
|
|
|
/* |
|
* Default delegate. |
|
*/ |
|
static route_table_delegate_t default_delegate = { |
|
.create_node = route_node_create, |
|
.destroy_node = route_node_destroy |
|
}; |
|
|
|
/* |
|
* route_table_init |
|
*/ |
|
struct route_table * |
|
route_table_init (void) |
|
{ |
|
return route_table_init_with_delegate (&default_delegate); |
|
} |
|
|
|
/** |
|
* route_table_prefix_iter_cmp |
|
* |
|
* Compare two prefixes according to the order in which they appear in |
|
* an iteration over a tree. |
|
* |
|
* @return -1 if p1 occurs before p2 (p1 < p2) |
|
* 0 if the prefixes are identical (p1 == p2) |
|
* +1 if p1 occurs after p2 (p1 > p2) |
|
*/ |
|
int |
|
route_table_prefix_iter_cmp (struct prefix *p1, struct prefix *p2) |
|
{ |
|
struct prefix common_space; |
|
struct prefix *common = &common_space; |
|
|
|
if (p1->prefixlen <= p2->prefixlen) |
|
{ |
|
if (prefix_match (p1, p2)) |
|
{ |
|
|
|
/* |
|
* p1 contains p2, or is equal to it. |
|
*/ |
|
return (p1->prefixlen == p2->prefixlen) ? 0 : -1; |
|
} |
|
} |
|
else |
|
{ |
|
|
|
/* |
|
* Check if p2 contains p1. |
|
*/ |
|
if (prefix_match (p2, p1)) |
|
return 1; |
|
} |
|
|
|
route_common (p1, p2, common); |
|
assert (common->prefixlen < p1->prefixlen); |
|
assert (common->prefixlen < p2->prefixlen); |
|
|
|
/* |
|
* Both prefixes are longer than the common prefix. |
|
* |
|
* We need to check the bit after the common prefixlen to determine |
|
* which one comes later. |
|
*/ |
|
if (prefix_bit (&p1->u.prefix, common->prefixlen)) |
|
{ |
|
|
|
/* |
|
* We branch to the right to get to p1 from the common prefix. |
|
*/ |
|
assert (!prefix_bit (&p2->u.prefix, common->prefixlen)); |
|
return 1; |
|
} |
|
|
|
/* |
|
* We branch to the right to get to p2 from the common prefix. |
|
*/ |
|
assert (prefix_bit (&p2->u.prefix, common->prefixlen)); |
|
return -1; |
|
} |
|
|
|
/* |
|
* route_get_subtree_next |
|
* |
|
* Helper function that returns the first node that follows the nodes |
|
* in the sub-tree under 'node' in iteration order. |
|
*/ |
|
static struct route_node * |
|
route_get_subtree_next (struct route_node *node) |
|
{ |
|
while (node->parent) |
|
{ |
|
if (node->parent->l_left == node && node->parent->l_right) |
|
return node->parent->l_right; |
|
|
|
node = node->parent; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
/** |
|
* route_table_get_next_internal |
|
* |
|
* Helper function to find the node that occurs after the given prefix in |
|
* order of iteration. |
|
* |
|
* @see route_table_get_next |
|
*/ |
|
static struct route_node * |
|
route_table_get_next_internal (const struct route_table *table, |
|
struct prefix *p) |
|
{ |
|
struct route_node *node, *tmp_node; |
|
u_char prefixlen; |
|
int cmp; |
|
|
|
prefixlen = p->prefixlen; |
|
|
|
node = table->top; |
|
|
|
while (node) |
|
{ |
|
int match; |
|
|
|
if (node->p.prefixlen < p->prefixlen) |
|
match = prefix_match (&node->p, p); |
|
else |
|
match = prefix_match (p, &node->p); |
|
|
|
if (match) |
|
{ |
|
if (node->p.prefixlen == p->prefixlen) |
|
{ |
|
|
|
/* |
|
* The prefix p exists in the tree, just return the next |
|
* node. |
|
*/ |
|
route_lock_node (node); |
|
node = route_next (node); |
|
if (node) |
|
route_unlock_node (node); |
|
|
|
return (node); |
|
} |
|
|
|
if (node->p.prefixlen > p->prefixlen) |
|
{ |
|
|
|
/* |
|
* Node is in the subtree of p, and hence greater than p. |
|
*/ |
|
return node; |
|
} |
|
|
|
/* |
|
* p is in the sub-tree under node. |
|
*/ |
|
tmp_node = node->link[prefix_bit (&p->u.prefix, node->p.prefixlen)]; |
|
|
|
if (tmp_node) |
|
{ |
|
node = tmp_node; |
|
continue; |
|
} |
|
|
|
/* |
|
* There are no nodes in the direction where p should be. If |
|
* node has a right child, then it must be greater than p. |
|
*/ |
|
if (node->l_right) |
|
return node->l_right; |
|
|
|
/* |
|
* No more children to follow, go upwards looking for the next |
|
* node. |
|
*/ |
|
return route_get_subtree_next (node); |
|
} |
|
|
|
/* |
|
* Neither node prefix nor 'p' contains the other. |
|
*/ |
|
cmp = route_table_prefix_iter_cmp (&node->p, p); |
|
if (cmp > 0) |
|
{ |
|
|
|
/* |
|
* Node follows p in iteration order. Return it. |
|
*/ |
|
return node; |
|
} |
|
|
|
assert (cmp < 0); |
|
|
|
/* |
|
* Node and the subtree under it come before prefix p in |
|
* iteration order. Prefix p and its sub-tree are not present in |
|
* the tree. Go upwards and find the first node that follows the |
|
* subtree. That node will also succeed p. |
|
*/ |
|
return route_get_subtree_next (node); |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
/** |
|
* route_table_get_next |
|
* |
|
* Find the node that occurs after the given prefix in order of |
|
* iteration. |
|
*/ |
|
struct route_node * |
|
route_table_get_next (const struct route_table *table, struct prefix *p) |
|
{ |
|
struct route_node *node; |
|
|
|
node = route_table_get_next_internal (table, p); |
|
if (node) |
|
{ |
|
assert (route_table_prefix_iter_cmp (&node->p, p) > 0); |
|
route_lock_node (node); |
|
} |
|
return node; |
|
} |
|
|
|
/* |
|
* route_table_iter_init |
|
*/ |
|
void |
|
route_table_iter_init (route_table_iter_t * iter, struct route_table *table) |
|
{ |
|
memset (iter, 0, sizeof (*iter)); |
|
iter->state = RT_ITER_STATE_INIT; |
|
iter->table = table; |
|
} |
|
|
|
/* |
|
* route_table_iter_pause |
|
* |
|
* Pause an iteration over the table. This allows the iteration to be |
|
* resumed point after arbitrary additions/deletions from the table. |
|
* An iteration can be resumed by just calling route_table_iter_next() |
|
* on the iterator. |
|
*/ |
|
void |
|
route_table_iter_pause (route_table_iter_t * iter) |
|
{ |
|
switch (iter->state) |
|
{ |
|
|
|
case RT_ITER_STATE_INIT: |
|
case RT_ITER_STATE_PAUSED: |
|
case RT_ITER_STATE_DONE: |
|
return; |
|
|
|
case RT_ITER_STATE_ITERATING: |
|
|
|
/* |
|
* Save the prefix that we are currently at. The next call to |
|
* route_table_iter_next() will return the node after this prefix |
|
* in the tree. |
|
*/ |
|
prefix_copy (&iter->pause_prefix, &iter->current->p); |
|
route_unlock_node (iter->current); |
|
iter->current = NULL; |
|
iter->state = RT_ITER_STATE_PAUSED; |
|
return; |
|
|
|
default: |
|
assert (0); |
|
} |
|
|
|
} |
|
|
|
/* |
|
* route_table_iter_cleanup |
|
* |
|
* Release any resources held by the iterator. |
|
*/ |
|
void |
|
route_table_iter_cleanup (route_table_iter_t * iter) |
|
{ |
|
if (iter->state == RT_ITER_STATE_ITERATING) |
|
{ |
|
route_unlock_node (iter->current); |
|
iter->current = NULL; |
|
} |
|
assert (!iter->current); |
|
|
|
/* |
|
* Set the state to RT_ITER_STATE_DONE to make any |
|
* route_table_iter_next() calls on this iterator return NULL. |
|
*/ |
|
iter->state = RT_ITER_STATE_DONE; |
} |
} |