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