Subject: Re: kern/37037
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Martti Kuparinen <martti.kuparinen@iki.fi>
List: netbsd-bugs
Date: 09/30/2007 07:20:02
The following reply was made to PR kern/37037; it has been noted by GNATS.

From: Martti Kuparinen <martti.kuparinen@iki.fi>
To: Darren Reed <darrenr@netbsd.org>
Cc: gnats-bugs@netbsd.org, juan@xtrarom.org
Subject: Re: kern/37037
Date: Sun, 30 Sep 2007 10:15:28 +0300 (EEST)

 Could you please try this patch (also at http://users.piuha.net/martti/tmp/p)
 
 Martti
 
 
 
 Index: ip_fil.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dist/ipf/netinet/ip_fil.h,v
 retrieving revision 1.14
 diff -u -r1.14 ip_fil.h
 --- ip_fil.h	19 Jul 2007 14:04:34 -0000	1.14
 +++ ip_fil.h	30 Sep 2007 07:10:15 -0000
 @@ -184,14 +184,14 @@
   			      HI63(a) < HI63(b)))))))
   #define	NLADD(n,x)	htonl(ntohl(n) + (x))
   #define	IP6_INC(a)	\
 -		{ i6addr_t *_i6 = (i6addr_t *)(a); \
 -		  _i6->i6[0] = NLADD(_i6->i6[0], 1); \
 -		  if (_i6->i6[0] == 0) { \
 -			_i6->i6[0] = NLADD(_i6->i6[1], 1); \
 -			if (_i6->i6[1] == 0) { \
 -				_i6->i6[0] = NLADD(_i6->i6[2], 1); \
 -				if (_i6->i6[2] == 0) { \
 -					_i6->i6[0] = NLADD(_i6->i6[3], 1); \
 +		{ u_32_t *_i6 = (u_32_t *)(a); \
 +		  _i6[3] = NLADD(_i6[3], 1); \
 +		  if (_i6[3] == 0) { \
 +			_i6[2] = NLADD(_i6[2], 1); \
 +			if (_i6[2] == 0) { \
 +				_i6[1] = NLADD(_i6[1], 1); \
 +				if (_i6[1] == 0) { \
 +					_i6[0] = NLADD(_i6[0], 1); \
   				} \
   			} \
   		  } \
 @@ -270,6 +270,7 @@
   #define	FI_WITH		0xeffe	/* Not FI_TCPUDP */
   #define	FI_V6EXTHDR	0x10000
   #define	FI_COALESCE	0x20000
 +#define	FI_NEWNAT	0x40000
   #define	FI_NOCKSUM	0x20000000	/* don't do a L4 checksum validation */
   #define	FI_DONTCACHE	0x40000000	/* don't cache the result */
   #define	FI_IGNORE	0x80000000
 @@ -329,6 +330,7 @@
   	u_short	fin_off;
   	int	fin_depth;		/* Group nesting depth */
   	int	fin_error;		/* Error code to return */
 +	int	fin_cksum;		/* -1 bad, 1 good, 0 not done */
   	void	*fin_nat;
   	void	*fin_state;
   	void	*fin_nattag;
 @@ -1203,6 +1205,8 @@
   } ipftable_t;
 
   #define	IPFTABLE_BUCKETS	1
 +#define	IPFTABLE_BUCKETS_NATIN	2
 +#define	IPFTABLE_BUCKETS_NATOUT	3
 
 
   /*
 @@ -1402,6 +1406,13 @@
   #  endif /* __ sgi */
   # endif /* MENTAT */
 
 +# if defined(__FreeBSD_version)
 +extern	int	ipf_pfil_hook __P((void));
 +extern	int	ipf_pfil_unhook __P((void));
 +extern	void	ipf_event_reg __P((void));
 +extern	void	ipf_event_dereg __P((void));
 +# endif
 +
   #endif /* #ifndef _KERNEL */
 
   extern	ipfmutex_t	ipl_mutex, ipf_authmx, ipf_rw, ipf_hostmap;
 Index: ip_frag.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dist/ipf/netinet/ip_frag.c,v
 retrieving revision 1.7
 diff -u -r1.7 ip_frag.c
 --- ip_frag.c	16 Jun 2007 10:52:28 -0000	1.7
 +++ ip_frag.c	30 Sep 2007 07:10:15 -0000
 @@ -106,7 +106,7 @@
   __KERNEL_RCSID(0, "$NetBSD: ip_frag.c,v 1.7 2007/06/16 10:52:28 martin Exp $");
   #else
   static const char sccsid[] = "@(#)ip_frag.c	1.11 3/24/96 (C) 1993-2000 Darren Reed";
 -static const char rcsid[] = "@(#)Id: ip_frag.c,v 2.77.2.9 2007/05/27 11:13:44 darrenr Exp";
 +static const char rcsid[] = "@(#)Id: ip_frag.c,v 2.77.2.12 2007/09/20 12:51:51 darrenr Exp $";
   #endif
   #endif
 
 @@ -943,16 +943,16 @@
   	} else {
   		bzero(&zero, sizeof(zero));
   		next = &zero;
 -		token->ipt_data = (void *)-1;
 +		token->ipt_data = NULL;
   	}
   	RWLOCK_EXIT(lock);
 
   	if (frag != NULL) {
 -		WRITE_ENTER(lock);
 -		frag->ipfr_ref--;
 -		if (frag->ipfr_ref <= 0)
 -			fr_fragfree(frag);
 -		RWLOCK_EXIT(lock);
 +#ifdef USE_MUTEXES
 +		fr_fragderef(&frag, lock);
 +#else
 +		fr_fragderef(&frag);
 +#endif
   	}
 
   	error = COPYOUT(next, itp->igi_data, sizeof(*next));
 Index: ip_nat.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dist/ipf/netinet/ip_nat.c,v
 retrieving revision 1.32
 diff -u -r1.32 ip_nat.c
 --- ip_nat.c	14 Sep 2007 11:28:45 -0000	1.32
 +++ ip_nat.c	30 Sep 2007 07:10:19 -0000
 @@ -1,4 +1,4 @@
 -/*	$NetBSD: ip_nat.c,v 1.32 2007/09/14 11:28:45 martti Exp $	*/
 +/*	$NetBSD: ip_nat.c,v 1.1.1.9 2007/06/16 10:33:21 martin Exp $	*/
 
   /*
    * Copyright (C) 1995-2003 by Darren Reed.
 @@ -16,12 +16,13 @@
   #include <sys/param.h>
   #include <sys/time.h>
   #include <sys/file.h>
 -#if (__NetBSD_Version__ >= 399002000) && defined(_KERNEL)
 +#if defined(_KERNEL) && defined(__NetBSD_Version__) && \
 +    (__NetBSD_Version__ >= 399002000)
   # include <sys/kauth.h>
   #endif
   #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \
       defined(_KERNEL)
 -# if (__NetBSD_Version__ < 399001400)
 +#if defined(__NetBSD_Version__) && (__NetBSD_Version__ < 399001400)
   #  include "opt_ipfilter_log.h"
   # else
   #  include "opt_ipfilter.h"
 @@ -116,7 +117,7 @@
 
   #if !defined(lint)
   static const char sccsid[] = "@(#)ip_nat.c	1.11 6/5/96 (C) 1995 Darren Reed";
 -static const char rcsid[] = "@(#)Id: ip_nat.c,v 2.195.2.87 2007/05/31 10:17:17 darrenr Exp";
 +static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.100 2007/09/25 08:27:31 darrenr Exp $";
   #endif
 
 
 @@ -177,7 +178,7 @@
   natstat_t nat_stats;
   int	fr_nat_lock = 0;
   int	fr_nat_init = 0;
 -#if SOLARIS
 +#if SOLARIS && !defined(_INET_IP_STACK_H)
   extern	int		pfil_delayed_copy;
   #endif
 
 @@ -186,13 +187,13 @@
   static	int	nat_clearlist __P((void));
   static	void	nat_addnat __P((struct ipnat *));
   static	void	nat_addrdr __P((struct ipnat *));
 -static	void	nat_delete __P((struct nat *, int));
   static	void	nat_delrdr __P((struct ipnat *));
   static	void	nat_delnat __P((struct ipnat *));
   static	int	fr_natgetent __P((caddr_t));
   static	int	fr_natgetsz __P((caddr_t));
   static	int	fr_natputent __P((caddr_t, int));
   static	int	nat_extraflush __P((int));
 +static	int	nat_gettable __P((char *));
   static	void	nat_tabmove __P((nat_t *));
   static	int	nat_match __P((fr_info_t *, ipnat_t *));
   static	INLINE	int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *));
 @@ -652,7 +653,7 @@
   	SPL_INT(s);
 
   #if (BSD >= 199306) && defined(_KERNEL)
 -# if (__NetBSD_Version__ >= 399002000)
 +# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 399002000)
   	if ((mode & FWRITE) &&
   	     kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL,
   				     KAUTH_REQ_NETWORK_FIREWALL_FW,
 @@ -943,6 +944,10 @@
   		error = fr_outobj(data, nat_tqb, IPFOBJ_STATETQTAB);
   		break;
 
 +	case SIOCGTABL :
 +		error = nat_gettable(data);
 +		break;
 +
   	default :
   		error = EINVAL;
   		break;
 @@ -1080,7 +1085,7 @@
 
   	n = NULL;
   	nat_stats.ns_rules++;
 -#if SOLARIS
 +#if SOLARIS && !defined(_INET_IP_STACK_H)
   	pfil_delayed_copy = 0;
   #endif
   	if (getlock) {
 @@ -1168,9 +1173,10 @@
   	if (n->in_use == 0) {
   		if (n->in_apr)
   			appr_free(n->in_apr);
 +		MUTEX_DESTROY(&n->in_lock);
   		KFREE(n);
   		nat_stats.ns_rules--;
 -#if SOLARIS
 +#if SOLARIS && !defined(_INET_IP_STACK_H)
   		if (nat_stats.ns_rules == 0)
   			pfil_delayed_copy = 1;
   #endif
 @@ -1660,11 +1666,12 @@
   /* Delete a nat entry from the various lists and table.  If NAT logging is  */
   /* enabled then generate a NAT log record for this event.                   */
   /* ------------------------------------------------------------------------ */
 -static void nat_delete(nat, logtype)
 +void nat_delete(nat, logtype)
   struct nat *nat;
   int logtype;
   {
   	struct ipnat *ipn;
 +	int removed = 0;
 
   	if (logtype != 0 && nat_logging != 0)
   		nat_log(nat, logtype);
 @@ -1674,6 +1681,8 @@
   	 * nat_pnext is set.
   	 */
   	if (nat->nat_pnext != NULL) {
 +		removed = 1;
 +
   		nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
   		nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
 
 @@ -1714,9 +1723,24 @@
   		nat_stats.ns_expire++;
 
   	MUTEX_ENTER(&nat->nat_lock);
 -	if (nat->nat_ref > 1) {
 +	/*
 +	 * NL_DESTROY should only be passed in when we've got nat_ref >= 2.
 +	 * This happens when a nat'd packet is blocked and we want to throw
 +	 * away the NAT session.
 +	 */
 +	if (logtype == NL_DESTROY) {
 +		if (nat->nat_ref > 2) {
 +			nat->nat_ref -= 2;
 +			MUTEX_EXIT(&nat->nat_lock);
 +			if (removed)
 +				nat_stats.ns_orphans++;
 +			return;
 +		}
 +	} else if (nat->nat_ref > 1) {
   		nat->nat_ref--;
   		MUTEX_EXIT(&nat->nat_lock);
 +		if (removed)
 +			nat_stats.ns_orphans++;
   		return;
   	}
   	MUTEX_EXIT(&nat->nat_lock);
 @@ -1725,6 +1749,8 @@
   	 * At this point, nat_ref is 1, doing "--" would make it 0..
   	 */
   	nat->nat_ref = 0;
 +	if (!removed)
 +		nat_stats.ns_orphans--;
 
   #ifdef	IPFILTER_SYNC
   	if (nat->nat_sync)
 @@ -1824,6 +1850,7 @@
   		if (n->in_use == 0) {
   			if (n->in_apr != NULL)
   				appr_free(n->in_apr);
 +			MUTEX_DESTROY(&n->in_lock);
   			KFREE(n);
   			nat_stats.ns_rules--;
   		} else {
 @@ -1832,7 +1859,7 @@
   		}
   		i++;
   	}
 -#if SOLARIS
 +#if SOLARIS && !defined(_INET_IP_STACK_H)
   	pfil_delayed_copy = 1;
   #endif
   	nat_masks = 0;
 @@ -2498,10 +2525,12 @@
   	}
 
   	if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) {
 +		fr_nat_doflush = 1;
   		goto badnat;
   	}
   	if (flags & SI_WILDP)
   		nat_stats.ns_wilds++;
 +	fin->fin_flx |= FI_NEWNAT;
   	goto done;
   badnat:
   	nat_stats.ns_badnat++;
 @@ -3011,10 +3040,22 @@
   			}
 
   			if (sumd2 != 0) {
 +				ipnat_t *np;
 +
 +				np = nat->nat_ptr;
   				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
   				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
   				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
 -				fix_incksum(fin, &icmp->icmp_cksum, sumd2);
 +
 +				if ((odst == 0) && (dir == NAT_OUTBOUND) &&
 +				    (fin->fin_rev == 0) && (np != NULL) &&
 +				    (np->in_redir & NAT_REDIRECT)) {
 +					fix_outcksum(fin, &icmp->icmp_cksum,
 +						     sumd2);
 +				} else {
 +					fix_incksum(fin, &icmp->icmp_cksum,
 +						    sumd2);
 +				}
   			}
   		}
   	} else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) {
 @@ -4684,9 +4725,10 @@
   	if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) {
   		if (in->in_apr)
   			appr_free(in->in_apr);
 +		MUTEX_DESTROY(&in->in_lock);
   		KFREE(in);
   		nat_stats.ns_rules--;
 -#if SOLARIS
 +#if SOLARIS && !defined(_INET_IP_STACK_H)
   		if (nat_stats.ns_rules == 0)
   			pfil_delayed_copy = 1;
   #endif
 @@ -5013,10 +5055,11 @@
   	ipnat_t *ipn, *nextipnat = NULL, zeroipn;
   	nat_t *nat, *nextnat = NULL, zeronat;
   	int error = 0, count;
 -	ipftoken_t *freet;
   	char *dst;
 
 -	freet = NULL;
 +	count = itp->igi_nitems;
 +	if (count < 1)
 +		return ENOSPC;
 
   	READ_ENTER(&ipf_nat);
 
 @@ -5054,61 +5097,52 @@
   	}
 
   	dst = itp->igi_data;
 -	for (count = itp->igi_nitems; count > 0; count--) {
 +	for (;;) {
   		switch (itp->igi_type)
   		{
   		case IPFGENITER_HOSTMAP :
   			if (nexthm != NULL) {
 -				if (nexthm->hm_next == NULL) {
 -					freet = t;
 -					count = 1;
 -					hm = NULL;
 -				}
   				if (count == 1) {
   					ATOMIC_INC32(nexthm->hm_ref);
 +					t->ipt_data = nexthm;
   				}
   			} else {
   				bzero(&zerohm, sizeof(zerohm));
   				nexthm = &zerohm;
   				count = 1;
 +				t->ipt_data = NULL;
   			}
   			break;
 
   		case IPFGENITER_IPNAT :
   			if (nextipnat != NULL) {
 -				if (nextipnat->in_next == NULL) {
 -					freet = t;
 -					count = 1;
 -					ipn = NULL;
 -				}
   				if (count == 1) {
   					MUTEX_ENTER(&nextipnat->in_lock);
   					nextipnat->in_use++;
   					MUTEX_EXIT(&nextipnat->in_lock);
 +					t->ipt_data = nextipnat;
   				}
   			} else {
   				bzero(&zeroipn, sizeof(zeroipn));
   				nextipnat = &zeroipn;
   				count = 1;
 +				t->ipt_data = NULL;
   			}
   			break;
 
   		case IPFGENITER_NAT :
   			if (nextnat != NULL) {
 -				if (nextnat->nat_next == NULL) {
 -					count = 1;
 -					freet = t;
 -					nat = NULL;
 -				}
   				if (count == 1) {
   					MUTEX_ENTER(&nextnat->nat_lock);
   					nextnat->nat_ref++;
   					MUTEX_EXIT(&nextnat->nat_lock);
 +					t->ipt_data = nextnat;
   				}
   			} else {
   				bzero(&zeronat, sizeof(zeronat));
   				nextnat = &zeronat;
   				count = 1;
 +				t->ipt_data = NULL;
   			}
   			break;
   		default :
 @@ -5116,20 +5150,12 @@
   		}
   		RWLOCK_EXIT(&ipf_nat);
 
 -		if (freet != NULL) {
 -			ipf_freetoken(freet);
 -			freet = NULL;
 -		}
 -
 +		/*
 +		 * Copying out to user space needs to be done without the lock.
 +		 */
   		switch (itp->igi_type)
   		{
   		case IPFGENITER_HOSTMAP :
 -			if (hm != NULL) {
 -				WRITE_ENTER(&ipf_nat);
 -				fr_hostmapdel(&hm);
 -				RWLOCK_EXIT(&ipf_nat);
 -			}
 -			t->ipt_data = nexthm;
   			error = COPYOUT(nexthm, dst, sizeof(*nexthm));
   			if (error != 0)
   				error = EFAULT;
 @@ -5138,9 +5164,6 @@
   			break;
 
   		case IPFGENITER_IPNAT :
 -			if (ipn != NULL)
 -				fr_ipnatderef(&ipn);
 -			t->ipt_data = nextipnat;
   			error = COPYOUT(nextipnat, dst, sizeof(*nextipnat));
   			if (error != 0)
   				error = EFAULT;
 @@ -5149,9 +5172,6 @@
   			break;
 
   		case IPFGENITER_NAT :
 -			if (nat != NULL)
 -				fr_natderef(&nat);
 -			t->ipt_data = nextnat;
   			error = COPYOUT(nextnat, dst, sizeof(*nextnat));
   			if (error != 0)
   				error = EFAULT;
 @@ -5163,29 +5183,52 @@
   		if ((count == 1) || (error != 0))
   			break;
 
 +		count--;
 +
   		READ_ENTER(&ipf_nat);
 
 +		/*
 +		 * We need to have the lock again here to make sure that
 +		 * using _next is consistent.
 +		 */
   		switch (itp->igi_type)
   		{
   		case IPFGENITER_HOSTMAP :
 -			hm = nexthm;
 -			nexthm = hm->hm_next;
 +			nexthm = nexthm->hm_next;
   			break;
 -
   		case IPFGENITER_IPNAT :
 -			ipn = nextipnat;
 -			nextipnat = ipn->in_next;
 +			nextipnat = nextipnat->in_next;
   			break;
 -
   		case IPFGENITER_NAT :
 -			nat = nextnat;
 -			nextnat = nat->nat_next;
 -			break;
 -		default :
 +			nextnat = nextnat->nat_next;
   			break;
   		}
   	}
 
 +
 +	switch (itp->igi_type)
 +	{
 +	case IPFGENITER_HOSTMAP :
 +		if (hm != NULL) {
 +			WRITE_ENTER(&ipf_nat);
 +			fr_hostmapdel(&hm);
 +			RWLOCK_EXIT(&ipf_nat);
 +		}
 +		break;
 +	case IPFGENITER_IPNAT :
 +		if (ipn != NULL) {
 +			fr_ipnatderef(&ipn);
 +		}
 +		break;
 +	case IPFGENITER_NAT :
 +		if (nat != NULL) {
 +			fr_natderef(&nat);
 +		}
 +		break;
 +	default :
 +		break;
 +	}
 +
   	return error;
   }
 
 @@ -5394,3 +5437,44 @@
   	nat_delete(entry, NL_FLUSH);
   	return 0;
   }
 +
 +
 +/* ------------------------------------------------------------------------ */
 +/* Function:    nat_gettable                                                */
 +/* Returns:     int     - 0 = success, else error                           */
 +/* Parameters:  data(I) - pointer to ioctl data                             */
 +/*                                                                          */
 +/* This function handles ioctl requests for tables of nat information.      */
 +/* At present the only table it deals with is the hash bucket statistics.   */
 +/* ------------------------------------------------------------------------ */
 +static int nat_gettable(data)
 +char *data;
 +{
 +	ipftable_t table;
 +	int error;
 +
 +	error = fr_inobj(data, &table, IPFOBJ_GTABLE);
 +	if (error != 0)
 +		return error;
 +
 +	switch (table.ita_type)
 +	{
 +	case IPFTABLE_BUCKETS_NATIN :
 +		error = COPYOUT(nat_stats.ns_bucketlen[0], table.ita_table, 
 +				ipf_nattable_sz * sizeof(u_long));
 +		break;
 +
 +	case IPFTABLE_BUCKETS_NATOUT :
 +		error = COPYOUT(nat_stats.ns_bucketlen[1], table.ita_table, 
 +				ipf_nattable_sz * sizeof(u_long));
 +		break;
 +
 +	default :
 +		return EINVAL;
 +	}
 +
 +	if (error != 0) {
 +		error = EFAULT;
 +	}
 +	return error;
 +}
 Index: ip_nat.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dist/ipf/netinet/ip_nat.h,v
 retrieving revision 1.12
 diff -u -r1.12 ip_nat.h
 --- ip_nat.h	14 Sep 2007 11:28:46 -0000	1.12
 +++ ip_nat.h	30 Sep 2007 07:10:19 -0000
 @@ -6,7 +6,7 @@
    * See the IPFILTER.LICENCE file for details on licencing.
    *
    * @(#)ip_nat.h	1.5 2/4/96
 - * Id: ip_nat.h,v 2.90.2.17 2007/05/11 10:19:11 darrenr Exp
 + * $Id: ip_nat.h,v 2.90.2.20 2007/09/25 08:27:32 darrenr Exp $
    */
 
   #ifndef	__IP_NAT_H__
 @@ -363,6 +363,7 @@
   	hostmap_t *ns_maplist;
   	u_long	*ns_bucketlen[2];
   	u_long	ns_ticks;
 +	u_int	ns_orphans;
   } natstat_t;
 
   typedef	struct	natlog {
 @@ -384,6 +385,7 @@
   #define	NL_NEWRDR	NAT_REDIRECT
   #define	NL_NEWBIMAP	NAT_BIMAP
   #define	NL_NEWBLOCK	NAT_MAPBLK
 +#define	NL_DESTROY	0xfffc
   #define	NL_CLONE	0xfffd
   #define	NL_FLUSH	0xfffe
   #define	NL_EXPIRE	0xffff
 @@ -447,6 +449,7 @@
   extern	nat_t	*nat_lookupredir __P((natlookup_t *));
   extern	nat_t	*nat_icmperrorlookup __P((fr_info_t *, int));
   extern	nat_t	*nat_icmperror __P((fr_info_t *, u_int *, int));
 +extern	void	nat_delete __P((struct nat *, int));
   extern	int	nat_insert __P((nat_t *, int));
 
   extern	int	fr_checknatout __P((fr_info_t *, u_32_t *));
 Index: ip_state.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dist/ipf/netinet/ip_state.c,v
 retrieving revision 1.29
 diff -u -r1.29 ip_state.c
 --- ip_state.c	17 Sep 2007 06:56:15 -0000	1.29
 +++ ip_state.c	30 Sep 2007 07:10:21 -0000
 @@ -117,7 +117,7 @@
   __KERNEL_RCSID(0, "$NetBSD: ip_state.c,v 1.29 2007/09/17 06:56:15 martti Exp $");
   #else
   static const char sccsid[] = "@(#)ip_state.c	1.8 6/5/96 (C) 1993-2000 Darren Reed";
 -static const char rcsid[] = "@(#)Id: ip_state.c,v 2.186.2.69 2007/05/26 13:05:14 darrenr Exp";
 +static const char rcsid[] = "@(#)$Id: ip_state.c,v 2.186.2.78 2007/09/20 12:51:53 darrenr Exp $";
   #endif
   #endif
 
 @@ -657,8 +657,8 @@
   	int error;
 
   	error = fr_inobj(data, &ips, IPFOBJ_STATESAVE);
 -	if (error)
 -		return EFAULT;
 +	if (error != 0)
 +		return error;
 
   	isn = ips.ips_next;
   	if (isn == NULL) {
 @@ -687,9 +687,7 @@
   		bcopy((char *)isn->is_rule, (char *)&ips.ips_fr,
   		      sizeof(ips.ips_fr));
   	error = fr_outobj(data, &ips, IPFOBJ_STATESAVE);
 -	if (error)
 -		return EFAULT;
 -	return 0;
 +	return error;
   }
 
 
 @@ -1444,7 +1442,7 @@
   			is->is_state[!source] = IPF_TCPS_CLOSED;
   			fr_movequeue(&is->is_sti, is->is_sti.tqe_ifq,
   				     &ips_deletetq);
 -			MUTEX_ENTER(&is->is_lock);
 +			MUTEX_EXIT(&is->is_lock);
   			return 0;
   		}
   	}
 @@ -2308,8 +2306,6 @@
   	ipstate_t **isp;
   	u_int hvm;
 
 -	ASSERT(rw_read_locked(&ipf_state.ipf_lk) == 0);
 -
   	hvm = is->is_hv;
   	/*
   	 * Remove the hash from the old location...
 @@ -2897,8 +2893,6 @@
   int why;
   {
 
 -	ASSERT(rw_read_locked(&ipf_state.ipf_lk) == 0);
 -
   	/*
   	 * Since we want to delete this, remove it from the state table,
   	 * where it can be found & used, first.
 @@ -4072,7 +4066,7 @@
   	if (itp->igi_data == NULL)
   		return EFAULT;
 
 -	if (itp->igi_nitems == 0)
 +	if (itp->igi_nitems < 1)
   		return ENOSPC;
 
   	if (itp->igi_type != IPFGENITER_STATE)
 @@ -4094,33 +4088,29 @@
   		next = is->is_next;
   	}
 
 -	for (count = itp->igi_nitems; count > 0; count--) {
 +	count = itp->igi_nitems;
 +	for (;;) {
   		if (next != NULL) {
   			/*
   			 * If we find a state entry to use, bump its
   			 * reference count so that it can be used for
   			 * is_next when we come back.
   			 */
 -			MUTEX_ENTER(&next->is_lock);
 -			next->is_ref++;
 -			MUTEX_EXIT(&next->is_lock);
 -			token->ipt_data = next;
 +			if (count == 1) {
 +				MUTEX_ENTER(&next->is_lock);
 +				next->is_ref++;
 +				MUTEX_EXIT(&next->is_lock);
 +				token->ipt_data = next;
 +			}
   		} else {
   			bzero(&zero, sizeof(zero));
   			next = &zero;
 -			token->ipt_data = (void *)-1;
   			count = 1;
 +			token->ipt_data = NULL;
   		}
   		RWLOCK_EXIT(&ipf_state);
 
   		/*
 -		 * If we had a prior pointer to a state entry, release it.
 -		 */
 -		if (is != NULL) {
 -			fr_statederef(&is);
 -		}
 -
 -		/*
   		 * This should arguably be via fr_outobj() so that the state
   		 * structure can (if required) be massaged going out.
   		 */
 @@ -4131,9 +4121,14 @@
   			break;
 
   		dst += sizeof(*next);
 +		count--;
 +
   		READ_ENTER(&ipf_state);
 -		is = next;
 -		next = is->is_next;
 +		next = next->is_next;
 +	}
 +
 +	if (is != NULL) {
 +		fr_statederef(&is);
   	}
 
   	return error;