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