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