xref: /illumos-gate/usr/src/uts/common/inet/ipf/ip_frag.c (revision ab25eeb5)
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.
5*ab25eeb5Syz  *
6*ab25eeb5Syz  * Copyright 2006 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
43*ab25eeb5Syz #if !defined(linux)
44*ab25eeb5Syz # include <sys/protosw.h>
45*ab25eeb5Syz #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__)
54*ab25eeb5Syz # 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>
73*ab25eeb5Syz #if !defined(linux)
74*ab25eeb5Syz # include <netinet/ip_var.h>
75*ab25eeb5Syz #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"
80*ab25eeb5Syz #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"
867c478bd9Sstevel@tonic-gate #include "netinet/ip_proxy.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
105*ab25eeb5Syz /* 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";
109*ab25eeb5Syz 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 
1137c478bd9Sstevel@tonic-gate static ipfr_t   *ipfr_list = NULL;
1147c478bd9Sstevel@tonic-gate static ipfr_t   **ipfr_tail = &ipfr_list;
1157c478bd9Sstevel@tonic-gate static ipfr_t	**ipfr_heads;
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate static ipfr_t   *ipfr_natlist = NULL;
1187c478bd9Sstevel@tonic-gate static ipfr_t   **ipfr_nattail = &ipfr_natlist;
1197c478bd9Sstevel@tonic-gate static ipfr_t	**ipfr_nattab;
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate static ipfr_t   *ipfr_ipidlist = NULL;
1227c478bd9Sstevel@tonic-gate static ipfr_t   **ipfr_ipidtail = &ipfr_ipidlist;
1237c478bd9Sstevel@tonic-gate static ipfr_t	**ipfr_ipidtab;
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate static ipfrstat_t ipfr_stats;
1267c478bd9Sstevel@tonic-gate static int	ipfr_inuse = 0;
1277c478bd9Sstevel@tonic-gate int		ipfr_size = IPFT_SIZE;
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate int	fr_ipfrttl = 120;	/* 60 seconds */
1307c478bd9Sstevel@tonic-gate int	fr_frag_lock = 0;
1317c478bd9Sstevel@tonic-gate int	fr_frag_init = 0;
1327c478bd9Sstevel@tonic-gate u_long	fr_ticks = 0;
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate static ipfr_t *ipfr_newfrag __P((fr_info_t *, u_32_t, ipfr_t **));
1367c478bd9Sstevel@tonic-gate static ipfr_t *fr_fraglookup __P((fr_info_t *, ipfr_t **));
1377c478bd9Sstevel@tonic-gate static void fr_fragdelete __P((ipfr_t *, ipfr_t ***));
1387c478bd9Sstevel@tonic-gate 
1397663b816Sml static frentry_t frblock;
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
1427c478bd9Sstevel@tonic-gate /* Function:    fr_fraginit                                                 */
1437c478bd9Sstevel@tonic-gate /* Returns:     int - 0 == success, -1 == error                             */
1447c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
1457c478bd9Sstevel@tonic-gate /*                                                                          */
1467c478bd9Sstevel@tonic-gate /* Initialise the hash tables for the fragment cache lookups.               */
1477c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
1487c478bd9Sstevel@tonic-gate int fr_fraginit()
1497c478bd9Sstevel@tonic-gate {
1507c478bd9Sstevel@tonic-gate 	KMALLOCS(ipfr_heads, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
1517c478bd9Sstevel@tonic-gate 	if (ipfr_heads == NULL)
1527c478bd9Sstevel@tonic-gate 		return -1;
1537c478bd9Sstevel@tonic-gate 	bzero((char *)ipfr_heads, ipfr_size * sizeof(ipfr_t *));
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	KMALLOCS(ipfr_nattab, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
1567c478bd9Sstevel@tonic-gate 	if (ipfr_nattab == NULL)
1577c478bd9Sstevel@tonic-gate 		return -1;
1587c478bd9Sstevel@tonic-gate 	bzero((char *)ipfr_nattab, ipfr_size * sizeof(ipfr_t *));
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate 	KMALLOCS(ipfr_ipidtab, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
1617c478bd9Sstevel@tonic-gate 	if (ipfr_ipidtab == NULL)
1627c478bd9Sstevel@tonic-gate 		return -1;
1637c478bd9Sstevel@tonic-gate 	bzero((char *)ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *));
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	RWLOCK_INIT(&ipf_frag, "ipf fragment rwlock");
1667663b816Sml 
1677663b816Sml 	/* Initialise frblock with "block in all" */
1687663b816Sml 	bzero((char *)&frblock, sizeof(frblock));
1697663b816Sml 	frblock.fr_flags = FR_BLOCK|FR_INQUE;	/* block in */
1707663b816Sml 	frblock.fr_ref = 1;
1717663b816Sml 
1727c478bd9Sstevel@tonic-gate 	fr_frag_init = 1;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	return 0;
1757c478bd9Sstevel@tonic-gate }
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
1797c478bd9Sstevel@tonic-gate /* Function:    fr_fragunload                                               */
1807c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
1817c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
1827c478bd9Sstevel@tonic-gate /*                                                                          */
1837c478bd9Sstevel@tonic-gate /* Free all memory allocated whilst running and from initialisation.        */
1847c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
1857c478bd9Sstevel@tonic-gate void fr_fragunload()
1867c478bd9Sstevel@tonic-gate {
1877c478bd9Sstevel@tonic-gate 	if (fr_frag_init == 1) {
1887c478bd9Sstevel@tonic-gate 		fr_fragclear();
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 		RW_DESTROY(&ipf_frag);
1917c478bd9Sstevel@tonic-gate 		fr_frag_init = 0;
1927c478bd9Sstevel@tonic-gate 	}
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 	if (ipfr_heads != NULL)
1957c478bd9Sstevel@tonic-gate 		KFREES(ipfr_heads, ipfr_size * sizeof(ipfr_t *));
1967c478bd9Sstevel@tonic-gate 	ipfr_heads = NULL;
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	if (ipfr_nattab != NULL)
1997c478bd9Sstevel@tonic-gate 		KFREES(ipfr_nattab, ipfr_size * sizeof(ipfr_t *));
2007c478bd9Sstevel@tonic-gate 	ipfr_nattab = NULL;
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	if (ipfr_ipidtab != NULL)
2037c478bd9Sstevel@tonic-gate 		KFREES(ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *));
2047c478bd9Sstevel@tonic-gate 	ipfr_ipidtab = NULL;
2057c478bd9Sstevel@tonic-gate }
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
2097c478bd9Sstevel@tonic-gate /* Function:    fr_fragstats                                                */
210*ab25eeb5Syz /* Returns:     ipfrstat_t* - pointer to struct with current frag stats     */
2117c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
2127c478bd9Sstevel@tonic-gate /*                                                                          */
2137c478bd9Sstevel@tonic-gate /* Updates ipfr_stats with current information and returns a pointer to it  */
2147c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
2157c478bd9Sstevel@tonic-gate ipfrstat_t *fr_fragstats()
2167c478bd9Sstevel@tonic-gate {
2177c478bd9Sstevel@tonic-gate 	ipfr_stats.ifs_table = ipfr_heads;
2187c478bd9Sstevel@tonic-gate 	ipfr_stats.ifs_nattab = ipfr_nattab;
2197c478bd9Sstevel@tonic-gate 	ipfr_stats.ifs_inuse = ipfr_inuse;
2207c478bd9Sstevel@tonic-gate 	return &ipfr_stats;
2217c478bd9Sstevel@tonic-gate }
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
2257c478bd9Sstevel@tonic-gate /* Function:    ipfr_newfrag                                                */
2267c478bd9Sstevel@tonic-gate /* Returns:     ipfr_t * - pointer to fragment cache state info or NULL     */
2277c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)   - pointer to packet information                    */
2287c478bd9Sstevel@tonic-gate /*              table(I) - pointer to frag table to add to                  */
2297c478bd9Sstevel@tonic-gate /*                                                                          */
2307c478bd9Sstevel@tonic-gate /* Add a new entry to the fragment cache, registering it as having come     */
2317c478bd9Sstevel@tonic-gate /* through this box, with the result of the filter operation.               */
2327c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
2337c478bd9Sstevel@tonic-gate static ipfr_t *ipfr_newfrag(fin, pass, table)
2347c478bd9Sstevel@tonic-gate fr_info_t *fin;
2357c478bd9Sstevel@tonic-gate u_32_t pass;
2367c478bd9Sstevel@tonic-gate ipfr_t *table[];
2377c478bd9Sstevel@tonic-gate {
2387c478bd9Sstevel@tonic-gate 	ipfr_t *fra, frag;
2397c478bd9Sstevel@tonic-gate 	u_int idx, off;
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	if (ipfr_inuse >= IPFT_SIZE)
2427c478bd9Sstevel@tonic-gate 		return NULL;
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
2457c478bd9Sstevel@tonic-gate 		return NULL;
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	if (pass & FR_FRSTRICT)
2487663b816Sml 		if (fin->fin_off != 0)
2497c478bd9Sstevel@tonic-gate 			return NULL;
2507c478bd9Sstevel@tonic-gate 
2517663b816Sml 	frag.ipfr_p = fin->fin_p;
2527663b816Sml 	idx = fin->fin_p;
2537663b816Sml 	frag.ipfr_id = fin->fin_id;
2547663b816Sml 	idx += fin->fin_id;
2557663b816Sml 	frag.ipfr_source = fin->fin_fi.fi_src;
2567663b816Sml 	idx += frag.ipfr_src.s_addr;
2577663b816Sml 	frag.ipfr_dest = fin->fin_fi.fi_dst;
2587663b816Sml 	idx += frag.ipfr_dst.s_addr;
2597c478bd9Sstevel@tonic-gate 	frag.ipfr_ifp = fin->fin_ifp;
2607c478bd9Sstevel@tonic-gate 	idx *= 127;
2617c478bd9Sstevel@tonic-gate 	idx %= IPFT_SIZE;
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
2647c478bd9Sstevel@tonic-gate 	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
2657c478bd9Sstevel@tonic-gate 	frag.ipfr_auth = fin->fin_fi.fi_auth;
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	/*
2687c478bd9Sstevel@tonic-gate 	 * first, make sure it isn't already there...
2697c478bd9Sstevel@tonic-gate 	 */
2707c478bd9Sstevel@tonic-gate 	for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext)
2717c478bd9Sstevel@tonic-gate 		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp,
2727c478bd9Sstevel@tonic-gate 			  IPFR_CMPSZ)) {
2737c478bd9Sstevel@tonic-gate 			ipfr_stats.ifs_exists++;
2747c478bd9Sstevel@tonic-gate 			return NULL;
2757c478bd9Sstevel@tonic-gate 		}
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate 	/*
2787c478bd9Sstevel@tonic-gate 	 * allocate some memory, if possible, if not, just record that we
2797c478bd9Sstevel@tonic-gate 	 * failed to do so.
2807c478bd9Sstevel@tonic-gate 	 */
2817c478bd9Sstevel@tonic-gate 	KMALLOC(fra, ipfr_t *);
2827c478bd9Sstevel@tonic-gate 	if (fra == NULL) {
2837c478bd9Sstevel@tonic-gate 		ipfr_stats.ifs_nomem++;
2847c478bd9Sstevel@tonic-gate 		return NULL;
2857c478bd9Sstevel@tonic-gate 	}
2867c478bd9Sstevel@tonic-gate 
287*ab25eeb5Syz 	fra->ipfr_rule = fin->fin_fr;
288*ab25eeb5Syz 	if (fra->ipfr_rule != NULL) {
289*ab25eeb5Syz 
290*ab25eeb5Syz 		frentry_t *fr;
291*ab25eeb5Syz 
292*ab25eeb5Syz 		fr = fin->fin_fr;
293*ab25eeb5Syz 		MUTEX_ENTER(&fr->fr_lock);
294*ab25eeb5Syz 		fr->fr_ref++;
295*ab25eeb5Syz 		MUTEX_EXIT(&fr->fr_lock);
2967663b816Sml 	}
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	/*
2997c478bd9Sstevel@tonic-gate 	 * Insert the fragment into the fragment table, copy the struct used
3007c478bd9Sstevel@tonic-gate 	 * in the search using bcopy rather than reassign each field.
3017c478bd9Sstevel@tonic-gate 	 * Set the ttl to the default.
3027c478bd9Sstevel@tonic-gate 	 */
3037c478bd9Sstevel@tonic-gate 	if ((fra->ipfr_hnext = table[idx]) != NULL)
3047c478bd9Sstevel@tonic-gate 		table[idx]->ipfr_hprev = &fra->ipfr_hnext;
3057c478bd9Sstevel@tonic-gate 	fra->ipfr_hprev = table + idx;
3067c478bd9Sstevel@tonic-gate 	fra->ipfr_data = NULL;
3077c478bd9Sstevel@tonic-gate 	table[idx] = fra;
3087c478bd9Sstevel@tonic-gate 	bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ);
3097c478bd9Sstevel@tonic-gate 	fra->ipfr_ttl = fr_ticks + fr_ipfrttl;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	/*
3127c478bd9Sstevel@tonic-gate 	 * Compute the offset of the expected start of the next packet.
3137c478bd9Sstevel@tonic-gate 	 */
314*ab25eeb5Syz 	off = fin->fin_off;
3157663b816Sml 	if (off == 0) {
3167c478bd9Sstevel@tonic-gate 		fra->ipfr_seen0 = 1;
3177663b816Sml 		fra->ipfr_firstend = fin->fin_flen;
3187663b816Sml 	} else {
3197663b816Sml 		fra->ipfr_seen0 = 0;
3207663b816Sml 		fra->ipfr_firstend = 0;
3217663b816Sml 	}
322*ab25eeb5Syz 	fra->ipfr_off = off + fin->fin_dlen;
3237c478bd9Sstevel@tonic-gate 	fra->ipfr_pass = pass;
3247c478bd9Sstevel@tonic-gate 	ipfr_stats.ifs_new++;
3257c478bd9Sstevel@tonic-gate 	ipfr_inuse++;
3267c478bd9Sstevel@tonic-gate 	return fra;
3277c478bd9Sstevel@tonic-gate }
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
3317c478bd9Sstevel@tonic-gate /* Function:    fr_newfrag                                                  */
3327c478bd9Sstevel@tonic-gate /* Returns:     int - 0 == success, -1 == error                             */
3337c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)  - pointer to packet information                     */
3347c478bd9Sstevel@tonic-gate /*                                                                          */
3357c478bd9Sstevel@tonic-gate /* Add a new entry to the fragment cache table based on the current packet  */
3367c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
3377c478bd9Sstevel@tonic-gate int fr_newfrag(fin, pass)
3387c478bd9Sstevel@tonic-gate u_32_t pass;
3397c478bd9Sstevel@tonic-gate fr_info_t *fin;
3407c478bd9Sstevel@tonic-gate {
3417c478bd9Sstevel@tonic-gate 	ipfr_t	*fra;
3427c478bd9Sstevel@tonic-gate 
3437663b816Sml 	if (fr_frag_lock != 0)
344*ab25eeb5Syz 		return -1;
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_frag);
3477c478bd9Sstevel@tonic-gate 	fra = ipfr_newfrag(fin, pass, ipfr_heads);
3487c478bd9Sstevel@tonic-gate 	if (fra != NULL) {
3497c478bd9Sstevel@tonic-gate 		*ipfr_tail = fra;
3507c478bd9Sstevel@tonic-gate 		fra->ipfr_prev = ipfr_tail;
3517c478bd9Sstevel@tonic-gate 		ipfr_tail = &fra->ipfr_next;
3527c478bd9Sstevel@tonic-gate 		if (ipfr_list == NULL)
3537c478bd9Sstevel@tonic-gate 			ipfr_list = fra;
3547c478bd9Sstevel@tonic-gate 		fra->ipfr_next = NULL;
3557c478bd9Sstevel@tonic-gate 	}
3567c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&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;
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	if ((fin->fin_v != 4) || (fr_frag_lock != 0))
3787c478bd9Sstevel@tonic-gate 		return 0;
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_natfrag);
3817c478bd9Sstevel@tonic-gate 	fra = ipfr_newfrag(fin, pass, ipfr_nattab);
3827c478bd9Sstevel@tonic-gate 	if (fra != NULL) {
3837c478bd9Sstevel@tonic-gate 		fra->ipfr_data = nat;
3847c478bd9Sstevel@tonic-gate 		nat->nat_data = fra;
3857c478bd9Sstevel@tonic-gate 		*ipfr_nattail = fra;
3867c478bd9Sstevel@tonic-gate 		fra->ipfr_prev = ipfr_nattail;
3877c478bd9Sstevel@tonic-gate 		ipfr_nattail = &fra->ipfr_next;
3887c478bd9Sstevel@tonic-gate 		fra->ipfr_next = NULL;
3897c478bd9Sstevel@tonic-gate 	}
3907c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_natfrag);
3917c478bd9Sstevel@tonic-gate 	return fra ? 0 : -1;
3927c478bd9Sstevel@tonic-gate }
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
3967c478bd9Sstevel@tonic-gate /* Function:    fr_ipid_newfrag                                             */
3977c478bd9Sstevel@tonic-gate /* Returns:     int - 0 == success, -1 == error                             */
3987c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)  - pointer to packet information                     */
3997c478bd9Sstevel@tonic-gate /*              ipid(I) - new IP ID for this fragmented packet              */
4007c478bd9Sstevel@tonic-gate /*                                                                          */
4017c478bd9Sstevel@tonic-gate /* Create a new fragment cache entry for this packet and store, as a data   */
4027c478bd9Sstevel@tonic-gate /* pointer, the new IP ID value.                                            */
4037c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
4047c478bd9Sstevel@tonic-gate int fr_ipid_newfrag(fin, ipid)
4057c478bd9Sstevel@tonic-gate fr_info_t *fin;
4067c478bd9Sstevel@tonic-gate u_32_t ipid;
4077c478bd9Sstevel@tonic-gate {
4087c478bd9Sstevel@tonic-gate 	ipfr_t	*fra;
4097c478bd9Sstevel@tonic-gate 
4107663b816Sml 	if (fr_frag_lock)
4117c478bd9Sstevel@tonic-gate 		return 0;
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_ipidfrag);
4147c478bd9Sstevel@tonic-gate 	fra = ipfr_newfrag(fin, 0, ipfr_ipidtab);
4157c478bd9Sstevel@tonic-gate 	if (fra != NULL) {
4167c478bd9Sstevel@tonic-gate 		fra->ipfr_data = (void *)(uintptr_t)ipid;
4177c478bd9Sstevel@tonic-gate 		*ipfr_ipidtail = fra;
4187c478bd9Sstevel@tonic-gate 		fra->ipfr_prev = ipfr_ipidtail;
4197c478bd9Sstevel@tonic-gate 		ipfr_ipidtail = &fra->ipfr_next;
4207c478bd9Sstevel@tonic-gate 		fra->ipfr_next = NULL;
4217c478bd9Sstevel@tonic-gate 	}
4227c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_ipidfrag);
4237c478bd9Sstevel@tonic-gate 	return fra ? 0 : -1;
4247c478bd9Sstevel@tonic-gate }
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
4287c478bd9Sstevel@tonic-gate /* Function:    fr_fraglookup                                               */
4297c478bd9Sstevel@tonic-gate /* Returns:     ipfr_t * - pointer to ipfr_t structure if there's a         */
4307c478bd9Sstevel@tonic-gate /*                         matching entry in the frag table, else NULL      */
4317c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)   - pointer to packet information                    */
4327c478bd9Sstevel@tonic-gate /*              table(I) - pointer to fragment cache table to search        */
4337c478bd9Sstevel@tonic-gate /*                                                                          */
4347c478bd9Sstevel@tonic-gate /* Check the fragment cache to see if there is already a record of this     */
4357c478bd9Sstevel@tonic-gate /* packet with its filter result known.                                     */
4367c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
4377c478bd9Sstevel@tonic-gate static ipfr_t *fr_fraglookup(fin, table)
4387c478bd9Sstevel@tonic-gate fr_info_t *fin;
4397c478bd9Sstevel@tonic-gate ipfr_t *table[];
4407c478bd9Sstevel@tonic-gate {
4417c478bd9Sstevel@tonic-gate 	ipfr_t *f, frag;
4427c478bd9Sstevel@tonic-gate 	u_int idx;
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
4457c478bd9Sstevel@tonic-gate 		return NULL;
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	/*
4487c478bd9Sstevel@tonic-gate 	 * For fragments, we record protocol, packet id, TOS and both IP#'s
4497c478bd9Sstevel@tonic-gate 	 * (these should all be the same for all fragments of a packet).
4507c478bd9Sstevel@tonic-gate 	 *
4517c478bd9Sstevel@tonic-gate 	 * build up a hash value to index the table with.
4527c478bd9Sstevel@tonic-gate 	 */
4537663b816Sml 	frag.ipfr_p = fin->fin_p;
4547663b816Sml 	idx = fin->fin_p;
4557663b816Sml 	frag.ipfr_id = fin->fin_id;
4567663b816Sml 	idx += fin->fin_id;
4577663b816Sml 	frag.ipfr_source = fin->fin_fi.fi_src;
4587663b816Sml 	idx += frag.ipfr_src.s_addr;
4597663b816Sml 	frag.ipfr_dest = fin->fin_fi.fi_dst;
4607663b816Sml 	idx += frag.ipfr_dst.s_addr;
4617c478bd9Sstevel@tonic-gate 	frag.ipfr_ifp = fin->fin_ifp;
4627c478bd9Sstevel@tonic-gate 	idx *= 127;
4637c478bd9Sstevel@tonic-gate 	idx %= IPFT_SIZE;
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
4667c478bd9Sstevel@tonic-gate 	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
4677c478bd9Sstevel@tonic-gate 	frag.ipfr_auth = fin->fin_fi.fi_auth;
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	/*
4707c478bd9Sstevel@tonic-gate 	 * check the table, careful to only compare the right amount of data
4717c478bd9Sstevel@tonic-gate 	 */
4727c478bd9Sstevel@tonic-gate 	for (f = table[idx]; f; f = f->ipfr_hnext)
4737c478bd9Sstevel@tonic-gate 		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp,
4747c478bd9Sstevel@tonic-gate 			  IPFR_CMPSZ)) {
4757c478bd9Sstevel@tonic-gate 			u_short	off;
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 			/*
4787c478bd9Sstevel@tonic-gate 			 * We don't want to let short packets match because
4797c478bd9Sstevel@tonic-gate 			 * they could be compromising the security of other
4807c478bd9Sstevel@tonic-gate 			 * rules that want to match on layer 4 fields (and
4817c478bd9Sstevel@tonic-gate 			 * can't because they have been fragmented off.)
4827c478bd9Sstevel@tonic-gate 			 * Why do this check here?  The counter acts as an
4837c478bd9Sstevel@tonic-gate 			 * indicator of this kind of attack, whereas if it was
4847c478bd9Sstevel@tonic-gate 			 * elsewhere, it wouldn't know if other matching
4857c478bd9Sstevel@tonic-gate 			 * packets had been seen.
4867c478bd9Sstevel@tonic-gate 			 */
4877c478bd9Sstevel@tonic-gate 			if (fin->fin_flx & FI_SHORT) {
4887c478bd9Sstevel@tonic-gate 				ATOMIC_INCL(ipfr_stats.ifs_short);
4897c478bd9Sstevel@tonic-gate 				continue;
4907c478bd9Sstevel@tonic-gate 			}
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 			/*
4937c478bd9Sstevel@tonic-gate 			 * XXX - We really need to be guarding against the
4947c478bd9Sstevel@tonic-gate 			 * retransmission of (src,dst,id,offset-range) here
4957c478bd9Sstevel@tonic-gate 			 * because a fragmented packet is never resent with
4967c478bd9Sstevel@tonic-gate 			 * the same IP ID# (or shouldn't).
4977c478bd9Sstevel@tonic-gate 			 */
498*ab25eeb5Syz 			off = fin->fin_off; /* same as in ipfr_newfrag() */
4997c478bd9Sstevel@tonic-gate 			if (f->ipfr_seen0) {
5007c478bd9Sstevel@tonic-gate 				if (off == 0) {
5017c478bd9Sstevel@tonic-gate 					ATOMIC_INCL(ipfr_stats.ifs_retrans0);
5027c478bd9Sstevel@tonic-gate 					continue;
5037c478bd9Sstevel@tonic-gate 				}
5047663b816Sml 			} else if (off == 0) {
5057c478bd9Sstevel@tonic-gate 				f->ipfr_seen0 = 1;
5067663b816Sml 				f->ipfr_firstend = fin->fin_flen;
5077663b816Sml 			}
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 			if (f != table[idx]) {
5107c478bd9Sstevel@tonic-gate 				ipfr_t **fp;
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 				/*
5137c478bd9Sstevel@tonic-gate 				 * Move fragment info. to the top of the list
5147c478bd9Sstevel@tonic-gate 				 * to speed up searches.  First, delink...
5157c478bd9Sstevel@tonic-gate 				 */
5167c478bd9Sstevel@tonic-gate 				fp = f->ipfr_hprev;
5177c478bd9Sstevel@tonic-gate 				(*fp) = f->ipfr_hnext;
5187c478bd9Sstevel@tonic-gate 				if (f->ipfr_hnext != NULL)
5197c478bd9Sstevel@tonic-gate 					f->ipfr_hnext->ipfr_hprev = fp;
5207c478bd9Sstevel@tonic-gate 				/*
5217c478bd9Sstevel@tonic-gate 				 * Then put back at the top of the chain.
5227c478bd9Sstevel@tonic-gate 				 */
5237c478bd9Sstevel@tonic-gate 				f->ipfr_hnext = table[idx];
5247c478bd9Sstevel@tonic-gate 				table[idx]->ipfr_hprev = &f->ipfr_hnext;
5257c478bd9Sstevel@tonic-gate 				f->ipfr_hprev = table + idx;
5267c478bd9Sstevel@tonic-gate 				table[idx] = f;
5277c478bd9Sstevel@tonic-gate 			}
5287c478bd9Sstevel@tonic-gate 
5297663b816Sml 			if (fin->fin_v == 6) {
530*ab25eeb5Syz 				if (f->ipfr_seen0 && (off < f->ipfr_firstend))
5317663b816Sml 					fin->fin_flx |= FI_BAD;
5327663b816Sml 			}
5337c478bd9Sstevel@tonic-gate 			/*
5347c478bd9Sstevel@tonic-gate 			 * If we've follwed the fragments, and this is the
5357c478bd9Sstevel@tonic-gate 			 * last (in order), shrink expiration time.
5367c478bd9Sstevel@tonic-gate 			 */
5377c478bd9Sstevel@tonic-gate 			if (off == f->ipfr_off) {
538*ab25eeb5Syz 				if (!(fin->fin_ip->ip_off & IP_MF))
5397c478bd9Sstevel@tonic-gate 					f->ipfr_ttl = fr_ticks + 1;
540*ab25eeb5Syz 				f->ipfr_off = fin->fin_dlen + off;
5417c478bd9Sstevel@tonic-gate 			} else if (f->ipfr_pass & FR_FRSTRICT)
5427c478bd9Sstevel@tonic-gate 				continue;
5437c478bd9Sstevel@tonic-gate 			ATOMIC_INCL(ipfr_stats.ifs_hits);
5447c478bd9Sstevel@tonic-gate 			return f;
5457c478bd9Sstevel@tonic-gate 		}
5467c478bd9Sstevel@tonic-gate 	return NULL;
5477c478bd9Sstevel@tonic-gate }
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
5517c478bd9Sstevel@tonic-gate /* Function:    fr_nat_knownfrag                                            */
5527c478bd9Sstevel@tonic-gate /* Returns:     nat_t* - pointer to 'parent' NAT structure if frag table    */
5537c478bd9Sstevel@tonic-gate /*                       match found, else NULL                             */
5547c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)  - pointer to packet information                     */
5557c478bd9Sstevel@tonic-gate /*                                                                          */
5567c478bd9Sstevel@tonic-gate /* Functional interface for NAT lookups of the NAT fragment cache           */
5577c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
5587c478bd9Sstevel@tonic-gate nat_t *fr_nat_knownfrag(fin)
5597c478bd9Sstevel@tonic-gate fr_info_t *fin;
5607c478bd9Sstevel@tonic-gate {
5617c478bd9Sstevel@tonic-gate 	nat_t	*nat;
5627c478bd9Sstevel@tonic-gate 	ipfr_t	*ipf;
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 	if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_natlist)
5657c478bd9Sstevel@tonic-gate 		return NULL;
5667c478bd9Sstevel@tonic-gate 	READ_ENTER(&ipf_natfrag);
5677c478bd9Sstevel@tonic-gate 	ipf = fr_fraglookup(fin, ipfr_nattab);
5687c478bd9Sstevel@tonic-gate 	if (ipf != NULL) {
5697c478bd9Sstevel@tonic-gate 		nat = ipf->ipfr_data;
5707c478bd9Sstevel@tonic-gate 		/*
5717c478bd9Sstevel@tonic-gate 		 * This is the last fragment for this packet.
5727c478bd9Sstevel@tonic-gate 		 */
5737c478bd9Sstevel@tonic-gate 		if ((ipf->ipfr_ttl == fr_ticks + 1) && (nat != NULL)) {
5747c478bd9Sstevel@tonic-gate 			nat->nat_data = NULL;
5757c478bd9Sstevel@tonic-gate 			ipf->ipfr_data = NULL;
5767c478bd9Sstevel@tonic-gate 		}
5777c478bd9Sstevel@tonic-gate 	} else
5787c478bd9Sstevel@tonic-gate 		nat = NULL;
5797c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_natfrag);
5807c478bd9Sstevel@tonic-gate 	return nat;
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
5857c478bd9Sstevel@tonic-gate /* Function:    fr_ipid_knownfrag                                           */
5867c478bd9Sstevel@tonic-gate /* Returns:     u_32_t - IPv4 ID for this packet if match found, else       */
5877c478bd9Sstevel@tonic-gate /*                       return 0xfffffff to indicate no match.             */
5887c478bd9Sstevel@tonic-gate /* Parameters:  fin(I) - pointer to packet information                      */
5897c478bd9Sstevel@tonic-gate /*                                                                          */
5907c478bd9Sstevel@tonic-gate /* Functional interface for IP ID lookups of the IP ID fragment cache       */
5917c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
5927c478bd9Sstevel@tonic-gate u_32_t fr_ipid_knownfrag(fin)
5937c478bd9Sstevel@tonic-gate fr_info_t *fin;
5947c478bd9Sstevel@tonic-gate {
5957c478bd9Sstevel@tonic-gate 	ipfr_t	*ipf;
5967c478bd9Sstevel@tonic-gate 	u_32_t	id;
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_ipidlist)
5997c478bd9Sstevel@tonic-gate 		return 0xffffffff;
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	READ_ENTER(&ipf_ipidfrag);
6027c478bd9Sstevel@tonic-gate 	ipf = fr_fraglookup(fin, ipfr_ipidtab);
6037c478bd9Sstevel@tonic-gate 	if (ipf != NULL)
6047c478bd9Sstevel@tonic-gate 		id = (u_32_t)(uintptr_t)ipf->ipfr_data;
6057c478bd9Sstevel@tonic-gate 	else
6067c478bd9Sstevel@tonic-gate 		id = 0xffffffff;
6077c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_ipidfrag);
6087c478bd9Sstevel@tonic-gate 	return id;
6097c478bd9Sstevel@tonic-gate }
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
6137c478bd9Sstevel@tonic-gate /* Function:    fr_knownfrag                                                */
6147c478bd9Sstevel@tonic-gate /* Returns:     frentry_t* - pointer to filter rule if a match is found in  */
6157c478bd9Sstevel@tonic-gate /*                           the frag cache table, else NULL.               */
6167c478bd9Sstevel@tonic-gate /* Parameters:  fin(I)   - pointer to packet information                    */
6177c478bd9Sstevel@tonic-gate /*              passp(O) - pointer to where to store rule flags resturned   */
6187c478bd9Sstevel@tonic-gate /*                                                                          */
6197c478bd9Sstevel@tonic-gate /* Functional interface for normal lookups of the fragment cache.  If a     */
6207c478bd9Sstevel@tonic-gate /* match is found, return the rule pointer and flags from the rule, except  */
6217c478bd9Sstevel@tonic-gate /* that if FR_LOGFIRST is set, reset FR_LOG.                                */
6227c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
6237c478bd9Sstevel@tonic-gate frentry_t *fr_knownfrag(fin, passp)
6247c478bd9Sstevel@tonic-gate fr_info_t *fin;
6257c478bd9Sstevel@tonic-gate u_32_t *passp;
6267c478bd9Sstevel@tonic-gate {
6277c478bd9Sstevel@tonic-gate 	frentry_t *fr = NULL;
6287c478bd9Sstevel@tonic-gate 	ipfr_t	*fra;
6297663b816Sml 	u_32_t pass, oflx;
6307c478bd9Sstevel@tonic-gate 
6317663b816Sml 	if ((fr_frag_lock) || (ipfr_list == NULL))
6327c478bd9Sstevel@tonic-gate 		return NULL;
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 	READ_ENTER(&ipf_frag);
6357663b816Sml 	oflx = fin->fin_flx;
6367c478bd9Sstevel@tonic-gate 	fra = fr_fraglookup(fin, ipfr_heads);
6377c478bd9Sstevel@tonic-gate 	if (fra != NULL) {
6387c478bd9Sstevel@tonic-gate 		fr = fra->ipfr_rule;
6397c478bd9Sstevel@tonic-gate 		fin->fin_fr = fr;
6407c478bd9Sstevel@tonic-gate 		if (fr != NULL) {
6417c478bd9Sstevel@tonic-gate 			pass = fr->fr_flags;
6427c478bd9Sstevel@tonic-gate 			if ((pass & FR_LOGFIRST) != 0)
6437c478bd9Sstevel@tonic-gate 				pass &= ~(FR_LOGFIRST|FR_LOG);
6447c478bd9Sstevel@tonic-gate 			*passp = pass;
6457c478bd9Sstevel@tonic-gate 		}
6467c478bd9Sstevel@tonic-gate 	}
6477663b816Sml 	if (!(oflx & FI_BAD) && (fin->fin_flx & FI_BAD)) {
6487663b816Sml 		*passp &= ~FR_CMDMASK;
6497663b816Sml 		*passp |= FR_BLOCK;
6507663b816Sml 		fr = &frblock;
6517663b816Sml 	}
6527c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_frag);
6537c478bd9Sstevel@tonic-gate 	return fr;
6547c478bd9Sstevel@tonic-gate }
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
6587c478bd9Sstevel@tonic-gate /* Function:    fr_forget                                                   */
6597c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
6607c478bd9Sstevel@tonic-gate /* Parameters:  ptr(I) - pointer to data structure                          */
6617c478bd9Sstevel@tonic-gate /*                                                                          */
6627c478bd9Sstevel@tonic-gate /* Search through all of the fragment cache entries and wherever a pointer  */
6637c478bd9Sstevel@tonic-gate /* is found to match ptr, reset it to NULL.                                 */
6647c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
6657c478bd9Sstevel@tonic-gate void fr_forget(ptr)
6667c478bd9Sstevel@tonic-gate void *ptr;
6677c478bd9Sstevel@tonic-gate {
6687c478bd9Sstevel@tonic-gate 	ipfr_t	*fr;
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_frag);
6717c478bd9Sstevel@tonic-gate 	for (fr = ipfr_list; fr; fr = fr->ipfr_next)
6727c478bd9Sstevel@tonic-gate 		if (fr->ipfr_data == ptr)
6737c478bd9Sstevel@tonic-gate 			fr->ipfr_data = NULL;
6747c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_frag);
6757c478bd9Sstevel@tonic-gate }
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
6797c478bd9Sstevel@tonic-gate /* Function:    fr_forgetnat                                                */
6807c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
6817c478bd9Sstevel@tonic-gate /* Parameters:  ptr(I) - pointer to data structure                          */
6827c478bd9Sstevel@tonic-gate /*                                                                          */
6837c478bd9Sstevel@tonic-gate /* Search through all of the fragment cache entries for NAT and wherever a  */
6847c478bd9Sstevel@tonic-gate /* pointer  is found to match ptr, reset it to NULL.                        */
6857c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
6867c478bd9Sstevel@tonic-gate void fr_forgetnat(ptr)
6877c478bd9Sstevel@tonic-gate void *ptr;
6887c478bd9Sstevel@tonic-gate {
6897c478bd9Sstevel@tonic-gate 	ipfr_t	*fr;
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_natfrag);
6927c478bd9Sstevel@tonic-gate 	for (fr = ipfr_natlist; fr; fr = fr->ipfr_next)
6937c478bd9Sstevel@tonic-gate 		if (fr->ipfr_data == ptr)
6947c478bd9Sstevel@tonic-gate 			fr->ipfr_data = NULL;
6957c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_natfrag);
6967c478bd9Sstevel@tonic-gate }
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
7007c478bd9Sstevel@tonic-gate /* Function:    fr_fragdelete                                               */
7017c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
7027c478bd9Sstevel@tonic-gate /* Parameters:  fra(I)   - pointer to fragment structure to delete          */
7037c478bd9Sstevel@tonic-gate /*              tail(IO) - pointer to the pointer to the tail of the frag   */
7047c478bd9Sstevel@tonic-gate /*                         list                                             */
7057c478bd9Sstevel@tonic-gate /*                                                                          */
7067c478bd9Sstevel@tonic-gate /* Remove a fragment cache table entry from the table & list.  Also free    */
7077c478bd9Sstevel@tonic-gate /* the filter rule it is associated with it if it is no longer used as a    */
7087c478bd9Sstevel@tonic-gate /* result of decreasing the reference count.                                */
7097c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
7107c478bd9Sstevel@tonic-gate static void fr_fragdelete(fra, tail)
7117c478bd9Sstevel@tonic-gate ipfr_t *fra, ***tail;
7127c478bd9Sstevel@tonic-gate {
7137c478bd9Sstevel@tonic-gate 	frentry_t *fr;
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 	fr = fra->ipfr_rule;
7167c478bd9Sstevel@tonic-gate 	if (fr != NULL)
7177c478bd9Sstevel@tonic-gate 		(void)fr_derefrule(&fr);
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 	if (fra->ipfr_next)
7207c478bd9Sstevel@tonic-gate 		fra->ipfr_next->ipfr_prev = fra->ipfr_prev;
7217c478bd9Sstevel@tonic-gate 	*fra->ipfr_prev = fra->ipfr_next;
7227c478bd9Sstevel@tonic-gate 	if (*tail == &fra->ipfr_next)
7237c478bd9Sstevel@tonic-gate 		*tail = fra->ipfr_prev;
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 	if (fra->ipfr_hnext)
7267c478bd9Sstevel@tonic-gate 		fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev;
7277c478bd9Sstevel@tonic-gate 	*fra->ipfr_hprev = fra->ipfr_hnext;
7287c478bd9Sstevel@tonic-gate 	KFREE(fra);
7297c478bd9Sstevel@tonic-gate }
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
7337c478bd9Sstevel@tonic-gate /* Function:    fr_fragclear                                                */
7347c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
7357c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
7367c478bd9Sstevel@tonic-gate /*                                                                          */
7377c478bd9Sstevel@tonic-gate /* Free memory in use by fragment state information kept.  Do the normal    */
7387c478bd9Sstevel@tonic-gate /* fragment state stuff first and then the NAT-fragment table.              */
7397c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
7407c478bd9Sstevel@tonic-gate void fr_fragclear()
7417c478bd9Sstevel@tonic-gate {
7427c478bd9Sstevel@tonic-gate 	ipfr_t	*fra;
7437c478bd9Sstevel@tonic-gate 	nat_t	*nat;
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_frag);
7467c478bd9Sstevel@tonic-gate 	while ((fra = ipfr_list) != NULL)
7477c478bd9Sstevel@tonic-gate 		fr_fragdelete(fra, &ipfr_tail);
7487c478bd9Sstevel@tonic-gate 	ipfr_tail = &ipfr_list;
7497c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_frag);
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_nat);
7527c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_natfrag);
7537c478bd9Sstevel@tonic-gate 	while ((fra = ipfr_natlist) != NULL) {
7547c478bd9Sstevel@tonic-gate 		nat = fra->ipfr_data;
7557c478bd9Sstevel@tonic-gate 		if (nat != NULL) {
7567c478bd9Sstevel@tonic-gate 			if (nat->nat_data == fra)
7577c478bd9Sstevel@tonic-gate 				nat->nat_data = NULL;
7587c478bd9Sstevel@tonic-gate 		}
7597c478bd9Sstevel@tonic-gate 		fr_fragdelete(fra, &ipfr_nattail);
7607c478bd9Sstevel@tonic-gate 	}
7617c478bd9Sstevel@tonic-gate 	ipfr_nattail = &ipfr_natlist;
7627c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_natfrag);
7637c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_nat);
7647c478bd9Sstevel@tonic-gate }
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
7687c478bd9Sstevel@tonic-gate /* Function:    fr_fragexpire                                               */
7697c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
7707c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
7717c478bd9Sstevel@tonic-gate /*                                                                          */
7727c478bd9Sstevel@tonic-gate /* Expire entries in the fragment cache table that have been there too long */
7737c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
7747c478bd9Sstevel@tonic-gate void fr_fragexpire()
7757c478bd9Sstevel@tonic-gate {
7767c478bd9Sstevel@tonic-gate 	ipfr_t	**fp, *fra;
7777c478bd9Sstevel@tonic-gate 	nat_t	*nat;
778*ab25eeb5Syz 	SPL_INT(s);
7797c478bd9Sstevel@tonic-gate 
7807c478bd9Sstevel@tonic-gate 	if (fr_frag_lock)
7817c478bd9Sstevel@tonic-gate 		return;
7827c478bd9Sstevel@tonic-gate 
7837c478bd9Sstevel@tonic-gate 	SPL_NET(s);
7847c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_frag);
7857c478bd9Sstevel@tonic-gate 	/*
7867c478bd9Sstevel@tonic-gate 	 * Go through the entire table, looking for entries to expire,
7877c478bd9Sstevel@tonic-gate 	 * which is indicated by the ttl being less than or equal to fr_ticks.
7887c478bd9Sstevel@tonic-gate 	 */
7897c478bd9Sstevel@tonic-gate 	for (fp = &ipfr_list; ((fra = *fp) != NULL); ) {
7907c478bd9Sstevel@tonic-gate 		if (fra->ipfr_ttl > fr_ticks)
7917c478bd9Sstevel@tonic-gate 			break;
7927c478bd9Sstevel@tonic-gate 		fr_fragdelete(fra, &ipfr_tail);
7937c478bd9Sstevel@tonic-gate 		ipfr_stats.ifs_expire++;
7947c478bd9Sstevel@tonic-gate 		ipfr_inuse--;
7957c478bd9Sstevel@tonic-gate 	}
7967c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_frag);
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_ipidfrag);
7997c478bd9Sstevel@tonic-gate 	for (fp = &ipfr_ipidlist; ((fra = *fp) != NULL); ) {
8007c478bd9Sstevel@tonic-gate 		if (fra->ipfr_ttl > fr_ticks)
8017c478bd9Sstevel@tonic-gate 			break;
8027c478bd9Sstevel@tonic-gate 		fr_fragdelete(fra, &ipfr_ipidtail);
8037c478bd9Sstevel@tonic-gate 		ipfr_stats.ifs_expire++;
8047c478bd9Sstevel@tonic-gate 		ipfr_inuse--;
8057c478bd9Sstevel@tonic-gate 	}
8067c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_ipidfrag);
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 	/*
8097c478bd9Sstevel@tonic-gate 	 * Same again for the NAT table, except that if the structure also
8107c478bd9Sstevel@tonic-gate 	 * still points to a NAT structure, and the NAT structure points back
8117c478bd9Sstevel@tonic-gate 	 * at the one to be free'd, NULL the reference from the NAT struct.
8127c478bd9Sstevel@tonic-gate 	 * NOTE: We need to grab both mutex's early, and in this order so as
8137c478bd9Sstevel@tonic-gate 	 * to prevent a deadlock if both try to expire at the same time.
8147c478bd9Sstevel@tonic-gate 	 */
8157c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_nat);
8167c478bd9Sstevel@tonic-gate 	WRITE_ENTER(&ipf_natfrag);
8177c478bd9Sstevel@tonic-gate 	for (fp = &ipfr_natlist; ((fra = *fp) != NULL); ) {
8187c478bd9Sstevel@tonic-gate 		if (fra->ipfr_ttl > fr_ticks)
8197c478bd9Sstevel@tonic-gate 			break;
8207c478bd9Sstevel@tonic-gate 		nat = fra->ipfr_data;
8217c478bd9Sstevel@tonic-gate 		if (nat != NULL) {
8227c478bd9Sstevel@tonic-gate 			if (nat->nat_data == fra)
8237c478bd9Sstevel@tonic-gate 				nat->nat_data = NULL;
8247c478bd9Sstevel@tonic-gate 		}
8257c478bd9Sstevel@tonic-gate 		fr_fragdelete(fra, &ipfr_nattail);
8267c478bd9Sstevel@tonic-gate 		ipfr_stats.ifs_expire++;
8277c478bd9Sstevel@tonic-gate 		ipfr_inuse--;
8287c478bd9Sstevel@tonic-gate 	}
8297c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_natfrag);
8307c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_nat);
8317c478bd9Sstevel@tonic-gate 	SPL_X(s);
8327c478bd9Sstevel@tonic-gate }
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
8367c478bd9Sstevel@tonic-gate /* Function:    fr_slowtimer                                                */
8377c478bd9Sstevel@tonic-gate /* Returns:     Nil                                                         */
8387c478bd9Sstevel@tonic-gate /* Parameters:  Nil                                                         */
8397c478bd9Sstevel@tonic-gate /*                                                                          */
8407c478bd9Sstevel@tonic-gate /* Slowly expire held state for fragments.  Timeouts are set * in           */
8417c478bd9Sstevel@tonic-gate /* expectation of this being called twice per second.                       */
8427c478bd9Sstevel@tonic-gate /* ------------------------------------------------------------------------ */
8437c478bd9Sstevel@tonic-gate #if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \
844*ab25eeb5Syz 			  !defined(__osf__) && !defined(linux))
8457c478bd9Sstevel@tonic-gate # if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi))
8467c478bd9Sstevel@tonic-gate void fr_slowtimer __P((void *ptr))
8477c478bd9Sstevel@tonic-gate # else
8487c478bd9Sstevel@tonic-gate int fr_slowtimer()
8497c478bd9Sstevel@tonic-gate # endif
8507c478bd9Sstevel@tonic-gate {
8517c478bd9Sstevel@tonic-gate 	READ_ENTER(&ipf_global);
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	fr_fragexpire();
8547c478bd9Sstevel@tonic-gate 	fr_timeoutstate();
8557c478bd9Sstevel@tonic-gate 	fr_natexpire();
8567c478bd9Sstevel@tonic-gate 	fr_authexpire();
8577c478bd9Sstevel@tonic-gate 	fr_ticks++;
8587c478bd9Sstevel@tonic-gate 	if (fr_running <= 0)
8597c478bd9Sstevel@tonic-gate 		goto done;
8607c478bd9Sstevel@tonic-gate # ifdef _KERNEL
8617c478bd9Sstevel@tonic-gate #  if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000)
8627c478bd9Sstevel@tonic-gate 	callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL);
8637c478bd9Sstevel@tonic-gate #  else
8647c478bd9Sstevel@tonic-gate #   if defined(__OpenBSD__)
8657c478bd9Sstevel@tonic-gate 	timeout_add(&fr_slowtimer_ch, hz/2);
8667c478bd9Sstevel@tonic-gate #   else
8677c478bd9Sstevel@tonic-gate #    if (__FreeBSD_version >= 300000)
8687c478bd9Sstevel@tonic-gate 	fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2);
8697c478bd9Sstevel@tonic-gate #    else
870*ab25eeb5Syz #     ifdef linux
871*ab25eeb5Syz 	;
872*ab25eeb5Syz #     else
8737c478bd9Sstevel@tonic-gate 	timeout(fr_slowtimer, NULL, hz/2);
874*ab25eeb5Syz #     endif
8757c478bd9Sstevel@tonic-gate #    endif /* FreeBSD */
8767c478bd9Sstevel@tonic-gate #   endif /* OpenBSD */
8777c478bd9Sstevel@tonic-gate #  endif /* NetBSD */
8787c478bd9Sstevel@tonic-gate # endif
8797c478bd9Sstevel@tonic-gate done:
8807c478bd9Sstevel@tonic-gate 	RWLOCK_EXIT(&ipf_global);
8817c478bd9Sstevel@tonic-gate # if (BSD < 199103) || !defined(_KERNEL)
8827c478bd9Sstevel@tonic-gate 	return 0;
8837c478bd9Sstevel@tonic-gate # endif
8847c478bd9Sstevel@tonic-gate }
8857c478bd9Sstevel@tonic-gate #endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */
886