1 /* 2 * Copyright (C) 1993-2001, 2003 by Darren Reed. 3 * 4 * See the IPFILTER.LICENCE file for details on licencing. 5 * 6 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 7 * Use is subject to license terms. 8 */ 9 10 #pragma ident "%Z%%M% %I% %E% SMI" 11 12 #if defined(KERNEL) || defined(_KERNEL) 13 # undef KERNEL 14 # undef _KERNEL 15 # define KERNEL 1 16 # define _KERNEL 1 17 #endif 18 #include <sys/errno.h> 19 #include <sys/types.h> 20 #include <sys/param.h> 21 #include <sys/file.h> 22 #if !defined(_KERNEL) && !defined(__KERNEL__) 23 # include <stdio.h> 24 # include <stdlib.h> 25 # include <string.h> 26 # define _KERNEL 27 # ifdef __OpenBSD__ 28 struct file; 29 # endif 30 # include <sys/uio.h> 31 # undef _KERNEL 32 #else 33 # include <sys/systm.h> 34 # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) 35 # include <sys/proc.h> 36 # endif 37 #endif 38 #include <sys/time.h> 39 #if !defined(linux) 40 # include <sys/protosw.h> 41 #endif 42 #include <sys/socket.h> 43 #if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__)) 44 # include <sys/mbuf.h> 45 #endif 46 #if defined(__SVR4) || defined(__svr4__) 47 # include <sys/filio.h> 48 # include <sys/byteorder.h> 49 # ifdef _KERNEL 50 # include <sys/dditypes.h> 51 # endif 52 # include <sys/stream.h> 53 # include <sys/kmem.h> 54 #endif 55 #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) 56 # include <sys/malloc.h> 57 #endif 58 59 #include <net/if.h> 60 #include <netinet/in.h> 61 62 #if SOLARIS2 >= 10 63 #include "ip_compat.h" 64 #include "ip_fil.h" 65 #include "ip_pool.h" 66 #else 67 #include "netinet/ip_compat.h" 68 #include "netinet/ip_fil.h" 69 #include "netinet/ip_pool.h" 70 #endif 71 /* END OF INCLUDES */ 72 73 #if !defined(lint) 74 static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; 75 static const char rcsid[] = "@(#)$Id: ip_pool.c,v 2.45 2003/11/12 09:34:31 darrenr Exp $"; 76 #endif 77 78 #ifdef IPFILTER_LOOKUP 79 80 ip_pool_stat_t ipoolstat; 81 82 /* 83 * Binary tree routines from Sedgewick and enhanced to do ranges of addresses. 84 * NOTE: Insertion *MUST* be from greatest range to least for it to work! 85 * These should be replaced, eventually, by something else - most notably a 86 * interval searching method. The important feature is to be able to find 87 * the best match. 88 * 89 * So why not use a radix tree for this? As the first line implies, it 90 * has been written to work with a _range_ of addresses. A range is not 91 * necessarily a match with any given netmask so what we end up dealing 92 * with is an interval tree. Implementations of these are hard to find 93 * and the one herein is far from bug free. 94 * 95 * Sigh, in the end I became convinced that the bugs the code contained did 96 * not make it worthwhile not using radix trees. For now the radix tree from 97 * 4.4 BSD is used, but this is not viewed as a long term solution. 98 */ 99 ip_pool_t *ip_pool_list[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL, 100 NULL, NULL, NULL, NULL }; 101 102 103 #ifdef TEST_POOL 104 ipfrwlock_t ip_poolrw; 105 void treeprint __P((ip_pool_t *)); 106 107 int 108 main(argc, argv) 109 int argc; 110 char *argv[]; 111 { 112 addrfamily_t a, b; 113 iplookupop_t op; 114 ip_pool_t *ipo; 115 i6addr_t ip; 116 117 RWLOCK_INIT(&ip_poolrw, "poolrw"); 118 ip_pool_init(); 119 120 bzero((char *)&a, sizeof(a)); 121 bzero((char *)&b, sizeof(b)); 122 bzero((char *)&ip, sizeof(ip)); 123 bzero((char *)&op, sizeof(op)); 124 strcpy(op.iplo_name, "0"); 125 126 if (ip_pool_create(&op) == 0) 127 ipo = ip_pool_find(0, "0"); 128 129 a.adf_addr.in4.s_addr = 0x0a010203; 130 b.adf_addr.in4.s_addr = 0xffffffff; 131 ip_pool_insert(ipo, &a, &b, 1); 132 ip_pool_insert(ipo, &a, &b, 1); 133 134 a.adf_addr.in4.s_addr = 0x0a000000; 135 b.adf_addr.in4.s_addr = 0xff000000; 136 ip_pool_insert(ipo, &a, &b, 0); 137 ip_pool_insert(ipo, &a, &b, 0); 138 139 a.adf_addr.in4.s_addr = 0x0a010100; 140 b.adf_addr.in4.s_addr = 0xffffff00; 141 ip_pool_insert(ipo, &a, &b, 1); 142 ip_pool_insert(ipo, &a, &b, 1); 143 144 a.adf_addr.in4.s_addr = 0x0a010200; 145 b.adf_addr.in4.s_addr = 0xffffff00; 146 ip_pool_insert(ipo, &a, &b, 0); 147 ip_pool_insert(ipo, &a, &b, 0); 148 149 a.adf_addr.in4.s_addr = 0x0a010000; 150 b.adf_addr.in4.s_addr = 0xffff0000; 151 ip_pool_insert(ipo, &a, &b, 1); 152 ip_pool_insert(ipo, &a, &b, 1); 153 154 a.adf_addr.in4.s_addr = 0x0a01020f; 155 b.adf_addr.in4.s_addr = 0xffffffff; 156 ip_pool_insert(ipo, &a, &b, 1); 157 ip_pool_insert(ipo, &a, &b, 1); 158 #ifdef DEBUG_POOL 159 treeprint(ipo); 160 #endif 161 ip.in4.s_addr = 0x0a00aabb; 162 printf("search(%#x) = %d (0)\n", ip.in4.s_addr, 163 ip_pool_search(ipo, 4, &ip)); 164 165 ip.in4.s_addr = 0x0a000001; 166 printf("search(%#x) = %d (0)\n", ip.in4.s_addr, 167 ip_pool_search(ipo, 4, &ip)); 168 169 ip.in4.s_addr = 0x0a000101; 170 printf("search(%#x) = %d (0)\n", ip.in4.s_addr, 171 ip_pool_search(ipo, 4, &ip)); 172 173 ip.in4.s_addr = 0x0a010001; 174 printf("search(%#x) = %d (1)\n", ip.in4.s_addr, 175 ip_pool_search(ipo, 4, &ip)); 176 177 ip.in4.s_addr = 0x0a010101; 178 printf("search(%#x) = %d (1)\n", ip.in4.s_addr, 179 ip_pool_search(ipo, 4, &ip)); 180 181 ip.in4.s_addr = 0x0a010201; 182 printf("search(%#x) = %d (0)\n", ip.in4.s_addr, 183 ip_pool_search(ipo, 4, &ip)); 184 185 ip.in4.s_addr = 0x0a010203; 186 printf("search(%#x) = %d (1)\n", ip.in4.s_addr, 187 ip_pool_search(ipo, 4, &ip)); 188 189 ip.in4.s_addr = 0x0a01020f; 190 printf("search(%#x) = %d (1)\n", ip.in4.s_addr, 191 ip_pool_search(ipo, 4, &ip)); 192 193 ip.in4.s_addr = 0x0b00aabb; 194 printf("search(%#x) = %d (-1)\n", ip.in4.s_addr, 195 ip_pool_search(ipo, 4, &ip)); 196 197 #ifdef DEBUG_POOL 198 treeprint(ipo); 199 #endif 200 201 ip_pool_fini(); 202 203 return 0; 204 } 205 206 207 void 208 treeprint(ipo) 209 ip_pool_t *ipo; 210 { 211 ip_pool_node_t *c; 212 213 for (c = ipo->ipo_list; c != NULL; c = c->ipn_next) 214 printf("Node %p(%s) (%#x/%#x) = %d hits %lu\n", 215 c, c->ipn_name, c->ipn_addr.adf_addr.in4.s_addr, 216 c->ipn_mask.adf_addr.in4.s_addr, 217 c->ipn_info, c->ipn_hits); 218 } 219 #endif /* TEST_POOL */ 220 221 222 /* ------------------------------------------------------------------------ */ 223 /* Function: ip_pool_init */ 224 /* Returns: int - 0 = success, else error */ 225 /* */ 226 /* Initialise the routing table data structures where required. */ 227 /* ------------------------------------------------------------------------ */ 228 int ip_pool_init() 229 { 230 231 #if !defined(_KERNEL) || (BSD < 199306) 232 rn_init(); 233 #endif 234 return 0; 235 } 236 237 238 /* ------------------------------------------------------------------------ */ 239 /* Function: ip_pool_fini */ 240 /* Returns: int - 0 = success, else error */ 241 /* Locks: WRITE(ipf_global) */ 242 /* */ 243 /* Clean up all the pool data structures allocated and call the cleanup */ 244 /* function for the radix tree that supports the pools. ip_pool_destroy() is*/ 245 /* used to delete the pools one by one to ensure they're properly freed up. */ 246 /* ------------------------------------------------------------------------ */ 247 void ip_pool_fini() 248 { 249 ip_pool_t *p, *q; 250 iplookupop_t op; 251 int i; 252 253 ASSERT(rw_read_locked(&ipf_global.ipf_lk) == 0); 254 255 for (i = 0; i <= IPL_LOGMAX; i++) { 256 for (q = ip_pool_list[i]; (p = q) != NULL; ) { 257 op.iplo_unit = i; 258 (void)strncpy(op.iplo_name, p->ipo_name, 259 sizeof(op.iplo_name)); 260 q = p->ipo_next; 261 (void) ip_pool_destroy(&op); 262 } 263 } 264 265 #if !defined(_KERNEL) || (BSD < 199306) 266 rn_fini(); 267 #endif 268 } 269 270 /* ------------------------------------------------------------------------ */ 271 /* Function: ip_pool_statistics */ 272 /* Returns: int - 0 = success, else error */ 273 /* Parameters: op(I) - pointer to lookup operation arguments */ 274 /* */ 275 /* Copy the current statistics out into user space, collecting pool list */ 276 /* pointers as appropriate for later use. */ 277 /* ------------------------------------------------------------------------ */ 278 int ip_pool_statistics(op) 279 iplookupop_t *op; 280 { 281 ip_pool_stat_t stats; 282 int unit, i, err = 0; 283 284 if (op->iplo_size != sizeof(ipoolstat)) 285 return EINVAL; 286 287 bcopy((char *)&ipoolstat, (char *)&stats, sizeof(stats)); 288 unit = op->iplo_unit; 289 if (unit == IPL_LOGALL) { 290 for (i = 0; i < IPL_LOGSIZE; i++) 291 stats.ipls_list[i] = ip_pool_list[i]; 292 } else if (unit >= 0 && unit < IPL_LOGSIZE) { 293 stats.ipls_list[unit] = ip_pool_list[unit]; 294 } else 295 err = EINVAL; 296 if (err == 0) 297 err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); 298 return err; 299 } 300 301 302 /* ------------------------------------------------------------------------ */ 303 /* Function: ip_pool_find */ 304 /* Returns: int - 0 = success, else error */ 305 /* Parameters: ipo(I) - pointer to the pool getting the new node. */ 306 /* */ 307 /* Find a matching pool inside the collection of pools for a particular */ 308 /* device, indicated by the unit number. */ 309 /* ------------------------------------------------------------------------ */ 310 void *ip_pool_find(unit, name) 311 int unit; 312 char *name; 313 { 314 ip_pool_t *p; 315 316 for (p = ip_pool_list[unit]; p != NULL; p = p->ipo_next) 317 if (strcmp(p->ipo_name, name) == 0) 318 break; 319 return p; 320 } 321 322 323 /* ------------------------------------------------------------------------ */ 324 /* Function: ip_pool_findeq */ 325 /* Returns: int - 0 = success, else error */ 326 /* Parameters: ipo(I) - pointer to the pool getting the new node. */ 327 /* addr(I) - */ 328 /* mask(I) - */ 329 /* */ 330 /* Searches for an exact match of an entry in the pool. */ 331 /* ------------------------------------------------------------------------ */ 332 ip_pool_node_t *ip_pool_findeq(ipo, addr, mask) 333 ip_pool_t *ipo; 334 addrfamily_t *addr, *mask; 335 { 336 struct radix_node *n; 337 338 n = ipo->ipo_head->rnh_lookup(addr, mask, ipo->ipo_head); 339 return (ip_pool_node_t *)n; 340 } 341 342 343 /* ------------------------------------------------------------------------ */ 344 /* Function: ip_pool_search */ 345 /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve match */ 346 /* Parameters: tptr(I) - pointer to the pool to search */ 347 /* version(I) - IP protocol version (4 or 6) */ 348 /* dptr(I) - pointer to address information */ 349 /* */ 350 /* Search the pool for a given address and return a search result. */ 351 /* ------------------------------------------------------------------------ */ 352 int ip_pool_search(tptr, version, dptr) 353 void *tptr; 354 int version; 355 void *dptr; 356 { 357 struct radix_node *rn; 358 ip_pool_node_t *m; 359 i6addr_t *addr; 360 addrfamily_t v; 361 ip_pool_t *ipo; 362 int rv; 363 364 ipo = tptr; 365 if (ipo == NULL) 366 return -1; 367 368 rv = 1; 369 m = NULL; 370 addr = (i6addr_t *)dptr; 371 bzero(&v, sizeof(v)); 372 v.adf_len = offsetof(addrfamily_t, adf_addr); 373 374 if (version == 4) { 375 v.adf_len += sizeof(addr->in4); 376 v.adf_addr.in4 = addr->in4; 377 #ifdef USE_INET6 378 } else if (version == 6) { 379 v.adf_len += sizeof(addr->in6); 380 v.adf_addr.in6 = addr->in6; 381 #endif 382 } else 383 return -1; 384 385 READ_ENTER(&ip_poolrw); 386 387 rn = ipo->ipo_head->rnh_matchaddr(&v, ipo->ipo_head); 388 389 if ((rn != NULL) && ((rn->rn_flags & RNF_ROOT) == 0)) { 390 m = (ip_pool_node_t *)rn; 391 ipo->ipo_hits++; 392 m->ipn_hits++; 393 rv = m->ipn_info; 394 } 395 RWLOCK_EXIT(&ip_poolrw); 396 return rv; 397 } 398 399 400 /* ------------------------------------------------------------------------ */ 401 /* Function: ip_pool_insert */ 402 /* Returns: int - 0 = success, else error */ 403 /* Parameters: ipo(I) - pointer to the pool getting the new node. */ 404 /* addr(I) - IPv4/6 address being added as a node */ 405 /* mask(I) - IPv4/6 netmask to with the node being added */ 406 /* info(I) - extra information to store in this node. */ 407 /* Locks: WRITE(ip_poolrw) */ 408 /* */ 409 /* Add another node to the pool given by ipo. The three parameters passed */ 410 /* in (addr, mask, info) shold all be stored in the node. */ 411 /* ------------------------------------------------------------------------ */ 412 int ip_pool_insert(ipo, addr, mask, info) 413 ip_pool_t *ipo; 414 addrfamily_t *addr, *mask; 415 int info; 416 { 417 struct radix_node *rn; 418 ip_pool_node_t *x; 419 420 ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); 421 422 KMALLOC(x, ip_pool_node_t *); 423 if (x == NULL) { 424 return ENOMEM; 425 } 426 427 bzero(x, sizeof(*x)); 428 429 x->ipn_info = info; 430 (void)strncpy(x->ipn_name, ipo->ipo_name, sizeof(x->ipn_name)); 431 432 bcopy(addr, &x->ipn_addr, sizeof(*addr)); 433 x->ipn_addr.adf_len = sizeof(x->ipn_addr); 434 bcopy(mask, &x->ipn_mask, sizeof(*mask)); 435 x->ipn_mask.adf_len = sizeof(x->ipn_mask); 436 437 rn = ipo->ipo_head->rnh_addaddr(&x->ipn_addr, &x->ipn_mask, 438 ipo->ipo_head, x->ipn_nodes); 439 #ifdef DEBUG_POOL 440 printf("Added %p at %p\n", x, rn); 441 #endif 442 443 if (rn == NULL) { 444 KFREE(x); 445 return ENOMEM; 446 } 447 448 x->ipn_next = ipo->ipo_list; 449 x->ipn_pnext = &ipo->ipo_list; 450 if (ipo->ipo_list != NULL) 451 ipo->ipo_list->ipn_pnext = &x->ipn_next; 452 ipo->ipo_list = x; 453 454 return 0; 455 } 456 457 458 /* ------------------------------------------------------------------------ */ 459 /* Function: ip_pool_create */ 460 /* Returns: int - 0 = success, else error */ 461 /* Parameters: op(I) - pointer to iplookup struct with call details */ 462 /* Locks: WRITE(ip_poolrw) */ 463 /* */ 464 /* Creates a new group according to the paramters passed in via the */ 465 /* iplookupop structure. Does not check to see if the group already exists */ 466 /* when being inserted - assume this has already been done. If the pool is */ 467 /* marked as being anonymous, give it a new, unique, identifier. Call any */ 468 /* other functions required to initialise the structure. */ 469 /* ------------------------------------------------------------------------ */ 470 int ip_pool_create(op) 471 iplookupop_t *op; 472 { 473 char name[FR_GROUPLEN]; 474 int poolnum, unit; 475 ip_pool_t *h; 476 477 ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); 478 479 KMALLOC(h, ip_pool_t *); 480 if (h == NULL) 481 return ENOMEM; 482 bzero(h, sizeof(*h)); 483 484 if (rn_inithead((void **)&h->ipo_head, 485 offsetof(addrfamily_t, adf_addr) << 3) == 0) { 486 KFREE(h); 487 return ENOMEM; 488 } 489 490 unit = op->iplo_unit; 491 492 if ((op->iplo_arg & IPOOL_ANON) != 0) { 493 ip_pool_t *p; 494 495 poolnum = IPOOL_ANON; 496 (void)sprintf(name, "%x", poolnum); 497 for (p = ip_pool_list[unit]; p != NULL; ) { 498 if (strcmp(name, p->ipo_name) == 0) { 499 poolnum++; 500 (void)sprintf(name, "%x", poolnum); 501 p = ip_pool_list[unit]; 502 } else 503 p = p->ipo_next; 504 } 505 506 (void)strncpy(h->ipo_name, name, sizeof(h->ipo_name)); 507 } else { 508 (void) strncpy(h->ipo_name, op->iplo_name, sizeof(h->ipo_name)); 509 } 510 511 h->ipo_ref = 1; 512 h->ipo_list = NULL; 513 h->ipo_unit = unit; 514 h->ipo_next = ip_pool_list[unit]; 515 if (ip_pool_list[unit] != NULL) 516 ip_pool_list[unit]->ipo_pnext = &h->ipo_next; 517 h->ipo_pnext = &ip_pool_list[unit]; 518 ip_pool_list[unit] = h; 519 ipoolstat.ipls_pools++; 520 return 0; 521 } 522 523 524 /* ------------------------------------------------------------------------ */ 525 /* Function: ip_pool_remove */ 526 /* Returns: int - 0 = success, else error */ 527 /* Parameters: ipo(I) - pointer to the pool to remove the node from. */ 528 /* ipe(I) - address being deleted as a node */ 529 /* Locks: WRITE(ip_poolrw) */ 530 /* */ 531 /* Add another node to the pool given by ipo. The three parameters passed */ 532 /* in (addr, mask, info) shold all be stored in the node. */ 533 /* ------------------------------------------------------------------------ */ 534 int ip_pool_remove(ipo, ipe) 535 ip_pool_t *ipo; 536 ip_pool_node_t *ipe; 537 { 538 ip_pool_node_t **ipp, *n; 539 540 ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); 541 542 for (ipp = &ipo->ipo_list; (n = *ipp) != NULL; ipp = &n->ipn_next) { 543 if (ipe == n) { 544 *n->ipn_pnext = n->ipn_next; 545 if (n->ipn_next) 546 n->ipn_next->ipn_pnext = n->ipn_pnext; 547 break; 548 } 549 } 550 551 if (n == NULL) 552 return ENOENT; 553 554 ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask, 555 ipo->ipo_head); 556 KFREE(n); 557 return 0; 558 } 559 560 561 /* ------------------------------------------------------------------------ */ 562 /* Function: ip_pool_destroy */ 563 /* Returns: int - 0 = success, else error */ 564 /* Parameters: op(I) - information about the pool to remove */ 565 /* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ 566 /* */ 567 /* Search for a pool using paramters passed in and if it's not otherwise */ 568 /* busy, free it. */ 569 /* */ 570 /* NOTE: Because this function is called out of ipldetach() where ip_poolrw */ 571 /* may not be initialised, we can't use an ASSERT to enforce the locking */ 572 /* assertion that one of the two (ip_poolrw,ipf_global) is held. */ 573 /* ------------------------------------------------------------------------ */ 574 int ip_pool_destroy(op) 575 iplookupop_t *op; 576 { 577 ip_pool_t *ipo; 578 579 ipo = ip_pool_find(op->iplo_unit, op->iplo_name); 580 if (ipo == NULL) 581 return ESRCH; 582 583 if (ipo->ipo_ref != 1) 584 return EBUSY; 585 586 ip_pool_free(ipo); 587 return 0; 588 } 589 590 591 /* ------------------------------------------------------------------------ */ 592 /* Function: ip_pool_free */ 593 /* Returns: void */ 594 /* Parameters: ipo(I) - pointer to pool structure */ 595 /* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ 596 /* */ 597 /* Deletes the pool strucutre passed in from the list of pools and deletes */ 598 /* all of the address information stored in it, including any tree data */ 599 /* structures also allocated. */ 600 /* */ 601 /* NOTE: Because this function is called out of ipldetach() where ip_poolrw */ 602 /* may not be initialised, we can't use an ASSERT to enforce the locking */ 603 /* assertion that one of the two (ip_poolrw,ipf_global) is held. */ 604 /* ------------------------------------------------------------------------ */ 605 void ip_pool_free(ipo) 606 ip_pool_t *ipo; 607 { 608 ip_pool_node_t *n; 609 610 while ((n = ipo->ipo_list) != NULL) { 611 ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask, 612 ipo->ipo_head); 613 *n->ipn_pnext = n->ipn_next; 614 if (n->ipn_next) 615 n->ipn_next->ipn_pnext = n->ipn_pnext; 616 KFREE(n); 617 } 618 619 ipo->ipo_list = NULL; 620 if (ipo->ipo_next != NULL) 621 ipo->ipo_next->ipo_pnext = ipo->ipo_pnext; 622 *ipo->ipo_pnext = ipo->ipo_next; 623 rn_freehead(ipo->ipo_head); 624 KFREE(ipo); 625 } 626 627 628 /* ------------------------------------------------------------------------ */ 629 /* Function: ip_pool_deref */ 630 /* Returns: void */ 631 /* Parameters: ipo(I) - pointer to pool structure */ 632 /* Locks: WRITE(ip_poolrw) */ 633 /* */ 634 /* Drop the number of known references to this pool structure by one and if */ 635 /* we arrive at zero known references, free it. */ 636 /* ------------------------------------------------------------------------ */ 637 void ip_pool_deref(ipo) 638 ip_pool_t *ipo; 639 { 640 641 ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); 642 643 ipo->ipo_ref--; 644 if (ipo->ipo_ref == 0) 645 ip_pool_free(ipo); 646 } 647 648 649 # if defined(_KERNEL) && (BSD > 199306) 650 static int 651 rn_freenode(struct radix_node *n, void *p) 652 { 653 struct radix_node_head *rnh = p; 654 struct radix_node *d; 655 656 d = rnh->rnh_deladdr(n->rn_key, NULL, rnh); 657 if (d != NULL) { 658 FreeS(d, max_keylen + 2 * sizeof (*d)); 659 } 660 return 0; 661 } 662 663 664 void 665 rn_freehead(rnh) 666 struct radix_node_head *rnh; 667 { 668 669 (void)rn_walktree(rnh, rn_freenode, rnh); 670 671 rnh->rnh_addaddr = NULL; 672 rnh->rnh_deladdr = NULL; 673 rnh->rnh_matchaddr = NULL; 674 rnh->rnh_lookup = NULL; 675 rnh->rnh_walktree = NULL; 676 677 Free(rnh); 678 } 679 # endif 680 681 #endif /* IPFILTER_LOOKUP */ 682