]> jfr.im git - irc/quakenet/newserv.git/blame - patricia/patricialib.c
PATRICIA: add leak detection code to patricia (instead of refcounting always return...
[irc/quakenet/newserv.git] / patricia / patricialib.c
CommitLineData
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 15char 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
35struct 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 */
45u_char *
46prefix_tochar (prefix_t * prefix)
47{
48 if (prefix == NULL)
49 return (NULL);
50
51 return ((u_char *) & prefix->sin);
52}
53
54int
55comp_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
69prefix_t *
70patricia_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
82prefix_t *
83patricia_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
95void
96patricia_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
112patricia_tree_t *
113patricia_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
125void
126patricia_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
164void
165patricia_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
177void
178patricia_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
188size_t
189patricia_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
211patricia_node_t *
212patricia_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 */
249patricia_node_t *
250patricia_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
302patricia_node_t *
303patricia_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
309patricia_node_t *
310patricia_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
456void
457patricia_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
547static 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
557patricia_node_t *
558refnode(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
583void
584derefnode(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
603patricia_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 615void 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
626void 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
637int 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}