1 | /*************************************** 2 | $Revision: 1.20 $ 3 | 4 | Radix tree (rx). rx_search.c - functions to search nodes of the tree 5 | 6 | Status: NOT REVUED, TESTED, INCOMPLETE 7 | 8 | Design and implementation by: Marek Bukowy 9 | 10 | ******************/ /****************** 11 | Copyright (c) 1999 RIPE NCC 12 | 13 | All Rights Reserved 14 | 15 | Permission to use, copy, modify, and distribute this software and its 16 | documentation for any purpose and without fee is hereby granted, 17 | provided that the above copyright notice appear in all copies and that 18 | both that copyright notice and this permission notice appear in 19 | supporting documentation, and that the name of the author not be 20 | used in advertising or publicity pertaining to distribution of the 21 | software without specific, written prior permission. 22 | 23 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 24 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL 25 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 26 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 27 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 28 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 29 | ***************************************/ 30 | 31 | 32 | #include <erroutines.h> 33 | #include <rxroutines.h> 34 | #include <stubs.h> 35 | 36 | #include "iproutines.h" 37 | 38 | /***************************************************************************/ 39 | 40 | /*++++++++++++++ 41 | Descends the given tree following the last prefix bit to get [past] 42 | the node with the given prefix. 43 | It fills up a stack of COPIES of nodes, including glue nodes. 44 | 45 | Then it also sets the number of elements on the stack: 46 | set maxdepth to the position where a next one would be written 47 | ( = last + 1, or number of nodes pushed) 48 | 49 | The dmodes: 50 | 51 | RX_STK_QUERY_NOGLUE = (search exact/less spec) stop when 52 | * the current prefix length >= newprefix length 53 | * the current prefix does not match anymore 54 | * do not add glue nodes 55 | 56 | RX_STK_QUERY_ALLNOD = as above, except that the glue and data nodes are 57 | treated equally (i.e. glue nodes are not skipped) 58 | 59 | RX_STK_CREAT = descend until the next non-glue node past the one found 60 | in exact mode (for creation) 61 | 62 | ++++++++++++++*/ 63 | 64 | er_ret_t 65 | rx_build_stack(rx_nodcpy_t stack[], 66 | int *maxdepth, 67 | rx_tree_t *tree, 68 | ip_prefix_t *newpref, 69 | rx_stk_mt dmode 70 | ) 71 | { 72 | register rx_node_t *curnode; 73 | register int link, quit_now=0; 74 | char bbf[IP_PREFSTR_MAX]; 75 | 76 | if( ER_is_traced( FAC_RX, ASP_RX_STKBLD_GEN)) { 77 | IP_pref_b2a( newpref , bbf, IP_PREFSTR_MAX); 78 | ER_dbg_va(FAC_RX, ASP_RX_STKBLD_GEN, 79 | "rx_build_stack: searching for %s in mode %d", bbf, dmode); 80 | } 81 | 82 | *maxdepth = 0; 83 | 84 | if ( tree -> num_nodes == 0) { 85 | // The tree was empty. 86 | return RX_OK; 87 | } 88 | 89 | curnode = tree->top_ptr; 90 | // this works for RAM, for SQL one would have to call a 'getsqlnode' here 91 | 92 | // OK, there is at least one node. Descend the tree 93 | // as long as the correct bit length is not exceeded 94 | // or a glue is being found (take the last non-glue node then) 95 | // or you run out of nodes in the direction of descending 96 | 97 | do { 98 | // check at the current node, where the one we look for would fit 99 | // (the second argument of IP_addr_bit_get starts with 0, 100 | // so this effectively looks at the bit next to the last significant bit 101 | // of the current node 102 | 103 | link = IP_addr_bit_get( & newpref->ip, curnode->prefix.bits ); 104 | 105 | // check conditions for leaving the loop 106 | if(curnode->child_ptr[link] == NULL) { 107 | // end of branch. quit after adding the current node to the stack 108 | // (or before - subject to bit test in QUERY mode) 109 | quit_now = 1; 110 | } 111 | else { 112 | /* check the node. 113 | BIG DIFFERENCE between the modes: 114 | in CREAT we don't mind the stack to go too deep, 115 | in QUERY it can lead to false answers 116 | (e.g. a /24 is found for a /23 query). 117 | 118 | So this must be "peeled off the stack" later in the search routine, 119 | if both types of stack are to work properly with query searches. 120 | 121 | ONE MORE THING: in LESS SPECIFIC searches a QUERY stack MUST BE USED, 122 | because only this one actually compares the prefixes. 123 | This has no effect on the exact search, but will cause less-spec 124 | to return an object that does not contain the search term. 125 | */ 126 | 127 | 128 | if( curnode->prefix.bits > newpref->bits ) { 129 | // deep enough. 130 | quit_now = 2; 131 | } 132 | 133 | if(dmode == RX_STK_CREAT && curnode->glue) { 134 | // mode: creation. 135 | // Cancel quitting if glue -- in CREAT mode the stack building 136 | // should stop at the next real (non-glue) node. 137 | // ("next" meaning following link #0) 138 | quit_now = 0; 139 | } 140 | } 141 | 142 | /* now that the conditions for leaving the loop after the node is 143 | added on the stack, see if we shouldn't leave the loop BEFOREHAND */ 144 | 145 | /* In query mode, we should quit as soon as we see a mismatch */ 146 | 147 | if(dmode != RX_STK_CREAT 148 | && 0 != IP_addr_cmp(&curnode->prefix.ip, &newpref->ip, 149 | curnode->prefix.bits) ) { 150 | //QUIT NOW! (do not add this node) 151 | quit_now = 64; 152 | } 153 | 154 | // push the current node on the stack. RAM only. 155 | // 156 | // (unless quit_now is 64 which means do NOT copy the current node. 157 | // 158 | // In CREAT and QUERY_ALLNOD modes, push everything. 159 | // In QUERY_NOGLUE mode, only non-glues. 160 | 161 | if( quit_now < 64 162 | && (dmode != RX_STK_QUERY_NOGLUE || curnode->glue == 0 )) { 163 | memcpy( & stack[*maxdepth].cpy, curnode, sizeof(rx_node_t)); 164 | stack[*maxdepth].srcptr = curnode; 165 | stack[*maxdepth].srckey = SQ_NOKEY; 166 | stack[*maxdepth].tree = tree; 167 | (*maxdepth)++; 168 | } 169 | 170 | // make debug info. 171 | 172 | if( ER_is_traced( FAC_RX, ASP_RX_STKBLD_DET)) { 173 | IP_pref_b2a( & curnode->prefix , bbf, IP_PREFSTR_MAX ); 174 | ER_dbg_va(FAC_RX, ASP_RX_STKBLD_DET, 175 | "rx_build_stack: %s%d at %s%s (stk len: %d)", 176 | quit_now ? "stop/" : "link ", 177 | quit_now ? quit_now : link, 178 | bbf, ( curnode->glue ) ? " ++glue++" : "", 179 | *maxdepth ); 180 | } 181 | 182 | curnode = curnode -> child_ptr[link]; 183 | 184 | } while( !quit_now ); 185 | 186 | return RX_OK; 187 | } 188 | 189 | /***************************************************************************/ 190 | /*+++++++++ 191 | helper for the nod_search routine: 192 | 193 | allocates a new node copy struct, copy the struct and add to nodlist 194 | ++++++++++*/ 195 | 196 | static 197 | er_ret_t 198 | rx_nod_append( GList **nodlist, rx_nodcpy_t *element) 199 | { 200 | rx_nodcpy_t *newcpy; 201 | er_ret_t err; 202 | 203 | if( (err=wr_calloc( (void **) & newcpy, 1, sizeof(rx_nodcpy_t))) != UT_OK) { 204 | return err; // die; 205 | } 206 | memcpy(newcpy, element, sizeof(rx_nodcpy_t)); 207 | (*nodlist) = g_list_prepend( *nodlist, newcpy ); 208 | 209 | return RX_OK; 210 | } 211 | 212 | 213 | 214 | 215 | /***************************************************************************/ 216 | 217 | /*+++++++++++ 218 | helper for MORE specific lookup in rx_nod_search 219 | 220 | adds a node to the list of answers. 221 | +++++++++++*/ 222 | 223 | static 224 | er_ret_t 225 | rx_walk_hook_addnode(rx_node_t *node, int level, int nodecounter, 226 | void *userptr) 227 | { 228 | rx_nodcpy_t nodcpy; 229 | hook_addnode_userdat_t *userdat = userptr; 230 | 231 | 232 | // do not append glue nodes 233 | if( node->glue == 1 ) return RX_OK; 234 | 235 | // in RAM mode, do not copy the node. 236 | // memcpy( &nodcpy.cpy, node, sizeof(rx_node_t)); 237 | /* XXX to avoid warnings from workshop: but slows things down! */ 238 | memset( &nodcpy.cpy, 0, sizeof(rx_node_t)); 239 | 240 | nodcpy.srcptr = node; 241 | nodcpy.srckey = SQ_NOKEY; 242 | nodcpy.tree = userdat->tree; 243 | 244 | return rx_nod_append( userdat->nodlist, &nodcpy); 245 | } 246 | 247 | 248 | /***************************************************************************/ 249 | 250 | /*+++++++++++ 251 | helper for DBLS lookup in rx_nod_search 252 | 253 | adds a node to the list of answers. 254 | +++++++++++*/ 255 | 256 | static 257 | er_ret_t 258 | rx_walk_hook_adddoubles(rx_node_t *node, int level, int nodecounter, 259 | void *userptr) 260 | { 261 | rx_nodcpy_t nodcpy; 262 | hook_addnode_userdat_t *userdat = userptr; 263 | int leaves = g_list_length(node->leaves_ptr); 264 | char buf[1024]; 265 | 266 | // do not append glue nodes 267 | if( node->glue == 1 ) return RX_OK; 268 | 269 | 270 | // add only nodes with more than 1 dataleaf 271 | if( leaves < 2 ) return RX_OK; 272 | 273 | if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) { 274 | rx_nod_print(node, buf, 1024); 275 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 276 | "rx_walk_hook_adddoubles: %30s, %d leaves", buf, leaves); 277 | } 278 | 279 | // memcpy( &nodcpy.cpy, node, sizeof(rx_node_t)); 280 | nodcpy.srcptr = node; 281 | nodcpy.srckey = SQ_NOKEY; 282 | nodcpy.tree = userdat->tree; 283 | 284 | return rx_nod_append( userdat->nodlist, &nodcpy); 285 | } 286 | 287 | 288 | /***************************************************************************/ 289 | er_ret_t 290 | rx_nod_search ( 291 | rx_srch_mt search_mode, 292 | int par_a, 293 | int par_b, 294 | /* see rx_asc_search() for explanation */ 295 | rx_tree_t *tree, // tree ptr 296 | ip_prefix_t *prefix, // binary prefix 297 | 298 | rx_nodcpy_t stack[], // stack==array of node_copies 299 | int stackcount, // number of element on the stack, 300 | // can come from a creat stack! 301 | 302 | GList **nodlist, // answers go here 303 | int max_count // max # of answers 304 | ) 305 | /* 306 | searches the stack for a given prefix, finds *nodes* in the stack 307 | and appends *copies of the nodes* to the nodlist; 308 | 309 | finds 310 | 0 or 1 nodes for exact search 311 | 0 or 1 nodes for exless (0 if no less specific node found) 312 | any number (incl. 0) for {more|less}^n-m specific 313 | 314 | returns errcode. 315 | 316 | 317 | */ 318 | { 319 | char buf[1024]; 320 | int sps = stackcount-1; // stack position. 321 | int depthcounter=0; 322 | er_ret_t err=RX_OK; 323 | int i; 324 | hook_addnode_userdat_t datstr; 325 | er_ret_t (*hook_function)(); // pointer to the walk_hook function 326 | // (see MORE spec lookup) 327 | 328 | /* structure for carrying data to walk_tree hook functions, used only 329 | in MORE, DBLS and RANG search modes 330 | */ 331 | datstr.nodlist = nodlist; 332 | datstr.tree = tree; 333 | datstr.prefix = prefix; 334 | 335 | 336 | if( ER_is_traced( FAC_RX, ASP_RX_SRCH_BOT)) { 337 | IP_pref_b2a( prefix , buf, IP_PREFSTR_MAX); 338 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, 339 | "rx_nod_search: searching for %s in mode %d", buf, search_mode); 340 | } 341 | 342 | /* in non-CREAT modes, glue nodes are skipped anyway. 343 | (they should normally not be there if the stack was created in 344 | the STK_QUERY mode, but it's possible to use a CREAT stack too). 345 | 346 | It's also possible that the stack is too deep. 347 | So, truncate the stack to the last non-glue node 348 | of the length <= search term. 349 | */ 350 | 351 | if( search_mode != RX_SRCH_CREAT && search_mode != RX_SRCH_RANG) { 352 | while( sps >= 0 353 | && ( 354 | stack[sps].cpy.prefix.bits > prefix->bits // too deep 355 | || ( search_mode != RX_SRCH_MORE && search_mode != RX_SRCH_DBLS 356 | && stack[sps].cpy.glue == 1 ) // is glue 357 | || ( search_mode == RX_SRCH_LESS 358 | && stack[sps].cpy.prefix.bits == prefix->bits )// too deep 359 | ) 360 | ) { 361 | 362 | if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) { 363 | rx_nod_print( & stack[sps].cpy , buf, IP_PREFSTR_MAX); 364 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 365 | "rx_nod_search: peeling off %d: %s", sps, buf); 366 | } 367 | sps--; 368 | } 369 | } 370 | 371 | // nothing left on the stack. Sorry. 372 | // we allow that for more spec search -- this means 373 | // that the search term is a shorter prefix than the one 374 | // in the top node. Possibly it's 0/0 which is valid for more spec search. 375 | 376 | if( search_mode != RX_SRCH_MORE && search_mode != RX_SRCH_DBLS 377 | && sps < 0 ) { 378 | return RX_OK; 379 | } 380 | 381 | switch(search_mode) { 382 | case RX_SRCH_EXACT: 383 | case RX_SRCH_CREAT: 384 | // go up the tree (stack) and exit when the proper prefix is found. 385 | // For RX_SRCH_EXACT skip glue nodes, for RX_SRCH_CREAT take all. 386 | // They may contain a valid prefix, so watch out. 387 | 388 | while(sps >= 0) { 389 | 390 | if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) { 391 | rx_nod_print(& stack[sps].cpy, buf, 1024); 392 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 393 | "rx_nod_search: position %d: %s", sps, buf); 394 | } 395 | 396 | if ( search_mode == RX_SRCH_EXACT 397 | && stack[sps].cpy.glue ) { 398 | die; 399 | } 400 | 401 | if ( memcmp( & stack[sps].cpy.prefix, 402 | prefix, 403 | sizeof(ip_prefix_t)) == 0 ) { 404 | // FOUND!! 405 | // add to the nodlist. 406 | 407 | if( (err=rx_nod_append( nodlist, & stack[sps])) != RX_OK ) { 408 | return err; 409 | } 410 | 411 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, "rx_nod_search: found!"); 412 | break; 413 | } 414 | sps--; 415 | } 416 | break; 417 | 418 | case RX_SRCH_EXLESS: 419 | // just fetch the last element off the stack (if any). 420 | // Must be non-glue for EXLESS. 421 | 422 | if( sps >= 0 ) { 423 | rx_nod_append( nodlist, & stack[sps]); 424 | } 425 | 426 | // else : nothing found. 427 | // For EXLESS: check if the stack contains only non-glue nodes. 428 | // If it contains a glue, it means it was created in the CREAT mode, 429 | // which renders the above algorithm absolutely useless. Then crash, 430 | // this is a programmer's error. 431 | 432 | while( sps >= 0 ) { 433 | if( stack[sps].cpy.glue ) { 434 | die; 435 | } 436 | sps--; 437 | } 438 | 439 | break; 440 | 441 | case RX_SRCH_LESS: 442 | while( sps >= 0 && depthcounter < par_a ) { 443 | if( stack[sps].cpy.glue == 0 ) { 444 | rx_nod_append( nodlist, & stack[sps]); 445 | depthcounter++; 446 | } 447 | sps--; 448 | } 449 | break; 450 | 451 | case RX_SRCH_MORE: 452 | case RX_SRCH_DBLS: // special (debug?) mode : find nodes with multiple 453 | // data leaves. Much like more specific, except that 454 | // most nodes will be skipped. 455 | // The difference is in calling another hook function 456 | hook_function = ( search_mode == RX_SRCH_MORE ) 457 | ? rx_walk_hook_addnode 458 | : rx_walk_hook_adddoubles; 459 | 460 | // the result of a more spec search should NOT contain the object exactly 461 | // matching the query, even if it exists in the database. So two walks are 462 | // performed, one for each child (if it exists). 463 | // MEMORY IMPLEMENTATION ONLY FOR THE MOMENT 464 | 465 | // COVER THE CASE 0.0.0.0/0 466 | // or any other prefix that the tree might be set to represent, 467 | // but there is no actual object for it (not even glue) 468 | 469 | if( sps < 0 ) { // start from the top node if contained 470 | if( prefix->bits >= tree->prefix.bits ) { 471 | 472 | rx_walk_tree( tree->top_ptr, hook_function, 473 | RX_WALK_REVERS | RX_WALK_SKPGLU, // skip glue nodes while counting 474 | par_a, // display this many levels 475 | 0, 0, &datstr, &err); 476 | if( err != RX_OK ) { 477 | return err; 478 | } 479 | } 480 | } 481 | else { 482 | for( i = 1; i >= 0; i--) { 483 | if( stack[sps].cpy.child_ptr[i] != NULL ) { 484 | if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) { 485 | IP_pref_b2a(& stack[sps].cpy.child_ptr[i]->prefix, buf, 1023); 486 | } 487 | 488 | if( 0 == IP_addr_cmp( & stack[sps].cpy.child_ptr[i]->prefix.ip, 489 | & prefix->ip, 490 | prefix->bits) ) { 491 | 492 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 493 | "rx_nod_search: digging child %d: %s", i, buf); 494 | 495 | rx_walk_tree( stack[sps].cpy.child_ptr[i], hook_function, 496 | RX_WALK_REVERS | RX_WALK_SKPGLU, // skip glue nodes while counting 497 | par_a, // display this many levels 498 | 0, 0, &datstr, &err); 499 | if( err != RX_OK ) { 500 | return err; 501 | } 502 | } 503 | else { 504 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 505 | "rx_nod_search: prefix mismatch with child %d: %s", 506 | i, buf); 507 | } 508 | } 509 | } 510 | } 511 | break; 512 | 513 | case RX_SRCH_RANG: 514 | /* OK, start from the node at the end of the stack (exless match including 515 | glue nodes) then 516 | 517 | 518 | if its prefix length is 519 | longer -> OK, it stopped too far, go up 520 | OK -> found! descend from here as long as the prefixes are in range 521 | shorter -> apparently there is even no such glue node. come back down 522 | one step 523 | 524 | */ 525 | 526 | i = sps; // go up the tree (down the stack) 527 | // until too far (one node too much, after >= ) 528 | while( i >= 0 && stack[i].cpy.prefix.bits >= prefix->bits ) { 529 | i--; 530 | } 531 | 532 | // look where you are: 533 | 534 | if( i < 0 ) // it was the top object, but its prefix was too long 535 | i=0; // take the top object as the base 536 | else 537 | i++; // went one too much, now come back one step 538 | 539 | 540 | rx_walk_tree( stack[i].srcptr, rx_walk_hook_addnode, 541 | RX_WALK_PRFLEN, // skip glue nodes while counting 542 | par_a, // display up to this max length 543 | 0, 0, &datstr, &err); 544 | if( err != RX_OK ) { 545 | return err; 546 | } 547 | 548 | break; 549 | 550 | // return RX_NOYETI; 551 | //not implemented 552 | // die; 553 | default: 554 | die; // are you nuts?? 555 | } 556 | 557 | return err; 558 | 559 | } 560 | 561 | 562 | 563 | /*****************************************************************************/ 564 | /*+ 565 | this is a specialised find function to find nodes in the list of 566 | answer structs that point to the given data leaf. 567 | This is used to avoid reporting data leaves more than once 568 | (eg. because the node is composed (inetnum) 569 | 570 | +*/ 571 | 572 | static 573 | GList * 574 | rx_find_leaf(GList *anslist, rx_dataleaf_t *leafptr) 575 | { 576 | GList *item; 577 | 578 | for(item = g_list_first(anslist); 579 | item != NULL; 580 | item = g_list_next(item)) { 581 | if( ((rx_datref_t *)(item->data))->leafptr == leafptr) { 582 | return item; 583 | } 584 | } 585 | 586 | return NULL; 587 | } 588 | 589 | /*****************************************************************************/ 590 | /*+++++++++++++ 591 | builds a stack for this prefix, finds *nodes* in the stack 592 | and appends *copies of the data leaves* to the LL of answers; 593 | 594 | sorts by SQL object keys and uniq's the data 595 | 596 | finds: 597 | 0 or 1 nodes for exact search 598 | 0 or 1 nodes for exless (0 if no less specific node found) 599 | any number (incl. 0) for {more|less}-n specific 600 | 601 | then copies the nodes/dataleaves to the answer structs and appends them 602 | to the given LL. So, effectively, the number of answers can be 603 | anything from 0 to infinity, because objects may be duplicate 604 | even at the same node. 605 | 606 | returns errcode. 607 | 608 | algorithm: 609 | 610 | builds stack[MAXBIT (==128)]; 611 | 612 | if( more/less-depth && par_a == 0) 613 | 614 | run rx_nod_search, then 615 | 616 | if(more spec) rx_nod_walk(maxdepth=n, append_to_LL() ); 617 | if(less spec) do { append(LL, stack[i]) } while(i-- && n--); 618 | otherwise just set LL 619 | 620 | 621 | The routine provides _at_least_ max_count answers. 622 | It will *try* to stop after max_count as soon as possible 623 | - but it's the higher level routine that should do the final cut. 624 | +++++++++++++++*/ 625 | 626 | er_ret_t 627 | RX_bin_search ( 628 | rx_srch_mt search_mode, 629 | int par_a, 630 | int par_b, 631 | rx_tree_t *tree, // tree ptr 632 | ip_prefix_t *prefix, // binary prefix 633 | GList **datleaves, // data leaves go here 634 | int max_count 635 | ) 636 | 637 | { 638 | char buf[256]; 639 | rx_nodcpy_t stack[128]; 640 | int i, k; 641 | int stkcnt, resnum = 0, maxleaves; 642 | GList *nodlist = NULL, *nitem; 643 | GList *complist = NULL; 644 | rx_node_t *curnode; 645 | rx_nodcpy_t *curcpy; 646 | rx_datref_t *datref; 647 | rx_stk_mt dmode; 648 | 649 | // more specific node search may start from a glue node, 650 | // for all others the stack should not contain glues. 651 | 652 | dmode = ( search_mode == RX_SRCH_MORE 653 | || search_mode == RX_SRCH_DBLS 654 | || search_mode == RX_SRCH_RANG ) 655 | ? RX_STK_QUERY_ALLNOD 656 | : RX_STK_QUERY_NOGLUE; 657 | 658 | rx_build_stack(stack, &stkcnt, tree, prefix, dmode); 659 | 660 | rx_nod_search( search_mode, par_a, par_b, tree, prefix, 661 | stack, stkcnt, &nodlist, 1000); 662 | 663 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, "RX_bin_search: processing nodes"); 664 | 665 | for( nitem = g_list_first(nodlist); 666 | nitem != NULL; 667 | nitem = g_list_next(nitem)) { 668 | 669 | resnum++; 670 | curcpy = nitem->data; 671 | 672 | /* 673 | if memory mode includes RAM: 674 | * do not expect copies of nodes in the list received from bin_search. 675 | * iterate through data leaves with g_list_nth_data. 676 | */ 677 | 678 | curnode = curcpy->srcptr; 679 | 680 | // rx_nod_print( curnode, buf, 1024 ); 681 | 682 | maxleaves = g_list_length(curnode->leaves_ptr); 683 | // fprintf(stderr,"###node %d, %d dataleaves attached:", i, maxleaves); 684 | 685 | // iterate through dataleafs attached to this node 686 | for(k=0; k<maxleaves; k++) { 687 | rx_dataleaf_t *leafptr = g_list_nth_data(curnode->leaves_ptr, k); 688 | 689 | // check the conditions to add the leaf: 690 | 691 | // 1. never add the same leaf twice (can occur for composed inetnums) 692 | // 2. never add composed inetnum for exact prefix search 693 | // (but do for exact range search...) - must be solved in upper layer. 694 | 695 | // add only if not yet on the list, i.e if it's composed then check, 696 | // otherwise just add 697 | if( tree->family == RX_FAM_IN && leafptr->composed == 1 ) { 698 | GList *item; 699 | int already_there = 0; 700 | 701 | for(item = g_list_first(complist); 702 | item != NULL; 703 | item = g_list_next(item)) { 704 | if( item->data == leafptr ) { 705 | already_there = 1; 706 | } 707 | } 708 | 709 | if( already_there == 1 ) { 710 | continue; 711 | } 712 | else { 713 | g_list_prepend(complist, leafptr); 714 | // and now safely add the answer 715 | } 716 | } 717 | 718 | if( wr_calloc( (void **) &datref, sizeof(rx_datref_t), 1) != UT_OK) { 719 | die; 720 | } 721 | datref->leafptr = leafptr; 722 | 723 | *datleaves = g_list_prepend(*datleaves, datref); 724 | } 725 | } 726 | 727 | g_list_foreach(nodlist, rx_free_list_element, NULL); 728 | g_list_free(nodlist); 729 | 730 | // the payload of complist are pointers or (cast) integers included 731 | // in the list elements. no need to free them, just the list 732 | g_list_free(complist); 733 | 734 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, 735 | "RX_bin_search: found %d nodes", resnum); 736 | 737 | 738 | /* the LL of answers (*datleaves) contains pointers to answer structs, 739 | that SHOULD BE NORMALIZED HERE (==with no redundant entries) 740 | */ 741 | 742 | return RX_OK; 743 | } 744 | 745 | /**************************************************************************/ 746 | /*+++++++++++ 747 | this routine goes through the list of prefixes and performs a bin_search 748 | on each of them; attaches the results to datlist. 749 | Then, frees the prefix list. 750 | +++++++++++*/ 751 | static 752 | er_ret_t 753 | rx_preflist_search ( 754 | rx_srch_mt search_mode, 755 | int par_a, 756 | int par_b, 757 | rx_tree_t *mytree, 758 | GList **preflist, 759 | GList **datlist 760 | ) 761 | 762 | { 763 | char prefstr[IP_PREFSTR_MAX]; 764 | GList *qitem; 765 | ip_prefix_t *querypref; 766 | er_ret_t err; 767 | 768 | for( qitem = g_list_first(*preflist); 769 | qitem != NULL; 770 | qitem = g_list_next(qitem)) { 771 | 772 | querypref = qitem->data; 773 | 774 | if( IP_pref_b2a( querypref, prefstr, IP_PREFSTR_MAX) != IP_OK ) { 775 | die; 776 | } 777 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, 778 | "rx_preflist_search: mode %d (par %d) for %s", 779 | search_mode, par_a, prefstr); 780 | 781 | if (mytree->num_nodes > 0) { 782 | err = RX_bin_search( search_mode, par_a, par_b, mytree, querypref, 783 | datlist, RX_ANS_ALL); 784 | if( err != RX_OK ) { 785 | return err; 786 | } 787 | } 788 | } 789 | 790 | g_list_foreach(*preflist, rx_free_list_element, NULL); 791 | g_list_free(*preflist); 792 | *preflist = NULL; 793 | return RX_OK; 794 | } 795 | 796 | 797 | /**************************************************************************/ 798 | /*+++++++++++++++ 799 | translates a query into a binary prefix (or prefixes, if range). 800 | for registry+space (or if they are zero, for all 801 | registries/spaces) 802 | finds tree 803 | calls RX_bin_search (returning node copies). 804 | will not put duplicate entries (composed inetnums). 805 | returns some sort of error code :-) 806 | 807 | Cuts the number of answers from RX_bin_search down to max_count, 808 | but since some of the answers may have been "normalized" in the 809 | underlying functions (multiple occurences removed), 810 | the result is _at_most_ max_count. 811 | 812 | appends to a given list of data blocks (not nodes!) 813 | 814 | returns RX_OK or a code from an underlying function 815 | ++++++++++++*/ 816 | 817 | er_ret_t 818 | RX_asc_search ( 819 | rx_srch_mt search_mode, 820 | /*+ MODE={exact|exless|less-depth|more-depth|more-bit} 821 | exless == our default search (exact or less-1 spec.) 822 | more-bit == more specific bit length searches: 823 | inclusive, exclusive, bit length, range of bit lengths... 824 | All of them can be expressed using a range of lengths. 825 | That means TWO integer parameters must be also given. 826 | +*/ 827 | int par_a, 828 | int par_b, 829 | /*+ the semantic of these parameters (par_a,par_b) is: 830 | - for more-bit: the length range [preflen - par_a], 831 | - for less/more-depth: par_a is the depth 832 | (1-2-3... means so many levels, 833 | RX_ALL_DEPTHS means all levels) 834 | - for exact/exless: both are ignored. 835 | 836 | par_b is ignored, it's there for historical/future reasons 837 | +*/ 838 | 839 | char *key, /*+ search term: (string) prefix/range/IP +*/ 840 | int reg_id, 841 | ip_space_t spc_id, /*+ space id, one of IPv4 IPv6. +*/ 842 | rx_fam_t fam_id, /*+ RX_FAM_RT or RX_FAM_IN +*/ 843 | GList **anslist, /*+ answers go here, please +*/ 844 | int max_count /*+ max # of answers. RX_ALLANS == unlimited +*/ 845 | ) 846 | 847 | { 848 | GList *datlist = NULL, *preflist = NULL, *ditem; 849 | ip_prefix_t testpref; 850 | char prefstr[IP_PREFSTR_MAX]; 851 | int prefcount = 0; 852 | rx_tree_t *mytree; 853 | er_ret_t err; 854 | ip_range_t myrang; 855 | int is_exless_inetnum = 0, is_exact_range_inetnum = 0; 856 | ip_rangesize_t min_span, span; 857 | 858 | 859 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, 860 | "rx_asc_search: mode %d (par %d) for %s", 861 | search_mode, par_a, key); 862 | 863 | if( (err = RX_get_tree ( &mytree, reg_id, spc_id, fam_id)) != RX_OK ) { 864 | return err; 865 | } 866 | 867 | /* 868 | the behaviour for a default inetnum (range) query is: 869 | do an exact match; 870 | if it fails, do an exless match on the encompassing prefix 871 | for routes(prefixes): 872 | do an exless match 873 | 874 | So if it's the default search mode on an inetnum tree, 875 | and the key is a range, 876 | then an exact search is performed on one of the composing prefixes. 877 | 878 | Then the resulting data leaves are checked for exact matching with 879 | the range queried for. 880 | Any dataleaves that do not match are discarded, and if none are left, 881 | the procedure falls back to searching for the encompassing prefix. 882 | (calculated in the smart_conv routine). 883 | 884 | Whatever way, the EXLESS search on inetnum tree should 885 | return the shortest range that was found. 886 | (or range*s* if of equal length). That is done at the end. 887 | */ 888 | 889 | /* EXACT search of a route tree for a composed range makes no sense */ 890 | 891 | if( fam_id == RX_FAM_RT && search_mode == RX_SRCH_EXACT 892 | && IP_rang_a2b(&myrang, key) == IP_OK ) { 893 | IP_rang_decomp(&myrang, &preflist); 894 | prefcount = g_list_length(preflist); 895 | 896 | if( prefcount > 1 ) { 897 | // abort search 898 | return RX_OK; 899 | } 900 | } 901 | 902 | if( fam_id == RX_FAM_IN ) { 903 | if (search_mode == RX_SRCH_EXLESS ) { 904 | /* save a flag for later, will be needed when processing the results */ 905 | is_exless_inetnum = 1; 906 | } 907 | 908 | if( (search_mode == RX_SRCH_EXLESS || search_mode == RX_SRCH_EXACT) 909 | && IP_rang_a2b(&myrang, key) == IP_OK ) { 910 | /* 911 | perform just one exact search on one of the composing prefixes 912 | - the object must be found if it's in the database 913 | */ 914 | 915 | if (search_mode == RX_SRCH_EXACT ) { 916 | /* save a flag for later, will be needed when processing the results */ 917 | is_exact_range_inetnum = 1; 918 | } 919 | 920 | IP_rang_decomp(&myrang, &preflist); 921 | 922 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, 923 | "rx_asc_search: quick decomp exact search"); 924 | 925 | err = RX_bin_search( RX_SRCH_EXACT, 0, 0, mytree, 926 | preflist->data, 927 | &datlist, RX_ANS_ALL); 928 | if( err != RX_OK ) { 929 | return err; 930 | } 931 | 932 | g_list_foreach(preflist, rx_free_list_element, NULL); 933 | g_list_free(preflist); 934 | preflist = NULL; 935 | 936 | /* now check the results */ 937 | 938 | ditem = g_list_first(datlist); 939 | while( ditem != NULL ) { 940 | rx_dataleaf_t *lptr = ( (rx_datref_t *)ditem->data)->leafptr; 941 | 942 | if( memcmp(&lptr->iprange, &myrang, sizeof(ip_range_t)) == 0) { 943 | // found! leave it, remove others 944 | ditem = g_list_next(ditem); 945 | } 946 | else { 947 | 948 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 949 | "rx_asc_search: ex/rang/in: range mismatch, discarded"); 950 | 951 | // mismatch. remove that one and start over (Aarggh!) 952 | datlist = g_list_remove(datlist, ditem->data); 953 | wr_free(ditem->data); 954 | ditem = g_list_first(datlist); 955 | } 956 | } 957 | } 958 | } 959 | 960 | /* now let's see if anything is left */ 961 | 962 | if( g_list_length(datlist) > 0 ) { 963 | 964 | /* YES! we hit an inetnum object. */ 965 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, 966 | "rx_asc_search: exact match found for %s", key); 967 | 968 | // do not treat it as exless anymore, it was exact and matched. 969 | is_exless_inetnum = 0; 970 | } 971 | else if( is_exact_range_inetnum != 1 ) { 972 | 973 | /* nothing found. 974 | 975 | Either it's not a range (and so wasn't even tried) 976 | or the mode was different from EXLESS and EXACT. 977 | Any way, we will now perform the search on anything that 978 | the query can be converted to. 979 | 980 | */ 981 | 982 | if( ( err = IP_smart_conv(key, 0, (search_mode == RX_SRCH_EXLESS), 983 | &preflist, IP_EXPN)) != IP_OK ) { 984 | return err; 985 | } 986 | 987 | prefcount = g_list_length(preflist); 988 | 989 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, 990 | "rx_asc_search: query translated into %d prefix subqueries", 991 | prefcount); 992 | 993 | // if there is only one prefix, store it for possible use later 994 | 995 | testpref = *( (ip_prefix_t *) g_list_first(preflist) -> data); 996 | 997 | // now go through the list of prefixes and perform the searches 998 | // rx_preflist_search deallocates the prefixes after use. 999 | 1000 | err = rx_preflist_search (search_mode, par_a, par_b, 1001 | mytree, &preflist, &datlist); 1002 | if( err != RX_OK ) { 1003 | return err; 1004 | } 1005 | } 1006 | 1007 | // find the smallest range span 1008 | 1009 | if( is_exless_inetnum == 1 ) { 1010 | min_span = 0xffffffff; 1011 | 1012 | // go through the list and find the shortest range. 1013 | for(ditem = g_list_first(datlist); 1014 | ditem != NULL; 1015 | ditem = g_list_next(ditem)) { 1016 | rx_datref_t *refptr = (rx_datref_t *) (ditem->data); 1017 | 1018 | span = IP_rang_span(refptr->leafptr->iprange); 1019 | 1020 | if( span < min_span ) { 1021 | min_span = span; 1022 | } 1023 | } 1024 | 1025 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 1026 | "rx_asc_search: minimal span is %d", min_span); 1027 | } 1028 | 1029 | // Add the dataleaf copies to the list of answers. 1030 | 1031 | for(ditem = g_list_first(datlist); 1032 | ditem != NULL; 1033 | ditem = g_list_next(ditem)) { 1034 | er_ret_t err; 1035 | rx_datref_t *refptr = (rx_datref_t *) (ditem->data); 1036 | rx_datcpy_t *datcpy; 1037 | void *dataptr; 1038 | 1039 | // For exless_inet_range search, discard all 1040 | // except the one(s) of the smallest size. 1041 | if( is_exless_inetnum == 1 1042 | && (span = IP_rang_span(refptr->leafptr->iprange)) != min_span ) { 1043 | 1044 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 1045 | "rx_asc_search: discarded object with span %d", span); 1046 | continue; 1047 | } 1048 | 1049 | // if this is an EXACT search on inetnums specified with a prefix, 1050 | // (i.e., one prefix was returned from the conversion) 1051 | // then check if the range in the object is equivalent to that prefix 1052 | 1053 | if( search_mode == RX_SRCH_EXACT 1054 | && fam_id == RX_FAM_IN 1055 | && prefcount == 1 ) { 1056 | ip_range_t testrang; 1057 | 1058 | IP_pref_2_rang( &testrang, &testpref ); 1059 | 1060 | if( memcmp( & refptr->leafptr->iprange, 1061 | &testrang, sizeof(ip_range_t)) != 0) { 1062 | 1063 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 1064 | "rx_asc_search: discarded an object from exact/inetnum/prefix search"); 1065 | 1066 | continue; 1067 | } 1068 | } 1069 | 1070 | 1071 | // OK, so we ACCEPT this result. Copy it. 1072 | 1073 | if( (err=wr_calloc( (void **)& datcpy, 1, sizeof(rx_datcpy_t))) != UT_OK) { 1074 | return err; // die; 1075 | } 1076 | 1077 | datcpy->leafcpy = *(refptr->leafptr); 1078 | 1079 | // copy the immediate data too. Set the ptr. 1080 | 1081 | if( (err=wr_calloc( (void **) & dataptr, 1, refptr->leafptr->data_len)) 1082 | != UT_OK) { 1083 | return err; // die; 1084 | } 1085 | memcpy(dataptr, refptr->leafptr->data_ptr, refptr->leafptr->data_len); 1086 | 1087 | datcpy->leafcpy.data_ptr = dataptr; 1088 | 1089 | *anslist = g_list_prepend(*anslist, datcpy); 1090 | } 1091 | 1092 | g_list_foreach(datlist, rx_free_list_element, NULL); 1093 | g_list_free(datlist); 1094 | 1095 | return RX_OK; 1096 | 1097 | } 1098 |