ip_frag.c revision 153876
1/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_frag.c 153876 2005-12-30 11:32:23Z guido $	*/
2
3/*
4 * Copyright (C) 1993-2003 by Darren Reed.
5 *
6 * See the IPFILTER.LICENCE file for details on licencing.
7 */
8#if defined(KERNEL) || defined(_KERNEL)
9# undef KERNEL
10# undef _KERNEL
11# define        KERNEL	1
12# define        _KERNEL	1
13#endif
14#include <sys/errno.h>
15#include <sys/types.h>
16#include <sys/param.h>
17#include <sys/time.h>
18#include <sys/file.h>
19#ifdef __hpux
20# include <sys/timeout.h>
21#endif
22#if !defined(_KERNEL)
23# include <stdio.h>
24# include <string.h>
25# include <stdlib.h>
26# define _KERNEL
27# ifdef __OpenBSD__
28struct file;
29# endif
30# include <sys/uio.h>
31# undef _KERNEL
32#endif
33#if defined(_KERNEL) && (__FreeBSD_version >= 220000)
34# include <sys/filio.h>
35# include <sys/fcntl.h>
36#else
37# include <sys/ioctl.h>
38#endif
39#if !defined(linux)
40# include <sys/protosw.h>
41#endif
42#include <sys/socket.h>
43#if defined(_KERNEL)
44# include <sys/systm.h>
45# if !defined(__SVR4) && !defined(__svr4__)
46#  include <sys/mbuf.h>
47# endif
48#endif
49#if !defined(__SVR4) && !defined(__svr4__)
50# if defined(_KERNEL) && !defined(__sgi) && !defined(AIX)
51#  include <sys/kernel.h>
52# endif
53#else
54# include <sys/byteorder.h>
55# ifdef _KERNEL
56#  include <sys/dditypes.h>
57# endif
58# include <sys/stream.h>
59# include <sys/kmem.h>
60#endif
61#include <net/if.h>
62#ifdef sun
63# include <net/af.h>
64#endif
65#include <net/route.h>
66#include <netinet/in.h>
67#include <netinet/in_systm.h>
68#include <netinet/ip.h>
69#if !defined(linux)
70# include <netinet/ip_var.h>
71#endif
72#include <netinet/tcp.h>
73#include <netinet/udp.h>
74#include <netinet/ip_icmp.h>
75#include "netinet/ip_compat.h"
76#include <netinet/tcpip.h>
77#include "netinet/ip_fil.h"
78#include "netinet/ip_nat.h"
79#include "netinet/ip_frag.h"
80#include "netinet/ip_state.h"
81#include "netinet/ip_auth.h"
82#include "netinet/ip_proxy.h"
83#if (__FreeBSD_version >= 300000)
84# include <sys/malloc.h>
85# if defined(_KERNEL)
86#  ifndef IPFILTER_LKM
87#   include <sys/libkern.h>
88#   include <sys/systm.h>
89#  endif
90extern struct callout_handle fr_slowtimer_ch;
91# endif
92#endif
93#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000)
94# include <sys/callout.h>
95extern struct callout fr_slowtimer_ch;
96#endif
97#if defined(__OpenBSD__)
98# include <sys/timeout.h>
99extern struct timeout fr_slowtimer_ch;
100#endif
101/* END OF INCLUDES */
102
103#if !defined(lint)
104static const char sccsid[] = "@(#)ip_frag.c	1.11 3/24/96 (C) 1993-2000 Darren Reed";
105static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_frag.c 153876 2005-12-30 11:32:23Z guido $";
106/* static const char rcsid[] = "@(#)Id: ip_frag.c,v 2.77 2004/01/27 00:24:54 darrenr Exp"; */
107#endif
108
109
110static ipfr_t   *ipfr_list = NULL;
111static ipfr_t   **ipfr_tail = &ipfr_list;
112static ipfr_t	**ipfr_heads;
113
114static ipfr_t   *ipfr_natlist = NULL;
115static ipfr_t   **ipfr_nattail = &ipfr_natlist;
116static ipfr_t	**ipfr_nattab;
117
118static ipfr_t   *ipfr_ipidlist = NULL;
119static ipfr_t   **ipfr_ipidtail = &ipfr_ipidlist;
120static ipfr_t	**ipfr_ipidtab;
121
122static ipfrstat_t ipfr_stats;
123static int	ipfr_inuse = 0;
124int		ipfr_size = IPFT_SIZE;
125
126int	fr_ipfrttl = 120;	/* 60 seconds */
127int	fr_frag_lock = 0;
128int	fr_frag_init = 0;
129u_long	fr_ticks = 0;
130
131
132static ipfr_t *ipfr_newfrag __P((fr_info_t *, u_32_t, ipfr_t **));
133static ipfr_t *fr_fraglookup __P((fr_info_t *, ipfr_t **));
134static void fr_fragdelete __P((ipfr_t *, ipfr_t ***));
135
136
137/* ------------------------------------------------------------------------ */
138/* Function:    fr_fraginit                                                 */
139/* Returns:     int - 0 == success, -1 == error                             */
140/* Parameters:  Nil                                                         */
141/*                                                                          */
142/* Initialise the hash tables for the fragment cache lookups.               */
143/* ------------------------------------------------------------------------ */
144int fr_fraginit()
145{
146	KMALLOCS(ipfr_heads, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
147	if (ipfr_heads == NULL)
148		return -1;
149	bzero((char *)ipfr_heads, ipfr_size * sizeof(ipfr_t *));
150
151	KMALLOCS(ipfr_nattab, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
152	if (ipfr_nattab == NULL)
153		return -1;
154	bzero((char *)ipfr_nattab, ipfr_size * sizeof(ipfr_t *));
155
156	KMALLOCS(ipfr_ipidtab, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
157	if (ipfr_ipidtab == NULL)
158		return -1;
159	bzero((char *)ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *));
160
161	RWLOCK_INIT(&ipf_frag, "ipf fragment rwlock");
162	fr_frag_init = 1;
163
164	return 0;
165}
166
167
168/* ------------------------------------------------------------------------ */
169/* Function:    fr_fragunload                                               */
170/* Returns:     Nil                                                         */
171/* Parameters:  Nil                                                         */
172/*                                                                          */
173/* Free all memory allocated whilst running and from initialisation.        */
174/* ------------------------------------------------------------------------ */
175void fr_fragunload()
176{
177	if (fr_frag_init == 1) {
178		fr_fragclear();
179
180		RW_DESTROY(&ipf_frag);
181		fr_frag_init = 0;
182	}
183
184	if (ipfr_heads != NULL)
185		KFREES(ipfr_heads, ipfr_size * sizeof(ipfr_t *));
186	ipfr_heads = NULL;
187
188	if (ipfr_nattab != NULL)
189		KFREES(ipfr_nattab, ipfr_size * sizeof(ipfr_t *));
190	ipfr_nattab = NULL;
191
192	if (ipfr_ipidtab != NULL)
193		KFREES(ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *));
194	ipfr_ipidtab = NULL;
195}
196
197
198/* ------------------------------------------------------------------------ */
199/* Function:    fr_fragstats                                                */
200/* Returns:     ipfrstat_t* - pointer to struct with current frag stats     */
201/* Parameters:  Nil                                                         */
202/*                                                                          */
203/* Updates ipfr_stats with current information and returns a pointer to it  */
204/* ------------------------------------------------------------------------ */
205ipfrstat_t *fr_fragstats()
206{
207	ipfr_stats.ifs_table = ipfr_heads;
208	ipfr_stats.ifs_nattab = ipfr_nattab;
209	ipfr_stats.ifs_inuse = ipfr_inuse;
210	return &ipfr_stats;
211}
212
213
214/* ------------------------------------------------------------------------ */
215/* Function:    ipfr_newfrag                                                */
216/* Returns:     ipfr_t * - pointer to fragment cache state info or NULL     */
217/* Parameters:  fin(I)   - pointer to packet information                    */
218/*              table(I) - pointer to frag table to add to                  */
219/*                                                                          */
220/* Add a new entry to the fragment cache, registering it as having come     */
221/* through this box, with the result of the filter operation.               */
222/* ------------------------------------------------------------------------ */
223static ipfr_t *ipfr_newfrag(fin, pass, table)
224fr_info_t *fin;
225u_32_t pass;
226ipfr_t *table[];
227{
228	ipfr_t *fra, frag;
229	u_int idx, off;
230	ip_t *ip;
231
232	if (ipfr_inuse >= IPFT_SIZE)
233		return NULL;
234
235	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
236		return NULL;
237
238	ip = fin->fin_ip;
239
240	if (pass & FR_FRSTRICT)
241		if (fin->fin_off != 0)
242			return NULL;
243
244	frag.ipfr_p = ip->ip_p;
245	idx = ip->ip_p;
246	frag.ipfr_id = ip->ip_id;
247	idx += ip->ip_id;
248	frag.ipfr_tos = ip->ip_tos;
249	frag.ipfr_src.s_addr = ip->ip_src.s_addr;
250	idx += ip->ip_src.s_addr;
251	frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
252	idx += ip->ip_dst.s_addr;
253	frag.ipfr_ifp = fin->fin_ifp;
254	idx *= 127;
255	idx %= IPFT_SIZE;
256
257	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
258	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
259	frag.ipfr_auth = fin->fin_fi.fi_auth;
260
261	/*
262	 * first, make sure it isn't already there...
263	 */
264	for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext)
265		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp,
266			  IPFR_CMPSZ)) {
267			ipfr_stats.ifs_exists++;
268			return NULL;
269		}
270
271	/*
272	 * allocate some memory, if possible, if not, just record that we
273	 * failed to do so.
274	 */
275	KMALLOC(fra, ipfr_t *);
276	if (fra == NULL) {
277		ipfr_stats.ifs_nomem++;
278		return NULL;
279	}
280
281	fra->ipfr_rule = fin->fin_fr;
282	if (fra->ipfr_rule != NULL) {
283
284		frentry_t *fr;
285
286		fr = fin->fin_fr;
287		MUTEX_ENTER(&fr->fr_lock);
288		fr->fr_ref++;
289		MUTEX_EXIT(&fr->fr_lock);
290	}
291
292	/*
293	 * Insert the fragment into the fragment table, copy the struct used
294	 * in the search using bcopy rather than reassign each field.
295	 * Set the ttl to the default.
296	 */
297	if ((fra->ipfr_hnext = table[idx]) != NULL)
298		table[idx]->ipfr_hprev = &fra->ipfr_hnext;
299	fra->ipfr_hprev = table + idx;
300	fra->ipfr_data = NULL;
301	table[idx] = fra;
302	bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ);
303	fra->ipfr_ttl = fr_ticks + fr_ipfrttl;
304
305	/*
306	 * Compute the offset of the expected start of the next packet.
307	 */
308	off = ip->ip_off & IP_OFFMASK;
309	if (off == 0)
310		fra->ipfr_seen0 = 1;
311	fra->ipfr_off = off + (fin->fin_dlen >> 3);
312	fra->ipfr_pass = pass;
313	ipfr_stats.ifs_new++;
314	ipfr_inuse++;
315	return fra;
316}
317
318
319/* ------------------------------------------------------------------------ */
320/* Function:    fr_newfrag                                                  */
321/* Returns:     int - 0 == success, -1 == error                             */
322/* Parameters:  fin(I)  - pointer to packet information                     */
323/*                                                                          */
324/* Add a new entry to the fragment cache table based on the current packet  */
325/* ------------------------------------------------------------------------ */
326int fr_newfrag(fin, pass)
327u_32_t pass;
328fr_info_t *fin;
329{
330	ipfr_t	*fra;
331
332	if ((fin->fin_v != 4) || (fr_frag_lock != 0))
333		return -1;
334
335	WRITE_ENTER(&ipf_frag);
336	fra = ipfr_newfrag(fin, pass, ipfr_heads);
337	if (fra != NULL) {
338		*ipfr_tail = fra;
339		fra->ipfr_prev = ipfr_tail;
340		ipfr_tail = &fra->ipfr_next;
341		if (ipfr_list == NULL)
342			ipfr_list = fra;
343		fra->ipfr_next = NULL;
344	}
345	RWLOCK_EXIT(&ipf_frag);
346	return fra ? 0 : -1;
347}
348
349
350/* ------------------------------------------------------------------------ */
351/* Function:    fr_nat_newfrag                                              */
352/* Returns:     int - 0 == success, -1 == error                             */
353/* Parameters:  fin(I)  - pointer to packet information                     */
354/*              nat(I)  - pointer to NAT structure                          */
355/*                                                                          */
356/* Create a new NAT fragment cache entry based on the current packet and    */
357/* the NAT structure for this "session".                                    */
358/* ------------------------------------------------------------------------ */
359int fr_nat_newfrag(fin, pass, nat)
360fr_info_t *fin;
361u_32_t pass;
362nat_t *nat;
363{
364	ipfr_t	*fra;
365
366	if ((fin->fin_v != 4) || (fr_frag_lock != 0))
367		return 0;
368
369	WRITE_ENTER(&ipf_natfrag);
370	fra = ipfr_newfrag(fin, pass, ipfr_nattab);
371	if (fra != NULL) {
372		fra->ipfr_data = nat;
373		nat->nat_data = fra;
374		*ipfr_nattail = fra;
375		fra->ipfr_prev = ipfr_nattail;
376		ipfr_nattail = &fra->ipfr_next;
377		fra->ipfr_next = NULL;
378	}
379	RWLOCK_EXIT(&ipf_natfrag);
380	return fra ? 0 : -1;
381}
382
383
384/* ------------------------------------------------------------------------ */
385/* Function:    fr_ipid_newfrag                                             */
386/* Returns:     int - 0 == success, -1 == error                             */
387/* Parameters:  fin(I)  - pointer to packet information                     */
388/*              ipid(I) - new IP ID for this fragmented packet              */
389/*                                                                          */
390/* Create a new fragment cache entry for this packet and store, as a data   */
391/* pointer, the new IP ID value.                                            */
392/* ------------------------------------------------------------------------ */
393int fr_ipid_newfrag(fin, ipid)
394fr_info_t *fin;
395u_32_t ipid;
396{
397	ipfr_t	*fra;
398
399	if ((fin->fin_v != 4) || (fr_frag_lock))
400		return 0;
401
402	WRITE_ENTER(&ipf_ipidfrag);
403	fra = ipfr_newfrag(fin, 0, ipfr_ipidtab);
404	if (fra != NULL) {
405		fra->ipfr_data = (void *)(uintptr_t)ipid;
406		*ipfr_ipidtail = fra;
407		fra->ipfr_prev = ipfr_ipidtail;
408		ipfr_ipidtail = &fra->ipfr_next;
409		fra->ipfr_next = NULL;
410	}
411	RWLOCK_EXIT(&ipf_ipidfrag);
412	return fra ? 0 : -1;
413}
414
415
416/* ------------------------------------------------------------------------ */
417/* Function:    fr_fraglookup                                               */
418/* Returns:     ipfr_t * - pointer to ipfr_t structure if there's a         */
419/*                         matching entry in the frag table, else NULL      */
420/* Parameters:  fin(I)   - pointer to packet information                    */
421/*              table(I) - pointer to fragment cache table to search        */
422/*                                                                          */
423/* Check the fragment cache to see if there is already a record of this     */
424/* packet with its filter result known.                                     */
425/* ------------------------------------------------------------------------ */
426static ipfr_t *fr_fraglookup(fin, table)
427fr_info_t *fin;
428ipfr_t *table[];
429{
430	ipfr_t *f, frag;
431	u_int idx;
432	ip_t *ip;
433
434	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
435		return NULL;
436
437	/*
438	 * For fragments, we record protocol, packet id, TOS and both IP#'s
439	 * (these should all be the same for all fragments of a packet).
440	 *
441	 * build up a hash value to index the table with.
442	 */
443	ip = fin->fin_ip;
444	frag.ipfr_p = ip->ip_p;
445	idx = ip->ip_p;
446	frag.ipfr_id = ip->ip_id;
447	idx += ip->ip_id;
448	frag.ipfr_tos = ip->ip_tos;
449	frag.ipfr_src.s_addr = ip->ip_src.s_addr;
450	idx += ip->ip_src.s_addr;
451	frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
452	idx += ip->ip_dst.s_addr;
453	frag.ipfr_ifp = fin->fin_ifp;
454	idx *= 127;
455	idx %= IPFT_SIZE;
456
457	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
458	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
459	frag.ipfr_auth = fin->fin_fi.fi_auth;
460
461	/*
462	 * check the table, careful to only compare the right amount of data
463	 */
464	for (f = table[idx]; f; f = f->ipfr_hnext)
465		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp,
466			  IPFR_CMPSZ)) {
467			u_short	off;
468
469			/*
470			 * We don't want to let short packets match because
471			 * they could be compromising the security of other
472			 * rules that want to match on layer 4 fields (and
473			 * can't because they have been fragmented off.)
474			 * Why do this check here?  The counter acts as an
475			 * indicator of this kind of attack, whereas if it was
476			 * elsewhere, it wouldn't know if other matching
477			 * packets had been seen.
478			 */
479			if (fin->fin_flx & FI_SHORT) {
480				ATOMIC_INCL(ipfr_stats.ifs_short);
481				continue;
482			}
483
484			/*
485			 * XXX - We really need to be guarding against the
486			 * retransmission of (src,dst,id,offset-range) here
487			 * because a fragmented packet is never resent with
488			 * the same IP ID# (or shouldn't).
489			 */
490			off = ip->ip_off & IP_OFFMASK;
491			if (f->ipfr_seen0) {
492				if (off == 0) {
493					ATOMIC_INCL(ipfr_stats.ifs_retrans0);
494					continue;
495				}
496			} else if (off == 0)
497				f->ipfr_seen0 = 1;
498
499			if (f != table[idx]) {
500				ipfr_t **fp;
501
502				/*
503				 * Move fragment info. to the top of the list
504				 * to speed up searches.  First, delink...
505				 */
506				fp = f->ipfr_hprev;
507				(*fp) = f->ipfr_hnext;
508				if (f->ipfr_hnext != NULL)
509					f->ipfr_hnext->ipfr_hprev = fp;
510				/*
511				 * Then put back at the top of the chain.
512				 */
513				f->ipfr_hnext = table[idx];
514				table[idx]->ipfr_hprev = &f->ipfr_hnext;
515				f->ipfr_hprev = table + idx;
516				table[idx] = f;
517			}
518
519			/*
520			 * If we've follwed the fragments, and this is the
521			 * last (in order), shrink expiration time.
522			 */
523			if (off == f->ipfr_off) {
524				if (!(ip->ip_off & IP_MF))
525					f->ipfr_ttl = fr_ticks + 1;
526				f->ipfr_off = (fin->fin_dlen >> 3) + off;
527			} else if (f->ipfr_pass & FR_FRSTRICT)
528				continue;
529			ATOMIC_INCL(ipfr_stats.ifs_hits);
530			return f;
531		}
532	return NULL;
533}
534
535
536/* ------------------------------------------------------------------------ */
537/* Function:    fr_nat_knownfrag                                            */
538/* Returns:     nat_t* - pointer to 'parent' NAT structure if frag table    */
539/*                       match found, else NULL                             */
540/* Parameters:  fin(I)  - pointer to packet information                     */
541/*                                                                          */
542/* Functional interface for NAT lookups of the NAT fragment cache           */
543/* ------------------------------------------------------------------------ */
544nat_t *fr_nat_knownfrag(fin)
545fr_info_t *fin;
546{
547	nat_t	*nat;
548	ipfr_t	*ipf;
549
550	if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_natlist)
551		return NULL;
552	READ_ENTER(&ipf_natfrag);
553	ipf = fr_fraglookup(fin, ipfr_nattab);
554	if (ipf != NULL) {
555		nat = ipf->ipfr_data;
556		/*
557		 * This is the last fragment for this packet.
558		 */
559		if ((ipf->ipfr_ttl == fr_ticks + 1) && (nat != NULL)) {
560			nat->nat_data = NULL;
561			ipf->ipfr_data = NULL;
562		}
563	} else
564		nat = NULL;
565	RWLOCK_EXIT(&ipf_natfrag);
566	return nat;
567}
568
569
570/* ------------------------------------------------------------------------ */
571/* Function:    fr_ipid_knownfrag                                           */
572/* Returns:     u_32_t - IPv4 ID for this packet if match found, else       */
573/*                       return 0xfffffff to indicate no match.             */
574/* Parameters:  fin(I) - pointer to packet information                      */
575/*                                                                          */
576/* Functional interface for IP ID lookups of the IP ID fragment cache       */
577/* ------------------------------------------------------------------------ */
578u_32_t fr_ipid_knownfrag(fin)
579fr_info_t *fin;
580{
581	ipfr_t	*ipf;
582	u_32_t	id;
583
584	if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_ipidlist)
585		return 0xffffffff;
586
587	READ_ENTER(&ipf_ipidfrag);
588	ipf = fr_fraglookup(fin, ipfr_ipidtab);
589	if (ipf != NULL)
590		id = (u_32_t)(uintptr_t)ipf->ipfr_data;
591	else
592		id = 0xffffffff;
593	RWLOCK_EXIT(&ipf_ipidfrag);
594	return id;
595}
596
597
598/* ------------------------------------------------------------------------ */
599/* Function:    fr_knownfrag                                                */
600/* Returns:     frentry_t* - pointer to filter rule if a match is found in  */
601/*                           the frag cache table, else NULL.               */
602/* Parameters:  fin(I)   - pointer to packet information                    */
603/*              passp(O) - pointer to where to store rule flags resturned   */
604/*                                                                          */
605/* Functional interface for normal lookups of the fragment cache.  If a     */
606/* match is found, return the rule pointer and flags from the rule, except  */
607/* that if FR_LOGFIRST is set, reset FR_LOG.                                */
608/* ------------------------------------------------------------------------ */
609frentry_t *fr_knownfrag(fin, passp)
610fr_info_t *fin;
611u_32_t *passp;
612{
613	frentry_t *fr = NULL;
614	ipfr_t	*fra;
615	u_32_t pass;
616
617	if ((fin->fin_v != 4) || (fr_frag_lock) || (ipfr_list == NULL))
618		return NULL;
619
620	READ_ENTER(&ipf_frag);
621	fra = fr_fraglookup(fin, ipfr_heads);
622	if (fra != NULL) {
623		fr = fra->ipfr_rule;
624		fin->fin_fr = fr;
625		if (fr != NULL) {
626			pass = fr->fr_flags;
627			if ((pass & FR_LOGFIRST) != 0)
628				pass &= ~(FR_LOGFIRST|FR_LOG);
629			*passp = pass;
630		}
631	}
632	RWLOCK_EXIT(&ipf_frag);
633	return fr;
634}
635
636
637/* ------------------------------------------------------------------------ */
638/* Function:    fr_forget                                                   */
639/* Returns:     Nil                                                         */
640/* Parameters:  ptr(I) - pointer to data structure                          */
641/*                                                                          */
642/* Search through all of the fragment cache entries and wherever a pointer  */
643/* is found to match ptr, reset it to NULL.                                 */
644/* ------------------------------------------------------------------------ */
645void fr_forget(ptr)
646void *ptr;
647{
648	ipfr_t	*fr;
649
650	WRITE_ENTER(&ipf_frag);
651	for (fr = ipfr_list; fr; fr = fr->ipfr_next)
652		if (fr->ipfr_data == ptr)
653			fr->ipfr_data = NULL;
654	RWLOCK_EXIT(&ipf_frag);
655}
656
657
658/* ------------------------------------------------------------------------ */
659/* Function:    fr_forgetnat                                                */
660/* Returns:     Nil                                                         */
661/* Parameters:  ptr(I) - pointer to data structure                          */
662/*                                                                          */
663/* Search through all of the fragment cache entries for NAT and wherever a  */
664/* pointer  is found to match ptr, reset it to NULL.                        */
665/* ------------------------------------------------------------------------ */
666void fr_forgetnat(ptr)
667void *ptr;
668{
669	ipfr_t	*fr;
670
671	WRITE_ENTER(&ipf_natfrag);
672	for (fr = ipfr_natlist; fr; fr = fr->ipfr_next)
673		if (fr->ipfr_data == ptr)
674			fr->ipfr_data = NULL;
675	RWLOCK_EXIT(&ipf_natfrag);
676}
677
678
679/* ------------------------------------------------------------------------ */
680/* Function:    fr_fragdelete                                               */
681/* Returns:     Nil                                                         */
682/* Parameters:  fra(I)   - pointer to fragment structure to delete          */
683/*              tail(IO) - pointer to the pointer to the tail of the frag   */
684/*                         list                                             */
685/*                                                                          */
686/* Remove a fragment cache table entry from the table & list.  Also free    */
687/* the filter rule it is associated with it if it is no longer used as a    */
688/* result of decreasing the reference count.                                */
689/* ------------------------------------------------------------------------ */
690static void fr_fragdelete(fra, tail)
691ipfr_t *fra, ***tail;
692{
693	frentry_t *fr;
694
695	fr = fra->ipfr_rule;
696	if (fr != NULL)
697		(void)fr_derefrule(&fr);
698
699	if (fra->ipfr_next)
700		fra->ipfr_next->ipfr_prev = fra->ipfr_prev;
701	*fra->ipfr_prev = fra->ipfr_next;
702	if (*tail == &fra->ipfr_next)
703		*tail = fra->ipfr_prev;
704
705	if (fra->ipfr_hnext)
706		fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev;
707	*fra->ipfr_hprev = fra->ipfr_hnext;
708	KFREE(fra);
709}
710
711
712/* ------------------------------------------------------------------------ */
713/* Function:    fr_fragclear                                                */
714/* Returns:     Nil                                                         */
715/* Parameters:  Nil                                                         */
716/*                                                                          */
717/* Free memory in use by fragment state information kept.  Do the normal    */
718/* fragment state stuff first and then the NAT-fragment table.              */
719/* ------------------------------------------------------------------------ */
720void fr_fragclear()
721{
722	ipfr_t	*fra;
723	nat_t	*nat;
724
725	WRITE_ENTER(&ipf_frag);
726	while ((fra = ipfr_list) != NULL)
727		fr_fragdelete(fra, &ipfr_tail);
728	ipfr_tail = &ipfr_list;
729	RWLOCK_EXIT(&ipf_frag);
730
731	WRITE_ENTER(&ipf_nat);
732	WRITE_ENTER(&ipf_natfrag);
733	while ((fra = ipfr_natlist) != NULL) {
734		nat = fra->ipfr_data;
735		if (nat != NULL) {
736			if (nat->nat_data == fra)
737				nat->nat_data = NULL;
738		}
739		fr_fragdelete(fra, &ipfr_nattail);
740	}
741	ipfr_nattail = &ipfr_natlist;
742	RWLOCK_EXIT(&ipf_natfrag);
743	RWLOCK_EXIT(&ipf_nat);
744}
745
746
747/* ------------------------------------------------------------------------ */
748/* Function:    fr_fragexpire                                               */
749/* Returns:     Nil                                                         */
750/* Parameters:  Nil                                                         */
751/*                                                                          */
752/* Expire entries in the fragment cache table that have been there too long */
753/* ------------------------------------------------------------------------ */
754void fr_fragexpire()
755{
756	ipfr_t	**fp, *fra;
757	nat_t	*nat;
758	SPL_INT(s);
759
760	if (fr_frag_lock)
761		return;
762
763	SPL_NET(s);
764	WRITE_ENTER(&ipf_frag);
765	/*
766	 * Go through the entire table, looking for entries to expire,
767	 * which is indicated by the ttl being less than or equal to fr_ticks.
768	 */
769	for (fp = &ipfr_list; ((fra = *fp) != NULL); ) {
770		if (fra->ipfr_ttl > fr_ticks)
771			break;
772		fr_fragdelete(fra, &ipfr_tail);
773		ipfr_stats.ifs_expire++;
774		ipfr_inuse--;
775	}
776	RWLOCK_EXIT(&ipf_frag);
777
778	WRITE_ENTER(&ipf_ipidfrag);
779	for (fp = &ipfr_ipidlist; ((fra = *fp) != NULL); ) {
780		if (fra->ipfr_ttl > fr_ticks)
781			break;
782		fr_fragdelete(fra, &ipfr_ipidtail);
783		ipfr_stats.ifs_expire++;
784		ipfr_inuse--;
785	}
786	RWLOCK_EXIT(&ipf_ipidfrag);
787
788	/*
789	 * Same again for the NAT table, except that if the structure also
790	 * still points to a NAT structure, and the NAT structure points back
791	 * at the one to be free'd, NULL the reference from the NAT struct.
792	 * NOTE: We need to grab both mutex's early, and in this order so as
793	 * to prevent a deadlock if both try to expire at the same time.
794	 */
795	WRITE_ENTER(&ipf_nat);
796	WRITE_ENTER(&ipf_natfrag);
797	for (fp = &ipfr_natlist; ((fra = *fp) != NULL); ) {
798		if (fra->ipfr_ttl > fr_ticks)
799			break;
800		nat = fra->ipfr_data;
801		if (nat != NULL) {
802			if (nat->nat_data == fra)
803				nat->nat_data = NULL;
804		}
805		fr_fragdelete(fra, &ipfr_nattail);
806		ipfr_stats.ifs_expire++;
807		ipfr_inuse--;
808	}
809	RWLOCK_EXIT(&ipf_natfrag);
810	RWLOCK_EXIT(&ipf_nat);
811	SPL_X(s);
812}
813
814
815/* ------------------------------------------------------------------------ */
816/* Function:    fr_slowtimer                                                */
817/* Returns:     Nil                                                         */
818/* Parameters:  Nil                                                         */
819/*                                                                          */
820/* Slowly expire held state for fragments.  Timeouts are set * in           */
821/* expectation of this being called twice per second.                       */
822/* ------------------------------------------------------------------------ */
823#if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \
824			  !defined(__osf__) && !defined(linux))
825# if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi))
826void fr_slowtimer __P((void *ptr))
827# else
828int fr_slowtimer()
829# endif
830{
831	READ_ENTER(&ipf_global);
832
833	fr_fragexpire();
834	fr_timeoutstate();
835	fr_natexpire();
836	fr_authexpire();
837	fr_ticks++;
838	if (fr_running <= 0)
839		goto done;
840# ifdef _KERNEL
841#  if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000)
842	callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL);
843#  else
844#   if defined(__OpenBSD__)
845	timeout_add(&fr_slowtimer_ch, hz/2);
846#   else
847#    if (__FreeBSD_version >= 300000)
848	fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2);
849#    else
850#     ifdef linux
851	;
852#     else
853	timeout(fr_slowtimer, NULL, hz/2);
854#     endif
855#    endif /* FreeBSD */
856#   endif /* OpenBSD */
857#  endif /* NetBSD */
858# endif
859done:
860	RWLOCK_EXIT(&ipf_global);
861# if (BSD < 199103) || !defined(_KERNEL)
862	return 0;
863# endif
864}
865#endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */
866