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