|  | /* | 
|  | * Copyright (C) 1993-2001, 2003 by Darren Reed. | 
|  | * | 
|  | * See the IPFILTER.LICENCE file for details on licencing. | 
|  | */ | 
|  | #if defined(KERNEL) || defined(_KERNEL) | 
|  | # undef KERNEL | 
|  | # undef _KERNEL | 
|  | # define        KERNEL	1 | 
|  | # define        _KERNEL	1 | 
|  | #endif | 
|  | #if defined(__osf__) | 
|  | # define _PROTO_NET_H_ | 
|  | #endif | 
|  | #include <sys/errno.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/param.h> | 
|  | #include <sys/file.h> | 
|  | #if !defined(_KERNEL) && !defined(__KERNEL__) | 
|  | # include <stdio.h> | 
|  | # include <stdlib.h> | 
|  | # include <string.h> | 
|  | # define _KERNEL | 
|  | # ifdef __OpenBSD__ | 
|  | struct file; | 
|  | # endif | 
|  | # include <sys/uio.h> | 
|  | # undef _KERNEL | 
|  | #else | 
|  | # include <sys/systm.h> | 
|  | # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) | 
|  | #  include <sys/proc.h> | 
|  | # endif | 
|  | #endif | 
|  | #include <sys/time.h> | 
|  | #if !defined(linux) | 
|  | # include <sys/protosw.h> | 
|  | #endif | 
|  | #include <sys/socket.h> | 
|  | #if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__)) | 
|  | # include <sys/mbuf.h> | 
|  | #endif | 
|  | #if defined(__SVR4) || defined(__svr4__) | 
|  | # include <sys/filio.h> | 
|  | # include <sys/byteorder.h> | 
|  | # ifdef _KERNEL | 
|  | #  include <sys/dditypes.h> | 
|  | # endif | 
|  | # include <sys/stream.h> | 
|  | # include <sys/kmem.h> | 
|  | #endif | 
|  | #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) | 
|  | # include <sys/malloc.h> | 
|  | #endif | 
|  |  | 
|  | #if defined(_KERNEL) && (defined(__osf__) || defined(AIX) || \ | 
|  | defined(__hpux) || defined(__sgi)) | 
|  | # ifdef __osf__ | 
|  | #  include <net/radix.h> | 
|  | # endif | 
|  | # include "radix_ipf_local.h" | 
|  | # define _RADIX_H_ | 
|  | #endif | 
|  | #include <net/if.h> | 
|  | #include <netinet/in.h> | 
|  |  | 
|  | #include "netinet/ip_compat.h" | 
|  | #include "netinet/ip_fil.h" | 
|  | #include "netinet/ip_pool.h" | 
|  |  | 
|  | #if defined(IPFILTER_LOOKUP) && defined(_KERNEL) && \ | 
|  | ((BSD >= 198911) && !defined(__osf__) && \ | 
|  | !defined(__hpux) && !defined(__sgi)) | 
|  | static int rn_freenode __P((struct radix_node *, void *)); | 
|  | #endif | 
|  |  | 
|  | /* END OF INCLUDES */ | 
|  |  | 
|  | #if !defined(lint) | 
|  | static const char sccsid[] = "@(#)ip_fil.c	2.41 6/5/96 (C) 1993-2000 Darren Reed"; | 
|  | static const char rcsid[] = "@(#)$Id$"; | 
|  | #endif | 
|  |  | 
|  | #ifdef IPFILTER_LOOKUP | 
|  |  | 
|  | # ifndef RADIX_NODE_HEAD_LOCK | 
|  | #  define RADIX_NODE_HEAD_LOCK(x)	; | 
|  | # endif | 
|  | # ifndef RADIX_NODE_HEAD_UNLOCK | 
|  | #  define RADIX_NODE_HEAD_UNLOCK(x)	; | 
|  | # endif | 
|  |  | 
|  | ip_pool_stat_t ipoolstat; | 
|  | ipfrwlock_t ip_poolrw; | 
|  |  | 
|  | /* | 
|  | * Binary tree routines from Sedgewick and enhanced to do ranges of addresses. | 
|  | * NOTE: Insertion *MUST* be from greatest range to least for it to work! | 
|  | * These should be replaced, eventually, by something else - most notably a | 
|  | * interval searching method.  The important feature is to be able to find | 
|  | * the best match. | 
|  | * | 
|  | * So why not use a radix tree for this?  As the first line implies, it | 
|  | * has been written to work with a _range_ of addresses.  A range is not | 
|  | * necessarily a match with any given netmask so what we end up dealing | 
|  | * with is an interval tree.  Implementations of these are hard to find | 
|  | * and the one herein is far from bug free. | 
|  | * | 
|  | * Sigh, in the end I became convinced that the bugs the code contained did | 
|  | * not make it worthwhile not using radix trees.  For now the radix tree from | 
|  | * 4.4 BSD is used, but this is not viewed as a long term solution. | 
|  | */ | 
|  | ip_pool_t *ip_pool_list[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL, | 
|  | NULL, NULL, NULL, NULL }; | 
|  |  | 
|  |  | 
|  | #ifdef TEST_POOL | 
|  | void treeprint __P((ip_pool_t *)); | 
|  |  | 
|  | int | 
|  | main(argc, argv) | 
|  | int argc; | 
|  | char *argv[]; | 
|  | { | 
|  | addrfamily_t a, b; | 
|  | iplookupop_t op; | 
|  | ip_pool_t *ipo; | 
|  | i6addr_t ip; | 
|  |  | 
|  | RWLOCK_INIT(&ip_poolrw, "poolrw"); | 
|  | ip_pool_init(); | 
|  |  | 
|  | bzero((char *)&a, sizeof(a)); | 
|  | bzero((char *)&b, sizeof(b)); | 
|  | bzero((char *)&ip, sizeof(ip)); | 
|  | bzero((char *)&op, sizeof(op)); | 
|  | strcpy(op.iplo_name, "0"); | 
|  |  | 
|  | if (ip_pool_create(&op) == 0) | 
|  | ipo = ip_pool_find(0, "0"); | 
|  |  | 
|  | a.adf_addr.in4.s_addr = 0x0a010203; | 
|  | b.adf_addr.in4.s_addr = 0xffffffff; | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); | 
|  |  | 
|  | a.adf_addr.in4.s_addr = 0x0a000000; | 
|  | b.adf_addr.in4.s_addr = 0xff000000; | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); | 
|  |  | 
|  | a.adf_addr.in4.s_addr = 0x0a010100; | 
|  | b.adf_addr.in4.s_addr = 0xffffff00; | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); | 
|  |  | 
|  | a.adf_addr.in4.s_addr = 0x0a010200; | 
|  | b.adf_addr.in4.s_addr = 0xffffff00; | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); | 
|  |  | 
|  | a.adf_addr.in4.s_addr = 0x0a010000; | 
|  | b.adf_addr.in4.s_addr = 0xffff0000; | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); | 
|  |  | 
|  | a.adf_addr.in4.s_addr = 0x0a01020f; | 
|  | b.adf_addr.in4.s_addr = 0xffffffff; | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); | 
|  | ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); | 
|  | #ifdef	DEBUG_POOL | 
|  | treeprint(ipo); | 
|  | #endif | 
|  | ip.in4.s_addr = 0x0a00aabb; | 
|  | printf("search(%#x) = %d (0)\n", ip.in4.s_addr, | 
|  | ip_pool_search(ipo, 4, &ip)); | 
|  |  | 
|  | ip.in4.s_addr = 0x0a000001; | 
|  | printf("search(%#x) = %d (0)\n", ip.in4.s_addr, | 
|  | ip_pool_search(ipo, 4, &ip)); | 
|  |  | 
|  | ip.in4.s_addr = 0x0a000101; | 
|  | printf("search(%#x) = %d (0)\n", ip.in4.s_addr, | 
|  | ip_pool_search(ipo, 4, &ip)); | 
|  |  | 
|  | ip.in4.s_addr = 0x0a010001; | 
|  | printf("search(%#x) = %d (1)\n", ip.in4.s_addr, | 
|  | ip_pool_search(ipo, 4, &ip)); | 
|  |  | 
|  | ip.in4.s_addr = 0x0a010101; | 
|  | printf("search(%#x) = %d (1)\n", ip.in4.s_addr, | 
|  | ip_pool_search(ipo, 4, &ip)); | 
|  |  | 
|  | ip.in4.s_addr = 0x0a010201; | 
|  | printf("search(%#x) = %d (0)\n", ip.in4.s_addr, | 
|  | ip_pool_search(ipo, 4, &ip)); | 
|  |  | 
|  | ip.in4.s_addr = 0x0a010203; | 
|  | printf("search(%#x) = %d (1)\n", ip.in4.s_addr, | 
|  | ip_pool_search(ipo, 4, &ip)); | 
|  |  | 
|  | ip.in4.s_addr = 0x0a01020f; | 
|  | printf("search(%#x) = %d (1)\n", ip.in4.s_addr, | 
|  | ip_pool_search(ipo, 4, &ip)); | 
|  |  | 
|  | ip.in4.s_addr = 0x0b00aabb; | 
|  | printf("search(%#x) = %d (-1)\n", ip.in4.s_addr, | 
|  | ip_pool_search(ipo, 4, &ip)); | 
|  |  | 
|  | #ifdef	DEBUG_POOL | 
|  | treeprint(ipo); | 
|  | #endif | 
|  |  | 
|  | ip_pool_fini(); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | treeprint(ipo) | 
|  | ip_pool_t *ipo; | 
|  | { | 
|  | ip_pool_node_t *c; | 
|  |  | 
|  | for (c = ipo->ipo_list; c != NULL; c = c->ipn_next) | 
|  | printf("Node %p(%s) (%#x/%#x) = %d hits %lu\n", | 
|  | c, c->ipn_name, c->ipn_addr.adf_addr.in4.s_addr, | 
|  | c->ipn_mask.adf_addr.in4.s_addr, | 
|  | c->ipn_info, c->ipn_hits); | 
|  | } | 
|  | #endif /* TEST_POOL */ | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_init                                                */ | 
|  | /* Returns:     int     - 0 = success, else error                           */ | 
|  | /*                                                                          */ | 
|  | /* Initialise the routing table data structures where required.             */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | int ip_pool_init() | 
|  | { | 
|  |  | 
|  | bzero((char *)&ipoolstat, sizeof(ipoolstat)); | 
|  |  | 
|  | #if (!defined(_KERNEL) || (BSD < 199306)) | 
|  | rn_init(); | 
|  | #endif | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_fini                                                */ | 
|  | /* Returns:     int     - 0 = success, else error                           */ | 
|  | /* Locks:       WRITE(ipf_global)                                           */ | 
|  | /*                                                                          */ | 
|  | /* Clean up all the pool data structures allocated and call the cleanup     */ | 
|  | /* function for the radix tree that supports the pools. ip_pool_destroy() is*/ | 
|  | /* used to delete the pools one by one to ensure they're properly freed up. */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | void ip_pool_fini() | 
|  | { | 
|  | ip_pool_t *p, *q; | 
|  | iplookupop_t op; | 
|  | int i; | 
|  |  | 
|  | ASSERT(rw_read_locked(&ipf_global.ipf_lk) == 0); | 
|  |  | 
|  | for (i = 0; i <= IPL_LOGMAX; i++) { | 
|  | for (q = ip_pool_list[i]; (p = q) != NULL; ) { | 
|  | op.iplo_unit = i; | 
|  | (void)strncpy(op.iplo_name, p->ipo_name, | 
|  | sizeof(op.iplo_name)); | 
|  | q = p->ipo_next; | 
|  | (void) ip_pool_destroy(&op); | 
|  | } | 
|  | } | 
|  |  | 
|  | #if (!defined(_KERNEL) || (BSD < 199306)) | 
|  | rn_fini(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_statistics                                          */ | 
|  | /* Returns:     int     - 0 = success, else error                           */ | 
|  | /* Parameters:  op(I)   - pointer to lookup operation arguments             */ | 
|  | /*                                                                          */ | 
|  | /* Copy the current statistics out into user space, collecting pool list    */ | 
|  | /* pointers as appropriate for later use.                                   */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | int ip_pool_statistics(op) | 
|  | iplookupop_t *op; | 
|  | { | 
|  | ip_pool_stat_t stats; | 
|  | int unit, i, err = 0; | 
|  |  | 
|  | if (op->iplo_size != sizeof(ipoolstat)) | 
|  | return EINVAL; | 
|  |  | 
|  | bcopy((char *)&ipoolstat, (char *)&stats, sizeof(stats)); | 
|  | unit = op->iplo_unit; | 
|  | if (unit == IPL_LOGALL) { | 
|  | for (i = 0; i < IPL_LOGSIZE; i++) | 
|  | stats.ipls_list[i] = ip_pool_list[i]; | 
|  | } else if (unit >= 0 && unit < IPL_LOGSIZE) { | 
|  | if (op->iplo_name[0] != '\0') | 
|  | stats.ipls_list[unit] = ip_pool_find(unit, | 
|  | op->iplo_name); | 
|  | else | 
|  | stats.ipls_list[unit] = ip_pool_list[unit]; | 
|  | } else | 
|  | err = EINVAL; | 
|  | if (err == 0) | 
|  | err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); | 
|  | return err; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_find                                                */ | 
|  | /* Returns:     int     - 0 = success, else error                           */ | 
|  | /* Parameters:  ipo(I)  - pointer to the pool getting the new node.         */ | 
|  | /*                                                                          */ | 
|  | /* Find a matching pool inside the collection of pools for a particular     */ | 
|  | /* device, indicated by the unit number.                                    */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | void *ip_pool_find(unit, name) | 
|  | int unit; | 
|  | char *name; | 
|  | { | 
|  | ip_pool_t *p; | 
|  |  | 
|  | for (p = ip_pool_list[unit]; p != NULL; p = p->ipo_next) | 
|  | if (strncmp(p->ipo_name, name, sizeof(p->ipo_name)) == 0) | 
|  | break; | 
|  | return p; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_findeq                                              */ | 
|  | /* Returns:     int     - 0 = success, else error                           */ | 
|  | /* Parameters:  ipo(I)  - pointer to the pool getting the new node.         */ | 
|  | /*              addr(I) - pointer to address information to delete          */ | 
|  | /*              mask(I) -                                                   */ | 
|  | /*                                                                          */ | 
|  | /* Searches for an exact match of an entry in the pool.                     */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | ip_pool_node_t *ip_pool_findeq(ipo, addr, mask) | 
|  | ip_pool_t *ipo; | 
|  | addrfamily_t *addr, *mask; | 
|  | { | 
|  | struct radix_node *n; | 
|  | SPL_INT(s); | 
|  |  | 
|  | SPL_NET(s); | 
|  | RADIX_NODE_HEAD_LOCK(ipo->ipo_head); | 
|  | n = ipo->ipo_head->rnh_lookup(addr, mask, ipo->ipo_head); | 
|  | RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); | 
|  | SPL_X(s); | 
|  | return (ip_pool_node_t *)n; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_search                                              */ | 
|  | /* Returns:     int     - 0 == +ve match, -1 == error, 1 == -ve/no match    */ | 
|  | /* Parameters:  tptr(I)    - pointer to the pool to search                  */ | 
|  | /*              version(I) - IP protocol version (4 or 6)                   */ | 
|  | /*              dptr(I)    - pointer to address information                 */ | 
|  | /*                                                                          */ | 
|  | /* Search the pool for a given address and return a search result.          */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | int ip_pool_search(tptr, version, dptr) | 
|  | void *tptr; | 
|  | int version; | 
|  | void *dptr; | 
|  | { | 
|  | struct radix_node *rn; | 
|  | ip_pool_node_t *m; | 
|  | i6addr_t *addr; | 
|  | addrfamily_t v; | 
|  | ip_pool_t *ipo; | 
|  | int rv; | 
|  |  | 
|  | ipo = tptr; | 
|  | if (ipo == NULL) | 
|  | return -1; | 
|  |  | 
|  | rv = 1; | 
|  | m = NULL; | 
|  | addr = (i6addr_t *)dptr; | 
|  | bzero(&v, sizeof(v)); | 
|  | v.adf_len = offsetof(addrfamily_t, adf_addr); | 
|  |  | 
|  | if (version == 4) { | 
|  | v.adf_len += sizeof(addr->in4); | 
|  | v.adf_addr.in4 = addr->in4; | 
|  | #ifdef USE_INET6 | 
|  | } else if (version == 6) { | 
|  | v.adf_len += sizeof(addr->in6); | 
|  | v.adf_addr.in6 = addr->in6; | 
|  | #endif | 
|  | } else | 
|  | return -1; | 
|  |  | 
|  | READ_ENTER(&ip_poolrw); | 
|  |  | 
|  | RADIX_NODE_HEAD_LOCK(ipo->ipo_head); | 
|  | rn = ipo->ipo_head->rnh_matchaddr(&v, ipo->ipo_head); | 
|  | RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); | 
|  |  | 
|  | if ((rn != NULL) && ((rn->rn_flags & RNF_ROOT) == 0)) { | 
|  | m = (ip_pool_node_t *)rn; | 
|  | ipo->ipo_hits++; | 
|  | m->ipn_hits++; | 
|  | rv = m->ipn_info; | 
|  | } | 
|  | RWLOCK_EXIT(&ip_poolrw); | 
|  | return rv; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_insert                                              */ | 
|  | /* Returns:     int     - 0 = success, else error                           */ | 
|  | /* Parameters:  ipo(I)  - pointer to the pool getting the new node.         */ | 
|  | /*              addr(I) - address being added as a node                     */ | 
|  | /*              mask(I) - netmask to with the node being added              */ | 
|  | /*              info(I) - extra information to store in this node.          */ | 
|  | /* Locks:       WRITE(ip_poolrw)                                            */ | 
|  | /*                                                                          */ | 
|  | /* Add another node to the pool given by ipo.  The three parameters passed  */ | 
|  | /* in (addr, mask, info) shold all be stored in the node.                   */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | int ip_pool_insert(ipo, addr, mask, info) | 
|  | ip_pool_t *ipo; | 
|  | i6addr_t *addr, *mask; | 
|  | int info; | 
|  | { | 
|  | struct radix_node *rn; | 
|  | ip_pool_node_t *x; | 
|  |  | 
|  | ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); | 
|  |  | 
|  | KMALLOC(x, ip_pool_node_t *); | 
|  | if (x == NULL) { | 
|  | return ENOMEM; | 
|  | } | 
|  |  | 
|  | bzero(x, sizeof(*x)); | 
|  |  | 
|  | x->ipn_info = info; | 
|  | (void)strncpy(x->ipn_name, ipo->ipo_name, sizeof(x->ipn_name)); | 
|  |  | 
|  | bcopy(addr, &x->ipn_addr.adf_addr, sizeof(*addr)); | 
|  | x->ipn_addr.adf_len = sizeof(x->ipn_addr); | 
|  | bcopy(mask, &x->ipn_mask.adf_addr, sizeof(*mask)); | 
|  | x->ipn_mask.adf_len = sizeof(x->ipn_mask); | 
|  |  | 
|  | RADIX_NODE_HEAD_LOCK(ipo->ipo_head); | 
|  | rn = ipo->ipo_head->rnh_addaddr(&x->ipn_addr, &x->ipn_mask, | 
|  | ipo->ipo_head, x->ipn_nodes); | 
|  | RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); | 
|  | #ifdef	DEBUG_POOL | 
|  | printf("Added %p at %p\n", x, rn); | 
|  | #endif | 
|  |  | 
|  | if (rn == NULL) { | 
|  | KFREE(x); | 
|  | return ENOMEM; | 
|  | } | 
|  |  | 
|  | x->ipn_ref = 1; | 
|  | x->ipn_next = ipo->ipo_list; | 
|  | x->ipn_pnext = &ipo->ipo_list; | 
|  | if (ipo->ipo_list != NULL) | 
|  | ipo->ipo_list->ipn_pnext = &x->ipn_next; | 
|  | ipo->ipo_list = x; | 
|  |  | 
|  | ipoolstat.ipls_nodes++; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_create                                              */ | 
|  | /* Returns:     int     - 0 = success, else error                           */ | 
|  | /* Parameters:  op(I) - pointer to iplookup struct with call details        */ | 
|  | /* Locks:       WRITE(ip_poolrw)                                            */ | 
|  | /*                                                                          */ | 
|  | /* Creates a new group according to the paramters passed in via the         */ | 
|  | /* iplookupop structure.  Does not check to see if the group already exists */ | 
|  | /* when being inserted - assume this has already been done.  If the pool is */ | 
|  | /* marked as being anonymous, give it a new, unique, identifier.  Call any  */ | 
|  | /* other functions required to initialise the structure.                    */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | int ip_pool_create(op) | 
|  | iplookupop_t *op; | 
|  | { | 
|  | char name[FR_GROUPLEN]; | 
|  | int poolnum, unit; | 
|  | ip_pool_t *h; | 
|  |  | 
|  | ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); | 
|  |  | 
|  | KMALLOC(h, ip_pool_t *); | 
|  | if (h == NULL) | 
|  | return ENOMEM; | 
|  | bzero(h, sizeof(*h)); | 
|  |  | 
|  | if (rn_inithead((void **)&h->ipo_head, | 
|  | offsetof(addrfamily_t, adf_addr) << 3) == 0) { | 
|  | KFREE(h); | 
|  | return ENOMEM; | 
|  | } | 
|  |  | 
|  | unit = op->iplo_unit; | 
|  |  | 
|  | if ((op->iplo_arg & IPOOL_ANON) != 0) { | 
|  | ip_pool_t *p; | 
|  |  | 
|  | poolnum = IPOOL_ANON; | 
|  |  | 
|  | #if defined(SNPRINTF) && defined(_KERNEL) | 
|  | SNPRINTF(name, sizeof(name), "%x", poolnum); | 
|  | #else | 
|  | (void)sprintf(name, "%x", poolnum); | 
|  | #endif | 
|  |  | 
|  | for (p = ip_pool_list[unit]; p != NULL; ) { | 
|  | if (strncmp(name, p->ipo_name, | 
|  | sizeof(p->ipo_name)) == 0) { | 
|  | poolnum++; | 
|  | #if defined(SNPRINTF) && defined(_KERNEL) | 
|  | SNPRINTF(name, sizeof(name), "%x", poolnum); | 
|  | #else | 
|  | (void)sprintf(name, "%x", poolnum); | 
|  | #endif | 
|  | p = ip_pool_list[unit]; | 
|  | } else | 
|  | p = p->ipo_next; | 
|  | } | 
|  |  | 
|  | (void)strncpy(h->ipo_name, name, sizeof(h->ipo_name)); | 
|  | (void)strncpy(op->iplo_name, name, sizeof(op->iplo_name)); | 
|  | } else { | 
|  | (void) strncpy(h->ipo_name, op->iplo_name, sizeof(h->ipo_name)); | 
|  | } | 
|  |  | 
|  | h->ipo_ref = 1; | 
|  | h->ipo_list = NULL; | 
|  | h->ipo_unit = unit; | 
|  | h->ipo_next = ip_pool_list[unit]; | 
|  | if (ip_pool_list[unit] != NULL) | 
|  | ip_pool_list[unit]->ipo_pnext = &h->ipo_next; | 
|  | h->ipo_pnext = &ip_pool_list[unit]; | 
|  | ip_pool_list[unit] = h; | 
|  |  | 
|  | ipoolstat.ipls_pools++; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_remove                                              */ | 
|  | /* Returns:     int    - 0 = success, else error                            */ | 
|  | /* Parameters:  ipo(I) - pointer to the pool to remove the node from.       */ | 
|  | /*              ipe(I) - address being deleted as a node                    */ | 
|  | /* Locks:       WRITE(ip_poolrw)                                            */ | 
|  | /*                                                                          */ | 
|  | /* Remove a node from the pool given by ipo.                                */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | int ip_pool_remove(ipo, ipe) | 
|  | ip_pool_t *ipo; | 
|  | ip_pool_node_t *ipe; | 
|  | { | 
|  |  | 
|  | ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); | 
|  |  | 
|  | if (ipe->ipn_pnext != NULL) | 
|  | *ipe->ipn_pnext = ipe->ipn_next; | 
|  | if (ipe->ipn_next != NULL) | 
|  | ipe->ipn_next->ipn_pnext = ipe->ipn_pnext; | 
|  |  | 
|  | RADIX_NODE_HEAD_LOCK(ipo->ipo_head); | 
|  | ipo->ipo_head->rnh_deladdr(&ipe->ipn_addr, &ipe->ipn_mask, | 
|  | ipo->ipo_head); | 
|  | RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); | 
|  |  | 
|  | ip_pool_node_deref(ipe); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_destroy                                             */ | 
|  | /* Returns:     int    - 0 = success, else error                            */ | 
|  | /* Parameters:  op(I)  -  information about the pool to remove              */ | 
|  | /* Locks:       WRITE(ip_poolrw) or WRITE(ipf_global)                       */ | 
|  | /*                                                                          */ | 
|  | /* Search for a pool using paramters passed in and if it's not otherwise    */ | 
|  | /* busy, free it.                                                           */ | 
|  | /*                                                                          */ | 
|  | /* NOTE: Because this function is called out of ipfdetach() where ip_poolrw */ | 
|  | /* may not be initialised, we can't use an ASSERT to enforce the locking    */ | 
|  | /* assertion that one of the two (ip_poolrw,ipf_global) is held.            */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | int ip_pool_destroy(op) | 
|  | iplookupop_t *op; | 
|  | { | 
|  | ip_pool_t *ipo; | 
|  |  | 
|  | ipo = ip_pool_find(op->iplo_unit, op->iplo_name); | 
|  | if (ipo == NULL) | 
|  | return ESRCH; | 
|  |  | 
|  | if (ipo->ipo_ref != 1) | 
|  | return EBUSY; | 
|  |  | 
|  | ip_pool_free(ipo); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_flush                                               */ | 
|  | /* Returns:     int    - number of pools deleted                            */ | 
|  | /* Parameters:  fp(I)  - which pool(s) to flush                             */ | 
|  | /* Locks:       WRITE(ip_poolrw) or WRITE(ipf_global)                       */ | 
|  | /*                                                                          */ | 
|  | /* Free all pools associated with the device that matches the unit number   */ | 
|  | /* passed in with operation.                                                */ | 
|  | /*                                                                          */ | 
|  | /* NOTE: Because this function is called out of ipfdetach() where ip_poolrw */ | 
|  | /* may not be initialised, we can't use an ASSERT to enforce the locking    */ | 
|  | /* assertion that one of the two (ip_poolrw,ipf_global) is held.            */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | int ip_pool_flush(fp) | 
|  | iplookupflush_t *fp; | 
|  | { | 
|  | int i, num = 0, unit, err; | 
|  | ip_pool_t *p, *q; | 
|  | iplookupop_t op; | 
|  |  | 
|  | unit = fp->iplf_unit; | 
|  |  | 
|  | for (i = 0; i <= IPL_LOGMAX; i++) { | 
|  | if (unit != IPLT_ALL && i != unit) | 
|  | continue; | 
|  | for (q = ip_pool_list[i]; (p = q) != NULL; ) { | 
|  | op.iplo_unit = i; | 
|  | (void)strncpy(op.iplo_name, p->ipo_name, | 
|  | sizeof(op.iplo_name)); | 
|  | q = p->ipo_next; | 
|  | err = ip_pool_destroy(&op); | 
|  | if (err == 0) | 
|  | num++; | 
|  | else | 
|  | break; | 
|  | } | 
|  | } | 
|  | return num; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_free                                                */ | 
|  | /* Returns:     void                                                        */ | 
|  | /* Parameters:  ipo(I) -  pointer to pool structure                         */ | 
|  | /* Locks:       WRITE(ip_poolrw) or WRITE(ipf_global)                       */ | 
|  | /*                                                                          */ | 
|  | /* Deletes the pool strucutre passed in from the list of pools and deletes  */ | 
|  | /* all of the address information stored in it, including any tree data     */ | 
|  | /* structures also allocated.                                               */ | 
|  | /*                                                                          */ | 
|  | /* NOTE: Because this function is called out of ipfdetach() where ip_poolrw */ | 
|  | /* may not be initialised, we can't use an ASSERT to enforce the locking    */ | 
|  | /* assertion that one of the two (ip_poolrw,ipf_global) is held.            */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | void ip_pool_free(ipo) | 
|  | ip_pool_t *ipo; | 
|  | { | 
|  | ip_pool_node_t *n; | 
|  |  | 
|  | RADIX_NODE_HEAD_LOCK(ipo->ipo_head); | 
|  | while ((n = ipo->ipo_list) != NULL) { | 
|  | ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask, | 
|  | ipo->ipo_head); | 
|  |  | 
|  | *n->ipn_pnext = n->ipn_next; | 
|  | if (n->ipn_next) | 
|  | n->ipn_next->ipn_pnext = n->ipn_pnext; | 
|  |  | 
|  | KFREE(n); | 
|  |  | 
|  | ipoolstat.ipls_nodes--; | 
|  | } | 
|  | RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); | 
|  |  | 
|  | ipo->ipo_list = NULL; | 
|  | if (ipo->ipo_next != NULL) | 
|  | ipo->ipo_next->ipo_pnext = ipo->ipo_pnext; | 
|  | *ipo->ipo_pnext = ipo->ipo_next; | 
|  | rn_freehead(ipo->ipo_head); | 
|  | KFREE(ipo); | 
|  |  | 
|  | ipoolstat.ipls_pools--; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_deref                                               */ | 
|  | /* Returns:     void                                                        */ | 
|  | /* Parameters:  ipo(I) -  pointer to pool structure                         */ | 
|  | /* Locks:       WRITE(ip_poolrw)                                            */ | 
|  | /*                                                                          */ | 
|  | /* Drop the number of known references to this pool structure by one and if */ | 
|  | /* we arrive at zero known references, free it.                             */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | void ip_pool_deref(ipo) | 
|  | ip_pool_t *ipo; | 
|  | { | 
|  |  | 
|  | ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); | 
|  |  | 
|  | ipo->ipo_ref--; | 
|  | if (ipo->ipo_ref == 0) | 
|  | ip_pool_free(ipo); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_node_deref                                          */ | 
|  | /* Returns:     void                                                        */ | 
|  | /* Parameters:  ipn(I) - pointer to pool structure                          */ | 
|  | /* Locks:       WRITE(ip_poolrw)                                            */ | 
|  | /*                                                                          */ | 
|  | /* Drop a reference to the pool node passed in and if we're the last, free  */ | 
|  | /* it all up and adjust the stats accordingly.                              */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | void ip_pool_node_deref(ipn) | 
|  | ip_pool_node_t *ipn; | 
|  | { | 
|  |  | 
|  | ipn->ipn_ref--; | 
|  |  | 
|  | if (ipn->ipn_ref == 0) { | 
|  | KFREE(ipn); | 
|  | ipoolstat.ipls_nodes--; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_getnext                                             */ | 
|  | /* Returns:     void                                                        */ | 
|  | /* Parameters:  token(I) - pointer to pool structure                        */ | 
|  | /* Parameters:  ilp(IO)   - pointer to pool iterating structure             */ | 
|  | /*                                                                          */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | int ip_pool_getnext(token, ilp) | 
|  | ipftoken_t *token; | 
|  | ipflookupiter_t *ilp; | 
|  | { | 
|  | ip_pool_node_t *node, zn, *nextnode; | 
|  | ip_pool_t *ipo, zp, *nextipo; | 
|  | int err; | 
|  |  | 
|  | err = 0; | 
|  | node = NULL; | 
|  | nextnode = NULL; | 
|  | ipo = NULL; | 
|  | nextipo = NULL; | 
|  |  | 
|  | READ_ENTER(&ip_poolrw); | 
|  |  | 
|  | switch (ilp->ili_otype) | 
|  | { | 
|  | case IPFLOOKUPITER_LIST : | 
|  | ipo = token->ipt_data; | 
|  | if (ipo == NULL) { | 
|  | nextipo = ip_pool_list[(int)ilp->ili_unit]; | 
|  | } else { | 
|  | nextipo = ipo->ipo_next; | 
|  | } | 
|  |  | 
|  | if (nextipo != NULL) { | 
|  | ATOMIC_INC(nextipo->ipo_ref); | 
|  | if (nextipo->ipo_next == NULL) | 
|  | token->ipt_alive = 0; | 
|  | } else { | 
|  | bzero((char *)&zp, sizeof(zp)); | 
|  | nextipo = &zp; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case IPFLOOKUPITER_NODE : | 
|  | node = token->ipt_data; | 
|  | if (node == NULL) { | 
|  | ipo = ip_pool_find(ilp->ili_unit, ilp->ili_name); | 
|  | if (ipo == NULL) | 
|  | err = ESRCH; | 
|  | else { | 
|  | nextnode = ipo->ipo_list; | 
|  | ipo = NULL; | 
|  | } | 
|  | } else { | 
|  | nextnode = node->ipn_next; | 
|  | } | 
|  |  | 
|  | if (nextnode != NULL) { | 
|  | ATOMIC_INC(nextnode->ipn_ref); | 
|  | if (nextnode->ipn_next == NULL) | 
|  | token->ipt_alive = 0; | 
|  | } else { | 
|  | bzero((char *)&zn, sizeof(zn)); | 
|  | nextnode = &zn; | 
|  | } | 
|  | break; | 
|  | default : | 
|  | err = EINVAL; | 
|  | break; | 
|  | } | 
|  |  | 
|  | RWLOCK_EXIT(&ip_poolrw); | 
|  |  | 
|  | if (err != 0) | 
|  | return err; | 
|  |  | 
|  | switch (ilp->ili_otype) | 
|  | { | 
|  | case IPFLOOKUPITER_LIST : | 
|  | if (ipo != NULL) { | 
|  | WRITE_ENTER(&ip_poolrw); | 
|  | ip_pool_deref(ipo); | 
|  | RWLOCK_EXIT(&ip_poolrw); | 
|  | } | 
|  | token->ipt_data = nextipo; | 
|  | err = COPYOUT(nextipo, ilp->ili_data, sizeof(*nextipo)); | 
|  | if (err != 0) | 
|  | err = EFAULT; | 
|  | break; | 
|  |  | 
|  | case IPFLOOKUPITER_NODE : | 
|  | if (node != NULL) { | 
|  | WRITE_ENTER(&ip_poolrw); | 
|  | ip_pool_node_deref(node); | 
|  | RWLOCK_EXIT(&ip_poolrw); | 
|  | } | 
|  | token->ipt_data = nextnode; | 
|  | err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); | 
|  | if (err != 0) | 
|  | err = EFAULT; | 
|  | break; | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | /* Function:    ip_pool_iterderef                                           */ | 
|  | /* Returns:     void                                                        */ | 
|  | /* Parameters:  ipn(I) - pointer to pool structure                          */ | 
|  | /* Locks:       WRITE(ip_poolrw)                                            */ | 
|  | /*                                                                          */ | 
|  | /* ------------------------------------------------------------------------ */ | 
|  | void ip_pool_iterderef(otype, unit, data) | 
|  | u_int otype; | 
|  | int unit; | 
|  | void *data; | 
|  | { | 
|  |  | 
|  | if (data == NULL) | 
|  | return; | 
|  |  | 
|  | if (unit < 0 || unit > IPL_LOGMAX) | 
|  | return; | 
|  |  | 
|  | switch (otype) | 
|  | { | 
|  | case IPFLOOKUPITER_LIST : | 
|  | WRITE_ENTER(&ip_poolrw); | 
|  | ip_pool_deref((ip_pool_t *)data); | 
|  | RWLOCK_EXIT(&ip_poolrw); | 
|  | break; | 
|  |  | 
|  | case IPFLOOKUPITER_NODE : | 
|  | WRITE_ENTER(&ip_poolrw); | 
|  | ip_pool_node_deref((ip_pool_node_t *)data); | 
|  | RWLOCK_EXIT(&ip_poolrw); | 
|  | break; | 
|  | default : | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | # if defined(_KERNEL) && ((BSD >= 198911) && !defined(__osf__) && \ | 
|  | !defined(__hpux) && !defined(__sgi)) | 
|  | static int | 
|  | rn_freenode(struct radix_node *n, void *p) | 
|  | { | 
|  | struct radix_node_head *rnh = p; | 
|  | struct radix_node *d; | 
|  |  | 
|  | d = rnh->rnh_deladdr(n->rn_key, NULL, rnh); | 
|  | if (d != NULL) { | 
|  | FreeS(d, max_keylen + 2 * sizeof (*d)); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | rn_freehead(rnh) | 
|  | struct radix_node_head *rnh; | 
|  | { | 
|  |  | 
|  | RADIX_NODE_HEAD_LOCK(rnh); | 
|  | (*rnh->rnh_walktree)(rnh, rn_freenode, rnh); | 
|  |  | 
|  | rnh->rnh_addaddr = NULL; | 
|  | rnh->rnh_deladdr = NULL; | 
|  | rnh->rnh_matchaddr = NULL; | 
|  | rnh->rnh_lookup = NULL; | 
|  | rnh->rnh_walktree = NULL; | 
|  | RADIX_NODE_HEAD_UNLOCK(rnh); | 
|  |  | 
|  | Free(rnh); | 
|  | } | 
|  | # endif | 
|  | #endif /* IPFILTER_LOOKUP */ |