xref: /illumos-gate/usr/src/uts/common/inet/ipf/ip_frag.c (revision bd5ba10f)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright (C) 1993-2003 by Darren Reed.
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * See the IPFILTER.LICENCE file for details on licencing.
5ab25eeb5Syz  *
6*bd5ba10fSan  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
77c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
87c478bd9Sstevel@tonic-gate  */
97c478bd9Sstevel@tonic-gate 
107c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
117c478bd9Sstevel@tonic-gate 
127c478bd9Sstevel@tonic-gate #if defined(KERNEL) || defined(_KERNEL)
137c478bd9Sstevel@tonic-gate # undef KERNEL
147c478bd9Sstevel@tonic-gate # undef _KERNEL
157c478bd9Sstevel@tonic-gate # define        KERNEL	1
167c478bd9Sstevel@tonic-gate # define        _KERNEL	1
177c478bd9Sstevel@tonic-gate #endif
187c478bd9Sstevel@tonic-gate #include <sys/errno.h>
197c478bd9Sstevel@tonic-gate #include <sys/types.h>
207c478bd9Sstevel@tonic-gate #include <sys/param.h>
217c478bd9Sstevel@tonic-gate #include <sys/time.h>
227c478bd9Sstevel@tonic-gate #include <sys/file.h>
237c478bd9Sstevel@tonic-gate #ifdef __hpux
247c478bd9Sstevel@tonic-gate # include <sys/timeout.h>
257c478bd9Sstevel@tonic-gate #endif
267c478bd9Sstevel@tonic-gate #if !defined(_KERNEL)
277c478bd9Sstevel@tonic-gate # include <stdio.h>
287c478bd9Sstevel@tonic-gate # include <string.h>
297c478bd9Sstevel@tonic-gate # include <stdlib.h>
307c478bd9Sstevel@tonic-gate # define _KERNEL
317c478bd9Sstevel@tonic-gate # ifdef __OpenBSD__
327c478bd9Sstevel@tonic-gate struct file;
337c478bd9Sstevel@tonic-gate # endif
347c478bd9Sstevel@tonic-gate # include <sys/uio.h>
357c478bd9Sstevel@tonic-gate # undef _KERNEL
367c478bd9Sstevel@tonic-gate #endif
377c478bd9Sstevel@tonic-gate #if defined(_KERNEL) && (__FreeBSD_version >= 220000)
387c478bd9Sstevel@tonic-gate # include <sys/filio.h>
397c478bd9Sstevel@tonic-gate # include <sys/fcntl.h>
407c478bd9Sstevel@tonic-gate #else
417c478bd9Sstevel@tonic-gate # include <sys/ioctl.h>
427c478bd9Sstevel@tonic-gate #endif
43ab25eeb5Syz #if !defined(linux)
44ab25eeb5Syz # include <sys/protosw.h>
45ab25eeb5Syz #endif
467c478bd9Sstevel@tonic-gate #include <sys/socket.h>
477c478bd9Sstevel@tonic-gate #if defined(_KERNEL)
487c478bd9Sstevel@tonic-gate # include <sys/systm.h>
497c478bd9Sstevel@tonic-gate # if !defined(__SVR4) && !defined(__svr4__)
507c478bd9Sstevel@tonic-gate #  include <sys/mbuf.h>
517c478bd9Sstevel@tonic-gate # endif
527c478bd9Sstevel@tonic-gate #endif
537c478bd9Sstevel@tonic-gate #if !defined(__SVR4) && !defined(__svr4__)
54ab25eeb5Syz # if defined(_KERNEL) && !defined(__sgi) && !defined(AIX)
557c478bd9Sstevel@tonic-gate #  include <sys/kernel.h>
567c478bd9Sstevel@tonic-gate # endif
577c478bd9Sstevel@tonic-gate #else
587c478bd9Sstevel@tonic-gate # include <sys/byteorder.h>
597c478bd9Sstevel@tonic-gate # ifdef _KERNEL
607c478bd9Sstevel@tonic-gate #  include <sys/dditypes.h>
617c478bd9Sstevel@tonic-gate # endif
627c478bd9Sstevel@tonic-gate # include <sys/stream.h>
637c478bd9Sstevel@tonic-gate # include <sys/kmem.h>
647c478bd9Sstevel@tonic-gate #endif
657c478bd9Sstevel@tonic-gate #include <net/if.h>
667c478bd9Sstevel@tonic-gate #ifdef sun
677c478bd9Sstevel@tonic-gate # include <net/af.h>
687c478bd9Sstevel@tonic-gate #endif
697c478bd9Sstevel@tonic-gate #include <net/route.h>
707c478bd9Sstevel@tonic-gate #include <netinet/in.h>
717c478bd9Sstevel@tonic-gate #include <netinet/in_systm.h>
727c478bd9Sstevel@tonic-gate #include <netinet/ip.h>
73ab25eeb5Syz #if !defined(linux)
74ab25eeb5Syz # include <netinet/ip_var.h>
75ab25eeb5Syz #endif
767c478bd9Sstevel@tonic-gate #include <netinet/tcp.h>
777c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
787c478bd9Sstevel@tonic-gate #include <netinet/ip_icmp.h>
797c478bd9Sstevel@tonic-gate #include "netinet/ip_compat.h"
80ab25eeb5Syz #include <netinet/tcpip.h>
817c478bd9Sstevel@tonic-gate #include "netinet/ip_fil.h"
827c478bd9Sstevel@tonic-gate #include "netinet/ip_nat.h"
837c478bd9Sstevel@tonic-gate #include "netinet/ip_frag.h"
847c478bd9Sstevel@tonic-gate #include "netinet/ip_state.h"
857c478bd9Sstevel@tonic-gate #include "netinet/ip_auth.h"
86f4b3ec61Sdh #include "netinet/ipf_stack.h"
877c478bd9Sstevel@tonic-gate #if (__FreeBSD_version >= 300000)
887c478bd9Sstevel@tonic-gate # include <sys/malloc.h>
897c478bd9Sstevel@tonic-gate # if defined(_KERNEL)
907c478bd9Sstevel@tonic-gate #  ifndef IPFILTER_LKM
917c478bd9Sstevel@tonic-gate #   include <sys/libkern.h>
927c478bd9Sstevel@tonic-gate #   include <sys/systm.h>
937c478bd9Sstevel@tonic-gate #  endif
947c478bd9Sstevel@tonic-gate extern struct callout_handle fr_slowtimer_ch;
957c478bd9Sstevel@tonic-gate # endif
967c478bd9Sstevel@tonic-gate #endif
977c478bd9Sstevel@tonic-gate #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000)
987c478bd9Sstevel@tonic-gate # include <sys/callout.h>
997c478bd9Sstevel@tonic-gate extern struct callout fr_slowtimer_ch;
1007c478bd9Sstevel@tonic-gate #endif
1017c478bd9Sstevel@tonic-gate #if defined(__OpenBSD__)
1027c478bd9Sstevel@tonic-gate # include <sys/timeout.h>
1037c478bd9Sstevel@tonic-gate extern struct timeout fr_slowtimer_ch;
1047c478bd9Sstevel@tonic-gate #endif
105ab25eeb5Syz /* END OF INCLUDES */
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate #if !defined(lint)
1087c478bd9Sstevel@tonic-gate static const char sccsid[] = "@(#)ip_frag.c	1.11 3/24/96 (C) 1993-2000 Darren Reed";
109ab25eeb5Syz static const char rcsid[] = "@(#)$Id: ip_frag.c,v 2.77.2.5 2005/08/11 14:33:10 darrenr Exp $";
1107c478bd9Sstevel@tonic-gate #endif
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate static ipfr_t *ipfr_newfrag __P((fr_info_t *, u_32_t, ipfr_t **));
1137c478bd9Sstevel@tonic-gate static ipfr_t *fr_fraglookup __P((fr_info_t *, ipfr_t **));
114f4b3ec61Sdh static void fr_fragdelete __P((ipfr_t *, ipfr_t ***, ipf_stack_t *));
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
1177c478bd9Sstevel@tonic-gate /* Function:    fr_fraginit                                                 */
1187c478bd9Sstevel@tonic-gate /* Returns:     int - 0 == success, -1 == error                             */
1197c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
1207c478bd9Sstevel@tonic-gate /*                                                                          */
1217c478bd9Sstevel@tonic-gate /* Initialise the hash tables for the fragment cache lookups.               */
1227c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
123f4b3ec61Sdh int fr_fraginit(ifs)
124f4b3ec61Sdh ipf_stack_t *ifs;
1257c478bd9Sstevel@tonic-gate {
126f4b3ec61Sdh 	ifs->ifs_ipfr_tail = &ifs->ifs_ipfr_list;
127f4b3ec61Sdh 	ifs->ifs_ipfr_nattail = &ifs->ifs_ipfr_natlist;
128f4b3ec61Sdh 	ifs->ifs_ipfr_ipidtail = &ifs->ifs_ipfr_ipidlist;
1298128a42dSan 	/* the IP frag related variables are set in ipftuneable_setdefs() to
1308128a42dSan 	 * their default values
1318128a42dSan 	 */
132f4b3ec61Sdh 
133f4b3ec61Sdh 	KMALLOCS(ifs->ifs_ipfr_heads, ipfr_t **,
134f4b3ec61Sdh 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
135f4b3ec61Sdh 	if (ifs->ifs_ipfr_heads == NULL)
1367c478bd9Sstevel@tonic-gate 		return -1;
137f4b3ec61Sdh 	bzero((char *)ifs->ifs_ipfr_heads,
138f4b3ec61Sdh 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1397c478bd9Sstevel@tonic-gate 
140f4b3ec61Sdh 	KMALLOCS(ifs->ifs_ipfr_nattab, ipfr_t **,
141f4b3ec61Sdh 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
142f4b3ec61Sdh 	if (ifs->ifs_ipfr_nattab == NULL)
1437c478bd9Sstevel@tonic-gate 		return -1;
144f4b3ec61Sdh 	bzero((char *)ifs->ifs_ipfr_nattab,
145f4b3ec61Sdh 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1467c478bd9Sstevel@tonic-gate 
147f4b3ec61Sdh 	KMALLOCS(ifs->ifs_ipfr_ipidtab, ipfr_t **,
148f4b3ec61Sdh 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
149f4b3ec61Sdh 	if (ifs->ifs_ipfr_ipidtab == NULL)
1507c478bd9Sstevel@tonic-gate 		return -1;
151f4b3ec61Sdh 	bzero((char *)ifs->ifs_ipfr_ipidtab,
152f4b3ec61Sdh 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1537c478bd9Sstevel@tonic-gate 
154f4b3ec61Sdh 	RWLOCK_INIT(&ifs->ifs_ipf_frag, "ipf fragment rwlock");
1557663b816Sml 
1567663b816Sml 	/* Initialise frblock with "block in all" */
157f4b3ec61Sdh 	bzero((char *)&ifs->ifs_frblock, sizeof(ifs->ifs_frblock));
158f4b3ec61Sdh 	ifs->ifs_frblock.fr_flags = FR_BLOCK|FR_INQUE;	/* block in */
159f4b3ec61Sdh 	ifs->ifs_frblock.fr_ref = 1;
1607663b816Sml 
161f4b3ec61Sdh 	ifs->ifs_fr_frag_init = 1;
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	return 0;
1647c478bd9Sstevel@tonic-gate }
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
1687c478bd9Sstevel@tonic-gate /* Function:    fr_fragunload                                               */
1697c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
1707c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
1717c478bd9Sstevel@tonic-gate /*                                                                          */
1727c478bd9Sstevel@tonic-gate /* Free all memory allocated whilst running and from initialisation.        */
1737c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
174f4b3ec61Sdh void fr_fragunload(ifs)
175f4b3ec61Sdh ipf_stack_t *ifs;
1767c478bd9Sstevel@tonic-gate {
177f4b3ec61Sdh 	if (ifs->ifs_fr_frag_init == 1) {
178f4b3ec61Sdh 		fr_fragclear(ifs);
1797c478bd9Sstevel@tonic-gate 
180f4b3ec61Sdh 		RW_DESTROY(&ifs->ifs_ipf_frag);
181f4b3ec61Sdh 		ifs->ifs_fr_frag_init = 0;
1827c478bd9Sstevel@tonic-gate 	}
1837c478bd9Sstevel@tonic-gate 
184f4b3ec61Sdh 	if (ifs->ifs_ipfr_heads != NULL) {
185f4b3ec61Sdh 		KFREES(ifs->ifs_ipfr_heads,
186f4b3ec61Sdh 		    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
187f4b3ec61Sdh 	}
188f4b3ec61Sdh 	ifs->ifs_ipfr_heads = NULL;
1897c478bd9Sstevel@tonic-gate 
190f4b3ec61Sdh 	if (ifs->ifs_ipfr_nattab != NULL) {
191f4b3ec61Sdh 		KFREES(ifs->ifs_ipfr_nattab,
192f4b3ec61Sdh 		    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
193f4b3ec61Sdh 	}
194f4b3ec61Sdh 	ifs->ifs_ipfr_nattab = NULL;
1957c478bd9Sstevel@tonic-gate 
196f4b3ec61Sdh 	if (ifs->ifs_ipfr_ipidtab != NULL) {
197f4b3ec61Sdh 		KFREES(ifs->ifs_ipfr_ipidtab,
198f4b3ec61Sdh 		    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
199f4b3ec61Sdh 	}
200f4b3ec61Sdh 	ifs->ifs_ipfr_ipidtab = NULL;
2017c478bd9Sstevel@tonic-gate }
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
2057c478bd9Sstevel@tonic-gate /* Function:    fr_fragstats                                                */
206ab25eeb5Syz /* Returns:     ipfrstat_t* - pointer to struct with current frag stats     */
2077c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
2087c478bd9Sstevel@tonic-gate /*                                                                          */
2097c478bd9Sstevel@tonic-gate /* Updates ipfr_stats with current information and returns a pointer to it  */
2107c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
211f4b3ec61Sdh ipfrstat_t *fr_fragstats(ifs)
212f4b3ec61Sdh ipf_stack_t *ifs;
2137c478bd9Sstevel@tonic-gate {
214f4b3ec61Sdh 	ifs->ifs_ipfr_stats.ifs_table = ifs->ifs_ipfr_heads;
215f4b3ec61Sdh 	ifs->ifs_ipfr_stats.ifs_nattab = ifs->ifs_ipfr_nattab;
216f4b3ec61Sdh 	ifs->ifs_ipfr_stats.ifs_inuse = ifs->ifs_ipfr_inuse;
217f4b3ec61Sdh 	return &ifs->ifs_ipfr_stats;
2187c478bd9Sstevel@tonic-gate }
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
2227c478bd9Sstevel@tonic-gate /* Function:    ipfr_newfrag                                                */
2237c478bd9Sstevel@tonic-gate /* Returns:     ipfr_t * - pointer to fragment cache state info or NULL     */
2247c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)   - pointer to packet information                    */
2257c478bd9Sstevel@tonic-gate /*              table(I) - pointer to frag table to add to                  */
2267c478bd9Sstevel@tonic-gate /*                                                                          */
2277c478bd9Sstevel@tonic-gate /* Add a new entry to the fragment cache, registering it as having come     */
2287c478bd9Sstevel@tonic-gate /* through this box, with the result of the filter operation.               */
2297c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
2307c478bd9Sstevel@tonic-gate static ipfr_t *ipfr_newfrag(fin, pass, table)
2317c478bd9Sstevel@tonic-gate fr_info_t *fin;
2327c478bd9Sstevel@tonic-gate u_32_t pass;
2337c478bd9Sstevel@tonic-gate ipfr_t *table[];
2347c478bd9Sstevel@tonic-gate {
2357c478bd9Sstevel@tonic-gate 	ipfr_t *fra, frag;
2367c478bd9Sstevel@tonic-gate 	u_int idx, off;
237f4b3ec61Sdh 	ipf_stack_t *ifs = fin->fin_ifs;
2387c478bd9Sstevel@tonic-gate 
239*bd5ba10fSan 	if (ifs->ifs_ipfr_inuse >= ifs->ifs_ipfr_size)
2407c478bd9Sstevel@tonic-gate 		return NULL;
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
2437c478bd9Sstevel@tonic-gate 		return NULL;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	if (pass & FR_FRSTRICT)
2467663b816Sml 		if (fin->fin_off != 0)
2477c478bd9Sstevel@tonic-gate 			return NULL;
2487c478bd9Sstevel@tonic-gate 
2497663b816Sml 	frag.ipfr_p = fin->fin_p;
2507663b816Sml 	idx = fin->fin_p;
2517663b816Sml 	frag.ipfr_id = fin->fin_id;
2527663b816Sml 	idx += fin->fin_id;
2537663b816Sml 	frag.ipfr_source = fin->fin_fi.fi_src;
2547663b816Sml 	idx += frag.ipfr_src.s_addr;
2557663b816Sml 	frag.ipfr_dest = fin->fin_fi.fi_dst;
2567663b816Sml 	idx += frag.ipfr_dst.s_addr;
2577c478bd9Sstevel@tonic-gate 	frag.ipfr_ifp = fin->fin_ifp;
2587c478bd9Sstevel@tonic-gate 	idx *= 127;
259*bd5ba10fSan 	idx %= ifs->ifs_ipfr_size;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
2627c478bd9Sstevel@tonic-gate 	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
2637c478bd9Sstevel@tonic-gate 	frag.ipfr_auth = fin->fin_fi.fi_auth;
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	/*
2667c478bd9Sstevel@tonic-gate 	 * first, make sure it isn't already there...
2677c478bd9Sstevel@tonic-gate 	 */
2687c478bd9Sstevel@tonic-gate 	for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext)
2697c478bd9Sstevel@tonic-gate 		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp,
2707c478bd9Sstevel@tonic-gate 			  IPFR_CMPSZ)) {
271f4b3ec61Sdh 			ifs->ifs_ipfr_stats.ifs_exists++;
2727c478bd9Sstevel@tonic-gate 			return NULL;
2737c478bd9Sstevel@tonic-gate 		}
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 	/*
2767c478bd9Sstevel@tonic-gate 	 * allocate some memory, if possible, if not, just record that we
2777c478bd9Sstevel@tonic-gate 	 * failed to do so.
2787c478bd9Sstevel@tonic-gate 	 */
2797c478bd9Sstevel@tonic-gate 	KMALLOC(fra, ipfr_t *);
2807c478bd9Sstevel@tonic-gate 	if (fra == NULL) {
281f4b3ec61Sdh 		ifs->ifs_ipfr_stats.ifs_nomem++;
2827c478bd9Sstevel@tonic-gate 		return NULL;
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 
285ab25eeb5Syz 	fra->ipfr_rule = fin->fin_fr;
286ab25eeb5Syz 	if (fra->ipfr_rule != NULL) {
287ab25eeb5Syz 
288ab25eeb5Syz 		frentry_t *fr;
289ab25eeb5Syz 
290ab25eeb5Syz 		fr = fin->fin_fr;
291ab25eeb5Syz 		MUTEX_ENTER(&fr->fr_lock);
292ab25eeb5Syz 		fr->fr_ref++;
293ab25eeb5Syz 		MUTEX_EXIT(&fr->fr_lock);
2947663b816Sml 	}
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	/*
2977c478bd9Sstevel@tonic-gate 	 * Insert the fragment into the fragment table, copy the struct used
2987c478bd9Sstevel@tonic-gate 	 * in the search using bcopy rather than reassign each field.
2997c478bd9Sstevel@tonic-gate 	 * Set the ttl to the default.
3007c478bd9Sstevel@tonic-gate 	 */
3017c478bd9Sstevel@tonic-gate 	if ((fra->ipfr_hnext = table[idx]) != NULL)
3027c478bd9Sstevel@tonic-gate 		table[idx]->ipfr_hprev = &fra->ipfr_hnext;
3037c478bd9Sstevel@tonic-gate 	fra->ipfr_hprev = table + idx;
3047c478bd9Sstevel@tonic-gate 	fra->ipfr_data = NULL;
3057c478bd9Sstevel@tonic-gate 	table[idx] = fra;
3067c478bd9Sstevel@tonic-gate 	bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ);
307f4b3ec61Sdh 	fra->ipfr_ttl = ifs->ifs_fr_ticks + ifs->ifs_fr_ipfrttl;
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	/*
3107c478bd9Sstevel@tonic-gate 	 * Compute the offset of the expected start of the next packet.
3117c478bd9Sstevel@tonic-gate 	 */
312ab25eeb5Syz 	off = fin->fin_off;
3137663b816Sml 	if (off == 0) {
3147c478bd9Sstevel@tonic-gate 		fra->ipfr_seen0 = 1;
3157663b816Sml 		fra->ipfr_firstend = fin->fin_flen;
3167663b816Sml 	} else {
3177663b816Sml 		fra->ipfr_seen0 = 0;
3187663b816Sml 		fra->ipfr_firstend = 0;
3197663b816Sml 	}
320ab25eeb5Syz 	fra->ipfr_off = off + fin->fin_dlen;
3217c478bd9Sstevel@tonic-gate 	fra->ipfr_pass = pass;
322f4b3ec61Sdh 	fra->ipfr_ref = 1;
323f4b3ec61Sdh 	ifs->ifs_ipfr_stats.ifs_new++;
324f4b3ec61Sdh 	ifs->ifs_ipfr_inuse++;
3257c478bd9Sstevel@tonic-gate 	return fra;
3267c478bd9Sstevel@tonic-gate }
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
3307c478bd9Sstevel@tonic-gate /* Function:    fr_newfrag                                                  */
3317c478bd9Sstevel@tonic-gate /* Returns:     int - 0 == success, -1 == error                             */
3327c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)  - pointer to packet information                     */
3337c478bd9Sstevel@tonic-gate /*                                                                          */
3347c478bd9Sstevel@tonic-gate /* Add a new entry to the fragment cache table based on the current packet  */
3357c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
3367c478bd9Sstevel@tonic-gate int fr_newfrag(fin, pass)
3377c478bd9Sstevel@tonic-gate u_32_t pass;
3387c478bd9Sstevel@tonic-gate fr_info_t *fin;
3397c478bd9Sstevel@tonic-gate {
3407c478bd9Sstevel@tonic-gate 	ipfr_t	*fra;
341f4b3ec61Sdh 	ipf_stack_t *ifs = fin->fin_ifs;
3427c478bd9Sstevel@tonic-gate 
343f4b3ec61Sdh 	if (ifs->ifs_fr_frag_lock != 0)
344ab25eeb5Syz 		return -1;
3457c478bd9Sstevel@tonic-gate 
346f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_frag);
347f4b3ec61Sdh 	fra = ipfr_newfrag(fin, pass, ifs->ifs_ipfr_heads);
3487c478bd9Sstevel@tonic-gate 	if (fra != NULL) {
349f4b3ec61Sdh 		*ifs->ifs_ipfr_tail = fra;
350f4b3ec61Sdh 		fra->ipfr_prev = ifs->ifs_ipfr_tail;
351f4b3ec61Sdh 		ifs->ifs_ipfr_tail = &fra->ipfr_next;
352f4b3ec61Sdh 		if (ifs->ifs_ipfr_list == NULL)
353f4b3ec61Sdh 			ifs->ifs_ipfr_list = fra;
3547c478bd9Sstevel@tonic-gate 		fra->ipfr_next = NULL;
3557c478bd9Sstevel@tonic-gate 	}
356f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_frag);
3577c478bd9Sstevel@tonic-gate 	return fra ? 0 : -1;
3587c478bd9Sstevel@tonic-gate }
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
3627c478bd9Sstevel@tonic-gate /* Function:    fr_nat_newfrag                                              */
3637c478bd9Sstevel@tonic-gate /* Returns:     int - 0 == success, -1 == error                             */
3647c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)  - pointer to packet information                     */
3657c478bd9Sstevel@tonic-gate /*              nat(I)  - pointer to NAT structure                          */
3667c478bd9Sstevel@tonic-gate /*                                                                          */
3677c478bd9Sstevel@tonic-gate /* Create a new NAT fragment cache entry based on the current packet and    */
3687c478bd9Sstevel@tonic-gate /* the NAT structure for this "session".                                    */
3697c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
3707c478bd9Sstevel@tonic-gate int fr_nat_newfrag(fin, pass, nat)
3717c478bd9Sstevel@tonic-gate fr_info_t *fin;
3727c478bd9Sstevel@tonic-gate u_32_t pass;
3737c478bd9Sstevel@tonic-gate nat_t *nat;
3747c478bd9Sstevel@tonic-gate {
3757c478bd9Sstevel@tonic-gate 	ipfr_t	*fra;
376f4b3ec61Sdh 	ipf_stack_t *ifs = fin->fin_ifs;
3777c478bd9Sstevel@tonic-gate 
378f4b3ec61Sdh 	if ((fin->fin_v != 4) || (ifs->ifs_fr_frag_lock != 0))
3797c478bd9Sstevel@tonic-gate 		return 0;
3807c478bd9Sstevel@tonic-gate 
381f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_natfrag);
382f4b3ec61Sdh 	fra = ipfr_newfrag(fin, pass, ifs->ifs_ipfr_nattab);
3837c478bd9Sstevel@tonic-gate 	if (fra != NULL) {
3847c478bd9Sstevel@tonic-gate 		fra->ipfr_data = nat;
3857c478bd9Sstevel@tonic-gate 		nat->nat_data = fra;
386f4b3ec61Sdh 		*ifs->ifs_ipfr_nattail = fra;
387f4b3ec61Sdh 		fra->ipfr_prev = ifs->ifs_ipfr_nattail;
388f4b3ec61Sdh 		ifs->ifs_ipfr_nattail = &fra->ipfr_next;
3897c478bd9Sstevel@tonic-gate 		fra->ipfr_next = NULL;
3907c478bd9Sstevel@tonic-gate 	}
391f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
3927c478bd9Sstevel@tonic-gate 	return fra ? 0 : -1;
3937c478bd9Sstevel@tonic-gate }
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
3977c478bd9Sstevel@tonic-gate /* Function:    fr_ipid_newfrag                                             */
3987c478bd9Sstevel@tonic-gate /* Returns:     int - 0 == success, -1 == error                             */
3997c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)  - pointer to packet information                     */
4007c478bd9Sstevel@tonic-gate /*              ipid(I) - new IP ID for this fragmented packet              */
4017c478bd9Sstevel@tonic-gate /*                                                                          */
4027c478bd9Sstevel@tonic-gate /* Create a new fragment cache entry for this packet and store, as a data   */
4037c478bd9Sstevel@tonic-gate /* pointer, the new IP ID value.                                            */
4047c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
4057c478bd9Sstevel@tonic-gate int fr_ipid_newfrag(fin, ipid)
4067c478bd9Sstevel@tonic-gate fr_info_t *fin;
4077c478bd9Sstevel@tonic-gate u_32_t ipid;
4087c478bd9Sstevel@tonic-gate {
4097c478bd9Sstevel@tonic-gate 	ipfr_t	*fra;
410f4b3ec61Sdh 	ipf_stack_t *ifs = fin->fin_ifs;
4117c478bd9Sstevel@tonic-gate 
412f4b3ec61Sdh 	if (ifs->ifs_fr_frag_lock)
4137c478bd9Sstevel@tonic-gate 		return 0;
4147c478bd9Sstevel@tonic-gate 
415f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_ipidfrag);
416f4b3ec61Sdh 	fra = ipfr_newfrag(fin, 0, ifs->ifs_ipfr_ipidtab);
4177c478bd9Sstevel@tonic-gate 	if (fra != NULL) {
4187c478bd9Sstevel@tonic-gate 		fra->ipfr_data = (void *)(uintptr_t)ipid;
419f4b3ec61Sdh 		*ifs->ifs_ipfr_ipidtail = fra;
420f4b3ec61Sdh 		fra->ipfr_prev = ifs->ifs_ipfr_ipidtail;
421f4b3ec61Sdh 		ifs->ifs_ipfr_ipidtail = &fra->ipfr_next;
4227c478bd9Sstevel@tonic-gate 		fra->ipfr_next = NULL;
4237c478bd9Sstevel@tonic-gate 	}
424f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_ipidfrag);
4257c478bd9Sstevel@tonic-gate 	return fra ? 0 : -1;
4267c478bd9Sstevel@tonic-gate }
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
4307c478bd9Sstevel@tonic-gate /* Function:    fr_fraglookup                                               */
4317c478bd9Sstevel@tonic-gate /* Returns:     ipfr_t * - pointer to ipfr_t structure if there's a         */
4327c478bd9Sstevel@tonic-gate /*                         matching entry in the frag table, else NULL      */
4337c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)   - pointer to packet information                    */
4347c478bd9Sstevel@tonic-gate /*              table(I) - pointer to fragment cache table to search        */
4357c478bd9Sstevel@tonic-gate /*                                                                          */
4367c478bd9Sstevel@tonic-gate /* Check the fragment cache to see if there is already a record of this     */
4377c478bd9Sstevel@tonic-gate /* packet with its filter result known.                                     */
4387c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
4397c478bd9Sstevel@tonic-gate static ipfr_t *fr_fraglookup(fin, table)
4407c478bd9Sstevel@tonic-gate fr_info_t *fin;
4417c478bd9Sstevel@tonic-gate ipfr_t *table[];
4427c478bd9Sstevel@tonic-gate {
4437c478bd9Sstevel@tonic-gate 	ipfr_t *f, frag;
4447c478bd9Sstevel@tonic-gate 	u_int idx;
445f4b3ec61Sdh 	ipf_stack_t *ifs = fin->fin_ifs;
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
4487c478bd9Sstevel@tonic-gate 		return NULL;
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 	/*
4517c478bd9Sstevel@tonic-gate 	 * For fragments, we record protocol, packet id, TOS and both IP#'s
4527c478bd9Sstevel@tonic-gate 	 * (these should all be the same for all fragments of a packet).
4537c478bd9Sstevel@tonic-gate 	 *
4547c478bd9Sstevel@tonic-gate 	 * build up a hash value to index the table with.
4557c478bd9Sstevel@tonic-gate 	 */
4567663b816Sml 	frag.ipfr_p = fin->fin_p;
4577663b816Sml 	idx = fin->fin_p;
4587663b816Sml 	frag.ipfr_id = fin->fin_id;
4597663b816Sml 	idx += fin->fin_id;
4607663b816Sml 	frag.ipfr_source = fin->fin_fi.fi_src;
4617663b816Sml 	idx += frag.ipfr_src.s_addr;
4627663b816Sml 	frag.ipfr_dest = fin->fin_fi.fi_dst;
4637663b816Sml 	idx += frag.ipfr_dst.s_addr;
4647c478bd9Sstevel@tonic-gate 	frag.ipfr_ifp = fin->fin_ifp;
4657c478bd9Sstevel@tonic-gate 	idx *= 127;
4667c478bd9Sstevel@tonic-gate 	idx %= IPFT_SIZE;
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
4697c478bd9Sstevel@tonic-gate 	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
4707c478bd9Sstevel@tonic-gate 	frag.ipfr_auth = fin->fin_fi.fi_auth;
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	/*
4737c478bd9Sstevel@tonic-gate 	 * check the table, careful to only compare the right amount of data
4747c478bd9Sstevel@tonic-gate 	 */
4757c478bd9Sstevel@tonic-gate 	for (f = table[idx]; f; f = f->ipfr_hnext)
4767c478bd9Sstevel@tonic-gate 		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp,
4777c478bd9Sstevel@tonic-gate 			  IPFR_CMPSZ)) {
4787c478bd9Sstevel@tonic-gate 			u_short	off;
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 			/*
4817c478bd9Sstevel@tonic-gate 			 * We don't want to let short packets match because
4827c478bd9Sstevel@tonic-gate 			 * they could be compromising the security of other
4837c478bd9Sstevel@tonic-gate 			 * rules that want to match on layer 4 fields (and
4847c478bd9Sstevel@tonic-gate 			 * can't because they have been fragmented off.)
4857c478bd9Sstevel@tonic-gate 			 * Why do this check here?  The counter acts as an
4867c478bd9Sstevel@tonic-gate 			 * indicator of this kind of attack, whereas if it was
4877c478bd9Sstevel@tonic-gate 			 * elsewhere, it wouldn't know if other matching
4887c478bd9Sstevel@tonic-gate 			 * packets had been seen.
4897c478bd9Sstevel@tonic-gate 			 */
4907c478bd9Sstevel@tonic-gate 			if (fin->fin_flx & FI_SHORT) {
491f4b3ec61Sdh 				ATOMIC_INCL(ifs->ifs_ipfr_stats.ifs_short);
4927c478bd9Sstevel@tonic-gate 				continue;
4937c478bd9Sstevel@tonic-gate 			}
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 			/*
4967c478bd9Sstevel@tonic-gate 			 * XXX - We really need to be guarding against the
4977c478bd9Sstevel@tonic-gate 			 * retransmission of (src,dst,id,offset-range) here
4987c478bd9Sstevel@tonic-gate 			 * because a fragmented packet is never resent with
4997c478bd9Sstevel@tonic-gate 			 * the same IP ID# (or shouldn't).
5007c478bd9Sstevel@tonic-gate 			 */
501ab25eeb5Syz 			off = fin->fin_off; /* same as in ipfr_newfrag() */
5027c478bd9Sstevel@tonic-gate 			if (f->ipfr_seen0) {
5037c478bd9Sstevel@tonic-gate 				if (off == 0) {
504f4b3ec61Sdh 					ATOMIC_INCL(ifs->ifs_ipfr_stats.ifs_retrans0);
5057c478bd9Sstevel@tonic-gate 					continue;
5067c478bd9Sstevel@tonic-gate 				}
5077663b816Sml 			} else if (off == 0) {
5087c478bd9Sstevel@tonic-gate 				f->ipfr_seen0 = 1;
5097663b816Sml 				f->ipfr_firstend = fin->fin_flen;
5107663b816Sml 			}
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 			if (f != table[idx]) {
5137c478bd9Sstevel@tonic-gate 				ipfr_t **fp;
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate 				/*
5167c478bd9Sstevel@tonic-gate 				 * Move fragment info. to the top of the list
5177c478bd9Sstevel@tonic-gate 				 * to speed up searches.  First, delink...
5187c478bd9Sstevel@tonic-gate 				 */
5197c478bd9Sstevel@tonic-gate 				fp = f->ipfr_hprev;
5207c478bd9Sstevel@tonic-gate 				(*fp) = f->ipfr_hnext;
5217c478bd9Sstevel@tonic-gate 				if (f->ipfr_hnext != NULL)
5227c478bd9Sstevel@tonic-gate 					f->ipfr_hnext->ipfr_hprev = fp;
5237c478bd9Sstevel@tonic-gate 				/*
5247c478bd9Sstevel@tonic-gate 				 * Then put back at the top of the chain.
5257c478bd9Sstevel@tonic-gate 				 */
5267c478bd9Sstevel@tonic-gate 				f->ipfr_hnext = table[idx];
5277c478bd9Sstevel@tonic-gate 				table[idx]->ipfr_hprev = &f->ipfr_hnext;
5287c478bd9Sstevel@tonic-gate 				f->ipfr_hprev = table + idx;
5297c478bd9Sstevel@tonic-gate 				table[idx] = f;
5307c478bd9Sstevel@tonic-gate 			}
5317c478bd9Sstevel@tonic-gate 
5327663b816Sml 			if (fin->fin_v == 6) {
533ab25eeb5Syz 				if (f->ipfr_seen0 && (off < f->ipfr_firstend))
5347663b816Sml 					fin->fin_flx |= FI_BAD;
5357663b816Sml 			}
5367c478bd9Sstevel@tonic-gate 			/*
5377c478bd9Sstevel@tonic-gate 			 * If we've follwed the fragments, and this is the
5387c478bd9Sstevel@tonic-gate 			 * last (in order), shrink expiration time.
5397c478bd9Sstevel@tonic-gate 			 */
5407c478bd9Sstevel@tonic-gate 			if (off == f->ipfr_off) {
541ab25eeb5Syz 				if (!(fin->fin_ip->ip_off & IP_MF))
542f4b3ec61Sdh 					f->ipfr_ttl = ifs->ifs_fr_ticks + 1;
543ab25eeb5Syz 				f->ipfr_off = fin->fin_dlen + off;
5447c478bd9Sstevel@tonic-gate 			} else if (f->ipfr_pass & FR_FRSTRICT)
5457c478bd9Sstevel@tonic-gate 				continue;
546f4b3ec61Sdh 			ATOMIC_INCL(ifs->ifs_ipfr_stats.ifs_hits);
5477c478bd9Sstevel@tonic-gate 			return f;
5487c478bd9Sstevel@tonic-gate 		}
5497c478bd9Sstevel@tonic-gate 	return NULL;
5507c478bd9Sstevel@tonic-gate }
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
5547c478bd9Sstevel@tonic-gate /* Function:    fr_nat_knownfrag                                            */
5557c478bd9Sstevel@tonic-gate /* Returns:     nat_t* - pointer to 'parent' NAT structure if frag table    */
5567c478bd9Sstevel@tonic-gate /*                       match found, else NULL                             */
5577c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)  - pointer to packet information                     */
5587c478bd9Sstevel@tonic-gate /*                                                                          */
5597c478bd9Sstevel@tonic-gate /* Functional interface for NAT lookups of the NAT fragment cache           */
5607c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
5617c478bd9Sstevel@tonic-gate nat_t *fr_nat_knownfrag(fin)
5627c478bd9Sstevel@tonic-gate fr_info_t *fin;
5637c478bd9Sstevel@tonic-gate {
5647c478bd9Sstevel@tonic-gate 	nat_t	*nat;
5657c478bd9Sstevel@tonic-gate 	ipfr_t	*ipf;
566f4b3ec61Sdh 	ipf_stack_t *ifs = fin->fin_ifs;
5677c478bd9Sstevel@tonic-gate 
568f4b3ec61Sdh 	if ((fin->fin_v != 4) || (ifs->ifs_fr_frag_lock) || !ifs->ifs_ipfr_natlist)
5697c478bd9Sstevel@tonic-gate 		return NULL;
570f4b3ec61Sdh 	READ_ENTER(&ifs->ifs_ipf_natfrag);
571f4b3ec61Sdh 	ipf = fr_fraglookup(fin, ifs->ifs_ipfr_nattab);
5727c478bd9Sstevel@tonic-gate 	if (ipf != NULL) {
5737c478bd9Sstevel@tonic-gate 		nat = ipf->ipfr_data;
5747c478bd9Sstevel@tonic-gate 		/*
5757c478bd9Sstevel@tonic-gate 		 * This is the last fragment for this packet.
5767c478bd9Sstevel@tonic-gate 		 */
577f4b3ec61Sdh 		if ((ipf->ipfr_ttl == ifs->ifs_fr_ticks + 1) && (nat != NULL)) {
5787c478bd9Sstevel@tonic-gate 			nat->nat_data = NULL;
5797c478bd9Sstevel@tonic-gate 			ipf->ipfr_data = NULL;
5807c478bd9Sstevel@tonic-gate 		}
5817c478bd9Sstevel@tonic-gate 	} else
5827c478bd9Sstevel@tonic-gate 		nat = NULL;
583f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
5847c478bd9Sstevel@tonic-gate 	return nat;
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
5897c478bd9Sstevel@tonic-gate /* Function:    fr_ipid_knownfrag                                           */
5907c478bd9Sstevel@tonic-gate /* Returns:     u_32_t - IPv4 ID for this packet if match found, else       */
5917c478bd9Sstevel@tonic-gate /*                       return 0xfffffff to indicate no match.             */
5927c478bd9Sstevel@tonic-gate /* Parameters:  fin(I) - pointer to packet information                      */
5937c478bd9Sstevel@tonic-gate /*                                                                          */
5947c478bd9Sstevel@tonic-gate /* Functional interface for IP ID lookups of the IP ID fragment cache       */
5957c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
5967c478bd9Sstevel@tonic-gate u_32_t fr_ipid_knownfrag(fin)
5977c478bd9Sstevel@tonic-gate fr_info_t *fin;
5987c478bd9Sstevel@tonic-gate {
5997c478bd9Sstevel@tonic-gate 	ipfr_t	*ipf;
6007c478bd9Sstevel@tonic-gate 	u_32_t	id;
601f4b3ec61Sdh 	ipf_stack_t *ifs = fin->fin_ifs;
6027c478bd9Sstevel@tonic-gate 
603f4b3ec61Sdh 	if ((fin->fin_v != 4) || (ifs->ifs_fr_frag_lock) || !ifs->ifs_ipfr_ipidlist)
6047c478bd9Sstevel@tonic-gate 		return 0xffffffff;
6057c478bd9Sstevel@tonic-gate 
606f4b3ec61Sdh 	READ_ENTER(&ifs->ifs_ipf_ipidfrag);
607f4b3ec61Sdh 	ipf = fr_fraglookup(fin, ifs->ifs_ipfr_ipidtab);
6087c478bd9Sstevel@tonic-gate 	if (ipf != NULL)
6097c478bd9Sstevel@tonic-gate 		id = (u_32_t)(uintptr_t)ipf->ipfr_data;
6107c478bd9Sstevel@tonic-gate 	else
6117c478bd9Sstevel@tonic-gate 		id = 0xffffffff;
612f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_ipidfrag);
6137c478bd9Sstevel@tonic-gate 	return id;
6147c478bd9Sstevel@tonic-gate }
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
6187c478bd9Sstevel@tonic-gate /* Function:    fr_knownfrag                                                */
6197c478bd9Sstevel@tonic-gate /* Returns:     frentry_t* - pointer to filter rule if a match is found in  */
6207c478bd9Sstevel@tonic-gate /*                           the frag cache table, else NULL.               */
6217c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)   - pointer to packet information                    */
6227c478bd9Sstevel@tonic-gate /*              passp(O) - pointer to where to store rule flags resturned   */
6237c478bd9Sstevel@tonic-gate /*                                                                          */
6247c478bd9Sstevel@tonic-gate /* Functional interface for normal lookups of the fragment cache.  If a     */
6257c478bd9Sstevel@tonic-gate /* match is found, return the rule pointer and flags from the rule, except  */
6267c478bd9Sstevel@tonic-gate /* that if FR_LOGFIRST is set, reset FR_LOG.                                */
6277c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
6287c478bd9Sstevel@tonic-gate frentry_t *fr_knownfrag(fin, passp)
6297c478bd9Sstevel@tonic-gate fr_info_t *fin;
6307c478bd9Sstevel@tonic-gate u_32_t *passp;
6317c478bd9Sstevel@tonic-gate {
6327c478bd9Sstevel@tonic-gate 	frentry_t *fr = NULL;
6337c478bd9Sstevel@tonic-gate 	ipfr_t	*fra;
6347663b816Sml 	u_32_t pass, oflx;
635f4b3ec61Sdh 	ipf_stack_t *ifs = fin->fin_ifs;
6367c478bd9Sstevel@tonic-gate 
637f4b3ec61Sdh 	if ((ifs->ifs_fr_frag_lock) || (ifs->ifs_ipfr_list == NULL))
6387c478bd9Sstevel@tonic-gate 		return NULL;
6397c478bd9Sstevel@tonic-gate 
640f4b3ec61Sdh 	READ_ENTER(&ifs->ifs_ipf_frag);
6417663b816Sml 	oflx = fin->fin_flx;
642f4b3ec61Sdh 	fra = fr_fraglookup(fin, ifs->ifs_ipfr_heads);
6437c478bd9Sstevel@tonic-gate 	if (fra != NULL) {
6447c478bd9Sstevel@tonic-gate 		fr = fra->ipfr_rule;
6457c478bd9Sstevel@tonic-gate 		fin->fin_fr = fr;
6467c478bd9Sstevel@tonic-gate 		if (fr != NULL) {
6477c478bd9Sstevel@tonic-gate 			pass = fr->fr_flags;
6487c478bd9Sstevel@tonic-gate 			if ((pass & FR_LOGFIRST) != 0)
6497c478bd9Sstevel@tonic-gate 				pass &= ~(FR_LOGFIRST|FR_LOG);
6507c478bd9Sstevel@tonic-gate 			*passp = pass;
6517c478bd9Sstevel@tonic-gate 		}
6527c478bd9Sstevel@tonic-gate 	}
6537663b816Sml 	if (!(oflx & FI_BAD) && (fin->fin_flx & FI_BAD)) {
6547663b816Sml 		*passp &= ~FR_CMDMASK;
6557663b816Sml 		*passp |= FR_BLOCK;
656f4b3ec61Sdh 		fr = &ifs->ifs_frblock;
6577663b816Sml 	}
658f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_frag);
6597c478bd9Sstevel@tonic-gate 	return fr;
6607c478bd9Sstevel@tonic-gate }
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
6647c478bd9Sstevel@tonic-gate /* Function:    fr_forget                                                   */
6657c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
6667c478bd9Sstevel@tonic-gate /* Parameters:  ptr(I) - pointer to data structure                          */
6677c478bd9Sstevel@tonic-gate /*                                                                          */
6687c478bd9Sstevel@tonic-gate /* Search through all of the fragment cache entries and wherever a pointer  */
6697c478bd9Sstevel@tonic-gate /* is found to match ptr, reset it to NULL.                                 */
6707c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
671f4b3ec61Sdh void fr_forget(ptr, ifs)
6727c478bd9Sstevel@tonic-gate void *ptr;
673f4b3ec61Sdh ipf_stack_t *ifs;
6747c478bd9Sstevel@tonic-gate {
6757c478bd9Sstevel@tonic-gate 	ipfr_t	*fr;
6767c478bd9Sstevel@tonic-gate 
677f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_frag);
678f4b3ec61Sdh 	for (fr = ifs->ifs_ipfr_list; fr; fr = fr->ipfr_next)
6797c478bd9Sstevel@tonic-gate 		if (fr->ipfr_data == ptr)
6807c478bd9Sstevel@tonic-gate 			fr->ipfr_data = NULL;
681f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_frag);
6827c478bd9Sstevel@tonic-gate }
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
6867c478bd9Sstevel@tonic-gate /* Function:    fr_forgetnat                                                */
6877c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
6887c478bd9Sstevel@tonic-gate /* Parameters:  ptr(I) - pointer to data structure                          */
6897c478bd9Sstevel@tonic-gate /*                                                                          */
6907c478bd9Sstevel@tonic-gate /* Search through all of the fragment cache entries for NAT and wherever a  */
6917c478bd9Sstevel@tonic-gate /* pointer  is found to match ptr, reset it to NULL.                        */
6927c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
693f4b3ec61Sdh void fr_forgetnat(ptr, ifs)
6947c478bd9Sstevel@tonic-gate void *ptr;
695f4b3ec61Sdh ipf_stack_t *ifs;
6967c478bd9Sstevel@tonic-gate {
6977c478bd9Sstevel@tonic-gate 	ipfr_t	*fr;
6987c478bd9Sstevel@tonic-gate 
699f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_natfrag);
700f4b3ec61Sdh 	for (fr = ifs->ifs_ipfr_natlist; fr; fr = fr->ipfr_next)
7017c478bd9Sstevel@tonic-gate 		if (fr->ipfr_data == ptr)
7027c478bd9Sstevel@tonic-gate 			fr->ipfr_data = NULL;
703f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
7047c478bd9Sstevel@tonic-gate }
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
7087c478bd9Sstevel@tonic-gate /* Function:    fr_fragdelete                                               */
7097c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
7107c478bd9Sstevel@tonic-gate /* Parameters:  fra(I)   - pointer to fragment structure to delete          */
7117c478bd9Sstevel@tonic-gate /*              tail(IO) - pointer to the pointer to the tail of the frag   */
7127c478bd9Sstevel@tonic-gate /*                         list                                             */
7137c478bd9Sstevel@tonic-gate /*                                                                          */
7147c478bd9Sstevel@tonic-gate /* Remove a fragment cache table entry from the table & list.  Also free    */
7157c478bd9Sstevel@tonic-gate /* the filter rule it is associated with it if it is no longer used as a    */
7167c478bd9Sstevel@tonic-gate /* result of decreasing the reference count.                                */
7177c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
718f4b3ec61Sdh static void fr_fragdelete(fra, tail, ifs)
7197c478bd9Sstevel@tonic-gate ipfr_t *fra, ***tail;
720f4b3ec61Sdh ipf_stack_t *ifs;
7217c478bd9Sstevel@tonic-gate {
7227c478bd9Sstevel@tonic-gate 	frentry_t *fr;
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	fr = fra->ipfr_rule;
7257c478bd9Sstevel@tonic-gate 	if (fr != NULL)
726f4b3ec61Sdh 	    (void)fr_derefrule(&fr, ifs);
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 	if (fra->ipfr_next)
7297c478bd9Sstevel@tonic-gate 		fra->ipfr_next->ipfr_prev = fra->ipfr_prev;
7307c478bd9Sstevel@tonic-gate 	*fra->ipfr_prev = fra->ipfr_next;
7317c478bd9Sstevel@tonic-gate 	if (*tail == &fra->ipfr_next)
7327c478bd9Sstevel@tonic-gate 		*tail = fra->ipfr_prev;
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	if (fra->ipfr_hnext)
7357c478bd9Sstevel@tonic-gate 		fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev;
7367c478bd9Sstevel@tonic-gate 	*fra->ipfr_hprev = fra->ipfr_hnext;
737f4b3ec61Sdh 
738f4b3ec61Sdh 	if (fra->ipfr_ref <= 0)
739f4b3ec61Sdh 		KFREE(fra);
7407c478bd9Sstevel@tonic-gate }
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
7447c478bd9Sstevel@tonic-gate /* Function:    fr_fragclear                                                */
7457c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
7467c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
7477c478bd9Sstevel@tonic-gate /*                                                                          */
7487c478bd9Sstevel@tonic-gate /* Free memory in use by fragment state information kept.  Do the normal    */
7497c478bd9Sstevel@tonic-gate /* fragment state stuff first and then the NAT-fragment table.              */
7507c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
751f4b3ec61Sdh void fr_fragclear(ifs)
752f4b3ec61Sdh ipf_stack_t *ifs;
7537c478bd9Sstevel@tonic-gate {
7547c478bd9Sstevel@tonic-gate 	ipfr_t	*fra;
7557c478bd9Sstevel@tonic-gate 	nat_t	*nat;
7567c478bd9Sstevel@tonic-gate 
757f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_frag);
758f4b3ec61Sdh 	while ((fra = ifs->ifs_ipfr_list) != NULL) {
759f4b3ec61Sdh 		fra->ipfr_ref--;
760f4b3ec61Sdh 		fr_fragdelete(fra, &ifs->ifs_ipfr_tail, ifs);
761f4b3ec61Sdh 	}
762f4b3ec61Sdh 	ifs->ifs_ipfr_tail = &ifs->ifs_ipfr_list;
763f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_frag);
7647c478bd9Sstevel@tonic-gate 
765f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_nat);
766f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_natfrag);
767f4b3ec61Sdh 	while ((fra = ifs->ifs_ipfr_natlist) != NULL) {
7687c478bd9Sstevel@tonic-gate 		nat = fra->ipfr_data;
7697c478bd9Sstevel@tonic-gate 		if (nat != NULL) {
7707c478bd9Sstevel@tonic-gate 			if (nat->nat_data == fra)
7717c478bd9Sstevel@tonic-gate 				nat->nat_data = NULL;
7727c478bd9Sstevel@tonic-gate 		}
773f4b3ec61Sdh 		fra->ipfr_ref--;
774f4b3ec61Sdh 		fr_fragdelete(fra, &ifs->ifs_ipfr_nattail, ifs);
7757c478bd9Sstevel@tonic-gate 	}
776f4b3ec61Sdh 	ifs->ifs_ipfr_nattail = &ifs->ifs_ipfr_natlist;
777f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
778f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
7797c478bd9Sstevel@tonic-gate }
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
7837c478bd9Sstevel@tonic-gate /* Function:    fr_fragexpire                                               */
7847c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
7857c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
7867c478bd9Sstevel@tonic-gate /*                                                                          */
7877c478bd9Sstevel@tonic-gate /* Expire entries in the fragment cache table that have been there too long */
7887c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
789f4b3ec61Sdh void fr_fragexpire(ifs)
790f4b3ec61Sdh ipf_stack_t *ifs;
7917c478bd9Sstevel@tonic-gate {
7927c478bd9Sstevel@tonic-gate 	ipfr_t	**fp, *fra;
7937c478bd9Sstevel@tonic-gate 	nat_t	*nat;
794ab25eeb5Syz 	SPL_INT(s);
7957c478bd9Sstevel@tonic-gate 
796f4b3ec61Sdh 	if (ifs->ifs_fr_frag_lock)
7977c478bd9Sstevel@tonic-gate 		return;
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 	SPL_NET(s);
800f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_frag);
8017c478bd9Sstevel@tonic-gate 	/*
8027c478bd9Sstevel@tonic-gate 	 * Go through the entire table, looking for entries to expire,
803f4b3ec61Sdh 	 * which is indicated by the ttl being less than or equal to
804f4b3ec61Sdh 	 * ifs_fr_ticks.
8057c478bd9Sstevel@tonic-gate 	 */
806f4b3ec61Sdh 	for (fp = &ifs->ifs_ipfr_list; ((fra = *fp) != NULL); ) {
807f4b3ec61Sdh 		if (fra->ipfr_ttl > ifs->ifs_fr_ticks)
8087c478bd9Sstevel@tonic-gate 			break;
809f4b3ec61Sdh 		fra->ipfr_ref--;
810f4b3ec61Sdh 		fr_fragdelete(fra, &ifs->ifs_ipfr_tail, ifs);
811f4b3ec61Sdh 		ifs->ifs_ipfr_stats.ifs_expire++;
812f4b3ec61Sdh 		ifs->ifs_ipfr_inuse--;
8137c478bd9Sstevel@tonic-gate 	}
814f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_frag);
8157c478bd9Sstevel@tonic-gate 
816f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_ipidfrag);
817f4b3ec61Sdh 	for (fp = &ifs->ifs_ipfr_ipidlist; ((fra = *fp) != NULL); ) {
818f4b3ec61Sdh 		if (fra->ipfr_ttl > ifs->ifs_fr_ticks)
8197c478bd9Sstevel@tonic-gate 			break;
820f4b3ec61Sdh 		fra->ipfr_ref--;
821f4b3ec61Sdh 		fr_fragdelete(fra, &ifs->ifs_ipfr_ipidtail, ifs);
822f4b3ec61Sdh 		ifs->ifs_ipfr_stats.ifs_expire++;
823f4b3ec61Sdh 		ifs->ifs_ipfr_inuse--;
8247c478bd9Sstevel@tonic-gate 	}
825f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_ipidfrag);
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 	/*
8287c478bd9Sstevel@tonic-gate 	 * Same again for the NAT table, except that if the structure also
8297c478bd9Sstevel@tonic-gate 	 * still points to a NAT structure, and the NAT structure points back
8307c478bd9Sstevel@tonic-gate 	 * at the one to be free'd, NULL the reference from the NAT struct.
8317c478bd9Sstevel@tonic-gate 	 * NOTE: We need to grab both mutex's early, and in this order so as
8327c478bd9Sstevel@tonic-gate 	 * to prevent a deadlock if both try to expire at the same time.
8337c478bd9Sstevel@tonic-gate 	 */
834f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_nat);
835f4b3ec61Sdh 	WRITE_ENTER(&ifs->ifs_ipf_natfrag);
836f4b3ec61Sdh 	for (fp = &ifs->ifs_ipfr_natlist; ((fra = *fp) != NULL); ) {
837f4b3ec61Sdh 		if (fra->ipfr_ttl > ifs->ifs_fr_ticks)
8387c478bd9Sstevel@tonic-gate 			break;
8397c478bd9Sstevel@tonic-gate 		nat = fra->ipfr_data;
8407c478bd9Sstevel@tonic-gate 		if (nat != NULL) {
8417c478bd9Sstevel@tonic-gate 			if (nat->nat_data == fra)
8427c478bd9Sstevel@tonic-gate 				nat->nat_data = NULL;
8437c478bd9Sstevel@tonic-gate 		}
844f4b3ec61Sdh 		fra->ipfr_ref--;
845f4b3ec61Sdh 		fr_fragdelete(fra, &ifs->ifs_ipfr_nattail, ifs);
846f4b3ec61Sdh 		ifs->ifs_ipfr_stats.ifs_expire++;
847f4b3ec61Sdh 		ifs->ifs_ipfr_inuse--;
8487c478bd9Sstevel@tonic-gate 	}
849f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
850f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
8517c478bd9Sstevel@tonic-gate 	SPL_X(s);
8527c478bd9Sstevel@tonic-gate }
8537c478bd9Sstevel@tonic-gate 
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
8567c478bd9Sstevel@tonic-gate /* Function:    fr_slowtimer                                                */
8577c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
8587c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
8597c478bd9Sstevel@tonic-gate /*                                                                          */
8607c478bd9Sstevel@tonic-gate /* Slowly expire held state for fragments.  Timeouts are set * in           */
8617c478bd9Sstevel@tonic-gate /* expectation of this being called twice per second.                       */
8627c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
8637c478bd9Sstevel@tonic-gate #if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \
864ab25eeb5Syz 			  !defined(__osf__) && !defined(linux))
8657c478bd9Sstevel@tonic-gate # if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi))
866f4b3ec61Sdh void fr_slowtimer __P((void *arg))
8677c478bd9Sstevel@tonic-gate # else
868f4b3ec61Sdh int fr_slowtimer(void *arg)
8697c478bd9Sstevel@tonic-gate # endif
8707c478bd9Sstevel@tonic-gate {
871f4b3ec61Sdh 	ipf_stack_t *ifs = arg;
872f4b3ec61Sdh 
873f4b3ec61Sdh 	READ_ENTER(&ifs->ifs_ipf_global);
874f4b3ec61Sdh 
875f4b3ec61Sdh 	fr_fragexpire(ifs);
876f4b3ec61Sdh 	fr_timeoutstate(ifs);
877f4b3ec61Sdh 	fr_natexpire(ifs);
878f4b3ec61Sdh 	fr_authexpire(ifs);
879f4b3ec61Sdh 	ifs->ifs_fr_ticks++;
880f4b3ec61Sdh 	if (ifs->ifs_fr_running <= 0)
8817c478bd9Sstevel@tonic-gate 		goto done;
8827c478bd9Sstevel@tonic-gate # ifdef _KERNEL
8837c478bd9Sstevel@tonic-gate #  if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000)
8847c478bd9Sstevel@tonic-gate 	callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL);
8857c478bd9Sstevel@tonic-gate #  else
8867c478bd9Sstevel@tonic-gate #   if defined(__OpenBSD__)
8877c478bd9Sstevel@tonic-gate 	timeout_add(&fr_slowtimer_ch, hz/2);
8887c478bd9Sstevel@tonic-gate #   else
8897c478bd9Sstevel@tonic-gate #    if (__FreeBSD_version >= 300000)
8907c478bd9Sstevel@tonic-gate 	fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2);
8917c478bd9Sstevel@tonic-gate #    else
892ab25eeb5Syz #     ifdef linux
893ab25eeb5Syz 	;
894ab25eeb5Syz #     else
8957c478bd9Sstevel@tonic-gate 	timeout(fr_slowtimer, NULL, hz/2);
896ab25eeb5Syz #     endif
8977c478bd9Sstevel@tonic-gate #    endif /* FreeBSD */
8987c478bd9Sstevel@tonic-gate #   endif /* OpenBSD */
8997c478bd9Sstevel@tonic-gate #  endif /* NetBSD */
9007c478bd9Sstevel@tonic-gate # endif
9017c478bd9Sstevel@tonic-gate done:
902f4b3ec61Sdh 	RWLOCK_EXIT(&ifs->ifs_ipf_global);
9037c478bd9Sstevel@tonic-gate # if (BSD < 199103) || !defined(_KERNEL)
9047c478bd9Sstevel@tonic-gate 	return 0;
9057c478bd9Sstevel@tonic-gate # endif
9067c478bd9Sstevel@tonic-gate }
9077c478bd9Sstevel@tonic-gate #endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */
908f4b3ec61Sdh 
909f4b3ec61Sdh /*ARGSUSED*/
910f4b3ec61Sdh int fr_nextfrag(token, itp, top, tail, lock, ifs)
911f4b3ec61Sdh ipftoken_t *token;
912f4b3ec61Sdh ipfgeniter_t *itp;
913f4b3ec61Sdh ipfr_t **top, ***tail;
914f4b3ec61Sdh ipfrwlock_t *lock;
915f4b3ec61Sdh ipf_stack_t *ifs;
916f4b3ec61Sdh {
917f4b3ec61Sdh 	ipfr_t *frag, *next, zero;
918f4b3ec61Sdh 	int error = 0;
919f4b3ec61Sdh 
920f4b3ec61Sdh 	frag = token->ipt_data;
921f4b3ec61Sdh 	if (frag == (ipfr_t *)-1) {
922f4b3ec61Sdh 		ipf_freetoken(token, ifs);
923f4b3ec61Sdh 		return ESRCH;
924f4b3ec61Sdh 	}
925f4b3ec61Sdh 
926f4b3ec61Sdh 	READ_ENTER(lock);
927f4b3ec61Sdh 	if (frag == NULL)
928f4b3ec61Sdh 		next = *top;
929f4b3ec61Sdh 	else
930f4b3ec61Sdh 		next = frag->ipfr_next;
931f4b3ec61Sdh 
932f4b3ec61Sdh 	if (next != NULL) {
933f4b3ec61Sdh 		ATOMIC_INC(next->ipfr_ref);
934f4b3ec61Sdh 		token->ipt_data = next;
935f4b3ec61Sdh 	} else {
936f4b3ec61Sdh 		bzero(&zero, sizeof(zero));
937f4b3ec61Sdh 		next = &zero;
938f4b3ec61Sdh 		token->ipt_data = (void *)-1;
939f4b3ec61Sdh 	}
940f4b3ec61Sdh 	RWLOCK_EXIT(lock);
941f4b3ec61Sdh 
942f4b3ec61Sdh 	if (frag != NULL) {
943f4b3ec61Sdh 		fr_fragderef(&frag, lock, ifs);
944f4b3ec61Sdh 	}
945f4b3ec61Sdh 
946f4b3ec61Sdh 	error = COPYOUT(next, itp->igi_data, sizeof(*next));
947f4b3ec61Sdh 	if (error != 0)
948f4b3ec61Sdh 		error = EFAULT;
949f4b3ec61Sdh 
950f4b3ec61Sdh 	return error;
951f4b3ec61Sdh }
952f4b3ec61Sdh 
953f4b3ec61Sdh 
954f4b3ec61Sdh void fr_fragderef(frp, lock, ifs)
955f4b3ec61Sdh ipfr_t **frp;
956f4b3ec61Sdh ipfrwlock_t *lock;
957f4b3ec61Sdh ipf_stack_t *ifs;
958f4b3ec61Sdh {
959f4b3ec61Sdh 	ipfr_t *fra;
960f4b3ec61Sdh 
961f4b3ec61Sdh 	fra = *frp;
962f4b3ec61Sdh 	*frp = NULL;
963f4b3ec61Sdh 
964f4b3ec61Sdh 	WRITE_ENTER(lock);
965f4b3ec61Sdh 	fra->ipfr_ref--;
966f4b3ec61Sdh 	if (fra->ipfr_ref <= 0) {
967f4b3ec61Sdh 		KFREE(fra);
968f4b3ec61Sdh 		ifs->ifs_ipfr_stats.ifs_expire++;
969f4b3ec61Sdh 		ifs->ifs_ipfr_inuse--;
970f4b3ec61Sdh 	}
971f4b3ec61Sdh 	RWLOCK_EXIT(lock);
972f4b3ec61Sdh }
973