modules/rx/rx_node.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- rx_creat_node
- rx_delete_node
- rx_free_list_element
- RX_bin_node
- RX_route_node
- RX_inum_node
- RX_asc_node
/***************************************
$Revision: 1.18 $
Radix tree (rx). rx_node.c - functions to operate on nodes of the tree
(creation/deletion).
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 <rxroutines.h>
#include <memwrap.h>
#include <stubs.h>
#include <glib.h>
#include <comparisons.h>
/***************************************************************************/
/*++++++++++++++++
rx_creat_node = create a new data node
(empty{glue} nodes get created automatically).
Takes a pointer to the (already allocated) data leaf to be included
in the list of data nodes (presumably empty as the node is only now being
created).
Requires a stack of nodes created in CREAT mode (with glue nodes,
until deep enough and the last node being non-glue).
MT notes: requires the tree to be locked.
Returns: RX_OK or error code.
+++++++++++++++++*/
static
er_ret_t
rx_creat_node (
/* [<][>][^][v][top][bottom][index][help] */
ip_prefix_t *newpref, /*+ prefix of the node to be added +*/
rx_tree_t *tree, /*+ tree the new node goes to +*/
rx_dataleaf_t *dataleaf, /*+ dataleaf to attach at this node+*/
rx_nodcpy_t stack[], /*+ stack==array of node_copies +*/
int stackdepth /*+ length of the stack +*/
)
{
rx_node_t *newnode, *curnode, *memnode, *gluenode;
int chk_bit, dif_bit, link, curpos;
char buf[1024];
er_ret_t err;
// assume no such node yet. Will die if there is one.
// calloc, because parent/child keys and child ptrs are not always set.
if( (err=wr_calloc( (void **) & newnode, 1, sizeof(rx_node_t))) != UT_OK) {
return err;
}
// increment the number of nodes in the tree
tree -> num_nodes ++;
newnode -> prefix = *newpref;
// attach the leaf to a (presumably empty?! hence NULL) list...
newnode->leaves_ptr = g_list_prepend(NULL, dataleaf);
newnode->glue = 0;
// OK, so take a look at the tree
if ( tree -> num_nodes == 1 ) {
// The tree was empty. Create a new top node.
tree -> top_ptr = newnode;
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, "Created as the top node");
return RX_OK;
}
// OK, there is at least one node in the tree. Take a look at the stack.
// we've got a real node there (not a glue), but we may be too deep.
// (it's not a glue, because glues have always two children.
// we had to go that deep because from a glue alone one doesn't know
// what it glues)
// GO UP.
// take the first differing bit from comparing
// the new and the found nodes' prefixes.
// (not deeper than the shorter of the two)
curpos = stackdepth-1;
curnode = & stack[curpos].cpy;
chk_bit = smaller(curnode->prefix.bits, newpref->bits );
for(dif_bit = 0; dif_bit < chk_bit; dif_bit++) {
// break the loop when the first different bit is found
if( IP_addr_bit_get( & curnode->prefix.ip, dif_bit)
!= IP_addr_bit_get( & newpref->ip, dif_bit) ) {
break;
}
}
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET,
"cur = %d, new = %d, chk_bit = %d, dif_bit = %d",
curnode->prefix.bits, newpref->bits, chk_bit, dif_bit );
if(dif_bit == IP_sizebits(newpref->ip.space)) die; // it mustn't happen!!!
// go up to that level (watch the head of the tree!)
while( curpos > 0 && stack[curpos-1].cpy.prefix.bits >= dif_bit) {
curpos--;
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET,
"up to level %d", curpos );
}
/*
if the bit lenghts of the node, new prefix and the diffbit are equal
{
YOU'VE GOT THE NODE where the new one will be attached.
Either it has data (and will be moved accordingly),
or is a glue (and will be turned into a regular node).
}
*/
curnode = & stack[curpos].cpy;
// RAM: set a pointer to the real node in memory
memnode = stack[curpos].srcptr;
if( dif_bit == newpref->bits
&& dif_bit == curnode->prefix.bits ) {
// such node already exists, nothing to change in the tree!!!
// this should be checked before calling this function, so..
die;
}
/*
else ** the branch ends here; we must create a new node... **
{
OK, how is the new node's prefix length w.r.t the dif_bit ?
longer -> make it a child of the node found
shorter -> make it the parent of the node found and take its place
equal -> make a glue node the parent of both
}
WHEN ATTACHING THE NODE, VALUES FROM THE STACK ARE USED,
TO PREVENT EXCESSIVE LOOKUPS AGAIN.
*/
else {
// **** attach it.
if( ER_is_traced(FAC_RX, ASP_RX_NODCRE_DET) ) {
rx_nod_print(curnode, buf, 1024);
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "Looking at node %s", buf);
}
if( curnode -> prefix.bits == dif_bit ) {
// attach here as a child of the node found
link = IP_addr_bit_get( &newpref->ip, dif_bit );
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, "attaching as child %d", link);
if( memnode -> child_ptr[link] != NULL ) {
die;
}
memnode -> child_ptr[link] = newnode;
newnode -> parent_ptr = memnode;
}
else if ( newpref->bits == dif_bit ) {
// make it the parent of the node found and take its place,
// moving it down.
// set the link from the NEW node to the OLD one (different than before)
link = IP_addr_bit_get( &curnode->prefix.ip, dif_bit );
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, "shifting down as child %d", link);
// PARENT<->NEW LINKS
// see if the node was the top_node
if (curnode -> parent_ptr == NULL) {
// update tree struct
tree -> top_ptr = newnode;
} else {
// no - fix the child link at the parent.
// at the link where it was attached
int link = (curnode->parent_ptr->child_ptr[1] == memnode);
memnode -> parent_ptr -> child_ptr[link] = newnode;
}
memnode -> parent_ptr = newnode;
// NEW<->CHILD LINKS
newnode -> parent_ptr = curnode->parent_ptr;
newnode -> child_ptr[link] = memnode;
}
else {
// create a glue and shift the curnode below the glue,
// then attach the new node at the glue
// calloc, because parent/child keys are not set.
if( (err=wr_calloc( (void **)& gluenode, 1, sizeof(rx_node_t))) != UT_OK) {
return err; // die;
}
tree -> num_nodes ++;
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, "created glue node at %p", gluenode);
gluenode -> prefix.bits = dif_bit;
// fill in the address. The glue node should get the prefix
// shorter by one than the shorter of the two prefixes that are glued
// (difbit)
//
gluenode -> prefix.ip = newpref->ip;
gluenode -> prefix.bits = dif_bit;
// the ip in this prefix is probably incorrect. Fix it.
IP_pref_bit_fix( & gluenode -> prefix );
gluenode -> leaves_ptr = NULL;
gluenode -> glue = 1;
// 1. Fix the link to and from the parent to the gluenode.
gluenode -> parent_ptr = curnode->parent_ptr;
if (gluenode->parent_ptr == NULL) {
tree -> top_ptr = gluenode;
}
else {
// fix the child link in the parent.
// if it was at 1, then let fix the link 1, 0 otherwise
link = (curnode->parent_ptr->child_ptr[1] == memnode);
memnode->parent_ptr->child_ptr[link] = gluenode;
}
// 2. Fix the links between gluenode and the OLD node
link = IP_addr_bit_get( &newpref->ip, dif_bit );
gluenode -> child_ptr[ ! link ] = memnode;
memnode->parent_ptr = gluenode;
// 3. Fix the links between gluenode and the NEW node
gluenode -> child_ptr[ link ] = newnode;
newnode -> parent_ptr = gluenode;
}
return RX_OK;
}
die;
return -1; //this is just to calm down the compiler
}
/******************************************************************
an auxiliary function to delete data from a node
(and delete the node or turn it into a glue afterwards)
takes
tree tree
curnode pointer to the node
dataleaf pointer to a dataleaf with ObjectID (dataleaf->data_key)
set; which is used to choose the right dataleaf
when browsing data leaves.
suceeds always or dies when dataleaf with such data cannot be found
in the node
*/
void
rx_delete_node (rx_tree_t *tree, rx_node_t *curnode, rx_dataleaf_t *dataleaf)
/* [<][>][^][v][top][bottom][index][help] */
{
rx_dataleaf_t *leaffound = NULL;
GList *qitem;
int leavesum=0;
/* go through leaves, comparing the objectID (data_key) */
for( qitem = g_list_first(curnode->leaves_ptr);
qitem != NULL;
qitem = g_list_next(qitem)) {
rx_dataleaf_t *leafptr = qitem->data;
if( leafptr->data_key == dataleaf->data_key ) {
leaffound = leafptr;
/* no break - we're counting leaves..*/
}
leavesum++;
}
/* return error if none of the dataleaves matched */
if( leaffound == NULL ) die;
/* NO error? good. Remove the leaf from the list */
curnode->leaves_ptr = g_list_remove ( curnode->leaves_ptr, leaffound );
/* if not >composed< then delete dataleaf */
if( leaffound->composed == 0 ) {
wr_free(leaffound);
}
/* else decrement the reference number ( == number of prefixes
composing the range minus 1 == the >composed< flag */
else {
leaffound->composed--;
}
/* if that was the last leave at this node, then delete node. */
if( leavesum == 1 ) {
rx_node_t *parent = curnode->parent_ptr;
assert(curnode->leaves_ptr == NULL);
/* To do this, check the number of children: */
/* 0 - just delete this node and the link to it */
if( curnode->child_ptr[0] == NULL && curnode->child_ptr[1] == NULL ) {
if( parent != NULL ) { /* watch the head! */
int plink = (parent->child_ptr[1] == curnode);
parent->child_ptr[plink] = NULL;
}
else {
assert(tree->top_ptr == curnode);
tree->top_ptr = NULL;
}
tree->num_nodes--;
wr_free(curnode);
/* very good :-) now if we deleted curnode, let's see if the parent node is a glue.
If it is, then hook the remaining child up the grandparent,
and delete the parent */
if( parent != NULL && parent->glue ) {
int slink = (parent->child_ptr[1] != NULL );
rx_node_t *schild = parent->child_ptr[slink];
rx_node_t *gparent = parent->parent_ptr;
assert( schild != NULL && parent->child_ptr[ ! slink] == NULL);
/* upd parent */
if( gparent != NULL ) { /* watch the head! */
int plink = (gparent->child_ptr[1] == parent);
gparent->child_ptr[plink] = parent->child_ptr[slink];
} else {
assert(tree->top_ptr == parent);
tree->top_ptr = parent->child_ptr[slink];
}
/* update the child's parent link too */
parent->child_ptr[slink]->parent_ptr = gparent;
/* del */
tree->num_nodes--;
wr_free(parent);
} /* if parent glue */
}
/* 2 - turn into a glue */
else if( curnode->child_ptr[0] != NULL
&& curnode->child_ptr[1] != NULL ) {
curnode->glue = 1;
}
/* 1 - copy the child's link to parent. then delete */
else {
int clink = (curnode->child_ptr[1] != NULL );
/* upd parent */
if( parent != NULL ) { /* watch the head! */
int plink = (parent->child_ptr[1] == curnode);
parent->child_ptr[plink] = curnode->child_ptr[clink];
}
/* update the child's parent link too */
curnode->child_ptr[clink]->parent_ptr = parent;
/* del */
tree->num_nodes--;
wr_free(curnode);
}
} /* leavesum == 1 <=> that was the last data leaf */
} /* rx_delete_node */
/***************************************************************************/
/*+ hook for g_list_foreach to free a list element +*/
void
rx_free_list_element(void *cpy, void *trash)
/* [<][>][^][v][top][bottom][index][help] */
{
wr_free(cpy);
}
/***************************************************************************/
/*+++++++++++++++++++
General function to operate on dataleaves attached to a single node
(create / modify / delete).
searches tree, finds and creates (modifies/deletes) a node,
copies modified nodes to disk using rx_sql_node_set (not yet implemented).
Updates memory rollback info.
Add a dataleaf at the node defined by prefix.
Create a new node if it doesn't exist yet.
MT notes: requires the tree to be locked.
Returns: RX_OK or error code.
Errors from:
rx_bin_search,
memory alloc routines.
- no such node (if not in create mode)
- too many nodes found (strange).
+++++++++++++++++*/
/*static*/
er_ret_t
RX_bin_node (
/* [<][>][^][v][top][bottom][index][help] */
rx_oper_mt mode, /*+ MODE={cre|mod|del} +*/
ip_prefix_t *newpref, /*+ prefix of the node +*/
rx_tree_t *tree, /*+ pointer to the tree structure +*/
rx_dataleaf_t *dataleaf /*+ dataleaf to attach at the node +*/
)
{
GList *nodlist = NULL;
int nodesfound, stackdepth;
int glue;
rx_nodcpy_t *curcpy;
rx_node_t *curnode;
rx_nodcpy_t *stack;
er_ret_t err;
char bbf[IP_PREFSTR_MAX];
if( ER_is_traced( FAC_RX, ASP_RX_NODCRE_BOT)) {
IP_pref_b2a( newpref , bbf, IP_PREFSTR_MAX);
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT,
"rx_bin_node: new %s in spc %d /fam %d /reg %d",
bbf, tree->space, tree->family, tree->reg_id);
}
// first check: are we using the correct tree ???
if( tree->space != newpref->ip.space ) {
/* trying to insert a prefix of space %d into a tree of space %d\n",
tree->space,
newpref->ip.space);
*/
die;
}
assert( dataleaf );
assert( newpref->bits <= IP_sizebits(tree->space) );
// fix the prefix, to make sure all insignificant bits are 0
IP_pref_bit_fix( newpref );
if( (err=wr_malloc( (void **) &stack,
sizeof(rx_nodcpy_t) * IP_sizebits(tree->space))) != UT_OK) {
return err; //die;
}
if( (err=rx_build_stack(stack, &stackdepth,
tree, newpref, RX_STK_CREAT) != RX_OK )) {
return err; //die
}
// rx_stk_print(stack, stackdepth);
// perform a search on the stack. The result is a list, and it must
// be properly deleted after use!!
if( rx_nod_search(RX_SRCH_CREAT, 0, 0,
tree, newpref, stack, stackdepth,
&nodlist, RX_ANS_ALL) != RX_OK ) {
return err; // die;
}
// count number of nodes in the answer
nodesfound = g_list_length (nodlist);
switch( nodesfound ) {
case 0:
/* no such node (yet). See what we're up to.
if( mode==cre ) create, else - program error, die */
if( mode != RX_OPER_CRE) {
die;
}
/* C R E A T I O N */
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT,
"Creating a new node ");
rx_creat_node( newpref, tree, dataleaf, stack, stackdepth );
break;
case 1: /* found */
/* set the curnode pointer */
curcpy = g_list_nth_data(nodlist, 0);
curnode = curcpy->srcptr;
switch( mode ) {
case RX_OPER_CRE:
// attach the data at the node that was found;
// was it glue ?
glue = curnode->glue;
curnode->leaves_ptr = g_list_prepend(curnode->leaves_ptr, dataleaf);
/* now it's not a glue anymore */
curnode->glue = 0;
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, "Appended data to a %s node",
glue ? "glue" : "data");
break;
case RX_OPER_MOD:
/* put new data in place of old - not used (
(the object ID and primary keys stay the same) */
break;
case RX_OPER_DEL:
rx_delete_node( tree, curnode, dataleaf);
break;
}
break;
default:
/* too many nodes found! from an exact/exact-less-1 search.
this cannot happen. Call Ghostbusters now.
*/
die;
}
g_list_foreach(nodlist, rx_free_list_element, NULL);
wr_free(stack);
return RX_OK;
}
/* ++++++++++++++++
A wrapper around RX_bin_node.
It's there only to control the freeing of dataleaf copies passed
for comparison during deletion.
+++++++++++++++++*/
er_ret_t
RX_route_node (
/* [<][>][^][v][top][bottom][index][help] */
rx_oper_mt mode, /*+ MODE={cre|mod|del} +*/
ip_prefix_t *newpref, /*+ prefix of the node +*/
rx_tree_t *tree, /*+ pointer to the tree structure +*/
rx_dataleaf_t *dataleaf /*+ dataleaf to attach at the node +*/
)
{
er_ret_t reterr;
reterr = RX_bin_node(mode, newpref, tree, dataleaf);
if( mode == RX_OPER_DEL ) { /* free the dataleaf copy AND the data */
wr_free( dataleaf->data_ptr );
wr_free( dataleaf );
}
return reterr;
}
/***************************************************************************/
/*+++++++++++++++
performs the actual update for inetnums (possibly composed of many prefixes).
Decomposes the ranges into prefixes and then falls back to rx_bin_node
to perform changes at the nodes.
Requires/returns - practically the same as rx_bin_node.
++++++++++++++++*/
er_ret_t
RX_inum_node( rx_oper_mt mode, /*+ MODE={cre|mod|del} +*/
/* [<][>][^][v][top][bottom][index][help] */
ip_range_t *rang, /*+ range of IP addresses +*/
rx_tree_t *tree, /*+ pointer to the tree structure +*/
rx_dataleaf_t *leafptr /*+ dataleaf to attach at the node +*/
)
{
int i, prefcount;
GList *preflist = NULL;
char buf[IP_RANGSTR_MAX];
if( ER_is_traced( FAC_RX, ASP_RX_NODCRE_BOT)) {
IP_rang_b2a(rang, buf, IP_RANGSTR_MAX );
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT,
"rx_inum_node: adding %s", buf);
}
// decompose, put links to the data leaf into every prefix
// that makes up this range.
IP_rang_decomp(rang, &preflist);
// see if there is more than 1 prefix, set the composed flag
prefcount = g_list_length(preflist);
leafptr->composed = (prefcount - 1) ;
leafptr->iprange = *rang;
for(i=0; i < prefcount; i++) {
ip_prefix_t *mypref = g_list_nth_data(preflist, i);
RX_bin_node(mode, mypref, tree, leafptr);
}
// free the storage from decomposition
g_list_foreach(preflist, rx_free_list_element, NULL);
g_list_free(preflist);
return RX_OK;
}
/***************************************************************************/
/*+++++++++++++++
translates ranges/prefixes into binary prefixes.
finds tree, locks it.
initiates memory rollback structure (???)
builds a dataleaf and puts into the node(s),
calling rx_bin_node for every prefix.
checks rollback condition and (possibly) rolls back ???
MT-note: locks/unlocks the tree.
Possible errors
- all errors from:
ip_asc_2_bin,
rx_get_tree,
rx_bin_node,
wr_free
+++++++++++++++++*/
er_ret_t
RX_asc_node ( rx_oper_mt mode, /*+ MODE={cre|mod|del} +*/
/* [<][>][^][v][top][bottom][index][help] */
char *rangstr, /*+ string prefix/range/IP +*/
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) +*/
void *data /*+ pointer to the payload +*/
)
{
/*
For creation of a new node:
READ-LOCK THE FOREST
get the root tree for this space (rx_get_tree)
got it ? good. No ? error!!!
Check if any of the prefixes spans more than one subtree...
Check if they all exist already..
if any is missing
then
WRITE-LOCK THE FOREST
fi
for all missing subtrees
create missing trees
rof
UNLOCK THE FOREST
**now start writing the data:**
put *data* records in memory and sql table
for all matchind [sub]trees (in order of the list)
WRITE-LOCK the in-memory [sub]tree
WRITE-LOCK the sql-table for it
for(all prefixes in memory that match this tree)
create a node in the tree pointing to the data
rof
UNLOCK the tree
rof
*/
ip_range_t myrang;
ip_prefix_t mypref;
rx_dataleaf_t *leafptr;
rx_tree_t *mytree;
int rang_ok;
if( RX_get_tree ( &mytree, reg_id, spc_id, fam_id) != RX_OK ) {
die;
}
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT,
"rx_asc_node: inserting object %s", rangstr);
// set the data leaf values
if( wr_calloc( (void **)& leafptr, sizeof(rx_dataleaf_t), 1)
!= UT_OK) {
die;
}
leafptr->data_ptr = data;
switch( fam_id )
{
case RX_FAM_IN:
rang_ok = 1;
if( IP_rang_e2b(&myrang, rangstr) == IP_OK ) {
// that's nice. everything is set.
} else {
// see if's a valid IP, maybe it's an IPv4 classful range
if( IP_addr_e2b( &myrang.begin, rangstr ) == IP_OK ) {
if( IP_rang_classful( &myrang , &myrang.begin ) != IP_OK ) {
rang_ok = 0;
}
}
else {
// sorry. we don't accept that.
rang_ok = 0;
}
}
if( rang_ok == 1 ) {
return RX_inum_node( mode, &myrang, mytree, leafptr );
}
// else: fall through to the end of the function. (unrecognized arg)
break;
case RX_FAM_RT:
if( IP_pref_e2b(&mypref, rangstr) == IP_OK ) {
return RX_bin_node(RX_OPER_CRE, &mypref, mytree, leafptr);
}
}
ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT,
"can't understand the key, discarding the OBJECT.");
wr_free(data);
wr_free(leafptr);
return RX_BADKEY;
}