From owner-svn-src-vendor@FreeBSD.ORG Tue Aug 20 13:24:45 2013 Return-Path: Delivered-To: svn-src-vendor@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id F352C6A8; Tue, 20 Aug 2013 13:24:44 +0000 (UTC) (envelope-from cy@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id E03E32E9F; Tue, 20 Aug 2013 13:24:44 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r7KDOi3b025412; Tue, 20 Aug 2013 13:24:44 GMT (envelope-from cy@svn.freebsd.org) Received: (from cy@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r7KDOiOr025410; Tue, 20 Aug 2013 13:24:44 GMT (envelope-from cy@svn.freebsd.org) Message-Id: <201308201324.r7KDOiOr025410@svn.freebsd.org> From: Cy Schubert Date: Tue, 20 Aug 2013 13:24:44 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-vendor@freebsd.org Subject: svn commit: r254562 - vendor-sys/ipfilter/dist/netinet X-SVN-Group: vendor-sys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-vendor@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the vendor work area tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 20 Aug 2013 13:24:45 -0000 Author: cy Date: Tue Aug 20 13:24:44 2013 New Revision: 254562 URL: http://svnweb.freebsd.org/changeset/base/254562 Log: Add kernel sources from IP-Filter 5.1.2 to vendor-sys/ branch. Approved by: glebius (mentor) Added: vendor-sys/ipfilter/dist/netinet/ip_dstlist.c (contents, props changed) vendor-sys/ipfilter/dist/netinet/ip_dstlist.h vendor-sys/ipfilter/dist/netinet/ipf_rb.h vendor-sys/ipfilter/dist/netinet/radix_ipf.c (contents, props changed) vendor-sys/ipfilter/dist/netinet/radix_ipf.h Added: vendor-sys/ipfilter/dist/netinet/ip_dstlist.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ vendor-sys/ipfilter/dist/netinet/ip_dstlist.c Tue Aug 20 13:24:44 2013 (r254562) @@ -0,0 +1,1351 @@ +/* + * Copyright (C) 2012 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 +#include +#include +#include +#if !defined(_KERNEL) && !defined(__KERNEL__) +# include +# include +# include +# define _KERNEL +# ifdef __OpenBSD__ +struct file; +# endif +# include +# undef _KERNEL +#else +# include +# if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) +# include +# endif +#endif +#include +#if !defined(linux) +# include +#endif +#include +#if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__)) +# include +#endif +#if defined(__SVR4) || defined(__svr4__) +# include +# include +# ifdef _KERNEL +# include +# endif +# include +# include +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) +# include +#endif + +#include +#include + +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" + +/* END OF INCLUDES */ + +#ifdef HAS_SYS_MD5_H +# include +#else +# include "md5.h" +#endif + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $"; +#endif + +typedef struct ipf_dstl_softc_s { + ippool_dst_t *dstlist[LOOKUP_POOL_SZ]; + ippool_dst_t **tails[LOOKUP_POOL_SZ]; + ipf_dstl_stat_t stats; +} ipf_dstl_softc_t; + + +static void *ipf_dstlist_soft_create __P((ipf_main_softc_t *)); +static void ipf_dstlist_soft_destroy __P((ipf_main_softc_t *, void *)); +static int ipf_dstlist_soft_init __P((ipf_main_softc_t *, void *)); +static void ipf_dstlist_soft_fini __P((ipf_main_softc_t *, void *)); +static int ipf_dstlist_addr_find __P((ipf_main_softc_t *, void *, int, + void *, u_int)); +static size_t ipf_dstlist_flush __P((ipf_main_softc_t *, void *, + iplookupflush_t *)); +static int ipf_dstlist_iter_deref __P((ipf_main_softc_t *, void *, int, int, + void *)); +static int ipf_dstlist_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *, + ipflookupiter_t *)); +static int ipf_dstlist_node_add __P((ipf_main_softc_t *, void *, + iplookupop_t *, int)); +static int ipf_dstlist_node_del __P((ipf_main_softc_t *, void *, + iplookupop_t *, int)); +static int ipf_dstlist_stats_get __P((ipf_main_softc_t *, void *, + iplookupop_t *)); +static int ipf_dstlist_table_add __P((ipf_main_softc_t *, void *, + iplookupop_t *)); +static int ipf_dstlist_table_del __P((ipf_main_softc_t *, void *, + iplookupop_t *)); +static int ipf_dstlist_table_deref __P((ipf_main_softc_t *, void *, void *)); +static void *ipf_dstlist_table_find __P((void *, int, char *)); +static void ipf_dstlist_table_free __P((ipf_dstl_softc_t *, ippool_dst_t *)); +static void ipf_dstlist_table_remove __P((ipf_main_softc_t *, + ipf_dstl_softc_t *, ippool_dst_t *)); +static void ipf_dstlist_table_clearnodes __P((ipf_dstl_softc_t *, + ippool_dst_t *)); +static ipf_dstnode_t *ipf_dstlist_select __P((fr_info_t *, ippool_dst_t *)); +static void *ipf_dstlist_select_ref __P((void *, int, char *)); +static void ipf_dstlist_node_free __P((ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *)); +static int ipf_dstlist_node_deref __P((void *, ipf_dstnode_t *)); +static void ipf_dstlist_expire __P((ipf_main_softc_t *, void *)); +static void ipf_dstlist_sync __P((ipf_main_softc_t *, void *)); + +ipf_lookup_t ipf_dstlist_backend = { + IPLT_DSTLIST, + ipf_dstlist_soft_create, + ipf_dstlist_soft_destroy, + ipf_dstlist_soft_init, + ipf_dstlist_soft_fini, + ipf_dstlist_addr_find, + ipf_dstlist_flush, + ipf_dstlist_iter_deref, + ipf_dstlist_iter_next, + ipf_dstlist_node_add, + ipf_dstlist_node_del, + ipf_dstlist_stats_get, + ipf_dstlist_table_add, + ipf_dstlist_table_del, + ipf_dstlist_table_deref, + ipf_dstlist_table_find, + ipf_dstlist_select_ref, + ipf_dstlist_select_node, + ipf_dstlist_expire, + ipf_dstlist_sync +}; + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_soft_create */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Allocating a chunk of memory filled with 0's is enough for the current */ +/* soft context used with destination lists. */ +/* ------------------------------------------------------------------------ */ +static void * +ipf_dstlist_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_dstl_softc_t *softd; + int i; + + KMALLOC(softd, ipf_dstl_softc_t *); + if (softd == NULL) { + IPFERROR(120028); + return NULL; + } + + bzero((char *)softd, sizeof(*softd)); + for (i = 0; i <= IPL_LOGMAX; i++) + softd->tails[i] = &softd->dstlist[i]; + + return softd; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_soft_destroy */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* For destination lists, the only thing we have to do when destroying the */ +/* soft context is free it! */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_dstl_softc_t *softd = arg; + + KFREE(softd); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_soft_init */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* There is currently no soft context for destination list management. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_soft_fini */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* */ +/* There is currently no soft context for destination list management. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_dstl_softc_t *softd = arg; + int i; + + for (i = -1; i <= IPL_LOGMAX; i++) { + while (softd->dstlist[i + 1] != NULL) { + ipf_dstlist_table_remove(softc, softd, + softd->dstlist[i + 1]); + } + } + + ASSERT(softd->stats.ipls_numderefnodes == 0); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_addr_find */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg1(I) - pointer to local context to use */ +/* arg2(I) - pointer to local context to use */ +/* arg3(I) - pointer to local context to use */ +/* arg4(I) - pointer to local context to use */ +/* */ +/* There is currently no such thing as searching a destination list for an */ +/* address so this function becomes a no-op. Its presence is required as */ +/* ipf_lookup_res_name() stores the "addr_find" function pointer in the */ +/* pointer passed in to it as funcptr, although it could be a generic null- */ +/* op function rather than a specific one. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +static int +ipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4) + ipf_main_softc_t *softc; + void *arg1, *arg3; + int arg2; + u_int arg4; +{ + return -1; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_flush */ +/* Returns: int - number of objects deleted */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* fop(I) - pointer to lookup flush operation data */ +/* */ +/* Flush all of the destination tables that match the data passed in with */ +/* the iplookupflush_t. There are two ways to match objects: the device for */ +/* which they are to be used with and their name. */ +/* ------------------------------------------------------------------------ */ +static size_t +ipf_dstlist_flush(softc, arg, fop) + ipf_main_softc_t *softc; + void *arg; + iplookupflush_t *fop; +{ + ipf_dstl_softc_t *softd = arg; + ippool_dst_t *node, *next; + int n, i; + + for (n = 0, i = -1; i <= IPL_LOGMAX; i++) { + if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i) + continue; + for (node = softd->dstlist[i + 1]; node != NULL; node = next) { + next = node->ipld_next; + + if ((*fop->iplf_name != '\0') && + strncmp(fop->iplf_name, node->ipld_name, + FR_GROUPLEN)) + continue; + + ipf_dstlist_table_remove(softc, softd, node); + n++; + } + } + return n; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_iter_deref */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* otype(I) - type of data structure to iterate through */ +/* unit(I) - device we are working with */ +/* data(I) - address of object in kernel space */ +/* */ +/* This function is called when the iteration token is being free'd and is */ +/* responsible for dropping the reference count of the structure it points */ +/* to. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_iter_deref(softc, arg, otype, unit, data) + ipf_main_softc_t *softc; + void *arg; + int otype, unit; + void *data; +{ + if (data == NULL) { + IPFERROR(120001); + return EINVAL; + } + + if (unit < -1 || unit > IPL_LOGMAX) { + IPFERROR(120002); + return EINVAL; + } + + switch (otype) + { + case IPFLOOKUPITER_LIST : + ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data); + break; + + case IPFLOOKUPITER_NODE : + ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data); + break; + } + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_iter_next */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* uid(I) - uid of process doing the ioctl */ +/* */ +/* This function is responsible for either selecting the next destination */ +/* list or node on a destination list to be returned as a user process */ +/* iterates through the list of destination lists or nodes. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_iter_next(softc, arg, token, iter) + ipf_main_softc_t *softc; + void *arg; + ipftoken_t *token; + ipflookupiter_t *iter; +{ + ipf_dstnode_t zn, *nextnode = NULL, *node = NULL; + ippool_dst_t zero, *next = NULL, *dsttab = NULL; + ipf_dstl_softc_t *softd = arg; + int err = 0; + void *hint; + + switch (iter->ili_otype) + { + case IPFLOOKUPITER_LIST : + dsttab = token->ipt_data; + if (dsttab == NULL) { + next = softd->dstlist[(int)iter->ili_unit + 1]; + } else { + next = dsttab->ipld_next; + } + + if (next != NULL) { + ATOMIC_INC32(next->ipld_ref); + token->ipt_data = next; + hint = next->ipld_next; + } else { + bzero((char *)&zero, sizeof(zero)); + next = &zero; + token->ipt_data = NULL; + hint = NULL; + } + break; + + case IPFLOOKUPITER_NODE : + node = token->ipt_data; + if (node == NULL) { + dsttab = ipf_dstlist_table_find(arg, iter->ili_unit, + iter->ili_name); + if (dsttab == NULL) { + IPFERROR(120004); + err = ESRCH; + nextnode = NULL; + } else { + if (dsttab->ipld_dests == NULL) + nextnode = NULL; + else + nextnode = *dsttab->ipld_dests; + dsttab = NULL; + } + } else { + nextnode = node->ipfd_next; + } + + if (nextnode != NULL) { + MUTEX_ENTER(&nextnode->ipfd_lock); + nextnode->ipfd_ref++; + MUTEX_EXIT(&nextnode->ipfd_lock); + token->ipt_data = nextnode; + hint = nextnode->ipfd_next; + } else { + bzero((char *)&zn, sizeof(zn)); + nextnode = &zn; + token->ipt_data = NULL; + hint = NULL; + } + break; + default : + IPFERROR(120003); + err = EINVAL; + break; + } + + if (err != 0) + return err; + + switch (iter->ili_otype) + { + case IPFLOOKUPITER_LIST : + if (dsttab != NULL) + ipf_dstlist_table_deref(softc, arg, dsttab); + err = COPYOUT(next, iter->ili_data, sizeof(*next)); + if (err != 0) { + IPFERROR(120005); + err = EFAULT; + } + break; + + case IPFLOOKUPITER_NODE : + if (node != NULL) + ipf_dstlist_node_deref(arg, node); + err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode)); + if (err != 0) { + IPFERROR(120006); + err = EFAULT; + } + break; + } + + if (hint == NULL) + ipf_token_mark_complete(token); + + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_node_add */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* uid(I) - uid of process doing the ioctl */ +/* Locks: WRITE(ipf_poolrw) */ +/* */ +/* Add a new node to a destination list. To do this, we only copy in the */ +/* frdest_t structure because that contains the only data required from the */ +/* application to create a new node. The frdest_t doesn't contain the name */ +/* itself. When loading filter rules, fd_name is a 'pointer' to the name. */ +/* In this case, the 'pointer' does not work, instead it is the length of */ +/* the name and the name is immediately following the frdest_t structure. */ +/* fd_name must include the trailing \0, so it should be strlen(str) + 1. */ +/* For simple sanity checking, an upper bound on the size of fd_name is */ +/* imposed - 128. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_node_add(softc, arg, op, uid) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; + int uid; +{ + ipf_dstl_softc_t *softd = arg; + ipf_dstnode_t *node, **nodes; + ippool_dst_t *d; + frdest_t dest; + int err; + + if (op->iplo_size < sizeof(frdest_t)) { + IPFERROR(120007); + return EINVAL; + } + + err = COPYIN(op->iplo_struct, &dest, sizeof(dest)); + if (err != 0) { + IPFERROR(120009); + return EFAULT; + } + + d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); + if (d == NULL) { + IPFERROR(120010); + return ESRCH; + } + + switch (dest.fd_addr.adf_family) + { + case AF_INET : + case AF_INET6 : + break; + default : + IPFERROR(120019); + return EINVAL; + } + + if (dest.fd_name < -1 || dest.fd_name > 128) { + IPFERROR(120018); + return EINVAL; + } + + KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name); + if (node == NULL) { + softd->stats.ipls_nomem++; + IPFERROR(120008); + return ENOMEM; + } + bzero((char *)node, sizeof(*node) + dest.fd_name); + + bcopy(&dest, &node->ipfd_dest, sizeof(dest)); + node->ipfd_size = sizeof(*node) + dest.fd_name; + + if (dest.fd_name > 0) { + /* + * fd_name starts out as the length of the string to copy + * in (including \0) and ends up being the offset from + * fd_names (0). + */ + err = COPYIN((char *)op->iplo_struct + sizeof(dest), + node->ipfd_names, dest.fd_name); + if (err != 0) { + IPFERROR(120017); + KFREES(node, node->ipfd_size); + return EFAULT; + } + node->ipfd_dest.fd_name = 0; + } else { + node->ipfd_dest.fd_name = -1; + } + + if (d->ipld_nodes == d->ipld_maxnodes) { + KMALLOCS(nodes, ipf_dstnode_t **, + sizeof(*nodes) * (d->ipld_maxnodes + 1)); + if (nodes == NULL) { + softd->stats.ipls_nomem++; + IPFERROR(120022); + KFREES(node, node->ipfd_size); + return ENOMEM; + } + if (d->ipld_dests != NULL) { + bcopy(d->ipld_dests, nodes, + sizeof(*nodes) * d->ipld_maxnodes); + KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes); + nodes[0]->ipfd_pnext = nodes; + } + d->ipld_dests = nodes; + d->ipld_maxnodes++; + } + d->ipld_dests[d->ipld_nodes] = node; + d->ipld_nodes++; + + if (d->ipld_nodes == 1) { + node->ipfd_pnext = d->ipld_dests; + } else if (d->ipld_nodes > 1) { + node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next; + } + *node->ipfd_pnext = node; + + MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock"); + node->ipfd_uid = uid; + node->ipfd_ref = 1; + if (node->ipfd_dest.fd_name == 0) + (void) ipf_resolvedest(softc, node->ipfd_names, + &node->ipfd_dest, AF_INET); +#ifdef USE_INET6 + if (node->ipfd_dest.fd_name == 0 && + node->ipfd_dest.fd_ptr == (void *)-1) + (void) ipf_resolvedest(softc, node->ipfd_names, + &node->ipfd_dest, AF_INET6); +#endif + + softd->stats.ipls_numnodes++; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_node_deref */ +/* Returns: int - 0 = success, else error */ +/* Parameters: arg(I) - pointer to local context to use */ +/* node(I) - pointer to destionation node to free */ +/* */ +/* Dereference the use count by one. If it drops to zero then we can assume */ +/* that it has been removed from any lists/tables and is ripe for freeing. */ +/* The pointer to context is required for the purpose of maintaining */ +/* statistics. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_node_deref(arg, node) + void *arg; + ipf_dstnode_t *node; +{ + ipf_dstl_softc_t *softd = arg; + int ref; + + MUTEX_ENTER(&node->ipfd_lock); + ref = --node->ipfd_ref; + MUTEX_EXIT(&node->ipfd_lock); + + if (ref > 0) + return 0; + + if ((node->ipfd_flags & IPDST_DELETE) != 0) + softd->stats.ipls_numderefnodes--; + MUTEX_DESTROY(&node->ipfd_lock); + KFREES(node, node->ipfd_size); + softd->stats.ipls_numnodes--; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_node_del */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* uid(I) - uid of process doing the ioctl */ +/* */ +/* Look for a matching destination node on the named table and free it if */ +/* found. Because the name embedded in the frdest_t is variable in length, */ +/* it is necessary to allocate some memory locally, to complete this op. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_node_del(softc, arg, op, uid) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; + int uid; +{ + ipf_dstl_softc_t *softd = arg; + ipf_dstnode_t *node; + frdest_t frd, *temp; + ippool_dst_t *d; + size_t size; + int err; + + d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); + if (d == NULL) { + IPFERROR(120012); + return ESRCH; + } + + err = COPYIN(op->iplo_struct, &frd, sizeof(frd)); + if (err != 0) { + IPFERROR(120011); + return EFAULT; + } + + size = sizeof(*temp) + frd.fd_name; + KMALLOCS(temp, frdest_t *, size); + if (temp == NULL) { + softd->stats.ipls_nomem++; + IPFERROR(120026); + return ENOMEM; + } + + err = COPYIN(op->iplo_struct, temp, size); + if (err != 0) { + IPFERROR(120027); + return EFAULT; + } + + MUTEX_ENTER(&d->ipld_lock); + for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) { + if ((uid != 0) && (node->ipfd_uid != uid)) + continue; + if (node->ipfd_size != size) + continue; + if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6, + size - offsetof(frdest_t, fd_ip6))) { + ipf_dstlist_node_free(softd, d, node); + MUTEX_EXIT(&d->ipld_lock); + KFREES(temp, size); + return 0; + } + } + MUTEX_EXIT(&d->ipld_lock); + KFREES(temp, size); + + return ESRCH; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_node_free */ +/* Returns: Nil */ +/* Parameters: softd(I) - pointer to the destination list context */ +/* d(I) - pointer to destination list */ +/* node(I) - pointer to node to free */ +/* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */ +/* */ +/* Free the destination node by first removing it from any lists and then */ +/* checking if this was the last reference held to the object. While the */ +/* array of pointers to nodes is compacted, its size isn't reduced (by way */ +/* of allocating a new smaller one and copying) because the belief is that */ +/* it is likely the array will again reach that size. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_node_free(softd, d, node) + ipf_dstl_softc_t *softd; + ippool_dst_t *d; + ipf_dstnode_t *node; +{ + int i; + + /* + * Compact the array of pointers to nodes. + */ + for (i = 0; i < d->ipld_nodes; i++) + if (d->ipld_dests[i] == node) + break; + if (d->ipld_nodes - i > 1) { + bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i], + sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1)); + } + d->ipld_nodes--; + + if (node->ipfd_pnext != NULL) + *node->ipfd_pnext = node->ipfd_next; + if (node->ipfd_next != NULL) + node->ipfd_next->ipfd_pnext = node->ipfd_pnext; + node->ipfd_pnext = NULL; + node->ipfd_next = NULL; + + if ((node->ipfd_flags & IPDST_DELETE) == 0) { + softd->stats.ipls_numderefnodes++; + node->ipfd_flags |= IPDST_DELETE; + } + + ipf_dstlist_node_deref(softd, node); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_stats_get */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Return the current statistics for destination lists. This may be for all */ +/* of them or just information pertaining to a particular table. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +static int +ipf_dstlist_stats_get(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + ipf_dstl_softc_t *softd = arg; + ipf_dstl_stat_t stats; + int unit, i, err = 0; + + if (op->iplo_size != sizeof(ipf_dstl_stat_t)) { + IPFERROR(120023); + return EINVAL; + } + + stats = softd->stats; + unit = op->iplo_unit; + if (unit == IPL_LOGALL) { + for (i = 0; i <= IPL_LOGMAX; i++) + stats.ipls_list[i] = softd->dstlist[i]; + } else if (unit >= 0 && unit <= IPL_LOGMAX) { + void *ptr; + + if (op->iplo_name[0] != '\0') + ptr = ipf_dstlist_table_find(softd, unit, + op->iplo_name); + else + ptr = softd->dstlist[unit + 1]; + stats.ipls_list[unit] = ptr; + } else { + IPFERROR(120024); + err = EINVAL; + } + + if (err == 0) { + err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); + if (err != 0) { + IPFERROR(120025); + return EFAULT; + } + } + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_add */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Add a new destination table to the list of those available for the given */ +/* device. Because we seldom operate on these objects (find/add/delete), */ +/* they are just kept in a simple linked list. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_table_add(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + ipf_dstl_softc_t *softd = arg; + ippool_dst_t user, *d, *new; + int unit, err; + + d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); + if (d != NULL) { + IPFERROR(120013); + return EEXIST; + } + + err = COPYIN(op->iplo_struct, &user, sizeof(user)); + if (err != 0) { + IPFERROR(120021); + return EFAULT; + } + + KMALLOC(new, ippool_dst_t *); + if (new == NULL) { + softd->stats.ipls_nomem++; + IPFERROR(120014); + return ENOMEM; + } + bzero((char *)new, sizeof(*new)); + + MUTEX_INIT(&new->ipld_lock, "ipf dst table lock"); + + strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN); + unit = op->iplo_unit; + new->ipld_unit = unit; + new->ipld_policy = user.ipld_policy; + new->ipld_seed = ipf_random(); + new->ipld_ref = 1; + + new->ipld_pnext = softd->tails[unit + 1]; + *softd->tails[unit + 1] = new; + softd->tails[unit + 1] = &new->ipld_next; + softd->stats.ipls_numlists++; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_del */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Find a named destinstion list table and delete it. If there are other */ +/* references to it, the caller isn't told. */ +/* ------------------------------------------------------------------------ */ +static int +ipf_dstlist_table_del(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; +{ + ippool_dst_t *d; + + d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); + if (d == NULL) { + IPFERROR(120015); + return ESRCH; + } + + if (d->ipld_dests != NULL) { + IPFERROR(120016); + return EBUSY; + } + + ipf_dstlist_table_remove(softc, arg, d); + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_remove */ +/* Returns: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* softd(I) - pointer to the destination list context */ +/* d(I) - pointer to destination list */ +/* */ +/* Remove a given destination list from existance. While the IPDST_DELETE */ +/* flag is set every time we call this function and the reference count is */ +/* non-zero, the "numdereflists" counter is always incremented because the */ +/* decision about whether it will be freed or not is not made here. This */ +/* means that the only action the code can take here is to treat it as if */ +/* it will become a detached. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_table_remove(softc, softd, d) + ipf_main_softc_t *softc; + ipf_dstl_softc_t *softd; + ippool_dst_t *d; +{ + + if (softd->tails[d->ipld_unit + 1] == &d->ipld_next) + softd->tails[d->ipld_unit + 1] = d->ipld_pnext; + + if (d->ipld_pnext != NULL) + *d->ipld_pnext = d->ipld_next; + if (d->ipld_next != NULL) + d->ipld_next->ipld_pnext = d->ipld_pnext; + d->ipld_pnext = NULL; + d->ipld_next = NULL; + + ipf_dstlist_table_clearnodes(softd, d); + + softd->stats.ipls_numdereflists++; + d->ipld_flags |= IPDST_DELETE; + + ipf_dstlist_table_deref(softc, softd, d); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_free */ +/* Returns: Nil */ +/* Parameters: softd(I) - pointer to the destination list context */ +/* d(I) - pointer to destination list */ +/* */ +/* Free up a destination list data structure and any other memory that was */ +/* directly allocated as part of creating it. Individual destination list */ +/* nodes are not freed. It is assumed the caller will have already emptied */ +/* the destination list. */ +/* ------------------------------------------------------------------------ */ +static void +ipf_dstlist_table_free(softd, d) + ipf_dstl_softc_t *softd; + ippool_dst_t *d; +{ + MUTEX_DESTROY(&d->ipld_lock); + + if ((d->ipld_flags & IPDST_DELETE) != 0) + softd->stats.ipls_numdereflists--; + softd->stats.ipls_numlists--; + + if (d->ipld_dests != NULL) { + KFREES(d->ipld_dests, + d->ipld_maxnodes * sizeof(*d->ipld_dests)); + } + + KFREE(d); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_dstlist_table_deref */ +/* Returns: int - 0 = success, else error */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to local context to use */ +/* op(I) - pointer to lookup operation data */ +/* */ +/* Drops the reference count on a destination list table object and free's */ +/* it if 0 has been reached. */ +/* ------------------------------------------------------------------------ */ *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***