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 |