1098c7bfb7
4.1.33 - Release 16 August 2009 2837931 wrong mode selected in ipf program for hash-entries 2826168 load_http can make ippool core dump 2825150 IPL_LOGMAX used to index some arrays 2825084 ipv6 fragments should not be allowed past 64k 2824713 ipfstat top output alternates between entries and nothing 2824712 ipfstat top output is shows negative ttl 2820965 a single bad ipv6 extension header should not impact others 2818197 ignored fragment bits defined as being reserved 2817667 IPv6 fragment header verification needs attention 2817098 fr_getrulen() finds the wrong rule 2817096 fr_rulen is unused 2741019 Lingering states (Established/Listen - 5/0) in state table 2702887 use of PBR/fastroute causes panic with ipv6 2671913 regression test in7 fails to execute 2598625 parsing empty config file results in an error 2698656 test parsing empty config files 2597956 not all pointers in a clone are reset 2543934 nat_t gets assigned ifp too early 2535795 No need to always bump fr_ref 2535778 Bad IPv6 packets droped by default 2031730 4.1.31 Nat drops fragmented packets after the first 2214661 ipf does not handle IPv6 fragments 2473273 NAT removed before RST/ICMP sent 2216500 fin_state serves no purpose 2424604 adding random MD5 data causes panic 2304435 Ineffecient lock usage in logging 2216491 fin_nat serves little purpose 2055619 duplicating a free-d packet will fail 2042949 Excessive locking when creating nat_t 2035610 nat_update does not need to get locks 2214658 ipf mostly ignores locking in NetBSD 1979427 Memory leak in user utilities - token never freed (rel br) * SunOS4 does not have a curproc, but it does have u. * The fix for 2020447 generated random port numbers but not within the range specified in the map rule. Add in a regression test to verify that the "random" part works. 2020447 NAT can undo name server random port selection 1988795 NetBSD does not build with kernel malloc stats 1988782 fr_movequeue can take a short cut 1988669 first nat creation failure prevents further success 1988668 hostmap searching does not work properly * on some 64bit architectures (such as alpha), the addrfamily_t is packed differently, throwing off the calculations for adf_len * one too many READ_ENTERs in ip_sync code. * clean up fr_fastroute a little by removing some #ifdefs and pushing the code around a bit to use the same variables (NetBSD) * more recent NetBSDs use VOP related macros differently
998 lines
30 KiB
C
998 lines
30 KiB
C
/* $NetBSD: ip_pool.c,v 1.1.1.9 2009/08/19 08:29:00 darrenr Exp $ */
|
|
|
|
/*
|
|
* Copyright (C) 1993-2001, 2003 by Darren Reed.
|
|
*
|
|
* See the IPFILTER.LICENCE file for details on licencing.
|
|
*
|
|
* Copyright 2008 Sun Microsystems, Inc.
|
|
*/
|
|
#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(_KERNEL) && !defined(SOLARIS2)
|
|
# include <sys/mbuf.h>
|
|
#endif
|
|
#if defined(__SVR4) || defined(__svr4__)
|
|
# 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(SOLARIS2) && !defined(_KERNEL)
|
|
# include "radix_ipf.h"
|
|
#endif
|
|
#if defined(_KERNEL) && (defined(__osf__) || defined(AIX) || \
|
|
defined(__hpux) || defined(__sgi))
|
|
# include "radix_ipf_local.h"
|
|
# define _RADIX_H_
|
|
#endif
|
|
#include <sys/socket.h>
|
|
#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: ip_pool.c,v 2.55.2.31 2009/07/18 19:05:39 darrenr Exp";
|
|
#endif
|
|
|
|
#ifdef IPFILTER_LOOKUP
|
|
|
|
# if !defined(RADIX_NODE_HEAD_LOCK) || !defined(RADIX_NODE_HEAD_UNLOCK) || \
|
|
!defined(_KERNEL)
|
|
# undef RADIX_NODE_HEAD_LOCK
|
|
# undef RADIX_NODE_HEAD_UNLOCK
|
|
# define RADIX_NODE_HEAD_LOCK(x) ;
|
|
# define RADIX_NODE_HEAD_UNLOCK(x) ;
|
|
# endif
|
|
|
|
static void ip_pool_clearnodes __P((ip_pool_t *));
|
|
static void *ip_pool_exists __P((int, char *));
|
|
|
|
ip_pool_stat_t ipoolstat;
|
|
ipfrwlock_t ip_poolrw;
|
|
|
|
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_exists(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) || !defined(BSD) || (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;
|
|
int i;
|
|
|
|
for (i = 0; i <= IPL_LOGMAX; i++) {
|
|
for (q = ip_pool_list[i]; (p = q) != NULL; ) {
|
|
q = p->ipo_next;
|
|
(void) ip_pool_destroy(i, p->ipo_name);
|
|
}
|
|
}
|
|
|
|
#if (!defined(_KERNEL) || !defined(BSD) || (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_exists(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_exists */
|
|
/* 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. */
|
|
/* ------------------------------------------------------------------------ */
|
|
static void *ip_pool_exists(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_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. If it is marked for deletion then */
|
|
/* pretend it does not exist. */
|
|
/* ------------------------------------------------------------------------ */
|
|
void *ip_pool_find(unit, name)
|
|
int unit;
|
|
char *name;
|
|
{
|
|
ip_pool_t *p;
|
|
|
|
p = ip_pool_exists(unit, name);
|
|
if ((p != NULL) && (p->ipo_flags & IPOOL_DELETE))
|
|
return NULL;
|
|
|
|
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, ipversion, dptr)
|
|
void *tptr;
|
|
int ipversion;
|
|
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 (ipversion == 4) {
|
|
v.adf_len += sizeof(addr->in4);
|
|
v.adf_addr.in4 = addr->in4;
|
|
#ifdef USE_INET6
|
|
} else if (ipversion == 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;
|
|
|
|
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. */
|
|
/* */
|
|
/* If the structure is flagged for deletion then reset the flag and return, */
|
|
/* as this likely means we've tried to free a pool that is in use (flush) */
|
|
/* and now want to repopulate it with "new" data. */
|
|
/* ------------------------------------------------------------------------ */
|
|
int ip_pool_create(op)
|
|
iplookupop_t *op;
|
|
{
|
|
char name[FR_GROUPLEN];
|
|
int poolnum, unit;
|
|
ip_pool_t *h;
|
|
|
|
unit = op->iplo_unit;
|
|
|
|
if ((op->iplo_arg & LOOKUP_ANON) == 0) {
|
|
h = ip_pool_exists(unit, op->iplo_name);
|
|
if (h != NULL) {
|
|
if ((h->ipo_flags & IPOOL_DELETE) == 0)
|
|
return EEXIST;
|
|
h->ipo_flags &= ~IPOOL_DELETE;
|
|
return 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;
|
|
}
|
|
|
|
if ((op->iplo_arg & LOOKUP_ANON) != 0) {
|
|
ip_pool_t *p;
|
|
|
|
h->ipo_flags |= IPOOL_ANON;
|
|
poolnum = LOOKUP_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;
|
|
{
|
|
|
|
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. If it is busy, clear all of its nodes, mark it for being */
|
|
/* deleted and return an error saying it is busy. */
|
|
/* */
|
|
/* 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(unit, name)
|
|
int unit;
|
|
char *name;
|
|
{
|
|
ip_pool_t *ipo;
|
|
|
|
ipo = ip_pool_exists(unit, name);
|
|
if (ipo == NULL)
|
|
return ESRCH;
|
|
|
|
if (ipo->ipo_ref != 1) {
|
|
ip_pool_clearnodes(ipo);
|
|
ipo->ipo_flags |= IPOOL_DELETE;
|
|
return 0;
|
|
}
|
|
|
|
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.iplo_unit, op.iplo_name);
|
|
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_clearnodes(ipo);
|
|
|
|
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_clearnodes */
|
|
/* Returns: void */
|
|
/* Parameters: ipo(I) - pointer to pool structure */
|
|
/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */
|
|
/* */
|
|
/* Deletes all nodes stored in a pool structure. */
|
|
/* ------------------------------------------------------------------------ */
|
|
static void ip_pool_clearnodes(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;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/* 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;
|
|
{
|
|
|
|
ipo->ipo_ref--;
|
|
|
|
if (ipo->ipo_ref == 0)
|
|
ip_pool_free(ipo);
|
|
|
|
else if ((ipo->ipo_ref == 1) && (ipo->ipo_flags & IPOOL_DELETE))
|
|
ip_pool_destroy(ipo->ipo_unit, ipo->ipo_name);
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/* 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 */
|
|
/* 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);
|
|
|
|
/*
|
|
* Get "previous" entry from token. Find next entry to process,
|
|
* and add reference to it and update the token.
|
|
*/
|
|
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);
|
|
token->ipt_data = nextipo;
|
|
} else {
|
|
bzero((char *)&zp, sizeof(zp));
|
|
nextipo = &zp;
|
|
token->ipt_data = NULL;
|
|
}
|
|
break;
|
|
|
|
case IPFLOOKUPITER_NODE :
|
|
node = token->ipt_data;
|
|
if (node == NULL) {
|
|
ipo = ip_pool_exists(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);
|
|
token->ipt_data = nextnode;
|
|
} else {
|
|
bzero((char *)&zn, sizeof(zn));
|
|
nextnode = &zn;
|
|
token->ipt_data = NULL;
|
|
}
|
|
break;
|
|
|
|
default :
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Now that we have ref, it's save to give up lock.
|
|
*/
|
|
RWLOCK_EXIT(&ip_poolrw);
|
|
|
|
if (err != 0)
|
|
return err;
|
|
|
|
/*
|
|
* Copy out the data and update the references and token as needed.
|
|
*/
|
|
switch (ilp->ili_otype)
|
|
{
|
|
case IPFLOOKUPITER_LIST :
|
|
err = COPYOUT(nextipo, ilp->ili_data, sizeof(*nextipo));
|
|
if (err != 0)
|
|
err = EFAULT;
|
|
if (token->ipt_data != NULL) {
|
|
if (ipo != NULL) {
|
|
WRITE_ENTER(&ip_poolrw);
|
|
ip_pool_deref(ipo);
|
|
RWLOCK_EXIT(&ip_poolrw);
|
|
}
|
|
if (nextipo->ipo_next == NULL)
|
|
token->ipt_data = NULL;
|
|
}
|
|
break;
|
|
|
|
case IPFLOOKUPITER_NODE :
|
|
err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode));
|
|
if (err != 0)
|
|
err = EFAULT;
|
|
if (token->ipt_data != NULL) {
|
|
if (node != NULL) {
|
|
WRITE_ENTER(&ip_poolrw);
|
|
ip_pool_node_deref(node);
|
|
RWLOCK_EXIT(&ip_poolrw);
|
|
}
|
|
if (nextnode->ipn_next == NULL)
|
|
token->ipt_data = NULL;
|
|
}
|
|
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) && (defined(BSD) && (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);
|
|
# if defined(__NetBSD_Version__) && (__NetBSD_Version__ > 499002000)
|
|
rn_walktree(rnh, rn_freenode, rnh);
|
|
# else
|
|
(*rnh->rnh_walktree)(rnh, rn_freenode, rnh);
|
|
# endif
|
|
|
|
rnh->rnh_addaddr = NULL;
|
|
rnh->rnh_deladdr = NULL;
|
|
rnh->rnh_matchaddr = NULL;
|
|
rnh->rnh_lookup = NULL;
|
|
RADIX_NODE_HEAD_UNLOCK(rnh);
|
|
|
|
Free(rnh);
|
|
}
|
|
# endif
|
|
#endif /* IPFILTER_LOOKUP */
|