]>
Commit | Line | Data |
---|---|---|
526e7c1d P |
1 | /* |
2 | * $Id: patricia.c,v 1.7 2005/12/07 20:46:41 dplonka Exp $ | |
3 | * Dave Plonka <plonka@doit.wisc.edu> | |
4 | * | |
5 | * This product includes software developed by the University of Michigan, | |
6 | * Merit Network, Inc., and their contributors. | |
7 | * | |
8 | * This file had been called "radix.c" in the MRT sources. | |
9 | * | |
10 | * I renamed it to "patricia.c" since it's not an implementation of a general | |
11 | * radix trie. Also I pulled in various requirements from "prefix.c" and | |
12 | * "demo.c" so that it could be used as a standalone API. | |
13 | */ | |
14 | ||
6f8dfee9 | 15 | char patricia_copyright[] = |
526e7c1d P |
16 | "This product includes software developed by the University of Michigan, Merit" |
17 | "Network, Inc., and their contributors."; | |
18 | ||
526e7c1d P |
19 | #include <assert.h> /* assert */ |
20 | #include <ctype.h> /* isdigit */ | |
21 | #include <errno.h> /* errno */ | |
22 | #include <math.h> /* sin */ | |
23 | #include <stddef.h> /* NULL */ | |
24 | #include <stdio.h> /* sprintf, fprintf, stderr */ | |
25 | #include <stdlib.h> /* free, atol, calloc */ | |
26 | #include <string.h> /* memcpy, strchr, strlen */ | |
27 | ||
28 | #include "patricia.h" | |
29 | ||
daf8fe87 CP |
30 | //#define LEAK_DETECTION |
31 | ||
32 | #ifdef LEAK_DETECTION | |
33 | #define MAGIC 0x6a3b4ef3 | |
34 | ||
35 | struct fake_node { | |
36 | patricia_node_t node; | |
37 | uint32_t magic; | |
38 | patricia_node_t *real_node; | |
39 | }; | |
40 | #endif | |
41 | ||
526e7c1d P |
42 | /* prefix_tochar |
43 | * convert prefix information to bytes | |
44 | */ | |
45 | u_char * | |
46 | prefix_tochar (prefix_t * prefix) | |
47 | { | |
48 | if (prefix == NULL) | |
49 | return (NULL); | |
50 | ||
51 | return ((u_char *) & prefix->sin); | |
52 | } | |
53 | ||
54 | int | |
55 | comp_with_mask (void *addr, void *dest, u_int mask) | |
56 | { | |
57 | ||
58 | if ( /* mask/8 == 0 || */ memcmp (addr, dest, mask / 8) == 0) { | |
59 | int n = mask / 8; | |
60 | int m = ((-1) << (8 - (mask % 8))); | |
61 | ||
62 | if (mask % 8 == 0 || (((u_char *)addr)[n] & m) == (((u_char *)dest)[n] & m)) | |
63 | return (1); | |
64 | } | |
65 | return (0); | |
66 | } | |
67 | ||
ba7e4a13 | 68 | |
526e7c1d P |
69 | prefix_t * |
70 | patricia_new_prefix (struct irc_in_addr *dest, int bitlen) | |
71 | { | |
7a25133f P |
72 | prefix_t *prefix = newprefix(); |
73 | assert (0 != prefix); | |
526e7c1d | 74 | |
526e7c1d | 75 | memcpy (&prefix->sin, dest, 16); |
526e7c1d P |
76 | prefix->bitlen = (bitlen >= 0)? bitlen: 128; |
77 | prefix->ref_count = 1; | |
7a25133f | 78 | |
526e7c1d P |
79 | return prefix; |
80 | } | |
81 | ||
82 | prefix_t * | |
83 | patricia_ref_prefix (prefix_t * prefix) | |
84 | { | |
85 | if (prefix == NULL) | |
86 | return (NULL); | |
87 | if (prefix->ref_count == 0) { | |
88 | /* make a copy in case of a static prefix */ | |
89 | return (patricia_new_prefix (&prefix->sin, prefix->bitlen)); | |
90 | } | |
91 | prefix->ref_count++; | |
526e7c1d P |
92 | return (prefix); |
93 | } | |
94 | ||
95 | void | |
96 | patricia_deref_prefix (prefix_t * prefix) | |
97 | { | |
98 | if (prefix == NULL) | |
99 | return; | |
100 | /* for secure programming, raise an assert. no static prefix can call this */ | |
101 | assert (prefix->ref_count > 0); | |
102 | ||
103 | prefix->ref_count--; | |
526e7c1d | 104 | if (prefix->ref_count <= 0) { |
801fd068 P |
105 | freeprefix(prefix); |
106 | return; | |
526e7c1d P |
107 | } |
108 | } | |
109 | ||
526e7c1d P |
110 | /* these routines support continuous mask only */ |
111 | ||
112 | patricia_tree_t * | |
113 | patricia_new_tree (int maxbits) | |
114 | { | |
115 | patricia_tree_t *patricia = calloc(1, sizeof *patricia); | |
116 | ||
117 | patricia->maxbits = maxbits; | |
118 | patricia->head = NULL; | |
119 | patricia->num_active_node = 0; | |
120 | assert (maxbits <= PATRICIA_MAXBITS); /* XXX */ | |
121 | return (patricia); | |
122 | } | |
123 | ||
124 | ||
125 | void | |
126 | patricia_clear_tree (patricia_tree_t *patricia, void_fn_t func) | |
127 | { | |
128 | assert (patricia); | |
129 | if (patricia->head) { | |
130 | ||
131 | patricia_node_t *Xstack[PATRICIA_MAXBITS+1]; | |
132 | patricia_node_t **Xsp = Xstack; | |
133 | patricia_node_t *Xrn = patricia->head; | |
134 | ||
135 | while (Xrn) { | |
136 | patricia_node_t *l = Xrn->l; | |
137 | patricia_node_t *r = Xrn->r; | |
138 | ||
139 | if (Xrn->prefix) { | |
a8ba1373 P |
140 | if (Xrn->exts && func) |
141 | func(Xrn->exts); | |
526e7c1d P |
142 | patricia_deref_prefix (Xrn->prefix); |
143 | } | |
7a25133f | 144 | freenode(Xrn); |
526e7c1d P |
145 | patricia->num_active_node--; |
146 | ||
147 | if (l) { | |
148 | if (r) { | |
149 | *Xsp++ = r; | |
150 | } | |
151 | Xrn = l; | |
152 | } else if (r) { | |
153 | Xrn = r; | |
154 | } else if (Xsp != Xstack) { | |
155 | Xrn = *(--Xsp); | |
156 | } else { | |
157 | Xrn = (patricia_node_t *) 0; | |
158 | } | |
159 | } | |
160 | } | |
161 | assert (patricia->num_active_node == 0); | |
526e7c1d P |
162 | } |
163 | ||
526e7c1d P |
164 | void |
165 | patricia_destroy_tree (patricia_tree_t *patricia, void_fn_t func) | |
166 | { | |
167 | patricia_clear_tree (patricia, func); | |
168 | free(patricia); | |
7a25133f | 169 | /*TODO: EXTENSIONS!*/ |
526e7c1d P |
170 | } |
171 | ||
172 | ||
173 | /* | |
174 | * if func is supplied, it will be called as func(node->prefix) | |
175 | */ | |
176 | ||
177 | void | |
178 | patricia_process (patricia_tree_t *patricia, void_fn_t func) | |
179 | { | |
180 | patricia_node_t *node; | |
181 | assert (func); | |
182 | ||
183 | PATRICIA_WALK (patricia->head, node) { | |
184 | func (node->prefix); | |
185 | } PATRICIA_WALK_END; | |
186 | } | |
187 | ||
188 | size_t | |
189 | patricia_walk_inorder(patricia_node_t *node, void_fn_t func) | |
190 | { | |
191 | size_t n = 0; | |
192 | assert(func); | |
193 | ||
194 | if (node->l) { | |
195 | n += patricia_walk_inorder(node->l, func); | |
196 | } | |
197 | ||
198 | if (node->prefix) { | |
199 | func(node->prefix); | |
200 | n++; | |
201 | } | |
202 | ||
203 | if (node->r) { | |
204 | n += patricia_walk_inorder(node->r, func); | |
205 | } | |
206 | ||
207 | return n; | |
208 | } | |
209 | ||
210 | ||
211 | patricia_node_t * | |
212 | patricia_search_exact (patricia_tree_t *patricia, struct irc_in_addr *sin, unsigned char bitlen) | |
213 | { | |
214 | patricia_node_t *node; | |
215 | u_char *addr; | |
216 | ||
217 | assert (patricia); | |
218 | assert (sin); | |
219 | assert (bitlen <= patricia->maxbits); | |
220 | ||
221 | if (patricia->head == NULL) | |
222 | return (NULL); | |
223 | ||
224 | node = patricia->head; | |
225 | addr = (u_char *)sin; | |
226 | ||
227 | while (node->bit < bitlen) { | |
a9b4ce08 | 228 | if (is_bit_set(addr,node->bit)) |
526e7c1d P |
229 | node = node->r; |
230 | else | |
231 | node = node->l; | |
232 | ||
233 | if (node == NULL) | |
234 | return (NULL); | |
235 | } | |
236 | ||
237 | if (node->bit > bitlen || node->prefix == NULL) | |
238 | return (NULL); | |
239 | assert (node->bit == bitlen); | |
240 | assert (node->bit == node->prefix->bitlen); | |
ba7e4a13 | 241 | if (comp_with_mask (prefix_tochar (node->prefix), addr, bitlen)) { |
526e7c1d P |
242 | return (node); |
243 | } | |
244 | return (NULL); | |
245 | } | |
246 | ||
247 | ||
248 | /* if inclusive != 0, "best" may be the given prefix itself */ | |
249 | patricia_node_t * | |
250 | patricia_search_best2 (patricia_tree_t *patricia, struct irc_in_addr *sin, unsigned char bitlen, int inclusive) | |
251 | { | |
252 | patricia_node_t *node; | |
253 | patricia_node_t *stack[PATRICIA_MAXBITS + 1]; | |
254 | u_char *addr; | |
255 | int cnt = 0; | |
256 | ||
257 | assert (patricia); | |
258 | assert (sin); | |
259 | assert (bitlen <= patricia->maxbits); | |
260 | ||
261 | if (patricia->head == NULL) | |
262 | return (NULL); | |
263 | ||
264 | node = patricia->head; | |
265 | addr = (u_char *)sin; | |
266 | ||
267 | while (node->bit < bitlen) { | |
268 | ||
269 | if (node->prefix) { | |
270 | stack[cnt++] = node; | |
271 | } | |
272 | ||
a9b4ce08 | 273 | if (is_bit_set(addr,node->bit)) { |
526e7c1d P |
274 | node = node->r; |
275 | } | |
276 | else { | |
277 | node = node->l; | |
278 | } | |
279 | ||
280 | if (node == NULL) | |
281 | break; | |
282 | } | |
283 | ||
284 | if (inclusive && node && node->prefix) | |
285 | stack[cnt++] = node; | |
286 | ||
287 | if (cnt <= 0) | |
288 | return (NULL); | |
289 | ||
290 | while (--cnt >= 0) { | |
291 | node = stack[cnt]; | |
292 | if (comp_with_mask (prefix_tochar (node->prefix), | |
293 | addr, | |
294 | node->prefix->bitlen)) { | |
295 | return (node); | |
296 | } | |
297 | } | |
298 | return (NULL); | |
299 | } | |
300 | ||
301 | ||
302 | patricia_node_t * | |
303 | patricia_search_best (patricia_tree_t *patricia, struct irc_in_addr *sin, unsigned char bitlen) | |
304 | { | |
305 | return (patricia_search_best2 (patricia, sin, bitlen, 1)); | |
306 | } | |
307 | ||
308 | ||
309 | patricia_node_t * | |
310 | patricia_lookup (patricia_tree_t *patricia, prefix_t *prefix) | |
311 | { | |
312 | patricia_node_t *node, *new_node, *parent, *glue; | |
313 | u_char *addr, *test_addr; | |
314 | u_int bitlen, check_bit, differ_bit; | |
315 | int i, j, r; | |
316 | ||
317 | assert (patricia); | |
318 | assert (prefix); | |
319 | assert (prefix->bitlen <= patricia->maxbits); | |
320 | ||
321 | /* if new trie, create the first node */ | |
322 | if (patricia->head == NULL) { | |
7a25133f | 323 | node = patricia_new_node(patricia, prefix->bitlen, patricia_ref_prefix (prefix)); |
526e7c1d | 324 | patricia->head = node; |
526e7c1d P |
325 | return (node); |
326 | } | |
327 | ||
328 | addr = prefix_touchar (prefix); | |
329 | bitlen = prefix->bitlen; | |
330 | node = patricia->head; | |
331 | ||
332 | /* while ( bitlength of tree node < bitlength of node we're searching for || the node has no prefix */ | |
333 | while (node->bit < bitlen || node->prefix == NULL) { | |
334 | /* check that we're not at the lowest leaf i.e. node->bit is less than max bits */ | |
335 | if (node->bit < patricia->maxbits && | |
a9b4ce08 | 336 | (is_bit_set(addr,node->bit))) { |
526e7c1d P |
337 | if (node->r == NULL) |
338 | break; | |
339 | node = node->r; | |
340 | } | |
341 | else { | |
342 | if (node->l == NULL) | |
343 | break; | |
344 | node = node->l; | |
345 | } | |
346 | ||
347 | assert (node); | |
348 | } | |
349 | ||
350 | assert (node->prefix); | |
351 | ||
352 | test_addr = prefix_touchar (node->prefix); | |
353 | /* find the first bit different */ | |
354 | check_bit = (node->bit < bitlen)? node->bit: bitlen; | |
355 | differ_bit = 0; | |
356 | for (i = 0; i*8 < check_bit; i++) { | |
357 | if ((r = (addr[i] ^ test_addr[i])) == 0) { | |
358 | differ_bit = (i + 1) * 8; | |
359 | continue; | |
360 | } | |
361 | /* I know the better way, but for now */ | |
ba7e4a13 | 362 | for (j = 0; j < 8; j++) { |
526e7c1d P |
363 | if ((r) & ((0x80 >> j))) |
364 | break; | |
365 | } | |
366 | /* must be found */ | |
367 | assert (j < 8); | |
368 | differ_bit = i * 8 + j; | |
369 | break; | |
370 | } | |
371 | if (differ_bit > check_bit) | |
372 | differ_bit = check_bit; | |
373 | ||
374 | ||
375 | parent = node->parent; | |
376 | while (parent && parent->bit >= differ_bit) { | |
377 | node = parent; | |
378 | parent = node->parent; | |
379 | } | |
380 | ||
381 | if (differ_bit == bitlen && node->bit == bitlen) { | |
950714ee P |
382 | if (!node->prefix) { |
383 | node->prefix = patricia_ref_prefix (prefix); | |
384 | } | |
526e7c1d P |
385 | return (node); |
386 | } | |
387 | ||
7a25133f | 388 | new_node = patricia_new_node(patricia, prefix->bitlen, patricia_ref_prefix (prefix)); |
526e7c1d P |
389 | if (node->bit == differ_bit) { |
390 | new_node->parent = node; | |
391 | if (node->bit < patricia->maxbits && | |
a9b4ce08 | 392 | (is_bit_set(addr, node->bit))) { |
526e7c1d P |
393 | assert (node->r == NULL); |
394 | node->r = new_node; | |
395 | } | |
396 | else { | |
397 | assert (node->l == NULL); | |
398 | node->l = new_node; | |
399 | } | |
400 | return (new_node); | |
401 | } | |
402 | ||
403 | if (bitlen == differ_bit) { | |
404 | if (bitlen < patricia->maxbits && | |
96644df6 | 405 | (is_bit_set(test_addr,bitlen))) { |
526e7c1d P |
406 | new_node->r = node; |
407 | } | |
408 | else { | |
409 | new_node->l = node; | |
410 | } | |
411 | new_node->parent = node->parent; | |
412 | if (node->parent == NULL) { | |
413 | assert (patricia->head == node); | |
414 | patricia->head = new_node; | |
415 | } | |
416 | else if (node->parent->r == node) { | |
417 | node->parent->r = new_node; | |
418 | } | |
419 | else { | |
420 | node->parent->l = new_node; | |
421 | } | |
422 | node->parent = new_node; | |
96644df6 | 423 | new_node->usercount = node->usercount; |
526e7c1d P |
424 | } |
425 | else { | |
7a25133f | 426 | glue = patricia_new_node(patricia, differ_bit, NULL); |
526e7c1d | 427 | glue->parent = node->parent; |
526e7c1d | 428 | if (differ_bit < patricia->maxbits && |
a9b4ce08 | 429 | (is_bit_set(addr, differ_bit))) { |
526e7c1d P |
430 | glue->r = new_node; |
431 | glue->l = node; | |
432 | } | |
433 | else { | |
434 | glue->r = node; | |
435 | glue->l = new_node; | |
436 | } | |
437 | new_node->parent = glue; | |
438 | ||
439 | if (node->parent == NULL) { | |
440 | assert (patricia->head == node); | |
441 | patricia->head = glue; | |
442 | } | |
443 | else if (node->parent->r == node) { | |
444 | node->parent->r = glue; | |
445 | } | |
446 | else { | |
447 | node->parent->l = glue; | |
448 | } | |
449 | node->parent = glue; | |
96644df6 | 450 | glue->usercount = node->usercount; |
526e7c1d | 451 | } |
96644df6 | 452 | |
526e7c1d P |
453 | return (new_node); |
454 | } | |
455 | ||
456 | void | |
457 | patricia_remove (patricia_tree_t *patricia, patricia_node_t *node) | |
458 | { | |
459 | patricia_node_t *parent, *child; | |
460 | ||
461 | assert (patricia); | |
462 | assert (node); | |
463 | ||
464 | if (node->r && node->l) { | |
465 | /* this might be a placeholder node -- have to check and make sure | |
466 | * there is a prefix aossciated with it ! */ | |
467 | if (node->prefix != NULL) | |
468 | patricia_deref_prefix (node->prefix); | |
469 | node->prefix = NULL; | |
470 | return; | |
471 | } | |
472 | ||
473 | if (node->r == NULL && node->l == NULL) { | |
474 | parent = node->parent; | |
475 | patricia_deref_prefix (node->prefix); | |
7a25133f | 476 | freenode(node); |
526e7c1d P |
477 | patricia->num_active_node--; |
478 | ||
479 | if (parent == NULL) { | |
480 | assert (patricia->head == node); | |
481 | patricia->head = NULL; | |
482 | return; | |
483 | } | |
484 | ||
485 | if (parent->r == node) { | |
486 | parent->r = NULL; | |
487 | child = parent->l; | |
488 | } | |
489 | else { | |
490 | assert (parent->l == node); | |
491 | parent->l = NULL; | |
492 | child = parent->r; | |
493 | } | |
494 | ||
495 | if (parent->prefix) | |
496 | return; | |
497 | ||
498 | /* we need to remove parent too */ | |
499 | ||
500 | if (parent->parent == NULL) { | |
501 | assert (patricia->head == parent); | |
502 | patricia->head = child; | |
503 | } | |
504 | else if (parent->parent->r == parent) { | |
505 | parent->parent->r = child; | |
506 | } | |
507 | else { | |
508 | assert (parent->parent->l == parent); | |
509 | parent->parent->l = child; | |
510 | } | |
511 | child->parent = parent->parent; | |
7a25133f | 512 | freenode(parent); |
526e7c1d P |
513 | patricia->num_active_node--; |
514 | return; | |
515 | } | |
516 | ||
517 | if (node->r) { | |
518 | child = node->r; | |
519 | } | |
520 | else { | |
521 | assert (node->l); | |
522 | child = node->l; | |
523 | } | |
524 | parent = node->parent; | |
525 | child->parent = parent; | |
526 | ||
527 | patricia_deref_prefix (node->prefix); | |
7a25133f | 528 | freenode(node); |
526e7c1d P |
529 | patricia->num_active_node--; |
530 | ||
531 | if (parent == NULL) { | |
532 | assert (patricia->head == node); | |
533 | patricia->head = child; | |
534 | return; | |
535 | } | |
536 | ||
537 | if (parent->r == node) { | |
538 | parent->r = child; | |
539 | } | |
540 | else { | |
541 | assert (parent->l == node); | |
542 | parent->l = child; | |
543 | } | |
544 | } | |
545 | ||
daf8fe87 CP |
546 | #ifdef LEAK_DETECTION |
547 | static patricia_node_t *getrealnode(patricia_node_t *node) { | |
548 | struct fake_node *f = (struct fake_node *)node; | |
549 | if(f->magic != MAGIC) { | |
550 | printf("magic failure\n"); | |
551 | abort(); | |
552 | } | |
553 | return f->real_node; | |
554 | } | |
555 | #endif | |
526e7c1d P |
556 | |
557 | patricia_node_t * | |
558 | refnode(patricia_tree_t *tree, struct irc_in_addr *sin, int bitlen) { | |
559 | patricia_node_t *node; | |
560 | prefix_t *prefix; | |
561 | ||
01a924c2 | 562 | node = patricia_search_exact(tree, sin, bitlen); |
526e7c1d P |
563 | |
564 | if (node == NULL) { | |
565 | prefix = patricia_new_prefix(sin, bitlen); | |
566 | node = patricia_lookup(tree, prefix); | |
526e7c1d P |
567 | patricia_deref_prefix(prefix); |
568 | } else if (node->prefix) { | |
569 | patricia_ref_prefix(node->prefix); | |
570 | } | |
571 | ||
daf8fe87 CP |
572 | #ifdef LEAK_DETECTION |
573 | struct fake_node *f = malloc(sizeof(struct fake_node)); | |
574 | f->magic = MAGIC; | |
575 | f->node = *node; | |
576 | f->real_node = node; | |
577 | node = (patricia_node_t *)f; | |
578 | #endif | |
579 | ||
526e7c1d P |
580 | return node; |
581 | } | |
582 | ||
583 | void | |
584 | derefnode(patricia_tree_t *tree, patricia_node_t *node) { | |
585 | if (!node || !node->prefix) | |
586 | return; | |
587 | ||
daf8fe87 CP |
588 | #ifdef LEAK_DETECTION |
589 | patricia_node_t *real_node = getrealnode(node); | |
590 | free(node); | |
591 | node = real_node; | |
592 | ||
593 | if (!node || !node->prefix) | |
594 | return; | |
595 | #endif | |
596 | ||
526e7c1d | 597 | if (node->prefix->ref_count == 1) { |
526e7c1d P |
598 | patricia_remove(tree, node); |
599 | } else | |
600 | patricia_deref_prefix(node->prefix); | |
601 | } | |
7a25133f P |
602 | |
603 | patricia_node_t *patricia_new_node(patricia_tree_t *patricia, unsigned char bit,prefix_t *prefix ) { | |
604 | patricia_node_t *new_node = newnode(); | |
605 | memset(new_node->exts, 0, PATRICIA_MAXSLOTS * sizeof(void *)); | |
606 | new_node->bit = bit; | |
607 | new_node->prefix = prefix; | |
608 | new_node->usercount = 0; | |
609 | new_node->parent = NULL; | |
610 | new_node->l = new_node->r = NULL; | |
611 | patricia->num_active_node++; | |
612 | return new_node; | |
613 | } | |
614 | ||
96644df6 | 615 | void node_increment_usercount( patricia_node_t *node) { |
daf8fe87 CP |
616 | #ifdef LEAK_DETECTION |
617 | node = getrealnode(node); | |
618 | #endif | |
619 | ||
96644df6 P |
620 | while(node) { |
621 | node->usercount++; | |
622 | node=node->parent; | |
623 | } | |
624 | } | |
625 | ||
626 | void node_decrement_usercount( patricia_node_t *node) { | |
daf8fe87 CP |
627 | #ifdef LEAK_DETECTION |
628 | node = getrealnode(node); | |
629 | #endif | |
630 | ||
96644df6 P |
631 | while(node) { |
632 | node->usercount--; | |
633 | node=node->parent; | |
634 | } | |
635 | } | |
2f726f77 P |
636 | |
637 | int is_normalized_ipmask( struct irc_in_addr *sin, unsigned char bitlen ) { | |
638 | u_char *addr = (u_char *)sin; | |
639 | ||
640 | while (bitlen < PATRICIA_MAXBITS) { | |
641 | if (is_bit_set(addr,bitlen)) | |
642 | return 0; | |
643 | bitlen++; | |
644 | } | |
645 | return 1; | |
646 | } |