modules/rx/rx_tree.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- rx_walk_tree
- RX_get_tree
- RX_tree_cre
- RX_attach2forest
- rx_check_walk_tree
- RX_treecheck
/***************************************
$Revision: 1.14 $
Radix tree (rx). rx_tree.c - functions to operate on trees
(creation/deletion/finding).
Status: NOT REVUED, TESTED, INCOMPLETE
Design and implementation by: Marek Bukowy
******************/ /******************
Copyright (c) 1999 RIPE NCC
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of the author not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
***************************************/
#include <erroutines.h>
#include <iproutines.h>
#include <memwrap.h>
#include <stubs.h>
/***************************************************************************/
#define RX_IMPL
#include <rxroutines.h>
/***************************************************************************/
/*+++++++++
go down the tree calling func on every node.
(func takes the node pointer and the current level)
the function is called recursively with level increased
it stops recursing when no child nodes are found or maxlevel is reached.
therefore the initial call must set level to 0.
the nodecounter increments at every node, and is the return value
of the function. So start with 0 to get the number of nodes traversed.
ERROR HANDLING IS DIFFERENT HERE!
Unlike other functions it is not the return value:
The error code from the func function IF DEFINED (== not NULL ) goes
to the variable pointed to by the last parameter.
++++++++++++*/
int
rx_walk_tree(rx_node_t *node,
/* [<][>][^][v][top][bottom][index][help] */
er_ret_t (*func)(rx_node_t *node, int level, int nodecounter,
void *userptr),
rx_walk_mt walk_mode,
// controls if glue nodes are counted
// and if levels or prefix lenghts are checked
int maxlevel,
int level,
int nodecounter,
void *userptr,
er_ret_t *err)
{
int i, link, skpglue=0;
if( node == NULL ) die; // program error. we expect a valid, checked, node.
// count the node appropriately:
// if (not glue) or (it doesn't matter)
if( node->glue == 0 || (walk_mode & RX_WALK_SKPGLU) == 0 ) {
level++;
} else { /* nodeglue = 1 && walkmode&skpglue = 1 */
skpglue = 1;
}
// check the limits and maybe quit here: prefix length for RX_WALK_PRFLEN,
// level otherwise
if(walk_mode & RX_WALK_PRFLEN) {
if(node->prefix.bits > maxlevel) {
return nodecounter;
}
}
else if( level > maxlevel ) {
return nodecounter;
}
// didn't quit ?? OK, count it too...
if( skpglue == 0 ) {
nodecounter++;
}
if( func != NULL && skpglue == 0 ) {
*err = func(node, level, nodecounter, userptr);
// abort the walk on error
if( *err != RX_OK ) {
ER_dbg_va(FAC_RX, ASP_RX_TREE_WALK,
"walk_tree: func returned error %d, aborting", *err);
return nodecounter;
}
}
for(i=0; i<=1; i++) {
// reverse the sense of the walk
link = ( walk_mode & RX_WALK_REVERS ) ? ! i : i;
if( node->child_ptr[link] != NULL ) {
nodecounter += rx_walk_tree(node->child_ptr[link], func, walk_mode,
maxlevel, level, 0, userptr, err);
// abort the walk on error
if( func != NULL && *err != RX_OK ) {
break;
}
}
}
return nodecounter;
}
/***************************************************************************/
/*++++++++++++++
finds a tree matching the specified criteria(registry+space+family).
MT-note: locks/unlocks forest (still to be done)
Returns: RX_OK or RX_NOTREE if no such tree can be found.
+++++++++++*/
er_ret_t
RX_get_tree ( rx_tree_t **treeptr, /*+ answer goes here, please +*/
/* [<][>][^][v][top][bottom][index][help] */
rx_regid_t reg_id, /*+ id of the registry +*/
ip_space_t spc_id, /*+ type of space (ipv4/ipv6) +*/
rx_fam_t fam_id /*+ family of objects (route/inetnum) +*/
)
{
GList *elem = g_list_first(rx_forest);
rx_tree_t *trp;
while( elem != NULL ) {
trp = (rx_tree_t *) elem->data;
if( trp->reg_id == reg_id
&& trp->space == spc_id && trp->family == fam_id) {
/* copy the value to user's data */
*treeptr = trp;
ER_dbg_va(FAC_RX, ASP_RX_TREE_BOT, "tree found at %08x",trp);
return RX_OK;
}
elem = g_list_next(elem);
}
*treeptr = NULL; // set no NOT FOUND
return RX_NOTREE;
}
/***************************************************************************/
/*++++++
creates a (top) tree for the space, fills out sql table of trees
generates a tablename for a tree (if NONE)
updates LL of trees
MT-note: locks/unlocks the forest (still to be done)
++++++++*/
er_ret_t
RX_tree_cre (
/* [<][>][^][v][top][bottom][index][help] */
rx_regid_t reg_id, /*+ id of the registry +*/
ip_space_t spc_id, /*+ space id, one of IPv4 IPv6. +*/
rx_fam_t fam_id, /*+ family of objects (route/inetnum) +*/
char *prefixstr, /*+ prefix the tree will cover (string) +*/
rx_mem_mt mem_mode, /* memory only, memory+sql, sql only +*/
rx_subtree_mt subtrees, /*+ one of NONE, AUTO, HAND +*/
rx_tree_t **treestore /* store the tree pointer here */
)
{
er_ret_t err;
rx_tree_t *newtree;
ip_prefix_t newpref;
if( IP_pref_e2b(&newpref, prefixstr) != IP_OK ) {
die;
}
RX_get_tree ( &newtree, reg_id, spc_id, fam_id);
if ( newtree != NULL ) {
// die; /* error RX_TRALEX == tree already exists */
return RX_TRALEX;
}
if ( (err=wr_malloc( (void **) & newtree, sizeof(rx_tree_t))) != UT_OK ) {
return err; // die
}
ER_dbg_va(FAC_RX, ASP_RX_TREE_BOT, "creating a tree at %08x", newtree);
/* copy tree settings */
newtree -> reg_id = reg_id;
newtree -> space = spc_id;
newtree -> family = fam_id;
newtree -> subtrees = subtrees;
newtree -> mem_mode = mem_mode;
/* set other tree values */
/* parent set to NULL because it's not a subtree */
newtree -> parent_tree = NULL;
// PR_zeroprefix(& newtree -> prefix);
newtree -> maxbits = IP_sizebits(spc_id);
strcpy(newtree->data_table.val,"");
strcpy(newtree->radix_table.val,"");
strcpy(newtree->leaves_table.val,"");
newtree->num_nodes = 0;
newtree->top_ptr = NULL;
newtree->top_key = SQ_NOKEY;
newtree->prefix = newpref;
TH_init_read_write_lock( &(newtree->rwlock));
*treestore = newtree;
return RX_OK;
}
void RX_attach2forest(rx_tree_t *newtree) {
/* [<][>][^][v][top][bottom][index][help] */
/* put into LL of trees; handle alloc err ??? */
/* if threads are supposed to be reading already,
set forest mutex; */
rx_forest = g_list_append (rx_forest, newtree);
/* release forest mutex; */
}
/* ************************************
special walk function for use in consistency checks - it checks the parent
pointer too.
************************************/
int rx_check_walk_tree( rx_node_t *node,
/* [<][>][^][v][top][bottom][index][help] */
rx_node_t *parent_node,
int nodecounter,
rx_treecheck_t *checkstruct )
{
int i;
// checks
if( node == NULL ) {
checkstruct->code |= 1;
}
if( node->parent_ptr != parent_node ) {
checkstruct->code |= 2;
}
if( node->glue && node->leaves_ptr ) {
checkstruct->code |= 4;
}
if( node->glue && (node->child_ptr[0] == NULL || node->child_ptr[1] == NULL ) ) {
checkstruct->code |= 8;
}
if( node->leaves_ptr && checkstruct->datatoo ) {
switch( checkstruct->tree->family ) {
case RX_FAM_IP:
/* the simplest (?) case: only one leaf attached to any node
(except for glues) */
if( g_list_length(node->leaves_ptr) != 1 ) {
checkstruct->code |= 16;
}
break;
case RX_FAM_RT:
/* many dataleaves attached to nodes. */
break;
case RX_FAM_IN:
/* many dataleaves attached to nodes.
Some leaves pointed to from many nodes => from as many as the number
of composing prefixes
*/
break;
}
}
if( checkstruct->code != 0 ) {
checkstruct->node = node;
return nodecounter; // abort the walk on error
}
nodecounter++;
for(i=0; i<=1; i++) {
if( node->child_ptr[i] != NULL ) {
nodecounter += rx_check_walk_tree( node->child_ptr[i],
node,
0, checkstruct );
// abort the walk on error
if ( checkstruct->code != 0 ) {
break;
}
}
}
return nodecounter;
}
/* **************************************************************************
tree consistency check.
if datatoo = 0, then only parent/child links are checked.
if datatoo = 1, then a check on the contents of the nodes is done too.
**************************************************************************/
er_ret_t
RX_treecheck( rx_tree_t *tree, int datatoo, rx_treecheck_t *errorfound)
/* [<][>][^][v][top][bottom][index][help] */
{
er_ret_t (*hook_function)(); // pointer to the walk_hook function
er_ret_t err = RX_OK;
int nodnum;
errorfound->tree = tree;
errorfound->datatoo = datatoo;
/* errorfound.node will be set by hook if it finds an error*/
errorfound->code = 0;
nodnum = rx_check_walk_tree( tree->top_ptr,
NULL,
0,
errorfound );
if( nodnum != tree->num_nodes ) {
errorfound->code |= 1024;
}
if( tree->num_nodes == 0 && tree->top_ptr != NULL ) {
errorfound->code |= 2048;
}
if( tree->num_nodes != 0 && tree->top_ptr == NULL ) {
errorfound->code |= 4096;
}
if( errorfound->code != 0) {
err = RX_DATNOF;
}
return err;
}