#if HAVE_CONFIG_H #include #endif /* conditional compile switches */ #define SUBNET_PARANOIA FALSE /* slows down program exponentially! */ #define SUBNET_STATIC_ALLOC FALSE #define SUBNET_FIND_MEMREFCNT TRUE #define SUBNET_DUMPS_LINE_NUM FALSE /* must be defined before the first use of the macro NODE_FROM_EXTERN() */ static Subnet *first_external = NULL; /* root of radix trie */ static POINTER_DATATYPE subnet_root; /* contiguously allocated pool of tree nodes */ #if SUBNET_STATIC_ALLOC static SubnetTree subnet_pool[ MAX_TREE_NODES ]; #else static SubnetTree *subnet_pool = NULL; #endif static unsigned subnet_nodes_per_level[ 4 ]; #if SUBNET_FIND_MEMREFCNT static unsigned subnets_found_per_level[ 4 ]; #endif /* for PrintSubnets() and DumpRawSubnets() */ #define MAX_FIXUPS 100000 #define DUMP_VISITED_FLAG 1234567 static Bit32 *dump_fixup_locations[ MAX_FIXUPS ]; static Bit32 dump_fixup_values[ MAX_FIXUPS ]; static Bit32 dump_fixup_count; /* for DumpRawSubnets() */ #define DUMP_SUBNET_OFFSET 0x10000000; #define DUMP_EXTERNAL_OFFSET 0x20000000; static Bit32 subnet_base, external_base; /* does any lookup-method-specific initialization * * returns TRUE if successful, FALSE if failure */ boolean InitSubnet2( void ) { unsigned count; subnet_root = NODE_FROM_EXTERN( NULL ); /* Alpha C compiler doesn't like expressions in declarations */ for( count=0; count<4; count++ ) { subnet_nodes_per_level[ count ] = 0; subnets_found_per_level[ count ] = 0; } #if !SUBNET_STATIC_ALLOC /* allocate the memory for all possible tree nodes */ subnet_pool = (SubnetTree *) flat_alloc( MAX_TREE_NODES * sizeof( SubnetTree ) ); if( !subnet_pool ) { fprintf( stderr, "InitSubnet2() error allocating %d bytes for %d subnet nodes\n", MAX_TREE_NODES * sizeof( SubnetTree ), MAX_TREE_NODES ); return FALSE; } #endif /* !SUBNET_STATIC_ALLOC */ /* set the subnet parms base address to high-values, so it * will be overwritten by the first call to InsertSubnet() */ first_external = (Subnet *) -1; return TRUE; } /* accepts address in network byte order * * returns pointer to routing information, * or NULL if no route can be found */ Subnet *FindSubnet( register Bit32 address ) { #if !defined(__unix__) && SUBNET_FIND_TIMING && !NETRAMET PentiumClock before; #endif POINTER_DATATYPE node; address = ntohl( address ); #if !defined(__unix__) && SUBNET_FIND_TIMING && !NETRAMET /* remember when we started referencing memory */ before.set(); #endif #if BITS_PER_NODE == 8 #if BITS_PER_ROOT == 8 /* use byte 3 (msb) as first index */ node = subnet_pool[0].pointers[ address >> 24 ]; if( IS_NODE_A_LEAF( node ) ) { #if !defined(__unix__) && SUBNET_FIND_TIMING && !NETRAMET /* calculate uncalibrated elapsed time */ find_timing.set(); find_timing -= before; #endif #if SUBNET_FIND_MEMREFCNT find_refcnt = 3; subnets_found_per_level[ 3 ]++; #endif return EXTERN_FROM_NODE( node ); } /* use byte 2 (2nd msb) as 2nd index */ node = subnet_pool[node].pointers[ (address >> 16) & NODE_INDEX_MASK ]; if( IS_NODE_A_LEAF( node ) ) { #if !defined(__unix__) && SUBNET_FIND_TIMING && !NETRAMET /* calculate uncalibrated elapsed time */ find_timing.set(); find_timing -= before; #endif #if SUBNET_FIND_MEMREFCNT find_refcnt = 2; subnets_found_per_level[ 2 ]++; #endif return EXTERN_FROM_NODE( node ); } #else /* BITS_PER_ROOT != 8 */ #if BITS_PER_ROOT == 16 /* use bytes 3 (msb) and 2 (2nd msb) as first index */ node = subnet_pool[0].pointers[ address >> 16 ]; if( IS_NODE_A_LEAF( node ) ) { #if !defined(__unix__) && SUBNET_FIND_TIMING && !NETRAMET /* calculate uncalibrated elapsed time */ find_timing.set(); find_timing -= before; #endif #if SUBNET_FIND_MEMREFCNT find_refcnt = 2; subnets_found_per_level[ 2 ]++; #endif return EXTERN_FROM_NODE( node ); } #else /* BITS_PER_NODE != 16 */ # error "This function is only implemented for 8 or 16 BITS_PER_ROOT!" #endif #endif /* BITS_PER_ROOT != 8 */ /* use byte 1 (2nd lsb) as next index */ node = subnet_pool[node].pointers[ (address >> 8) & NODE_INDEX_MASK ]; if( IS_NODE_A_LEAF( node ) ) { #if !defined(__unix__) && SUBNET_FIND_TIMING && !NETRAMET /* calculate uncalibrated elapsed time */ find_timing.set(); find_timing -= before; #endif #if SUBNET_FIND_MEMREFCNT find_refcnt = 1; subnets_found_per_level[ 1 ]++; #endif return EXTERN_FROM_NODE( node ); } /* use byte 0 (lsb) as last index * * assumes node is definitely a leaf */ node = subnet_pool[node].pointers[ address & NODE_INDEX_MASK ]; #if !defined(__unix__) && SUBNET_FIND_TIMING && !NETRAMET /* calculate uncalibrated elapsed time */ find_timing.set(); find_timing -= before; #endif #if SUBNET_FIND_MEMREFCNT find_refcnt = 0; subnets_found_per_level[ 0 ]++; #endif return EXTERN_FROM_NODE( node ); #else /* BITS_PER_NODE != 8 */ #error "This function is only implemented for 8 BITS_PER_NODE!" #endif } static boolean FillSpecificSubnet( Subnet *external, POINTER_DATATYPE *pnode, Bit32 addr, int new_mask_bits, int is_insertion ) { POINTER_DATATYPE node = *pnode; Subnet *old_external; int count; int result; int old_mask_bits; char ip_string[ 30 ]; /* if this isn't a leaf... */ if( !IS_NODE_A_LEAF( node ) ) { /* change all the leaves we can find inside * which are less specific, to be my leaf */ result = FALSE; for( count=0; countmask ); else old_mask_bits = -1; if( is_insertion ) { /* if there is no old leaf or we have more bits... */ if( new_mask_bits > old_mask_bits ) /* it is safe to overwrite the pointer */ *pnode = NODE_FROM_EXTERN( external ); /* otherwise if mask and address are the same... */ else if( new_mask_bits == old_mask_bits ) /* print a warning */ printf( "subnet %s 0x%X '%s' originally inserted by line %d found; new insertion" " '%s' from line %d ignored\n", inet_ntoa(ip_string,ntohl(external->addr)), ntohl(external->mask), old_external->as_path, old_external->line, external->as_path, external->line ); } else { /* if destination address and mask don't match, * this must be some other route, so leave it alone */ if( new_mask_bits != old_mask_bits ) return FALSE; /* it is safe to overwrite the pointer */ *pnode = NODE_FROM_EXTERN( NULL ); } return TRUE; } static boolean AlterSubnetRecurse( Subnet *external, POINTER_DATATYPE *pnode, Bit32 addr, int addr_bits_left, int mask_bits_left, int level_bits, int level_pointers, int level_mask, int is_insertion ) { POINTER_DATATYPE node = *pnode; int count; int result; int original_mask_bits; unsigned index; char ip_string[ 30 ]; /* if the caller is pointing at a leaf (NIL or actual)... */ if( IS_NODE_A_LEAF( node ) ) { /* if this is an deletion... */ if( !is_insertion ) { /* this must be a more general (or no) route, so * we don't have the route they are talking about */ fprintf( stderr, "DeleteSubnet() found leaf node too soon," " so we didn't have route %s/%d\n", inet_ntoa( ip_string, ntohl( external->addr ) ), bits_in_mask( external->mask ) ); return FALSE; } /* allocate a node */ index = level_pointers / POINTERS_PER_NODE; if( subnet_node_count + index > MAX_TREE_NODES ) { fprintf( stderr, "InsertSubnet() cannot allocate a node\n" ); return FALSE; } *pnode = subnet_node_count; /* keep track of how many nodes there are of each kind */ subnet_node_count += index; subnet_nodes_per_level[ level_from_bits(addr_bits_left,level_bits) ] += index; /* all child pointers will have parent's old value */ for( count=0; count> addr_bits_left) & level_mask; /* if there are still mask bits left... */ if( mask_bits_left > 0 ) /* we just need to point at another, deeper SubnetTree */ return AlterSubnetRecurse( external, subnet_pool[node].pointers+index, addr, addr_bits_left, mask_bits_left, BITS_PER_NODE, POINTERS_PER_NODE, NODE_INDEX_MASK, is_insertion ); /* calculate how many pointers we have to alter */ count = 1 << (-mask_bits_left); /* fill in all the rest of the pointers */ original_mask_bits = MAX_ADDR_BITS + mask_bits_left - addr_bits_left; result = FALSE; while( --count >= 0 ) result |= FillSpecificSubnet( external, subnet_pool[node].pointers+index+count, addr, original_mask_bits, is_insertion ); if( !result ) fprintf( stderr, "DeleteSubnet() never found the right leaf node," " so we didn't have route %s/%d\n", inet_ntoa( ip_string, ntohl( external->addr ) ), original_mask_bits ); return result; } /* assumes Subnet address and mask fields are in network byte order * * allows zero masks * * assumes memory pointed to by "external" argument * was allocated from heap and can be owned by us afterwards * * returns TRUE if successful, FALSE otherwise */ static boolean AlterSubnet( Subnet *external, int is_insertion ) { int index; int count; int mask_bits_left; int result; #if SUBNET_PARANOIA int heap_result; #endif Bit32 addr; Bit32 trial_node; /* must have more bits than POINTER_DATATYPE */ #if SUBNET_PARANOIA heap_result = heapcheck(); if( heap_result < 0 ) { fprintf( stderr, "heapcheck() before returned %d\n", heap_result ); return FALSE; } #endif /* make sure the caller does not have any bits set * in their address that are cleared in their mask * (enforce the mask) */ external->addr &= external->mask; /* get the address in host byte order */ addr = ntohl( external->addr ); /* find out how many bits are set at the beginning * of the IP address mask (prefix length) */ mask_bits_left = bits_in_mask( external->mask ); /* make sure that mask has some bits set */ if( mask_bits_left > MAX_ADDR_BITS ) return FALSE; result = AlterSubnetRecurse( external, &subnet_root, addr, MAX_ADDR_BITS, mask_bits_left, BITS_PER_ROOT, POINTERS_PER_ROOT, ROOT_INDEX_MASK, is_insertion ); if( result == TRUE ) /* keep track of how many subnets had this mask length */ if( is_insertion ) subnets_per_mask_length[ mask_bits_left - 1 ]++; else subnets_per_mask_length[ mask_bits_left - 1 ]--; return result; } /* assumes Subnet address and mask fields are in network byte order * * zero masks not allowed * * assumes memory pointed to by "external" argument * was allocated from heap and can be owned by us afterwards * * returns TRUE if successful, FALSE otherwise * * must be followed by a call to InsertSubnet() for the route * with the next-shortest mask, since the tree itself cannot * find that route */ boolean DeleteSubnet( Subnet *external ) { return AlterSubnet( external, FALSE ); } /* assumes Subnet address and mask fields are in network byte order * * allows zero masks * * assumes memory pointed to by "external" argument * was allocated from heap and can be owned by us afterwards * * returns TRUE if successful, FALSE otherwise */ boolean InsertSubnet( Subnet *external ) { Bit32 trial_node; /* must have more bits than POINTER_DATATYPE */ /* make sure this subnet can be represented as a leaf pointer */ trial_node = NODE_FROM_EXTERN( external ); if( trial_node > POINTER_MAX ) { fprintf( stderr, "error: cannot insert subnet %d > %d\n", trial_node, MAX_TREE_NODES ); return FALSE; } return AlterSubnet( external, TRUE ); } /* calls a function for each node, in order of preference for routing * * note: when we are using direct indexing instead of a patricia tree, * this function needs one more argument to tell it size of node */ static boolean WalkSubnetsRecurse( WalkSubnetFunction function, void *args, POINTER_DATATYPE node, unsigned num_pointers ) { int result; unsigned count; Subnet *external; Bit32 *pflag; /* if this node is a leaf... */ if( IS_NODE_A_LEAF( node ) ) { /* if this is a NIL pointer, nothing more to do */ external = EXTERN_FROM_NODE( node ); if( external == NULL ) return TRUE; /* if we already visited this leaf, nothing more to do */ pflag = (Bit32 *) external; if( *pflag == DUMP_VISITED_FLAG ) return TRUE; /* give it to the user function and return whatever he does */ result = function( TRUE, external, args ); if( result ) result = function( FALSE, external, args ); /* mark this leaf as visited */ if( dump_fixup_count >= MAX_FIXUPS ) { fprintf( stderr, "out of walk fixup trackers!\n" ); return FALSE; } dump_fixup_locations[dump_fixup_count] = pflag; dump_fixup_values[dump_fixup_count] = *pflag; dump_fixup_count++; *pflag = DUMP_VISITED_FLAG; return result; } /* for every one of our child nodes... * * warning: one child may be listed many times if it is a leaf, * and this code doesn't enforce uniqueness! */ for( count=0; countline, #endif inet_ntoa( temp, ntohl( external->addr ) ), bits_in_mask( external->mask ) ); } else { pflag = (Bit32 *) (subnet_pool + node); if( *pflag == DUMP_VISITED_FLAG ) { fprintf( stderr, "error: DumpRawSubnetsRecurse() already visited node %d\n", node ); return FALSE; } printf( "level %d node 0x%X\n", level_from_bits(addr_bits_left,level_bits), XLATE_PTR(PTR_FROM_NODE(node),subnet_base) ); for( count=0; count= MAX_FIXUPS ) { fprintf( stderr, "out of dump fixup trackers!\n" ); return FALSE; } dump_fixup_locations[dump_fixup_count] = pflag; dump_fixup_values[dump_fixup_count] = *pflag; dump_fixup_count++; *pflag = DUMP_VISITED_FLAG; return TRUE; } /* used for comparison of internal state with other implementations * of same algorithm for same data structures, or different inputs * * temporarily destroys the first word in each node or leaf, to know * whether it was visited yet */ void DumpRawSubnets( void ) { unsigned count; /* make sure the root node exists */ if( subnet_root == NODE_FROM_EXTERN( NULL ) ) { fprintf( stdout, "no SubnetTree's have been allocated yet\n" ); return; } /* nothing flagged yet */ dump_fixup_count = 0; /* calculate base addresses we want to use for printing pointers */ subnet_base = (POINTER_DATATYPE)(subnet_pool+subnet_root) - DUMP_SUBNET_OFFSET; external_base = (POINTER_DATATYPE)first_external - DUMP_EXTERNAL_OFFSET; printf( "raw internal SubnetTree state follows:\n" ); printf( "(0x%.8X was subtracted from all node pointers)\n", subnet_base ); printf( "(0x%.8X was subtracted from all leaf pointers)\n", external_base ); DumpRawSubnetsRecurse( subnet_root, MAX_ADDR_BITS, BITS_PER_ROOT, POINTERS_PER_ROOT ); /* undo all our flagging */ for( count=0; count=0; count-- ) fprintf( stdout, "%4d subnet nodes for byte %d\n", subnet_nodes_per_level[count], count ); #if SUBNET_FIND_MEMREFCNT fprintf( stdout, "\n" ); for( count=3; count>=0; count-- ) fprintf( stdout, "%8d subnets found with %d memory accesses\n", subnets_found_per_level[count], MAX_SUBNET_MEMREFS-count ); #endif fprintf( stdout, "\n" ); for( count=0; count