ip_frag.c revision 145579
1330569Sgordon/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_frag.c 145579 2005-04-27 03:48:10Z darrenr $	*/
2330569Sgordon
3330569Sgordon/*
4330569Sgordon * Copyright (C) 1993-2003 by Darren Reed.
5330569Sgordon *
6330569Sgordon * See the IPFILTER.LICENCE file for details on licencing.
7330569Sgordon */
8330569Sgordon#if defined(KERNEL) || defined(_KERNEL)
9330569Sgordon# undef KERNEL
10330569Sgordon# undef _KERNEL
11330569Sgordon# define        KERNEL	1
12330569Sgordon# define        _KERNEL	1
13330569Sgordon#endif
14330569Sgordon#include <sys/errno.h>
15330569Sgordon#include <sys/types.h>
16330569Sgordon#include <sys/param.h>
17330569Sgordon#include <sys/time.h>
18330569Sgordon#include <sys/file.h>
19330569Sgordon#ifdef __hpux
20330569Sgordon# include <sys/timeout.h>
21330569Sgordon#endif
22330569Sgordon#if !defined(_KERNEL)
23330569Sgordon# include <stdio.h>
24330569Sgordon# include <string.h>
25330569Sgordon# include <stdlib.h>
26330569Sgordon# define _KERNEL
27330569Sgordon# ifdef __OpenBSD__
28330569Sgordonstruct file;
29330569Sgordon# endif
30330569Sgordon# include <sys/uio.h>
31330569Sgordon# undef _KERNEL
32330569Sgordon#endif
33330569Sgordon#if defined(_KERNEL) && (__FreeBSD_version >= 220000)
34330569Sgordon# include <sys/filio.h>
35330569Sgordon# include <sys/fcntl.h>
36330569Sgordon#else
37330569Sgordon# include <sys/ioctl.h>
38330569Sgordon#endif
39330569Sgordon#if !defined(linux)
40330569Sgordon# include <sys/protosw.h>
41330569Sgordon#endif
42330569Sgordon#include <sys/socket.h>
43330569Sgordon#if defined(_KERNEL)
44330569Sgordon# include <sys/systm.h>
45330569Sgordon# if !defined(__SVR4) && !defined(__svr4__)
46330569Sgordon#  include <sys/mbuf.h>
47330569Sgordon# endif
48330569Sgordon#endif
49330569Sgordon#if !defined(__SVR4) && !defined(__svr4__)
50330569Sgordon# if defined(_KERNEL) && !defined(__sgi)
51330569Sgordon#  include <sys/kernel.h>
52330569Sgordon# endif
53330569Sgordon#else
54330569Sgordon# include <sys/byteorder.h>
55330569Sgordon# ifdef _KERNEL
56330569Sgordon#  include <sys/dditypes.h>
57330569Sgordon# endif
58330569Sgordon# 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 145579 2005-04-27 03:48:10Z darrenr $";
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 ((ip->ip_off & IP_OFFMASK) != 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	if ((fra->ipfr_rule = fin->fin_fr) != NULL)
282		fin->fin_fr->fr_ref++;
283
284	/*
285	 * Insert the fragment into the fragment table, copy the struct used
286	 * in the search using bcopy rather than reassign each field.
287	 * Set the ttl to the default.
288	 */
289	if ((fra->ipfr_hnext = table[idx]) != NULL)
290		table[idx]->ipfr_hprev = &fra->ipfr_hnext;
291	fra->ipfr_hprev = table + idx;
292	fra->ipfr_data = NULL;
293	table[idx] = fra;
294	bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ);
295	fra->ipfr_ttl = fr_ticks + fr_ipfrttl;
296
297	/*
298	 * Compute the offset of the expected start of the next packet.
299	 */
300	off = ip->ip_off & IP_OFFMASK;
301	if (off == 0)
302		fra->ipfr_seen0 = 1;
303	fra->ipfr_off = off + (fin->fin_dlen >> 3);
304	fra->ipfr_pass = pass;
305	ipfr_stats.ifs_new++;
306	ipfr_inuse++;
307	return fra;
308}
309
310
311/* ------------------------------------------------------------------------ */
312/* Function:    fr_newfrag                                                  */
313/* Returns:     int - 0 == success, -1 == error                             */
314/* Parameters:  fin(I)  - pointer to packet information                     */
315/*                                                                          */
316/* Add a new entry to the fragment cache table based on the current packet  */
317/* ------------------------------------------------------------------------ */
318int fr_newfrag(fin, pass)
319u_32_t pass;
320fr_info_t *fin;
321{
322	ipfr_t	*fra;
323
324	if ((fin->fin_v != 4) || (fr_frag_lock != 0))
325		return -1;
326
327	WRITE_ENTER(&ipf_frag);
328	fra = ipfr_newfrag(fin, pass, ipfr_heads);
329	if (fra != NULL) {
330		*ipfr_tail = fra;
331		fra->ipfr_prev = ipfr_tail;
332		ipfr_tail = &fra->ipfr_next;
333		if (ipfr_list == NULL)
334			ipfr_list = fra;
335		fra->ipfr_next = NULL;
336	}
337	RWLOCK_EXIT(&ipf_frag);
338	return fra ? 0 : -1;
339}
340
341
342/* ------------------------------------------------------------------------ */
343/* Function:    fr_nat_newfrag                                              */
344/* Returns:     int - 0 == success, -1 == error                             */
345/* Parameters:  fin(I)  - pointer to packet information                     */
346/*              nat(I)  - pointer to NAT structure                          */
347/*                                                                          */
348/* Create a new NAT fragment cache entry based on the current packet and    */
349/* the NAT structure for this "session".                                    */
350/* ------------------------------------------------------------------------ */
351int fr_nat_newfrag(fin, pass, nat)
352fr_info_t *fin;
353u_32_t pass;
354nat_t *nat;
355{
356	ipfr_t	*fra;
357
358	if ((fin->fin_v != 4) || (fr_frag_lock != 0))
359		return 0;
360
361	WRITE_ENTER(&ipf_natfrag);
362	fra = ipfr_newfrag(fin, pass, ipfr_nattab);
363	if (fra != NULL) {
364		fra->ipfr_data = nat;
365		nat->nat_data = fra;
366		*ipfr_nattail = fra;
367		fra->ipfr_prev = ipfr_nattail;
368		ipfr_nattail = &fra->ipfr_next;
369		fra->ipfr_next = NULL;
370	}
371	RWLOCK_EXIT(&ipf_natfrag);
372	return fra ? 0 : -1;
373}
374
375
376/* ------------------------------------------------------------------------ */
377/* Function:    fr_ipid_newfrag                                             */
378/* Returns:     int - 0 == success, -1 == error                             */
379/* Parameters:  fin(I)  - pointer to packet information                     */
380/*              ipid(I) - new IP ID for this fragmented packet              */
381/*                                                                          */
382/* Create a new fragment cache entry for this packet and store, as a data   */
383/* pointer, the new IP ID value.                                            */
384/* ------------------------------------------------------------------------ */
385int fr_ipid_newfrag(fin, ipid)
386fr_info_t *fin;
387u_32_t ipid;
388{
389	ipfr_t	*fra;
390
391	if ((fin->fin_v != 4) || (fr_frag_lock))
392		return 0;
393
394	WRITE_ENTER(&ipf_ipidfrag);
395	fra = ipfr_newfrag(fin, 0, ipfr_ipidtab);
396	if (fra != NULL) {
397		fra->ipfr_data = (void *)ipid;
398		*ipfr_ipidtail = fra;
399		fra->ipfr_prev = ipfr_ipidtail;
400		ipfr_ipidtail = &fra->ipfr_next;
401		fra->ipfr_next = NULL;
402	}
403	RWLOCK_EXIT(&ipf_ipidfrag);
404	return fra ? 0 : -1;
405}
406
407
408/* ------------------------------------------------------------------------ */
409/* Function:    fr_fraglookup                                               */
410/* Returns:     ipfr_t * - pointer to ipfr_t structure if there's a         */
411/*                         matching entry in the frag table, else NULL      */
412/* Parameters:  fin(I)   - pointer to packet information                    */
413/*              table(I) - pointer to fragment cache table to search        */
414/*                                                                          */
415/* Check the fragment cache to see if there is already a record of this     */
416/* packet with its filter result known.                                     */
417/* ------------------------------------------------------------------------ */
418static ipfr_t *fr_fraglookup(fin, table)
419fr_info_t *fin;
420ipfr_t *table[];
421{
422	ipfr_t *f, frag;
423	u_int idx;
424	ip_t *ip;
425
426	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
427		return NULL;
428
429	/*
430	 * For fragments, we record protocol, packet id, TOS and both IP#'s
431	 * (these should all be the same for all fragments of a packet).
432	 *
433	 * build up a hash value to index the table with.
434	 */
435	ip = fin->fin_ip;
436	frag.ipfr_p = ip->ip_p;
437	idx = ip->ip_p;
438	frag.ipfr_id = ip->ip_id;
439	idx += ip->ip_id;
440	frag.ipfr_tos = ip->ip_tos;
441	frag.ipfr_src.s_addr = ip->ip_src.s_addr;
442	idx += ip->ip_src.s_addr;
443	frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
444	idx += ip->ip_dst.s_addr;
445	frag.ipfr_ifp = fin->fin_ifp;
446	idx *= 127;
447	idx %= IPFT_SIZE;
448
449	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
450	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
451	frag.ipfr_auth = fin->fin_fi.fi_auth;
452
453	/*
454	 * check the table, careful to only compare the right amount of data
455	 */
456	for (f = table[idx]; f; f = f->ipfr_hnext)
457		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp,
458			  IPFR_CMPSZ)) {
459			u_short	off;
460
461			/*
462			 * We don't want to let short packets match because
463			 * they could be compromising the security of other
464			 * rules that want to match on layer 4 fields (and
465			 * can't because they have been fragmented off.)
466			 * Why do this check here?  The counter acts as an
467			 * indicator of this kind of attack, whereas if it was
468			 * elsewhere, it wouldn't know if other matching
469			 * packets had been seen.
470			 */
471			if (fin->fin_flx & FI_SHORT) {
472				ATOMIC_INCL(ipfr_stats.ifs_short);
473				continue;
474			}
475
476			/*
477			 * XXX - We really need to be guarding against the
478			 * retransmission of (src,dst,id,offset-range) here
479			 * because a fragmented packet is never resent with
480			 * the same IP ID# (or shouldn't).
481			 */
482			off = ip->ip_off & IP_OFFMASK;
483			if (f->ipfr_seen0) {
484				if (off == 0) {
485					ATOMIC_INCL(ipfr_stats.ifs_retrans0);
486					continue;
487				}
488			} else if (off == 0)
489				f->ipfr_seen0 = 1;
490
491			if (f != table[idx]) {
492				ipfr_t **fp;
493
494				/*
495				 * Move fragment info. to the top of the list
496				 * to speed up searches.  First, delink...
497				 */
498				fp = f->ipfr_hprev;
499				(*fp) = f->ipfr_hnext;
500				if (f->ipfr_hnext != NULL)
501					f->ipfr_hnext->ipfr_hprev = fp;
502				/*
503				 * Then put back at the top of the chain.
504				 */
505				f->ipfr_hnext = table[idx];
506				table[idx]->ipfr_hprev = &f->ipfr_hnext;
507				f->ipfr_hprev = table + idx;
508				table[idx] = f;
509			}
510
511			/*
512			 * If we've follwed the fragments, and this is the
513			 * last (in order), shrink expiration time.
514			 */
515			if (off == f->ipfr_off) {
516				if (!(ip->ip_off & IP_MF))
517					f->ipfr_ttl = fr_ticks + 1;
518				f->ipfr_off = (fin->fin_dlen >> 3) + off;
519			} else if (f->ipfr_pass & FR_FRSTRICT)
520				continue;
521			ATOMIC_INCL(ipfr_stats.ifs_hits);
522			return f;
523		}
524	return NULL;
525}
526
527
528/* ------------------------------------------------------------------------ */
529/* Function:    fr_nat_knownfrag                                            */
530/* Returns:     nat_t* - pointer to 'parent' NAT structure if frag table    */
531/*                       match found, else NULL                             */
532/* Parameters:  fin(I)  - pointer to packet information                     */
533/*                                                                          */
534/* Functional interface for NAT lookups of the NAT fragment cache           */
535/* ------------------------------------------------------------------------ */
536nat_t *fr_nat_knownfrag(fin)
537fr_info_t *fin;
538{
539	nat_t	*nat;
540	ipfr_t	*ipf;
541
542	if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_natlist)
543		return NULL;
544	READ_ENTER(&ipf_natfrag);
545	ipf = fr_fraglookup(fin, ipfr_nattab);
546	if (ipf != NULL) {
547		nat = ipf->ipfr_data;
548		/*
549		 * This is the last fragment for this packet.
550		 */
551		if ((ipf->ipfr_ttl == fr_ticks + 1) && (nat != NULL)) {
552			nat->nat_data = NULL;
553			ipf->ipfr_data = NULL;
554		}
555	} else
556		nat = NULL;
557	RWLOCK_EXIT(&ipf_natfrag);
558	return nat;
559}
560
561
562/* ------------------------------------------------------------------------ */
563/* Function:    fr_ipid_knownfrag                                           */
564/* Returns:     u_32_t - IPv4 ID for this packet if match found, else       */
565/*                       return 0xfffffff to indicate no match.             */
566/* Parameters:  fin(I) - pointer to packet information                      */
567/*                                                                          */
568/* Functional interface for IP ID lookups of the IP ID fragment cache       */
569/* ------------------------------------------------------------------------ */
570u_32_t fr_ipid_knownfrag(fin)
571fr_info_t *fin;
572{
573	ipfr_t	*ipf;
574	u_32_t	id;
575
576	if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_ipidlist)
577		return 0xffffffff;
578
579	READ_ENTER(&ipf_ipidfrag);
580	ipf = fr_fraglookup(fin, ipfr_ipidtab);
581	if (ipf != NULL)
582		id = (u_32_t)ipf->ipfr_data;
583	else
584		id = 0xffffffff;
585	RWLOCK_EXIT(&ipf_ipidfrag);
586	return id;
587}
588
589
590/* ------------------------------------------------------------------------ */
591/* Function:    fr_knownfrag                                                */
592/* Returns:     frentry_t* - pointer to filter rule if a match is found in  */
593/*                           the frag cache table, else NULL.               */
594/* Parameters:  fin(I)   - pointer to packet information                    */
595/*              passp(O) - pointer to where to store rule flags resturned   */
596/*                                                                          */
597/* Functional interface for normal lookups of the fragment cache.  If a     */
598/* match is found, return the rule pointer and flags from the rule, except  */
599/* that if FR_LOGFIRST is set, reset FR_LOG.                                */
600/* ------------------------------------------------------------------------ */
601frentry_t *fr_knownfrag(fin, passp)
602fr_info_t *fin;
603u_32_t *passp;
604{
605	frentry_t *fr = NULL;
606	ipfr_t	*fra;
607	u_32_t pass;
608
609	if ((fin->fin_v != 4) || (fr_frag_lock) || (ipfr_list == NULL))
610		return NULL;
611
612	READ_ENTER(&ipf_frag);
613	fra = fr_fraglookup(fin, ipfr_heads);
614	if (fra != NULL) {
615		fr = fra->ipfr_rule;
616		fin->fin_fr = fr;
617		if (fr != NULL) {
618			pass = fr->fr_flags;
619			if ((pass & FR_LOGFIRST) != 0)
620				pass &= ~(FR_LOGFIRST|FR_LOG);
621			*passp = pass;
622		}
623	}
624	RWLOCK_EXIT(&ipf_frag);
625	return fr;
626}
627
628
629/* ------------------------------------------------------------------------ */
630/* Function:    fr_forget                                                   */
631/* Returns:     Nil                                                         */
632/* Parameters:  ptr(I) - pointer to data structure                          */
633/*                                                                          */
634/* Search through all of the fragment cache entries and wherever a pointer  */
635/* is found to match ptr, reset it to NULL.                                 */
636/* ------------------------------------------------------------------------ */
637void fr_forget(ptr)
638void *ptr;
639{
640	ipfr_t	*fr;
641
642	WRITE_ENTER(&ipf_frag);
643	for (fr = ipfr_list; fr; fr = fr->ipfr_next)
644		if (fr->ipfr_data == ptr)
645			fr->ipfr_data = NULL;
646	RWLOCK_EXIT(&ipf_frag);
647}
648
649
650/* ------------------------------------------------------------------------ */
651/* Function:    fr_forgetnat                                                */
652/* Returns:     Nil                                                         */
653/* Parameters:  ptr(I) - pointer to data structure                          */
654/*                                                                          */
655/* Search through all of the fragment cache entries for NAT and wherever a  */
656/* pointer  is found to match ptr, reset it to NULL.                        */
657/* ------------------------------------------------------------------------ */
658void fr_forgetnat(ptr)
659void *ptr;
660{
661	ipfr_t	*fr;
662
663	WRITE_ENTER(&ipf_natfrag);
664	for (fr = ipfr_natlist; fr; fr = fr->ipfr_next)
665		if (fr->ipfr_data == ptr)
666			fr->ipfr_data = NULL;
667	RWLOCK_EXIT(&ipf_natfrag);
668}
669
670
671/* ------------------------------------------------------------------------ */
672/* Function:    fr_fragdelete                                               */
673/* Returns:     Nil                                                         */
674/* Parameters:  fra(I)   - pointer to fragment structure to delete          */
675/*              tail(IO) - pointer to the pointer to the tail of the frag   */
676/*                         list                                             */
677/*                                                                          */
678/* Remove a fragment cache table entry from the table & list.  Also free    */
679/* the filter rule it is associated with it if it is no longer used as a    */
680/* result of decreasing the reference count.                                */
681/* ------------------------------------------------------------------------ */
682static void fr_fragdelete(fra, tail)
683ipfr_t *fra, ***tail;
684{
685	frentry_t *fr;
686
687	fr = fra->ipfr_rule;
688	if (fr != NULL)
689		(void)fr_derefrule(&fr);
690
691	if (fra->ipfr_next)
692		fra->ipfr_next->ipfr_prev = fra->ipfr_prev;
693	*fra->ipfr_prev = fra->ipfr_next;
694	if (*tail == &fra->ipfr_next)
695		*tail = fra->ipfr_prev;
696
697	if (fra->ipfr_hnext)
698		fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev;
699	*fra->ipfr_hprev = fra->ipfr_hnext;
700	KFREE(fra);
701}
702
703
704/* ------------------------------------------------------------------------ */
705/* Function:    fr_fragclear                                                */
706/* Returns:     Nil                                                         */
707/* Parameters:  Nil                                                         */
708/*                                                                          */
709/* Free memory in use by fragment state information kept.  Do the normal    */
710/* fragment state stuff first and then the NAT-fragment table.              */
711/* ------------------------------------------------------------------------ */
712void fr_fragclear()
713{
714	ipfr_t	*fra;
715	nat_t	*nat;
716
717	WRITE_ENTER(&ipf_frag);
718	while ((fra = ipfr_list) != NULL)
719		fr_fragdelete(fra, &ipfr_tail);
720	ipfr_tail = &ipfr_list;
721	RWLOCK_EXIT(&ipf_frag);
722
723	WRITE_ENTER(&ipf_nat);
724	WRITE_ENTER(&ipf_natfrag);
725	while ((fra = ipfr_natlist) != NULL) {
726		nat = fra->ipfr_data;
727		if (nat != NULL) {
728			if (nat->nat_data == fra)
729				nat->nat_data = NULL;
730		}
731		fr_fragdelete(fra, &ipfr_nattail);
732	}
733	ipfr_nattail = &ipfr_natlist;
734	RWLOCK_EXIT(&ipf_natfrag);
735	RWLOCK_EXIT(&ipf_nat);
736}
737
738
739/* ------------------------------------------------------------------------ */
740/* Function:    fr_fragexpire                                               */
741/* Returns:     Nil                                                         */
742/* Parameters:  Nil                                                         */
743/*                                                                          */
744/* Expire entries in the fragment cache table that have been there too long */
745/* ------------------------------------------------------------------------ */
746void fr_fragexpire()
747{
748	ipfr_t	**fp, *fra;
749	nat_t	*nat;
750#if defined(USE_SPL) && defined(_KERNEL)
751	int	s;
752#endif
753
754	if (fr_frag_lock)
755		return;
756
757	SPL_NET(s);
758	WRITE_ENTER(&ipf_frag);
759	/*
760	 * Go through the entire table, looking for entries to expire,
761	 * which is indicated by the ttl being less than or equal to fr_ticks.
762	 */
763	for (fp = &ipfr_list; ((fra = *fp) != NULL); ) {
764		if (fra->ipfr_ttl > fr_ticks)
765			break;
766		fr_fragdelete(fra, &ipfr_tail);
767		ipfr_stats.ifs_expire++;
768		ipfr_inuse--;
769	}
770	RWLOCK_EXIT(&ipf_frag);
771
772	WRITE_ENTER(&ipf_ipidfrag);
773	for (fp = &ipfr_ipidlist; ((fra = *fp) != NULL); ) {
774		if (fra->ipfr_ttl > fr_ticks)
775			break;
776		fr_fragdelete(fra, &ipfr_ipidtail);
777		ipfr_stats.ifs_expire++;
778		ipfr_inuse--;
779	}
780	RWLOCK_EXIT(&ipf_ipidfrag);
781
782	/*
783	 * Same again for the NAT table, except that if the structure also
784	 * still points to a NAT structure, and the NAT structure points back
785	 * at the one to be free'd, NULL the reference from the NAT struct.
786	 * NOTE: We need to grab both mutex's early, and in this order so as
787	 * to prevent a deadlock if both try to expire at the same time.
788	 */
789	WRITE_ENTER(&ipf_nat);
790	WRITE_ENTER(&ipf_natfrag);
791	for (fp = &ipfr_natlist; ((fra = *fp) != NULL); ) {
792		if (fra->ipfr_ttl > fr_ticks)
793			break;
794		nat = fra->ipfr_data;
795		if (nat != NULL) {
796			if (nat->nat_data == fra)
797				nat->nat_data = NULL;
798		}
799		fr_fragdelete(fra, &ipfr_nattail);
800		ipfr_stats.ifs_expire++;
801		ipfr_inuse--;
802	}
803	RWLOCK_EXIT(&ipf_natfrag);
804	RWLOCK_EXIT(&ipf_nat);
805	SPL_X(s);
806}
807
808
809/* ------------------------------------------------------------------------ */
810/* Function:    fr_slowtimer                                                */
811/* Returns:     Nil                                                         */
812/* Parameters:  Nil                                                         */
813/*                                                                          */
814/* Slowly expire held state for fragments.  Timeouts are set * in           */
815/* expectation of this being called twice per second.                       */
816/* ------------------------------------------------------------------------ */
817#if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \
818			  !defined(__osf__))
819# if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi))
820void fr_slowtimer __P((void *ptr))
821# else
822int fr_slowtimer()
823# endif
824{
825	READ_ENTER(&ipf_global);
826
827	fr_fragexpire();
828	fr_timeoutstate();
829	fr_natexpire();
830	fr_authexpire();
831	fr_ticks++;
832	if (fr_running <= 0)
833		goto done;
834# ifdef _KERNEL
835#  if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000)
836	callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL);
837#  else
838#   if defined(__OpenBSD__)
839	timeout_add(&fr_slowtimer_ch, hz/2);
840#   else
841#    if (__FreeBSD_version >= 300000)
842	fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2);
843#    else
844#     ifdef linux
845	;
846#     else
847	timeout(fr_slowtimer, NULL, hz/2);
848#     endif
849#    endif /* FreeBSD */
850#   endif /* OpenBSD */
851#  endif /* NetBSD */
852# endif
853done:
854	RWLOCK_EXIT(&ipf_global);
855# if (BSD < 199103) || !defined(_KERNEL)
856	return 0;
857# endif
858}
859#endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */
860