From 811a7577e8047be12faccc01a8722b60335b8933 Mon Sep 17 00:00:00 2001 From: martti Date: Tue, 2 Oct 2007 06:15:11 +0000 Subject: [PATCH] Bring some fixes from IPF 4.1.27 in order to fix kern/37037. --- sys/dist/ipf/netinet/ip_fil.h | 28 +++-- sys/dist/ipf/netinet/ip_frag.c | 18 +-- sys/dist/ipf/netinet/ip_nat.c | 202 ++++++++++++++++++++++---------- sys/dist/ipf/netinet/ip_nat.h | 7 +- sys/dist/ipf/netinet/ip_state.c | 53 ++++----- 5 files changed, 200 insertions(+), 108 deletions(-) diff --git a/sys/dist/ipf/netinet/ip_fil.h b/sys/dist/ipf/netinet/ip_fil.h index 744100a4c99e..7c73543a74d8 100644 --- a/sys/dist/ipf/netinet/ip_fil.h +++ b/sys/dist/ipf/netinet/ip_fil.h @@ -1,4 +1,4 @@ -/* $NetBSD: ip_fil.h,v 1.14 2007/07/19 14:04:34 gdt Exp $ */ +/* $NetBSD: ip_fil.h,v 1.15 2007/10/02 06:15:11 martti Exp $ */ /* * Copyright (C) 1993-2001, 2003 by Darren Reed. @@ -184,14 +184,14 @@ typedef union i6addr { 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 @@ typedef struct fr_ip { #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 @@ -1203,6 +1204,8 @@ typedef struct ipftable { } ipftable_t; #define IPFTABLE_BUCKETS 1 +#define IPFTABLE_BUCKETS_NATIN 2 +#define IPFTABLE_BUCKETS_NATOUT 3 /* @@ -1402,6 +1405,13 @@ extern int iplwrite __P((dev_t, struct uio *)); # 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; diff --git a/sys/dist/ipf/netinet/ip_frag.c b/sys/dist/ipf/netinet/ip_frag.c index c7eb89172d17..c163b681998f 100644 --- a/sys/dist/ipf/netinet/ip_frag.c +++ b/sys/dist/ipf/netinet/ip_frag.c @@ -1,4 +1,4 @@ -/* $NetBSD: ip_frag.c,v 1.7 2007/06/16 10:52:28 martin Exp $ */ +/* $NetBSD: ip_frag.c,v 1.8 2007/10/02 06:15:12 martti Exp $ */ /* * Copyright (C) 1993-2003 by Darren Reed. @@ -103,10 +103,10 @@ extern struct timeout fr_slowtimer_ch; #if !defined(lint) #if defined(__NetBSD__) #include -__KERNEL_RCSID(0, "$NetBSD: ip_frag.c,v 1.7 2007/06/16 10:52:28 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_frag.c,v 1.8 2007/10/02 06:15:12 martti 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 @@ ipfrwlock_t *lock; } 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)); diff --git a/sys/dist/ipf/netinet/ip_nat.c b/sys/dist/ipf/netinet/ip_nat.c index 4018b36f4a78..5cd6ea85578b 100644 --- a/sys/dist/ipf/netinet/ip_nat.c +++ b/sys/dist/ipf/netinet/ip_nat.c @@ -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.33 2007/10/02 06:15:12 martti Exp $ */ /* * Copyright (C) 1995-2003 by Darren Reed. @@ -16,12 +16,13 @@ #include #include #include -#if (__NetBSD_Version__ >= 399002000) && defined(_KERNEL) +#if defined(_KERNEL) && defined(__NetBSD_Version__) && \ + (__NetBSD_Version__ >= 399002000) # include #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 @@ extern struct ifnet vpnif; #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 1.33 2007/10/02 06:15:12 martti Exp $"; #endif @@ -177,7 +178,7 @@ u_long fr_defnatage = DEF_NAT_AGE, 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_flushtable __P((void)); 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 @@ void *ctx; 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 @@ void *ctx; error = fr_outobj(data, nat_tqb, IPFOBJ_STATETQTAB); break; + case SIOCGTABL : + error = nat_gettable(data); + break; + default : error = EINVAL; break; @@ -1080,7 +1085,7 @@ int getlock; 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 @@ int getlock; 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 @@ junkput: /* 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 @@ int logtype; * 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 @@ int logtype; 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 @@ int logtype; * 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 @@ static int nat_clearlist() 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 @@ static int nat_clearlist() } i++; } -#if SOLARIS +#if SOLARIS && !defined(_INET_IP_STACK_H) pfil_delayed_copy = 1; #endif nat_masks = 0; @@ -2498,10 +2525,12 @@ int direction; } 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 @@ int dir; } 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 @@ ipnat_t **inp; 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 @@ ipfgeniter_t *itp; 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 @@ ipfgeniter_t *itp; } 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 @@ ipfgeniter_t *itp; } 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 @@ ipfgeniter_t *itp; 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 @@ ipfgeniter_t *itp; 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 @@ ipfgeniter_t *itp; 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 @@ void *entry; 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; +} diff --git a/sys/dist/ipf/netinet/ip_nat.h b/sys/dist/ipf/netinet/ip_nat.h index 19ea84e82cbb..a2f77d292d54 100644 --- a/sys/dist/ipf/netinet/ip_nat.h +++ b/sys/dist/ipf/netinet/ip_nat.h @@ -1,4 +1,4 @@ -/* $NetBSD: ip_nat.h,v 1.12 2007/09/14 11:28:46 martti Exp $ */ +/* $NetBSD: ip_nat.h,v 1.13 2007/10/02 06:15:12 martti Exp $ */ /* * Copyright (C) 1995-2001, 2003 by Darren Reed. @@ -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 1.13 2007/10/02 06:15:12 martti Exp $ */ #ifndef __IP_NAT_H__ @@ -363,6 +363,7 @@ typedef struct natstat { 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 @@ typedef struct natlog { #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_maplookup __P((void *, u_int, struct in_addr, 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 *)); diff --git a/sys/dist/ipf/netinet/ip_state.c b/sys/dist/ipf/netinet/ip_state.c index 647cd516e376..7a68b89cf743 100644 --- a/sys/dist/ipf/netinet/ip_state.c +++ b/sys/dist/ipf/netinet/ip_state.c @@ -1,4 +1,4 @@ -/* $NetBSD: ip_state.c,v 1.29 2007/09/17 06:56:15 martti Exp $ */ +/* $NetBSD: ip_state.c,v 1.30 2007/10/02 06:15:12 martti Exp $ */ /* * Copyright (C) 1995-2003 by Darren Reed. @@ -114,10 +114,10 @@ struct file; #if !defined(lint) #if defined(__NetBSD__) #include -__KERNEL_RCSID(0, "$NetBSD: ip_state.c,v 1.29 2007/09/17 06:56:15 martti Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_state.c,v 1.30 2007/10/02 06:15:12 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 1.30 2007/10/02 06:15:12 martti Exp $"; #endif #endif @@ -657,8 +657,8 @@ void *data; 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 @@ void *data; 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 @@ ipstate_t *is; 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 @@ u_int hv; 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 @@ ipstate_t *is; 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 @@ ipfgeniter_t *itp; 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,32 +4088,28 @@ ipfgeniter_t *itp; 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 @@ ipfgeniter_t *itp; 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;