xref: /onnv-gate/usr/src/uts/common/inet/ipf/ip_frag.c (revision 9876:fd1599e394c8)
12393Syz155240 /*
22393Syz155240  * Copyright (C) 1993-2003 by Darren Reed.
32393Syz155240  *
42393Syz155240  * See the IPFILTER.LICENCE file for details on licencing.
52393Syz155240  *
6*9876SDarren.Reed@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
72393Syz155240  * Use is subject to license terms.
82393Syz155240  */
92393Syz155240 
102393Syz155240 #if defined(KERNEL) || defined(_KERNEL)
112393Syz155240 # undef KERNEL
122393Syz155240 # undef _KERNEL
132393Syz155240 # define        KERNEL	1
142393Syz155240 # define        _KERNEL	1
152393Syz155240 #endif
162393Syz155240 #include <sys/errno.h>
172393Syz155240 #include <sys/types.h>
182393Syz155240 #include <sys/param.h>
192393Syz155240 #include <sys/time.h>
202393Syz155240 #include <sys/file.h>
212393Syz155240 #ifdef __hpux
222393Syz155240 # include <sys/timeout.h>
232393Syz155240 #endif
242393Syz155240 #if !defined(_KERNEL)
252393Syz155240 # include <stdio.h>
262393Syz155240 # include <string.h>
272393Syz155240 # include <stdlib.h>
282393Syz155240 # define _KERNEL
292393Syz155240 # ifdef __OpenBSD__
302393Syz155240 struct file;
312393Syz155240 # endif
322393Syz155240 # include <sys/uio.h>
332393Syz155240 # undef _KERNEL
342393Syz155240 #endif
352393Syz155240 #if defined(_KERNEL) && (__FreeBSD_version >= 220000)
362393Syz155240 # include <sys/filio.h>
372393Syz155240 # include <sys/fcntl.h>
382393Syz155240 #else
392393Syz155240 # include <sys/ioctl.h>
402393Syz155240 #endif
412393Syz155240 #if !defined(linux)
422393Syz155240 # include <sys/protosw.h>
432393Syz155240 #endif
442393Syz155240 #include <sys/socket.h>
452393Syz155240 #if defined(_KERNEL)
462393Syz155240 # include <sys/systm.h>
472393Syz155240 # if !defined(__SVR4) && !defined(__svr4__)
482393Syz155240 #  include <sys/mbuf.h>
492393Syz155240 # endif
502393Syz155240 #endif
512393Syz155240 #if !defined(__SVR4) && !defined(__svr4__)
522393Syz155240 # if defined(_KERNEL) && !defined(__sgi) && !defined(AIX)
532393Syz155240 #  include <sys/kernel.h>
542393Syz155240 # endif
552393Syz155240 #else
562393Syz155240 # include <sys/byteorder.h>
572393Syz155240 # ifdef _KERNEL
582393Syz155240 #  include <sys/dditypes.h>
592393Syz155240 # endif
602393Syz155240 # include <sys/stream.h>
612393Syz155240 # include <sys/kmem.h>
622393Syz155240 #endif
632393Syz155240 #include <net/if.h>
642393Syz155240 #ifdef sun
652393Syz155240 # include <net/af.h>
662393Syz155240 #endif
672393Syz155240 #include <net/route.h>
682393Syz155240 #include <netinet/in.h>
692393Syz155240 #include <netinet/in_systm.h>
702393Syz155240 #include <netinet/ip.h>
712393Syz155240 #if !defined(linux)
722393Syz155240 # include <netinet/ip_var.h>
732393Syz155240 #endif
742393Syz155240 #include <netinet/tcp.h>
752393Syz155240 #include <netinet/udp.h>
762393Syz155240 #include <netinet/ip_icmp.h>
772393Syz155240 #include "netinet/ip_compat.h"
782393Syz155240 #include <netinet/tcpip.h>
792393Syz155240 #include "netinet/ip_fil.h"
802393Syz155240 #include "netinet/ip_nat.h"
812393Syz155240 #include "netinet/ip_frag.h"
822393Syz155240 #include "netinet/ip_state.h"
832393Syz155240 #include "netinet/ip_auth.h"
843448Sdh155122 #include "netinet/ipf_stack.h"
852393Syz155240 #if (__FreeBSD_version >= 300000)
862393Syz155240 # include <sys/malloc.h>
872393Syz155240 # if defined(_KERNEL)
882393Syz155240 #  ifndef IPFILTER_LKM
892393Syz155240 #   include <sys/libkern.h>
902393Syz155240 #   include <sys/systm.h>
912393Syz155240 #  endif
922393Syz155240 extern struct callout_handle fr_slowtimer_ch;
932393Syz155240 # endif
942393Syz155240 #endif
952393Syz155240 #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000)
962393Syz155240 # include <sys/callout.h>
972393Syz155240 extern struct callout fr_slowtimer_ch;
982393Syz155240 #endif
992393Syz155240 #if defined(__OpenBSD__)
1002393Syz155240 # include <sys/timeout.h>
1012393Syz155240 extern struct timeout fr_slowtimer_ch;
1022393Syz155240 #endif
1032393Syz155240 /* END OF INCLUDES */
1042393Syz155240 
1052393Syz155240 #if !defined(lint)
1062393Syz155240 static const char sccsid[] = "@(#)ip_frag.c	1.11 3/24/96 (C) 1993-2000 Darren Reed";
1072393Syz155240 static const char rcsid[] = "@(#)$Id: ip_frag.c,v 2.77.2.5 2005/08/11 14:33:10 darrenr Exp $";
1082393Syz155240 #endif
1092393Syz155240 
110*9876SDarren.Reed@Sun.COM static INLINE int ipfr_index __P((fr_info_t *, ipfr_t *));
1112393Syz155240 static ipfr_t *ipfr_newfrag __P((fr_info_t *, u_32_t, ipfr_t **));
1122393Syz155240 static ipfr_t *fr_fraglookup __P((fr_info_t *, ipfr_t **));
1133448Sdh155122 static void fr_fragdelete __P((ipfr_t *, ipfr_t ***, ipf_stack_t *));
1142393Syz155240 
1152393Syz155240 /* ------------------------------------------------------------------------ */
1162393Syz155240 /* Function:    fr_fraginit                                                 */
1172393Syz155240 /* Returns:     int - 0 == success, -1 == error                             */
1182393Syz155240 /* Parameters:  Nil                                                         */
1192393Syz155240 /*                                                                          */
1202393Syz155240 /* Initialise the hash tables for the fragment cache lookups.               */
1212393Syz155240 /* ------------------------------------------------------------------------ */
fr_fraginit(ifs)1223448Sdh155122 int fr_fraginit(ifs)
1233448Sdh155122 ipf_stack_t *ifs;
1242393Syz155240 {
1253448Sdh155122 	ifs->ifs_ipfr_tail = &ifs->ifs_ipfr_list;
1263448Sdh155122 	ifs->ifs_ipfr_nattail = &ifs->ifs_ipfr_natlist;
1273448Sdh155122 	ifs->ifs_ipfr_ipidtail = &ifs->ifs_ipfr_ipidlist;
1284251San207044 	/* the IP frag related variables are set in ipftuneable_setdefs() to
1294251San207044 	 * their default values
1304251San207044 	 */
1313448Sdh155122 
1323448Sdh155122 	KMALLOCS(ifs->ifs_ipfr_heads, ipfr_t **,
1333448Sdh155122 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1343448Sdh155122 	if (ifs->ifs_ipfr_heads == NULL)
1352393Syz155240 		return -1;
1363448Sdh155122 	bzero((char *)ifs->ifs_ipfr_heads,
1373448Sdh155122 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1382393Syz155240 
1393448Sdh155122 	KMALLOCS(ifs->ifs_ipfr_nattab, ipfr_t **,
1403448Sdh155122 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1413448Sdh155122 	if (ifs->ifs_ipfr_nattab == NULL)
1422393Syz155240 		return -1;
1433448Sdh155122 	bzero((char *)ifs->ifs_ipfr_nattab,
1443448Sdh155122 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1452393Syz155240 
1463448Sdh155122 	KMALLOCS(ifs->ifs_ipfr_ipidtab, ipfr_t **,
1473448Sdh155122 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1483448Sdh155122 	if (ifs->ifs_ipfr_ipidtab == NULL)
1492393Syz155240 		return -1;
1503448Sdh155122 	bzero((char *)ifs->ifs_ipfr_ipidtab,
1513448Sdh155122 	    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1522393Syz155240 
1533448Sdh155122 	RWLOCK_INIT(&ifs->ifs_ipf_frag, "ipf fragment rwlock");
1542393Syz155240 
1552393Syz155240 	/* Initialise frblock with "block in all" */
1563448Sdh155122 	bzero((char *)&ifs->ifs_frblock, sizeof(ifs->ifs_frblock));
1573448Sdh155122 	ifs->ifs_frblock.fr_flags = FR_BLOCK|FR_INQUE;	/* block in */
1583448Sdh155122 	ifs->ifs_frblock.fr_ref = 1;
1592393Syz155240 
1603448Sdh155122 	ifs->ifs_fr_frag_init = 1;
1612393Syz155240 
1622393Syz155240 	return 0;
1632393Syz155240 }
1642393Syz155240 
1652393Syz155240 
1662393Syz155240 /* ------------------------------------------------------------------------ */
1672393Syz155240 /* Function:    fr_fragunload                                               */
1682393Syz155240 /* Returns:     Nil                                                         */
1692393Syz155240 /* Parameters:  Nil                                                         */
1702393Syz155240 /*                                                                          */
1712393Syz155240 /* Free all memory allocated whilst running and from initialisation.        */
1722393Syz155240 /* ------------------------------------------------------------------------ */
fr_fragunload(ifs)1733448Sdh155122 void fr_fragunload(ifs)
1743448Sdh155122 ipf_stack_t *ifs;
1752393Syz155240 {
1763448Sdh155122 	if (ifs->ifs_fr_frag_init == 1) {
1773448Sdh155122 		fr_fragclear(ifs);
1782393Syz155240 
1793448Sdh155122 		RW_DESTROY(&ifs->ifs_ipf_frag);
1803448Sdh155122 		ifs->ifs_fr_frag_init = 0;
1812393Syz155240 	}
1822393Syz155240 
1833448Sdh155122 	if (ifs->ifs_ipfr_heads != NULL) {
1843448Sdh155122 		KFREES(ifs->ifs_ipfr_heads,
1853448Sdh155122 		    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1863448Sdh155122 	}
1873448Sdh155122 	ifs->ifs_ipfr_heads = NULL;
1882393Syz155240 
1893448Sdh155122 	if (ifs->ifs_ipfr_nattab != NULL) {
1903448Sdh155122 		KFREES(ifs->ifs_ipfr_nattab,
1913448Sdh155122 		    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1923448Sdh155122 	}
1933448Sdh155122 	ifs->ifs_ipfr_nattab = NULL;
1942393Syz155240 
1953448Sdh155122 	if (ifs->ifs_ipfr_ipidtab != NULL) {
1963448Sdh155122 		KFREES(ifs->ifs_ipfr_ipidtab,
1973448Sdh155122 		    ifs->ifs_ipfr_size * sizeof(ipfr_t *));
1983448Sdh155122 	}
1993448Sdh155122 	ifs->ifs_ipfr_ipidtab = NULL;
2002393Syz155240 }
2012393Syz155240 
2022393Syz155240 
2032393Syz155240 /* ------------------------------------------------------------------------ */
2042393Syz155240 /* Function:    fr_fragstats                                                */
2052393Syz155240 /* Returns:     ipfrstat_t* - pointer to struct with current frag stats     */
2062393Syz155240 /* Parameters:  Nil                                                         */
2072393Syz155240 /*                                                                          */
2082393Syz155240 /* Updates ipfr_stats with current information and returns a pointer to it  */
2092393Syz155240 /* ------------------------------------------------------------------------ */
fr_fragstats(ifs)2103448Sdh155122 ipfrstat_t *fr_fragstats(ifs)
2113448Sdh155122 ipf_stack_t *ifs;
2122393Syz155240 {
2133448Sdh155122 	ifs->ifs_ipfr_stats.ifs_table = ifs->ifs_ipfr_heads;
2143448Sdh155122 	ifs->ifs_ipfr_stats.ifs_nattab = ifs->ifs_ipfr_nattab;
2153448Sdh155122 	ifs->ifs_ipfr_stats.ifs_inuse = ifs->ifs_ipfr_inuse;
2163448Sdh155122 	return &ifs->ifs_ipfr_stats;
2172393Syz155240 }
2182393Syz155240 
2192393Syz155240 
2202393Syz155240 /* ------------------------------------------------------------------------ */
221*9876SDarren.Reed@Sun.COM /* Function:    ipfr_index                                                  */
222*9876SDarren.Reed@Sun.COM /* Returns:     int     - index in fragment table for given packet          */
223*9876SDarren.Reed@Sun.COM /* Parameters:  fin(I)  - pointer to packet information                     */
224*9876SDarren.Reed@Sun.COM /*              frag(O) - pointer to ipfr_t structure to fill               */
225*9876SDarren.Reed@Sun.COM /*                                                                          */
226*9876SDarren.Reed@Sun.COM /* Compute the index in the fragment table while filling the per packet     */
227*9876SDarren.Reed@Sun.COM /* part of the fragment state.                                              */
228*9876SDarren.Reed@Sun.COM /* ------------------------------------------------------------------------ */
ipfr_index(fin,frag)229*9876SDarren.Reed@Sun.COM static INLINE int ipfr_index(fin, frag)
230*9876SDarren.Reed@Sun.COM fr_info_t *fin;
231*9876SDarren.Reed@Sun.COM ipfr_t *frag;
232*9876SDarren.Reed@Sun.COM {
233*9876SDarren.Reed@Sun.COM 	u_int idx;
234*9876SDarren.Reed@Sun.COM 
235*9876SDarren.Reed@Sun.COM 	/*
236*9876SDarren.Reed@Sun.COM 	 * For fragments, we record protocol, packet id, TOS and both IP#'s
237*9876SDarren.Reed@Sun.COM 	 * (these should all be the same for all fragments of a packet).
238*9876SDarren.Reed@Sun.COM 	 *
239*9876SDarren.Reed@Sun.COM 	 * build up a hash value to index the table with.
240*9876SDarren.Reed@Sun.COM 	 */
241*9876SDarren.Reed@Sun.COM 
242*9876SDarren.Reed@Sun.COM #ifdef	USE_INET6
243*9876SDarren.Reed@Sun.COM 	if (fin->fin_v == 6) {
244*9876SDarren.Reed@Sun.COM 		ip6_t *ip6 = (ip6_t *)fin->fin_ip;
245*9876SDarren.Reed@Sun.COM 
246*9876SDarren.Reed@Sun.COM 		frag->ipfr_p = fin->fin_fi.fi_p;
247*9876SDarren.Reed@Sun.COM 		frag->ipfr_id = fin->fin_id;
248*9876SDarren.Reed@Sun.COM 		frag->ipfr_tos = ip6->ip6_flow & IPV6_FLOWINFO_MASK;
249*9876SDarren.Reed@Sun.COM 		frag->ipfr_src.in6 = ip6->ip6_src;
250*9876SDarren.Reed@Sun.COM 		frag->ipfr_dst.in6 = ip6->ip6_dst;
251*9876SDarren.Reed@Sun.COM 	} else
252*9876SDarren.Reed@Sun.COM #endif
253*9876SDarren.Reed@Sun.COM 	{
254*9876SDarren.Reed@Sun.COM 		ip_t *ip = fin->fin_ip;
255*9876SDarren.Reed@Sun.COM 
256*9876SDarren.Reed@Sun.COM 		frag->ipfr_p = ip->ip_p;
257*9876SDarren.Reed@Sun.COM 		frag->ipfr_id = ip->ip_id;
258*9876SDarren.Reed@Sun.COM 		frag->ipfr_tos = ip->ip_tos;
259*9876SDarren.Reed@Sun.COM 		frag->ipfr_src.in4.s_addr = ip->ip_src.s_addr;
260*9876SDarren.Reed@Sun.COM 		frag->ipfr_src.i6[1] = 0;
261*9876SDarren.Reed@Sun.COM 		frag->ipfr_src.i6[2] = 0;
262*9876SDarren.Reed@Sun.COM 		frag->ipfr_src.i6[3] = 0;
263*9876SDarren.Reed@Sun.COM 		frag->ipfr_dst.in4.s_addr = ip->ip_dst.s_addr;
264*9876SDarren.Reed@Sun.COM 		frag->ipfr_dst.i6[1] = 0;
265*9876SDarren.Reed@Sun.COM 		frag->ipfr_dst.i6[2] = 0;
266*9876SDarren.Reed@Sun.COM 		frag->ipfr_dst.i6[3] = 0;
267*9876SDarren.Reed@Sun.COM 	}
268*9876SDarren.Reed@Sun.COM 	frag->ipfr_ifp = fin->fin_ifp;
269*9876SDarren.Reed@Sun.COM 	frag->ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
270*9876SDarren.Reed@Sun.COM 	frag->ipfr_secmsk = fin->fin_fi.fi_secmsk;
271*9876SDarren.Reed@Sun.COM 	frag->ipfr_auth = fin->fin_fi.fi_auth;
272*9876SDarren.Reed@Sun.COM 
273*9876SDarren.Reed@Sun.COM 	idx = frag->ipfr_p;
274*9876SDarren.Reed@Sun.COM 	idx += frag->ipfr_id;
275*9876SDarren.Reed@Sun.COM 	idx += frag->ipfr_src.i6[0];
276*9876SDarren.Reed@Sun.COM 	idx += frag->ipfr_src.i6[1];
277*9876SDarren.Reed@Sun.COM 	idx += frag->ipfr_src.i6[2];
278*9876SDarren.Reed@Sun.COM 	idx += frag->ipfr_src.i6[3];
279*9876SDarren.Reed@Sun.COM 	idx += frag->ipfr_dst.i6[0];
280*9876SDarren.Reed@Sun.COM 	idx += frag->ipfr_dst.i6[1];
281*9876SDarren.Reed@Sun.COM 	idx += frag->ipfr_dst.i6[2];
282*9876SDarren.Reed@Sun.COM 	idx += frag->ipfr_dst.i6[3];
283*9876SDarren.Reed@Sun.COM 	idx *= 127;
284*9876SDarren.Reed@Sun.COM 	idx %= IPFT_SIZE;
285*9876SDarren.Reed@Sun.COM 
286*9876SDarren.Reed@Sun.COM 	return idx;
287*9876SDarren.Reed@Sun.COM }
288*9876SDarren.Reed@Sun.COM 
289*9876SDarren.Reed@Sun.COM 
290*9876SDarren.Reed@Sun.COM /* ------------------------------------------------------------------------ */
2912393Syz155240 /* Function:    ipfr_newfrag                                                */
2922393Syz155240 /* Returns:     ipfr_t * - pointer to fragment cache state info or NULL     */
2932393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
2942393Syz155240 /*              table(I) - pointer to frag table to add to                  */
2952393Syz155240 /*                                                                          */
2962393Syz155240 /* Add a new entry to the fragment cache, registering it as having come     */
2972393Syz155240 /* through this box, with the result of the filter operation.               */
2982393Syz155240 /* ------------------------------------------------------------------------ */
ipfr_newfrag(fin,pass,table)2992393Syz155240 static ipfr_t *ipfr_newfrag(fin, pass, table)
3002393Syz155240 fr_info_t *fin;
3012393Syz155240 u_32_t pass;
3022393Syz155240 ipfr_t *table[];
3032393Syz155240 {
3042393Syz155240 	ipfr_t *fra, frag;
3052393Syz155240 	u_int idx, off;
3063448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
3072393Syz155240 
3086254San207044 	if (ifs->ifs_ipfr_inuse >= ifs->ifs_ipfr_size)
3092393Syz155240 		return NULL;
3102393Syz155240 
3112393Syz155240 	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
3122393Syz155240 		return NULL;
3132393Syz155240 
3142393Syz155240 	if (pass & FR_FRSTRICT)
3152393Syz155240 		if (fin->fin_off != 0)
3162393Syz155240 			return NULL;
3172393Syz155240 
318*9876SDarren.Reed@Sun.COM 	idx = ipfr_index(fin, &frag);
3192393Syz155240 
3202393Syz155240 	/*
3212393Syz155240 	 * first, make sure it isn't already there...
3222393Syz155240 	 */
3232393Syz155240 	for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext)
3242393Syz155240 		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp,
3252393Syz155240 			  IPFR_CMPSZ)) {
3263448Sdh155122 			ifs->ifs_ipfr_stats.ifs_exists++;
3272393Syz155240 			return NULL;
3282393Syz155240 		}
3292393Syz155240 
3302393Syz155240 	/*
3312393Syz155240 	 * allocate some memory, if possible, if not, just record that we
3322393Syz155240 	 * failed to do so.
3332393Syz155240 	 */
3342393Syz155240 	KMALLOC(fra, ipfr_t *);
3352393Syz155240 	if (fra == NULL) {
3363448Sdh155122 		ifs->ifs_ipfr_stats.ifs_nomem++;
3372393Syz155240 		return NULL;
3382393Syz155240 	}
3392393Syz155240 
3402393Syz155240 	fra->ipfr_rule = fin->fin_fr;
3412393Syz155240 	if (fra->ipfr_rule != NULL) {
3422393Syz155240 
3432393Syz155240 		frentry_t *fr;
3442393Syz155240 
3452393Syz155240 		fr = fin->fin_fr;
3462393Syz155240 		MUTEX_ENTER(&fr->fr_lock);
3472393Syz155240 		fr->fr_ref++;
3482393Syz155240 		MUTEX_EXIT(&fr->fr_lock);
3492393Syz155240 	}
3502393Syz155240 
3512393Syz155240 	/*
3522393Syz155240 	 * Insert the fragment into the fragment table, copy the struct used
3532393Syz155240 	 * in the search using bcopy rather than reassign each field.
3542393Syz155240 	 * Set the ttl to the default.
3552393Syz155240 	 */
3562393Syz155240 	if ((fra->ipfr_hnext = table[idx]) != NULL)
3572393Syz155240 		table[idx]->ipfr_hprev = &fra->ipfr_hnext;
3582393Syz155240 	fra->ipfr_hprev = table + idx;
3592393Syz155240 	fra->ipfr_data = NULL;
3602393Syz155240 	table[idx] = fra;
3612393Syz155240 	bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ);
3623448Sdh155122 	fra->ipfr_ttl = ifs->ifs_fr_ticks + ifs->ifs_fr_ipfrttl;
3632393Syz155240 
3642393Syz155240 	/*
3652393Syz155240 	 * Compute the offset of the expected start of the next packet.
3662393Syz155240 	 */
367*9876SDarren.Reed@Sun.COM 	off = fin->fin_off >> 3;
3682393Syz155240 	if (off == 0) {
3692393Syz155240 		fra->ipfr_seen0 = 1;
3702393Syz155240 	} else {
3712393Syz155240 		fra->ipfr_seen0 = 0;
3722393Syz155240 	}
3732393Syz155240 	fra->ipfr_off = off + fin->fin_dlen;
3742393Syz155240 	fra->ipfr_pass = pass;
3753448Sdh155122 	fra->ipfr_ref = 1;
3763448Sdh155122 	ifs->ifs_ipfr_stats.ifs_new++;
3773448Sdh155122 	ifs->ifs_ipfr_inuse++;
3782393Syz155240 	return fra;
3792393Syz155240 }
3802393Syz155240 
3812393Syz155240 
3822393Syz155240 /* ------------------------------------------------------------------------ */
3832393Syz155240 /* Function:    fr_newfrag                                                  */
3842393Syz155240 /* Returns:     int - 0 == success, -1 == error                             */
3852393Syz155240 /* Parameters:  fin(I)  - pointer to packet information                     */
3862393Syz155240 /*                                                                          */
3872393Syz155240 /* Add a new entry to the fragment cache table based on the current packet  */
3882393Syz155240 /* ------------------------------------------------------------------------ */
fr_newfrag(fin,pass)3892393Syz155240 int fr_newfrag(fin, pass)
3902393Syz155240 u_32_t pass;
3912393Syz155240 fr_info_t *fin;
3922393Syz155240 {
3932393Syz155240 	ipfr_t	*fra;
3943448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
3952393Syz155240 
3963448Sdh155122 	if (ifs->ifs_fr_frag_lock != 0)
3972393Syz155240 		return -1;
3982393Syz155240 
3993448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_frag);
4003448Sdh155122 	fra = ipfr_newfrag(fin, pass, ifs->ifs_ipfr_heads);
4012393Syz155240 	if (fra != NULL) {
4023448Sdh155122 		*ifs->ifs_ipfr_tail = fra;
4033448Sdh155122 		fra->ipfr_prev = ifs->ifs_ipfr_tail;
4043448Sdh155122 		ifs->ifs_ipfr_tail = &fra->ipfr_next;
4053448Sdh155122 		if (ifs->ifs_ipfr_list == NULL)
4063448Sdh155122 			ifs->ifs_ipfr_list = fra;
4072393Syz155240 		fra->ipfr_next = NULL;
4082393Syz155240 	}
4093448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_frag);
4102393Syz155240 	return fra ? 0 : -1;
4112393Syz155240 }
4122393Syz155240 
4132393Syz155240 
4142393Syz155240 /* ------------------------------------------------------------------------ */
4152393Syz155240 /* Function:    fr_nat_newfrag                                              */
4162393Syz155240 /* Returns:     int - 0 == success, -1 == error                             */
4172393Syz155240 /* Parameters:  fin(I)  - pointer to packet information                     */
4182393Syz155240 /*              nat(I)  - pointer to NAT structure                          */
4192393Syz155240 /*                                                                          */
4202393Syz155240 /* Create a new NAT fragment cache entry based on the current packet and    */
4212393Syz155240 /* the NAT structure for this "session".                                    */
4222393Syz155240 /* ------------------------------------------------------------------------ */
fr_nat_newfrag(fin,pass,nat)4232393Syz155240 int fr_nat_newfrag(fin, pass, nat)
4242393Syz155240 fr_info_t *fin;
4252393Syz155240 u_32_t pass;
4262393Syz155240 nat_t *nat;
4272393Syz155240 {
4282393Syz155240 	ipfr_t	*fra;
4293448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
4302393Syz155240 
431*9876SDarren.Reed@Sun.COM 	if (ifs->ifs_fr_frag_lock != 0)
4322393Syz155240 		return 0;
4332393Syz155240 
4343448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_natfrag);
4353448Sdh155122 	fra = ipfr_newfrag(fin, pass, ifs->ifs_ipfr_nattab);
4362393Syz155240 	if (fra != NULL) {
4372393Syz155240 		fra->ipfr_data = nat;
4382393Syz155240 		nat->nat_data = fra;
4393448Sdh155122 		*ifs->ifs_ipfr_nattail = fra;
4403448Sdh155122 		fra->ipfr_prev = ifs->ifs_ipfr_nattail;
4413448Sdh155122 		ifs->ifs_ipfr_nattail = &fra->ipfr_next;
4422393Syz155240 		fra->ipfr_next = NULL;
4432393Syz155240 	}
4443448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
4452393Syz155240 	return fra ? 0 : -1;
4462393Syz155240 }
4472393Syz155240 
4482393Syz155240 
4492393Syz155240 /* ------------------------------------------------------------------------ */
4502393Syz155240 /* Function:    fr_ipid_newfrag                                             */
4512393Syz155240 /* Returns:     int - 0 == success, -1 == error                             */
4522393Syz155240 /* Parameters:  fin(I)  - pointer to packet information                     */
4532393Syz155240 /*              ipid(I) - new IP ID for this fragmented packet              */
4542393Syz155240 /*                                                                          */
4552393Syz155240 /* Create a new fragment cache entry for this packet and store, as a data   */
4562393Syz155240 /* pointer, the new IP ID value.                                            */
4572393Syz155240 /* ------------------------------------------------------------------------ */
fr_ipid_newfrag(fin,ipid)4582393Syz155240 int fr_ipid_newfrag(fin, ipid)
4592393Syz155240 fr_info_t *fin;
4602393Syz155240 u_32_t ipid;
4612393Syz155240 {
4622393Syz155240 	ipfr_t	*fra;
4633448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
4642393Syz155240 
4653448Sdh155122 	if (ifs->ifs_fr_frag_lock)
4662393Syz155240 		return 0;
4672393Syz155240 
4683448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_ipidfrag);
4693448Sdh155122 	fra = ipfr_newfrag(fin, 0, ifs->ifs_ipfr_ipidtab);
4702393Syz155240 	if (fra != NULL) {
4712393Syz155240 		fra->ipfr_data = (void *)(uintptr_t)ipid;
4723448Sdh155122 		*ifs->ifs_ipfr_ipidtail = fra;
4733448Sdh155122 		fra->ipfr_prev = ifs->ifs_ipfr_ipidtail;
4743448Sdh155122 		ifs->ifs_ipfr_ipidtail = &fra->ipfr_next;
4752393Syz155240 		fra->ipfr_next = NULL;
4762393Syz155240 	}
4773448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_ipidfrag);
4782393Syz155240 	return fra ? 0 : -1;
4792393Syz155240 }
4802393Syz155240 
4812393Syz155240 
4822393Syz155240 /* ------------------------------------------------------------------------ */
4832393Syz155240 /* Function:    fr_fraglookup                                               */
4842393Syz155240 /* Returns:     ipfr_t * - pointer to ipfr_t structure if there's a         */
4852393Syz155240 /*                         matching entry in the frag table, else NULL      */
4862393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
4872393Syz155240 /*              table(I) - pointer to fragment cache table to search        */
4882393Syz155240 /*                                                                          */
4892393Syz155240 /* Check the fragment cache to see if there is already a record of this     */
4902393Syz155240 /* packet with its filter result known.                                     */
4912393Syz155240 /* ------------------------------------------------------------------------ */
fr_fraglookup(fin,table)4922393Syz155240 static ipfr_t *fr_fraglookup(fin, table)
4932393Syz155240 fr_info_t *fin;
4942393Syz155240 ipfr_t *table[];
4952393Syz155240 {
4962393Syz155240 	ipfr_t *f, frag;
4972393Syz155240 	u_int idx;
4983448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
4992393Syz155240 
5002393Syz155240 	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
5012393Syz155240 		return NULL;
5022393Syz155240 
5032393Syz155240 	/*
5042393Syz155240 	 * For fragments, we record protocol, packet id, TOS and both IP#'s
5052393Syz155240 	 * (these should all be the same for all fragments of a packet).
5062393Syz155240 	 *
5072393Syz155240 	 * build up a hash value to index the table with.
5082393Syz155240 	 */
509*9876SDarren.Reed@Sun.COM 	idx = ipfr_index(fin, &frag);
5102393Syz155240 
5112393Syz155240 	/*
5122393Syz155240 	 * check the table, careful to only compare the right amount of data
5132393Syz155240 	 */
5142393Syz155240 	for (f = table[idx]; f; f = f->ipfr_hnext)
5152393Syz155240 		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp,
5162393Syz155240 			  IPFR_CMPSZ)) {
5172393Syz155240 			u_short	off;
5182393Syz155240 
5192393Syz155240 			/*
5202393Syz155240 			 * We don't want to let short packets match because
5212393Syz155240 			 * they could be compromising the security of other
5222393Syz155240 			 * rules that want to match on layer 4 fields (and
5232393Syz155240 			 * can't because they have been fragmented off.)
5242393Syz155240 			 * Why do this check here?  The counter acts as an
5252393Syz155240 			 * indicator of this kind of attack, whereas if it was
5262393Syz155240 			 * elsewhere, it wouldn't know if other matching
5272393Syz155240 			 * packets had been seen.
5282393Syz155240 			 */
5292393Syz155240 			if (fin->fin_flx & FI_SHORT) {
5303448Sdh155122 				ATOMIC_INCL(ifs->ifs_ipfr_stats.ifs_short);
5312393Syz155240 				continue;
5322393Syz155240 			}
5332393Syz155240 
5342393Syz155240 			/*
5352393Syz155240 			 * XXX - We really need to be guarding against the
5362393Syz155240 			 * retransmission of (src,dst,id,offset-range) here
5372393Syz155240 			 * because a fragmented packet is never resent with
5382393Syz155240 			 * the same IP ID# (or shouldn't).
5392393Syz155240 			 */
540*9876SDarren.Reed@Sun.COM 			off = fin->fin_off >> 3;
5412393Syz155240 			if (f->ipfr_seen0) {
5422393Syz155240 				if (off == 0) {
5433448Sdh155122 					ATOMIC_INCL(ifs->ifs_ipfr_stats.ifs_retrans0);
5442393Syz155240 					continue;
5452393Syz155240 				}
5462393Syz155240 			} else if (off == 0) {
5472393Syz155240 				f->ipfr_seen0 = 1;
5482393Syz155240 			}
5492393Syz155240 
5502393Syz155240 			if (f != table[idx]) {
5512393Syz155240 				ipfr_t **fp;
5522393Syz155240 
5532393Syz155240 				/*
5542393Syz155240 				 * Move fragment info. to the top of the list
5552393Syz155240 				 * to speed up searches.  First, delink...
5562393Syz155240 				 */
5572393Syz155240 				fp = f->ipfr_hprev;
5582393Syz155240 				(*fp) = f->ipfr_hnext;
5592393Syz155240 				if (f->ipfr_hnext != NULL)
5602393Syz155240 					f->ipfr_hnext->ipfr_hprev = fp;
5612393Syz155240 				/*
5622393Syz155240 				 * Then put back at the top of the chain.
5632393Syz155240 				 */
5642393Syz155240 				f->ipfr_hnext = table[idx];
5652393Syz155240 				table[idx]->ipfr_hprev = &f->ipfr_hnext;
5662393Syz155240 				f->ipfr_hprev = table + idx;
5672393Syz155240 				table[idx] = f;
5682393Syz155240 			}
5692393Syz155240 
5702393Syz155240 			/*
5712393Syz155240 			 * If we've follwed the fragments, and this is the
5722393Syz155240 			 * last (in order), shrink expiration time.
5732393Syz155240 			 */
5742393Syz155240 			if (off == f->ipfr_off) {
575*9876SDarren.Reed@Sun.COM 				if (!(fin->fin_flx & FI_MOREFRAG))
5763448Sdh155122 					f->ipfr_ttl = ifs->ifs_fr_ticks + 1;
5772393Syz155240 				f->ipfr_off = fin->fin_dlen + off;
5782393Syz155240 			} else if (f->ipfr_pass & FR_FRSTRICT)
5792393Syz155240 				continue;
5803448Sdh155122 			ATOMIC_INCL(ifs->ifs_ipfr_stats.ifs_hits);
5812393Syz155240 			return f;
5822393Syz155240 		}
5832393Syz155240 	return NULL;
5842393Syz155240 }
5852393Syz155240 
5862393Syz155240 
5872393Syz155240 /* ------------------------------------------------------------------------ */
5882393Syz155240 /* Function:    fr_nat_knownfrag                                            */
5892393Syz155240 /* Returns:     nat_t* - pointer to 'parent' NAT structure if frag table    */
5902393Syz155240 /*                       match found, else NULL                             */
5912393Syz155240 /* Parameters:  fin(I)  - pointer to packet information                     */
5922393Syz155240 /*                                                                          */
5932393Syz155240 /* Functional interface for NAT lookups of the NAT fragment cache           */
5942393Syz155240 /* ------------------------------------------------------------------------ */
fr_nat_knownfrag(fin)5952393Syz155240 nat_t *fr_nat_knownfrag(fin)
5962393Syz155240 fr_info_t *fin;
5972393Syz155240 {
5982393Syz155240 	nat_t	*nat;
5992393Syz155240 	ipfr_t	*ipf;
6003448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
6012393Syz155240 
602*9876SDarren.Reed@Sun.COM 	if (ifs->ifs_fr_frag_lock || !ifs->ifs_ipfr_natlist)
6032393Syz155240 		return NULL;
6043448Sdh155122 	READ_ENTER(&ifs->ifs_ipf_natfrag);
6053448Sdh155122 	ipf = fr_fraglookup(fin, ifs->ifs_ipfr_nattab);
6062393Syz155240 	if (ipf != NULL) {
6072393Syz155240 		nat = ipf->ipfr_data;
6082393Syz155240 		/*
6092393Syz155240 		 * This is the last fragment for this packet.
6102393Syz155240 		 */
6113448Sdh155122 		if ((ipf->ipfr_ttl == ifs->ifs_fr_ticks + 1) && (nat != NULL)) {
6122393Syz155240 			nat->nat_data = NULL;
6132393Syz155240 			ipf->ipfr_data = NULL;
6142393Syz155240 		}
6152393Syz155240 	} else
6162393Syz155240 		nat = NULL;
6173448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
6182393Syz155240 	return nat;
6192393Syz155240 }
6202393Syz155240 
6212393Syz155240 
6222393Syz155240 /* ------------------------------------------------------------------------ */
6232393Syz155240 /* Function:    fr_ipid_knownfrag                                           */
6242393Syz155240 /* Returns:     u_32_t - IPv4 ID for this packet if match found, else       */
6252393Syz155240 /*                       return 0xfffffff to indicate no match.             */
6262393Syz155240 /* Parameters:  fin(I) - pointer to packet information                      */
6272393Syz155240 /*                                                                          */
6282393Syz155240 /* Functional interface for IP ID lookups of the IP ID fragment cache       */
6292393Syz155240 /* ------------------------------------------------------------------------ */
fr_ipid_knownfrag(fin)6302393Syz155240 u_32_t fr_ipid_knownfrag(fin)
6312393Syz155240 fr_info_t *fin;
6322393Syz155240 {
6332393Syz155240 	ipfr_t	*ipf;
6342393Syz155240 	u_32_t	id;
6353448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
6362393Syz155240 
637*9876SDarren.Reed@Sun.COM 	if (ifs->ifs_fr_frag_lock || !ifs->ifs_ipfr_ipidlist)
6382393Syz155240 		return 0xffffffff;
6392393Syz155240 
6403448Sdh155122 	READ_ENTER(&ifs->ifs_ipf_ipidfrag);
6413448Sdh155122 	ipf = fr_fraglookup(fin, ifs->ifs_ipfr_ipidtab);
6422393Syz155240 	if (ipf != NULL)
6432393Syz155240 		id = (u_32_t)(uintptr_t)ipf->ipfr_data;
6442393Syz155240 	else
6452393Syz155240 		id = 0xffffffff;
6463448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_ipidfrag);
6472393Syz155240 	return id;
6482393Syz155240 }
6492393Syz155240 
6502393Syz155240 
6512393Syz155240 /* ------------------------------------------------------------------------ */
6522393Syz155240 /* Function:    fr_knownfrag                                                */
6532393Syz155240 /* Returns:     frentry_t* - pointer to filter rule if a match is found in  */
6542393Syz155240 /*                           the frag cache table, else NULL.               */
6552393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
6562393Syz155240 /*              passp(O) - pointer to where to store rule flags resturned   */
6572393Syz155240 /*                                                                          */
6582393Syz155240 /* Functional interface for normal lookups of the fragment cache.  If a     */
6592393Syz155240 /* match is found, return the rule pointer and flags from the rule, except  */
6602393Syz155240 /* that if FR_LOGFIRST is set, reset FR_LOG.                                */
6612393Syz155240 /* ------------------------------------------------------------------------ */
fr_knownfrag(fin,passp)6622393Syz155240 frentry_t *fr_knownfrag(fin, passp)
6632393Syz155240 fr_info_t *fin;
6642393Syz155240 u_32_t *passp;
6652393Syz155240 {
6662393Syz155240 	frentry_t *fr = NULL;
6672393Syz155240 	ipfr_t	*fra;
6682393Syz155240 	u_32_t pass, oflx;
6693448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
6702393Syz155240 
671*9876SDarren.Reed@Sun.COM 	if (ifs->ifs_fr_frag_lock || (ifs->ifs_ipfr_list == NULL))
6722393Syz155240 		return NULL;
6732393Syz155240 
6743448Sdh155122 	READ_ENTER(&ifs->ifs_ipf_frag);
6752393Syz155240 	oflx = fin->fin_flx;
6763448Sdh155122 	fra = fr_fraglookup(fin, ifs->ifs_ipfr_heads);
6772393Syz155240 	if (fra != NULL) {
6782393Syz155240 		fr = fra->ipfr_rule;
6792393Syz155240 		fin->fin_fr = fr;
6802393Syz155240 		if (fr != NULL) {
6812393Syz155240 			pass = fr->fr_flags;
6822393Syz155240 			if ((pass & FR_LOGFIRST) != 0)
6832393Syz155240 				pass &= ~(FR_LOGFIRST|FR_LOG);
6842393Syz155240 			*passp = pass;
6852393Syz155240 		}
6862393Syz155240 	}
6872393Syz155240 	if (!(oflx & FI_BAD) && (fin->fin_flx & FI_BAD)) {
6882393Syz155240 		*passp &= ~FR_CMDMASK;
6892393Syz155240 		*passp |= FR_BLOCK;
6903448Sdh155122 		fr = &ifs->ifs_frblock;
6912393Syz155240 	}
6923448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_frag);
6932393Syz155240 	return fr;
6942393Syz155240 }
6952393Syz155240 
6962393Syz155240 
6972393Syz155240 /* ------------------------------------------------------------------------ */
6982393Syz155240 /* Function:    fr_forget                                                   */
6992393Syz155240 /* Returns:     Nil                                                         */
7002393Syz155240 /* Parameters:  ptr(I) - pointer to data structure                          */
7012393Syz155240 /*                                                                          */
7022393Syz155240 /* Search through all of the fragment cache entries and wherever a pointer  */
7032393Syz155240 /* is found to match ptr, reset it to NULL.                                 */
7042393Syz155240 /* ------------------------------------------------------------------------ */
fr_forget(ptr,ifs)7053448Sdh155122 void fr_forget(ptr, ifs)
7062393Syz155240 void *ptr;
7073448Sdh155122 ipf_stack_t *ifs;
7082393Syz155240 {
7092393Syz155240 	ipfr_t	*fr;
7102393Syz155240 
7113448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_frag);
7123448Sdh155122 	for (fr = ifs->ifs_ipfr_list; fr; fr = fr->ipfr_next)
7132393Syz155240 		if (fr->ipfr_data == ptr)
7142393Syz155240 			fr->ipfr_data = NULL;
7153448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_frag);
7162393Syz155240 }
7172393Syz155240 
7182393Syz155240 
7192393Syz155240 /* ------------------------------------------------------------------------ */
7202393Syz155240 /* Function:    fr_forgetnat                                                */
7212393Syz155240 /* Returns:     Nil                                                         */
7222393Syz155240 /* Parameters:  ptr(I) - pointer to data structure                          */
7232393Syz155240 /*                                                                          */
7242393Syz155240 /* Search through all of the fragment cache entries for NAT and wherever a  */
7252393Syz155240 /* pointer  is found to match ptr, reset it to NULL.                        */
7262393Syz155240 /* ------------------------------------------------------------------------ */
fr_forgetnat(ptr,ifs)7273448Sdh155122 void fr_forgetnat(ptr, ifs)
7282393Syz155240 void *ptr;
7293448Sdh155122 ipf_stack_t *ifs;
7302393Syz155240 {
7312393Syz155240 	ipfr_t	*fr;
7322393Syz155240 
7333448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_natfrag);
7343448Sdh155122 	for (fr = ifs->ifs_ipfr_natlist; fr; fr = fr->ipfr_next)
7352393Syz155240 		if (fr->ipfr_data == ptr)
7362393Syz155240 			fr->ipfr_data = NULL;
7373448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
7382393Syz155240 }
7392393Syz155240 
7402393Syz155240 
7412393Syz155240 /* ------------------------------------------------------------------------ */
7422393Syz155240 /* Function:    fr_fragdelete                                               */
7432393Syz155240 /* Returns:     Nil                                                         */
7442393Syz155240 /* Parameters:  fra(I)   - pointer to fragment structure to delete          */
7452393Syz155240 /*              tail(IO) - pointer to the pointer to the tail of the frag   */
7462393Syz155240 /*                         list                                             */
7472393Syz155240 /*                                                                          */
7482393Syz155240 /* Remove a fragment cache table entry from the table & list.  Also free    */
7492393Syz155240 /* the filter rule it is associated with it if it is no longer used as a    */
7502393Syz155240 /* result of decreasing the reference count.                                */
7512393Syz155240 /* ------------------------------------------------------------------------ */
fr_fragdelete(fra,tail,ifs)7523448Sdh155122 static void fr_fragdelete(fra, tail, ifs)
7532393Syz155240 ipfr_t *fra, ***tail;
7543448Sdh155122 ipf_stack_t *ifs;
7552393Syz155240 {
7562393Syz155240 	frentry_t *fr;
7572393Syz155240 
7582393Syz155240 	fr = fra->ipfr_rule;
7592393Syz155240 	if (fr != NULL)
7603448Sdh155122 	    (void)fr_derefrule(&fr, ifs);
7612393Syz155240 
7622393Syz155240 	if (fra->ipfr_next)
7632393Syz155240 		fra->ipfr_next->ipfr_prev = fra->ipfr_prev;
7642393Syz155240 	*fra->ipfr_prev = fra->ipfr_next;
7652393Syz155240 	if (*tail == &fra->ipfr_next)
7662393Syz155240 		*tail = fra->ipfr_prev;
7672393Syz155240 
7682393Syz155240 	if (fra->ipfr_hnext)
7692393Syz155240 		fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev;
7702393Syz155240 	*fra->ipfr_hprev = fra->ipfr_hnext;
7713448Sdh155122 
7723448Sdh155122 	if (fra->ipfr_ref <= 0)
7733448Sdh155122 		KFREE(fra);
7742393Syz155240 }
7752393Syz155240 
7762393Syz155240 
7772393Syz155240 /* ------------------------------------------------------------------------ */
7782393Syz155240 /* Function:    fr_fragclear                                                */
7792393Syz155240 /* Returns:     Nil                                                         */
7802393Syz155240 /* Parameters:  Nil                                                         */
7812393Syz155240 /*                                                                          */
7822393Syz155240 /* Free memory in use by fragment state information kept.  Do the normal    */
7832393Syz155240 /* fragment state stuff first and then the NAT-fragment table.              */
7842393Syz155240 /* ------------------------------------------------------------------------ */
fr_fragclear(ifs)7853448Sdh155122 void fr_fragclear(ifs)
7863448Sdh155122 ipf_stack_t *ifs;
7872393Syz155240 {
7882393Syz155240 	ipfr_t	*fra;
7892393Syz155240 	nat_t	*nat;
7902393Syz155240 
7913448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_frag);
7923448Sdh155122 	while ((fra = ifs->ifs_ipfr_list) != NULL) {
7933448Sdh155122 		fra->ipfr_ref--;
7943448Sdh155122 		fr_fragdelete(fra, &ifs->ifs_ipfr_tail, ifs);
7953448Sdh155122 	}
7963448Sdh155122 	ifs->ifs_ipfr_tail = &ifs->ifs_ipfr_list;
7973448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_frag);
7982393Syz155240 
7993448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
8003448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_natfrag);
8013448Sdh155122 	while ((fra = ifs->ifs_ipfr_natlist) != NULL) {
8022393Syz155240 		nat = fra->ipfr_data;
8032393Syz155240 		if (nat != NULL) {
8042393Syz155240 			if (nat->nat_data == fra)
8052393Syz155240 				nat->nat_data = NULL;
8062393Syz155240 		}
8073448Sdh155122 		fra->ipfr_ref--;
8083448Sdh155122 		fr_fragdelete(fra, &ifs->ifs_ipfr_nattail, ifs);
8092393Syz155240 	}
8103448Sdh155122 	ifs->ifs_ipfr_nattail = &ifs->ifs_ipfr_natlist;
8113448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
8123448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
8132393Syz155240 }
8142393Syz155240 
8152393Syz155240 
8162393Syz155240 /* ------------------------------------------------------------------------ */
8172393Syz155240 /* Function:    fr_fragexpire                                               */
8182393Syz155240 /* Returns:     Nil                                                         */
8192393Syz155240 /* Parameters:  Nil                                                         */
8202393Syz155240 /*                                                                          */
8212393Syz155240 /* Expire entries in the fragment cache table that have been there too long */
8222393Syz155240 /* ------------------------------------------------------------------------ */
fr_fragexpire(ifs)8233448Sdh155122 void fr_fragexpire(ifs)
8243448Sdh155122 ipf_stack_t *ifs;
8252393Syz155240 {
8262393Syz155240 	ipfr_t	**fp, *fra;
8272393Syz155240 	nat_t	*nat;
8282393Syz155240 	SPL_INT(s);
8292393Syz155240 
8303448Sdh155122 	if (ifs->ifs_fr_frag_lock)
8312393Syz155240 		return;
8322393Syz155240 
8332393Syz155240 	SPL_NET(s);
8343448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_frag);
8352393Syz155240 	/*
8362393Syz155240 	 * Go through the entire table, looking for entries to expire,
8373448Sdh155122 	 * which is indicated by the ttl being less than or equal to
8383448Sdh155122 	 * ifs_fr_ticks.
8392393Syz155240 	 */
8403448Sdh155122 	for (fp = &ifs->ifs_ipfr_list; ((fra = *fp) != NULL); ) {
8413448Sdh155122 		if (fra->ipfr_ttl > ifs->ifs_fr_ticks)
8422393Syz155240 			break;
8433448Sdh155122 		fra->ipfr_ref--;
8443448Sdh155122 		fr_fragdelete(fra, &ifs->ifs_ipfr_tail, ifs);
8453448Sdh155122 		ifs->ifs_ipfr_stats.ifs_expire++;
8463448Sdh155122 		ifs->ifs_ipfr_inuse--;
8472393Syz155240 	}
8483448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_frag);
8492393Syz155240 
8503448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_ipidfrag);
8513448Sdh155122 	for (fp = &ifs->ifs_ipfr_ipidlist; ((fra = *fp) != NULL); ) {
8523448Sdh155122 		if (fra->ipfr_ttl > ifs->ifs_fr_ticks)
8532393Syz155240 			break;
8543448Sdh155122 		fra->ipfr_ref--;
8553448Sdh155122 		fr_fragdelete(fra, &ifs->ifs_ipfr_ipidtail, ifs);
8563448Sdh155122 		ifs->ifs_ipfr_stats.ifs_expire++;
8573448Sdh155122 		ifs->ifs_ipfr_inuse--;
8582393Syz155240 	}
8593448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_ipidfrag);
8602393Syz155240 
8612393Syz155240 	/*
8622393Syz155240 	 * Same again for the NAT table, except that if the structure also
8632393Syz155240 	 * still points to a NAT structure, and the NAT structure points back
8642393Syz155240 	 * at the one to be free'd, NULL the reference from the NAT struct.
8652393Syz155240 	 * NOTE: We need to grab both mutex's early, and in this order so as
8662393Syz155240 	 * to prevent a deadlock if both try to expire at the same time.
8672393Syz155240 	 */
8683448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
8693448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_natfrag);
8703448Sdh155122 	for (fp = &ifs->ifs_ipfr_natlist; ((fra = *fp) != NULL); ) {
8713448Sdh155122 		if (fra->ipfr_ttl > ifs->ifs_fr_ticks)
8722393Syz155240 			break;
8732393Syz155240 		nat = fra->ipfr_data;
8742393Syz155240 		if (nat != NULL) {
8752393Syz155240 			if (nat->nat_data == fra)
8762393Syz155240 				nat->nat_data = NULL;
8772393Syz155240 		}
8783448Sdh155122 		fra->ipfr_ref--;
8793448Sdh155122 		fr_fragdelete(fra, &ifs->ifs_ipfr_nattail, ifs);
8803448Sdh155122 		ifs->ifs_ipfr_stats.ifs_expire++;
8813448Sdh155122 		ifs->ifs_ipfr_inuse--;
8822393Syz155240 	}
8833448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
8843448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
8852393Syz155240 	SPL_X(s);
8862393Syz155240 }
8872393Syz155240 
8882393Syz155240 
8892393Syz155240 /* ------------------------------------------------------------------------ */
8902393Syz155240 /* Function:    fr_slowtimer                                                */
8912393Syz155240 /* Returns:     Nil                                                         */
8922393Syz155240 /* Parameters:  Nil                                                         */
8932393Syz155240 /*                                                                          */
8942393Syz155240 /* Slowly expire held state for fragments.  Timeouts are set * in           */
8952393Syz155240 /* expectation of this being called twice per second.                       */
8962393Syz155240 /* ------------------------------------------------------------------------ */
8972393Syz155240 #if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \
8982393Syz155240 			  !defined(__osf__) && !defined(linux))
8992393Syz155240 # if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi))
fr_slowtimer(void * arg)9003448Sdh155122 void fr_slowtimer __P((void *arg))
9012393Syz155240 # else
9023448Sdh155122 int fr_slowtimer(void *arg)
9032393Syz155240 # endif
9042393Syz155240 {
9053448Sdh155122 	ipf_stack_t *ifs = arg;
9063448Sdh155122 
9073448Sdh155122 	READ_ENTER(&ifs->ifs_ipf_global);
9082393Syz155240 
9093448Sdh155122 	fr_fragexpire(ifs);
9103448Sdh155122 	fr_timeoutstate(ifs);
9113448Sdh155122 	fr_natexpire(ifs);
9123448Sdh155122 	fr_authexpire(ifs);
9133448Sdh155122 	ifs->ifs_fr_ticks++;
9143448Sdh155122 	if (ifs->ifs_fr_running <= 0)
9152393Syz155240 		goto done;
9162393Syz155240 # ifdef _KERNEL
9172393Syz155240 #  if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000)
9182393Syz155240 	callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL);
9192393Syz155240 #  else
9202393Syz155240 #   if defined(__OpenBSD__)
9212393Syz155240 	timeout_add(&fr_slowtimer_ch, hz/2);
9222393Syz155240 #   else
9232393Syz155240 #    if (__FreeBSD_version >= 300000)
9242393Syz155240 	fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2);
9252393Syz155240 #    else
9262393Syz155240 #     ifdef linux
9272393Syz155240 	;
9282393Syz155240 #     else
9292393Syz155240 	timeout(fr_slowtimer, NULL, hz/2);
9302393Syz155240 #     endif
9312393Syz155240 #    endif /* FreeBSD */
9322393Syz155240 #   endif /* OpenBSD */
9332393Syz155240 #  endif /* NetBSD */
9342393Syz155240 # endif
9352393Syz155240 done:
9363448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_global);
9372393Syz155240 # if (BSD < 199103) || !defined(_KERNEL)
9382393Syz155240 	return 0;
9392393Syz155240 # endif
9402393Syz155240 }
9412393Syz155240 #endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */
9423448Sdh155122 
9433448Sdh155122 /*ARGSUSED*/
fr_nextfrag(token,itp,top,tail,lock,ifs)9443448Sdh155122 int fr_nextfrag(token, itp, top, tail, lock, ifs)
9453448Sdh155122 ipftoken_t *token;
9463448Sdh155122 ipfgeniter_t *itp;
9473448Sdh155122 ipfr_t **top, ***tail;
9483448Sdh155122 ipfrwlock_t *lock;
9493448Sdh155122 ipf_stack_t *ifs;
9503448Sdh155122 {
9513448Sdh155122 	ipfr_t *frag, *next, zero;
9523448Sdh155122 	int error = 0;
9533448Sdh155122 
9546518Sjojemann 	READ_ENTER(lock);
9556518Sjojemann 
9566518Sjojemann 	/*
9576518Sjojemann 	 * Retrieve "previous" entry from token and find the next entry.
9586518Sjojemann 	 */
9593448Sdh155122 	frag = token->ipt_data;
9603448Sdh155122 	if (frag == NULL)
9613448Sdh155122 		next = *top;
9623448Sdh155122 	else
9633448Sdh155122 		next = frag->ipfr_next;
9643448Sdh155122 
9656518Sjojemann 	/*
9666518Sjojemann 	 * If we found an entry, add reference to it and update token.
9676518Sjojemann 	 * Otherwise, zero out data to be returned and NULL out token.
9686518Sjojemann 	 */
9693448Sdh155122 	if (next != NULL) {
9703448Sdh155122 		ATOMIC_INC(next->ipfr_ref);
9713448Sdh155122 		token->ipt_data = next;
9723448Sdh155122 	} else {
9733448Sdh155122 		bzero(&zero, sizeof(zero));
9743448Sdh155122 		next = &zero;
9756518Sjojemann 		token->ipt_data = NULL;
9763448Sdh155122 	}
9776518Sjojemann 
9786518Sjojemann 	/*
9796518Sjojemann 	 * Now that we have ref, it's save to give up lock.
9806518Sjojemann 	 */
9813448Sdh155122 	RWLOCK_EXIT(lock);
9823448Sdh155122 
9836518Sjojemann 	/*
9846518Sjojemann 	 * Copy out data and clean up references and token as needed.
9856518Sjojemann 	 */
9863448Sdh155122 	error = COPYOUT(next, itp->igi_data, sizeof(*next));
9873448Sdh155122 	if (error != 0)
9883448Sdh155122 		error = EFAULT;
9896518Sjojemann 	if (token->ipt_data == NULL) {
9906518Sjojemann 		ipf_freetoken(token, ifs);
9916518Sjojemann 	} else {
9926518Sjojemann 		if (frag != NULL)
9936518Sjojemann 			fr_fragderef(&frag, lock, ifs);
9946518Sjojemann 		if (next->ipfr_next == NULL)
9956518Sjojemann 			ipf_freetoken(token, ifs);
9966518Sjojemann 	}
9973448Sdh155122 	return error;
9983448Sdh155122 }
9993448Sdh155122 
10003448Sdh155122 
fr_fragderef(frp,lock,ifs)10013448Sdh155122 void fr_fragderef(frp, lock, ifs)
10023448Sdh155122 ipfr_t **frp;
10033448Sdh155122 ipfrwlock_t *lock;
10043448Sdh155122 ipf_stack_t *ifs;
10053448Sdh155122 {
10063448Sdh155122 	ipfr_t *fra;
10073448Sdh155122 
10083448Sdh155122 	fra = *frp;
10093448Sdh155122 	*frp = NULL;
10103448Sdh155122 
10113448Sdh155122 	WRITE_ENTER(lock);
10123448Sdh155122 	fra->ipfr_ref--;
10133448Sdh155122 	if (fra->ipfr_ref <= 0) {
10143448Sdh155122 		KFREE(fra);
10153448Sdh155122 		ifs->ifs_ipfr_stats.ifs_expire++;
10163448Sdh155122 		ifs->ifs_ipfr_inuse--;
10173448Sdh155122 	}
10183448Sdh155122 	RWLOCK_EXIT(lock);
10193448Sdh155122 }
1020