2 * Copyright (C) 1993-2003 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
6 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
7 * Use is subject to license terms.
10 #if defined(KERNEL) || defined(_KERNEL)
16 #include <sys/errno.h>
17 #include <sys/types.h>
18 #include <sys/param.h>
22 # include <sys/timeout.h>
35 #if defined(_KERNEL) && (__FreeBSD_version >= 220000)
36 # include <sys/filio.h>
37 # include <sys/fcntl.h>
39 # include <sys/ioctl.h>
42 # include <sys/protosw.h>
44 #include <sys/socket.h>
46 # include <sys/systm.h>
47 # if !defined(__SVR4) && !defined(__svr4__)
48 # include <sys/mbuf.h>
51 #if !defined(__SVR4) && !defined(__svr4__)
52 # if defined(_KERNEL) && !defined(__sgi) && !defined(AIX)
53 # include <sys/kernel.h>
56 # include <sys/byteorder.h>
58 # include <sys/dditypes.h>
60 # include <sys/stream.h>
61 # include <sys/kmem.h>
67 #include <net/route.h>
68 #include <netinet/in.h>
69 #include <netinet/in_systm.h>
70 #include <netinet/ip.h>
72 # include <netinet/ip_var.h>
74 #include <netinet/tcp.h>
75 #include <netinet/udp.h>
76 #include <netinet/ip_icmp.h>
77 #include "netinet/ip_compat.h"
78 #include <netinet/tcpip.h>
79 #include "netinet/ip_fil.h"
80 #include "netinet/ip_nat.h"
81 #include "netinet/ip_frag.h"
82 #include "netinet/ip_state.h"
83 #include "netinet/ip_auth.h"
84 #include "netinet/ipf_stack.h"
85 #if (__FreeBSD_version >= 300000)
86 # include <sys/malloc.h>
89 # include <sys/libkern.h>
90 # include <sys/systm.h>
92 extern struct callout_handle fr_slowtimer_ch
;
95 #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000)
96 # include <sys/callout.h>
97 extern struct callout fr_slowtimer_ch
;
99 #if defined(__OpenBSD__)
100 # include <sys/timeout.h>
101 extern struct timeout fr_slowtimer_ch
;
103 /* END OF INCLUDES */
105 static const char sccsid
[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed";
106 static const char rcsid
[] = "@(#)$Id: ip_frag.c,v 2.77.2.5 2005/08/11 14:33:10 darrenr Exp $";
108 static INLINE
int ipfr_index
__P((fr_info_t
*, ipfr_t
*));
109 static ipfr_t
*ipfr_newfrag
__P((fr_info_t
*, u_32_t
, ipfr_t
**));
110 static ipfr_t
*fr_fraglookup
__P((fr_info_t
*, ipfr_t
**));
111 static void fr_fragdelete
__P((ipfr_t
*, ipfr_t
***, ipf_stack_t
*));
113 /* ------------------------------------------------------------------------ */
114 /* Function: fr_fraginit */
115 /* Returns: int - 0 == success, -1 == error */
116 /* Parameters: Nil */
118 /* Initialise the hash tables for the fragment cache lookups. */
119 /* ------------------------------------------------------------------------ */
123 ifs
->ifs_ipfr_tail
= &ifs
->ifs_ipfr_list
;
124 ifs
->ifs_ipfr_nattail
= &ifs
->ifs_ipfr_natlist
;
125 ifs
->ifs_ipfr_ipidtail
= &ifs
->ifs_ipfr_ipidlist
;
126 /* the IP frag related variables are set in ipftuneable_setdefs() to
127 * their default values
130 KMALLOCS(ifs
->ifs_ipfr_heads
, ipfr_t
**,
131 ifs
->ifs_ipfr_size
* sizeof(ipfr_t
*));
132 if (ifs
->ifs_ipfr_heads
== NULL
)
134 bzero((char *)ifs
->ifs_ipfr_heads
,
135 ifs
->ifs_ipfr_size
* sizeof(ipfr_t
*));
137 KMALLOCS(ifs
->ifs_ipfr_nattab
, ipfr_t
**,
138 ifs
->ifs_ipfr_size
* sizeof(ipfr_t
*));
139 if (ifs
->ifs_ipfr_nattab
== NULL
)
141 bzero((char *)ifs
->ifs_ipfr_nattab
,
142 ifs
->ifs_ipfr_size
* sizeof(ipfr_t
*));
144 KMALLOCS(ifs
->ifs_ipfr_ipidtab
, ipfr_t
**,
145 ifs
->ifs_ipfr_size
* sizeof(ipfr_t
*));
146 if (ifs
->ifs_ipfr_ipidtab
== NULL
)
148 bzero((char *)ifs
->ifs_ipfr_ipidtab
,
149 ifs
->ifs_ipfr_size
* sizeof(ipfr_t
*));
151 RWLOCK_INIT(&ifs
->ifs_ipf_frag
, "ipf fragment rwlock");
153 /* Initialise frblock with "block in all" */
154 bzero((char *)&ifs
->ifs_frblock
, sizeof(ifs
->ifs_frblock
));
155 ifs
->ifs_frblock
.fr_flags
= FR_BLOCK
|FR_INQUE
; /* block in */
156 ifs
->ifs_frblock
.fr_ref
= 1;
158 ifs
->ifs_fr_frag_init
= 1;
164 /* ------------------------------------------------------------------------ */
165 /* Function: fr_fragunload */
167 /* Parameters: Nil */
169 /* Free all memory allocated whilst running and from initialisation. */
170 /* ------------------------------------------------------------------------ */
171 void fr_fragunload(ifs
)
174 if (ifs
->ifs_fr_frag_init
== 1) {
177 RW_DESTROY(&ifs
->ifs_ipf_frag
);
178 ifs
->ifs_fr_frag_init
= 0;
181 if (ifs
->ifs_ipfr_heads
!= NULL
) {
182 KFREES(ifs
->ifs_ipfr_heads
,
183 ifs
->ifs_ipfr_size
* sizeof(ipfr_t
*));
185 ifs
->ifs_ipfr_heads
= NULL
;
187 if (ifs
->ifs_ipfr_nattab
!= NULL
) {
188 KFREES(ifs
->ifs_ipfr_nattab
,
189 ifs
->ifs_ipfr_size
* sizeof(ipfr_t
*));
191 ifs
->ifs_ipfr_nattab
= NULL
;
193 if (ifs
->ifs_ipfr_ipidtab
!= NULL
) {
194 KFREES(ifs
->ifs_ipfr_ipidtab
,
195 ifs
->ifs_ipfr_size
* sizeof(ipfr_t
*));
197 ifs
->ifs_ipfr_ipidtab
= NULL
;
201 /* ------------------------------------------------------------------------ */
202 /* Function: fr_fragstats */
203 /* Returns: ipfrstat_t* - pointer to struct with current frag stats */
204 /* Parameters: Nil */
206 /* Updates ipfr_stats with current information and returns a pointer to it */
207 /* ------------------------------------------------------------------------ */
208 ipfrstat_t
*fr_fragstats(ifs
)
211 ifs
->ifs_ipfr_stats
.ifs_table
= ifs
->ifs_ipfr_heads
;
212 ifs
->ifs_ipfr_stats
.ifs_nattab
= ifs
->ifs_ipfr_nattab
;
213 ifs
->ifs_ipfr_stats
.ifs_inuse
= ifs
->ifs_ipfr_inuse
;
214 return &ifs
->ifs_ipfr_stats
;
218 /* ------------------------------------------------------------------------ */
219 /* Function: ipfr_index */
220 /* Returns: int - index in fragment table for given packet */
221 /* Parameters: fin(I) - pointer to packet information */
222 /* frag(O) - pointer to ipfr_t structure to fill */
224 /* Compute the index in the fragment table while filling the per packet */
225 /* part of the fragment state. */
226 /* ------------------------------------------------------------------------ */
227 static INLINE
int ipfr_index(fin
, frag
)
234 * For fragments, we record protocol, packet id, TOS and both IP#'s
235 * (these should all be the same for all fragments of a packet).
237 * build up a hash value to index the table with.
241 if (fin
->fin_v
== 6) {
242 ip6_t
*ip6
= (ip6_t
*)fin
->fin_ip
;
244 frag
->ipfr_p
= fin
->fin_fi
.fi_p
;
245 frag
->ipfr_id
= fin
->fin_id
;
246 frag
->ipfr_tos
= ip6
->ip6_flow
& IPV6_FLOWINFO_MASK
;
247 frag
->ipfr_src
.in6
= ip6
->ip6_src
;
248 frag
->ipfr_dst
.in6
= ip6
->ip6_dst
;
252 ip_t
*ip
= fin
->fin_ip
;
254 frag
->ipfr_p
= ip
->ip_p
;
255 frag
->ipfr_id
= ip
->ip_id
;
256 frag
->ipfr_tos
= ip
->ip_tos
;
257 frag
->ipfr_src
.in4
.s_addr
= ip
->ip_src
.s_addr
;
258 frag
->ipfr_src
.i6
[1] = 0;
259 frag
->ipfr_src
.i6
[2] = 0;
260 frag
->ipfr_src
.i6
[3] = 0;
261 frag
->ipfr_dst
.in4
.s_addr
= ip
->ip_dst
.s_addr
;
262 frag
->ipfr_dst
.i6
[1] = 0;
263 frag
->ipfr_dst
.i6
[2] = 0;
264 frag
->ipfr_dst
.i6
[3] = 0;
266 frag
->ipfr_ifp
= fin
->fin_ifp
;
267 frag
->ipfr_optmsk
= fin
->fin_fi
.fi_optmsk
& IPF_OPTCOPY
;
268 frag
->ipfr_secmsk
= fin
->fin_fi
.fi_secmsk
;
269 frag
->ipfr_auth
= fin
->fin_fi
.fi_auth
;
272 idx
+= frag
->ipfr_id
;
273 idx
+= frag
->ipfr_src
.i6
[0];
274 idx
+= frag
->ipfr_src
.i6
[1];
275 idx
+= frag
->ipfr_src
.i6
[2];
276 idx
+= frag
->ipfr_src
.i6
[3];
277 idx
+= frag
->ipfr_dst
.i6
[0];
278 idx
+= frag
->ipfr_dst
.i6
[1];
279 idx
+= frag
->ipfr_dst
.i6
[2];
280 idx
+= frag
->ipfr_dst
.i6
[3];
288 /* ------------------------------------------------------------------------ */
289 /* Function: ipfr_newfrag */
290 /* Returns: ipfr_t * - pointer to fragment cache state info or NULL */
291 /* Parameters: fin(I) - pointer to packet information */
292 /* table(I) - pointer to frag table to add to */
294 /* Add a new entry to the fragment cache, registering it as having come */
295 /* through this box, with the result of the filter operation. */
296 /* ------------------------------------------------------------------------ */
297 static ipfr_t
*ipfr_newfrag(fin
, pass
, table
)
304 ipf_stack_t
*ifs
= fin
->fin_ifs
;
306 if (ifs
->ifs_ipfr_inuse
>= ifs
->ifs_ipfr_size
)
309 if ((fin
->fin_flx
& (FI_FRAG
|FI_BAD
)) != FI_FRAG
)
312 if (pass
& FR_FRSTRICT
)
313 if (fin
->fin_off
!= 0)
316 idx
= ipfr_index(fin
, &frag
);
319 * first, make sure it isn't already there...
321 for (fra
= table
[idx
]; (fra
!= NULL
); fra
= fra
->ipfr_hnext
)
322 if (!bcmp((char *)&frag
.ipfr_ifp
, (char *)&fra
->ipfr_ifp
,
324 ifs
->ifs_ipfr_stats
.ifs_exists
++;
329 * allocate some memory, if possible, if not, just record that we
332 KMALLOC(fra
, ipfr_t
*);
334 ifs
->ifs_ipfr_stats
.ifs_nomem
++;
338 fra
->ipfr_rule
= fin
->fin_fr
;
339 if (fra
->ipfr_rule
!= NULL
) {
344 MUTEX_ENTER(&fr
->fr_lock
);
346 MUTEX_EXIT(&fr
->fr_lock
);
350 * Insert the fragment into the fragment table, copy the struct used
351 * in the search using bcopy rather than reassign each field.
352 * Set the ttl to the default.
354 if ((fra
->ipfr_hnext
= table
[idx
]) != NULL
)
355 table
[idx
]->ipfr_hprev
= &fra
->ipfr_hnext
;
356 fra
->ipfr_hprev
= table
+ idx
;
357 fra
->ipfr_data
= NULL
;
359 bcopy((char *)&frag
.ipfr_ifp
, (char *)&fra
->ipfr_ifp
, IPFR_CMPSZ
);
360 fra
->ipfr_ttl
= ifs
->ifs_fr_ticks
+ ifs
->ifs_fr_ipfrttl
;
363 * Compute the offset of the expected start of the next packet.
365 off
= fin
->fin_off
>> 3;
371 fra
->ipfr_off
= off
+ fin
->fin_dlen
;
372 fra
->ipfr_pass
= pass
;
374 ifs
->ifs_ipfr_stats
.ifs_new
++;
375 ifs
->ifs_ipfr_inuse
++;
380 /* ------------------------------------------------------------------------ */
381 /* Function: fr_newfrag */
382 /* Returns: int - 0 == success, -1 == error */
383 /* Parameters: fin(I) - pointer to packet information */
385 /* Add a new entry to the fragment cache table based on the current packet */
386 /* ------------------------------------------------------------------------ */
387 int fr_newfrag(fin
, pass
)
392 ipf_stack_t
*ifs
= fin
->fin_ifs
;
394 if (ifs
->ifs_fr_frag_lock
!= 0)
397 WRITE_ENTER(&ifs
->ifs_ipf_frag
);
398 fra
= ipfr_newfrag(fin
, pass
, ifs
->ifs_ipfr_heads
);
400 *ifs
->ifs_ipfr_tail
= fra
;
401 fra
->ipfr_prev
= ifs
->ifs_ipfr_tail
;
402 ifs
->ifs_ipfr_tail
= &fra
->ipfr_next
;
403 if (ifs
->ifs_ipfr_list
== NULL
)
404 ifs
->ifs_ipfr_list
= fra
;
405 fra
->ipfr_next
= NULL
;
407 RWLOCK_EXIT(&ifs
->ifs_ipf_frag
);
412 /* ------------------------------------------------------------------------ */
413 /* Function: fr_nat_newfrag */
414 /* Returns: int - 0 == success, -1 == error */
415 /* Parameters: fin(I) - pointer to packet information */
416 /* nat(I) - pointer to NAT structure */
418 /* Create a new NAT fragment cache entry based on the current packet and */
419 /* the NAT structure for this "session". */
420 /* ------------------------------------------------------------------------ */
421 int fr_nat_newfrag(fin
, pass
, nat
)
427 ipf_stack_t
*ifs
= fin
->fin_ifs
;
429 if (ifs
->ifs_fr_frag_lock
!= 0)
432 WRITE_ENTER(&ifs
->ifs_ipf_natfrag
);
433 fra
= ipfr_newfrag(fin
, pass
, ifs
->ifs_ipfr_nattab
);
435 fra
->ipfr_data
= nat
;
437 *ifs
->ifs_ipfr_nattail
= fra
;
438 fra
->ipfr_prev
= ifs
->ifs_ipfr_nattail
;
439 ifs
->ifs_ipfr_nattail
= &fra
->ipfr_next
;
440 fra
->ipfr_next
= NULL
;
442 RWLOCK_EXIT(&ifs
->ifs_ipf_natfrag
);
447 /* ------------------------------------------------------------------------ */
448 /* Function: fr_ipid_newfrag */
449 /* Returns: int - 0 == success, -1 == error */
450 /* Parameters: fin(I) - pointer to packet information */
451 /* ipid(I) - new IP ID for this fragmented packet */
453 /* Create a new fragment cache entry for this packet and store, as a data */
454 /* pointer, the new IP ID value. */
455 /* ------------------------------------------------------------------------ */
456 int fr_ipid_newfrag(fin
, ipid
)
461 ipf_stack_t
*ifs
= fin
->fin_ifs
;
463 if (ifs
->ifs_fr_frag_lock
)
466 WRITE_ENTER(&ifs
->ifs_ipf_ipidfrag
);
467 fra
= ipfr_newfrag(fin
, 0, ifs
->ifs_ipfr_ipidtab
);
469 fra
->ipfr_data
= (void *)(uintptr_t)ipid
;
470 *ifs
->ifs_ipfr_ipidtail
= fra
;
471 fra
->ipfr_prev
= ifs
->ifs_ipfr_ipidtail
;
472 ifs
->ifs_ipfr_ipidtail
= &fra
->ipfr_next
;
473 fra
->ipfr_next
= NULL
;
475 RWLOCK_EXIT(&ifs
->ifs_ipf_ipidfrag
);
480 /* ------------------------------------------------------------------------ */
481 /* Function: fr_fraglookup */
482 /* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */
483 /* matching entry in the frag table, else NULL */
484 /* Parameters: fin(I) - pointer to packet information */
485 /* table(I) - pointer to fragment cache table to search */
487 /* Check the fragment cache to see if there is already a record of this */
488 /* packet with its filter result known. */
489 /* ------------------------------------------------------------------------ */
490 static ipfr_t
*fr_fraglookup(fin
, table
)
496 ipf_stack_t
*ifs
= fin
->fin_ifs
;
498 if ((fin
->fin_flx
& (FI_FRAG
|FI_BAD
)) != FI_FRAG
)
502 * For fragments, we record protocol, packet id, TOS and both IP#'s
503 * (these should all be the same for all fragments of a packet).
505 * build up a hash value to index the table with.
507 idx
= ipfr_index(fin
, &frag
);
510 * check the table, careful to only compare the right amount of data
512 for (f
= table
[idx
]; f
; f
= f
->ipfr_hnext
)
513 if (!bcmp((char *)&frag
.ipfr_ifp
, (char *)&f
->ipfr_ifp
,
518 * We don't want to let short packets match because
519 * they could be compromising the security of other
520 * rules that want to match on layer 4 fields (and
521 * can't because they have been fragmented off.)
522 * Why do this check here? The counter acts as an
523 * indicator of this kind of attack, whereas if it was
524 * elsewhere, it wouldn't know if other matching
525 * packets had been seen.
527 if (fin
->fin_flx
& FI_SHORT
) {
528 ATOMIC_INCL(ifs
->ifs_ipfr_stats
.ifs_short
);
533 * XXX - We really need to be guarding against the
534 * retransmission of (src,dst,id,offset-range) here
535 * because a fragmented packet is never resent with
536 * the same IP ID# (or shouldn't).
538 off
= fin
->fin_off
>> 3;
541 ATOMIC_INCL(ifs
->ifs_ipfr_stats
.ifs_retrans0
);
544 } else if (off
== 0) {
548 if (f
!= table
[idx
]) {
552 * Move fragment info. to the top of the list
553 * to speed up searches. First, delink...
556 (*fp
) = f
->ipfr_hnext
;
557 if (f
->ipfr_hnext
!= NULL
)
558 f
->ipfr_hnext
->ipfr_hprev
= fp
;
560 * Then put back at the top of the chain.
562 f
->ipfr_hnext
= table
[idx
];
563 table
[idx
]->ipfr_hprev
= &f
->ipfr_hnext
;
564 f
->ipfr_hprev
= table
+ idx
;
569 * If we've follwed the fragments, and this is the
570 * last (in order), shrink expiration time.
572 if (off
== f
->ipfr_off
) {
573 if (!(fin
->fin_flx
& FI_MOREFRAG
))
574 f
->ipfr_ttl
= ifs
->ifs_fr_ticks
+ 1;
575 f
->ipfr_off
= fin
->fin_dlen
+ off
;
576 } else if (f
->ipfr_pass
& FR_FRSTRICT
)
578 ATOMIC_INCL(ifs
->ifs_ipfr_stats
.ifs_hits
);
585 /* ------------------------------------------------------------------------ */
586 /* Function: fr_nat_knownfrag */
587 /* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */
588 /* match found, else NULL */
589 /* Parameters: fin(I) - pointer to packet information */
591 /* Functional interface for NAT lookups of the NAT fragment cache */
592 /* ------------------------------------------------------------------------ */
593 nat_t
*fr_nat_knownfrag(fin
)
598 ipf_stack_t
*ifs
= fin
->fin_ifs
;
600 if (ifs
->ifs_fr_frag_lock
|| !ifs
->ifs_ipfr_natlist
)
602 READ_ENTER(&ifs
->ifs_ipf_natfrag
);
603 ipf
= fr_fraglookup(fin
, ifs
->ifs_ipfr_nattab
);
605 nat
= ipf
->ipfr_data
;
607 * This is the last fragment for this packet.
609 if ((ipf
->ipfr_ttl
== ifs
->ifs_fr_ticks
+ 1) && (nat
!= NULL
)) {
610 nat
->nat_data
= NULL
;
611 ipf
->ipfr_data
= NULL
;
615 RWLOCK_EXIT(&ifs
->ifs_ipf_natfrag
);
620 /* ------------------------------------------------------------------------ */
621 /* Function: fr_ipid_knownfrag */
622 /* Returns: u_32_t - IPv4 ID for this packet if match found, else */
623 /* return 0xfffffff to indicate no match. */
624 /* Parameters: fin(I) - pointer to packet information */
626 /* Functional interface for IP ID lookups of the IP ID fragment cache */
627 /* ------------------------------------------------------------------------ */
628 u_32_t
fr_ipid_knownfrag(fin
)
633 ipf_stack_t
*ifs
= fin
->fin_ifs
;
635 if (ifs
->ifs_fr_frag_lock
|| !ifs
->ifs_ipfr_ipidlist
)
638 READ_ENTER(&ifs
->ifs_ipf_ipidfrag
);
639 ipf
= fr_fraglookup(fin
, ifs
->ifs_ipfr_ipidtab
);
641 id
= (u_32_t
)(uintptr_t)ipf
->ipfr_data
;
644 RWLOCK_EXIT(&ifs
->ifs_ipf_ipidfrag
);
649 /* ------------------------------------------------------------------------ */
650 /* Function: fr_knownfrag */
651 /* Returns: frentry_t* - pointer to filter rule if a match is found in */
652 /* the frag cache table, else NULL. */
653 /* Parameters: fin(I) - pointer to packet information */
654 /* passp(O) - pointer to where to store rule flags resturned */
656 /* Functional interface for normal lookups of the fragment cache. If a */
657 /* match is found, return the rule pointer and flags from the rule, except */
658 /* that if FR_LOGFIRST is set, reset FR_LOG. */
659 /* ------------------------------------------------------------------------ */
660 frentry_t
*fr_knownfrag(fin
, passp
)
664 frentry_t
*fr
= NULL
;
667 ipf_stack_t
*ifs
= fin
->fin_ifs
;
669 if (ifs
->ifs_fr_frag_lock
|| (ifs
->ifs_ipfr_list
== NULL
))
672 READ_ENTER(&ifs
->ifs_ipf_frag
);
674 fra
= fr_fraglookup(fin
, ifs
->ifs_ipfr_heads
);
680 if ((pass
& FR_LOGFIRST
) != 0)
681 pass
&= ~(FR_LOGFIRST
|FR_LOG
);
685 if (!(oflx
& FI_BAD
) && (fin
->fin_flx
& FI_BAD
)) {
686 *passp
&= ~FR_CMDMASK
;
688 fr
= &ifs
->ifs_frblock
;
690 RWLOCK_EXIT(&ifs
->ifs_ipf_frag
);
695 /* ------------------------------------------------------------------------ */
696 /* Function: fr_forget */
698 /* Parameters: ptr(I) - pointer to data structure */
700 /* Search through all of the fragment cache entries and wherever a pointer */
701 /* is found to match ptr, reset it to NULL. */
702 /* ------------------------------------------------------------------------ */
703 void fr_forget(ptr
, ifs
)
709 WRITE_ENTER(&ifs
->ifs_ipf_frag
);
710 for (fr
= ifs
->ifs_ipfr_list
; fr
; fr
= fr
->ipfr_next
)
711 if (fr
->ipfr_data
== ptr
)
712 fr
->ipfr_data
= NULL
;
713 RWLOCK_EXIT(&ifs
->ifs_ipf_frag
);
717 /* ------------------------------------------------------------------------ */
718 /* Function: fr_forgetnat */
720 /* Parameters: ptr(I) - pointer to data structure */
722 /* Search through all of the fragment cache entries for NAT and wherever a */
723 /* pointer is found to match ptr, reset it to NULL. */
724 /* ------------------------------------------------------------------------ */
725 void fr_forgetnat(ptr
, ifs
)
731 WRITE_ENTER(&ifs
->ifs_ipf_natfrag
);
732 for (fr
= ifs
->ifs_ipfr_natlist
; fr
; fr
= fr
->ipfr_next
)
733 if (fr
->ipfr_data
== ptr
)
734 fr
->ipfr_data
= NULL
;
735 RWLOCK_EXIT(&ifs
->ifs_ipf_natfrag
);
739 /* ------------------------------------------------------------------------ */
740 /* Function: fr_fragdelete */
742 /* Parameters: fra(I) - pointer to fragment structure to delete */
743 /* tail(IO) - pointer to the pointer to the tail of the frag */
746 /* Remove a fragment cache table entry from the table & list. Also free */
747 /* the filter rule it is associated with it if it is no longer used as a */
748 /* result of decreasing the reference count. */
749 /* ------------------------------------------------------------------------ */
750 static void fr_fragdelete(fra
, tail
, ifs
)
751 ipfr_t
*fra
, ***tail
;
758 (void)fr_derefrule(&fr
, ifs
);
761 fra
->ipfr_next
->ipfr_prev
= fra
->ipfr_prev
;
762 *fra
->ipfr_prev
= fra
->ipfr_next
;
763 if (*tail
== &fra
->ipfr_next
)
764 *tail
= fra
->ipfr_prev
;
767 fra
->ipfr_hnext
->ipfr_hprev
= fra
->ipfr_hprev
;
768 *fra
->ipfr_hprev
= fra
->ipfr_hnext
;
770 if (fra
->ipfr_ref
<= 0)
775 /* ------------------------------------------------------------------------ */
776 /* Function: fr_fragclear */
778 /* Parameters: Nil */
780 /* Free memory in use by fragment state information kept. Do the normal */
781 /* fragment state stuff first and then the NAT-fragment table. */
782 /* ------------------------------------------------------------------------ */
783 void fr_fragclear(ifs
)
789 WRITE_ENTER(&ifs
->ifs_ipf_frag
);
790 while ((fra
= ifs
->ifs_ipfr_list
) != NULL
) {
792 fr_fragdelete(fra
, &ifs
->ifs_ipfr_tail
, ifs
);
794 ifs
->ifs_ipfr_tail
= &ifs
->ifs_ipfr_list
;
795 RWLOCK_EXIT(&ifs
->ifs_ipf_frag
);
797 WRITE_ENTER(&ifs
->ifs_ipf_nat
);
798 WRITE_ENTER(&ifs
->ifs_ipf_natfrag
);
799 while ((fra
= ifs
->ifs_ipfr_natlist
) != NULL
) {
800 nat
= fra
->ipfr_data
;
802 if (nat
->nat_data
== fra
)
803 nat
->nat_data
= NULL
;
806 fr_fragdelete(fra
, &ifs
->ifs_ipfr_nattail
, ifs
);
808 ifs
->ifs_ipfr_nattail
= &ifs
->ifs_ipfr_natlist
;
809 RWLOCK_EXIT(&ifs
->ifs_ipf_natfrag
);
810 RWLOCK_EXIT(&ifs
->ifs_ipf_nat
);
814 /* ------------------------------------------------------------------------ */
815 /* Function: fr_fragexpire */
817 /* Parameters: Nil */
819 /* Expire entries in the fragment cache table that have been there too long */
820 /* ------------------------------------------------------------------------ */
821 void fr_fragexpire(ifs
)
828 if (ifs
->ifs_fr_frag_lock
)
832 WRITE_ENTER(&ifs
->ifs_ipf_frag
);
834 * Go through the entire table, looking for entries to expire,
835 * which is indicated by the ttl being less than or equal to
838 for (fp
= &ifs
->ifs_ipfr_list
; ((fra
= *fp
) != NULL
); ) {
839 if (fra
->ipfr_ttl
> ifs
->ifs_fr_ticks
)
842 fr_fragdelete(fra
, &ifs
->ifs_ipfr_tail
, ifs
);
843 ifs
->ifs_ipfr_stats
.ifs_expire
++;
844 ifs
->ifs_ipfr_inuse
--;
846 RWLOCK_EXIT(&ifs
->ifs_ipf_frag
);
848 WRITE_ENTER(&ifs
->ifs_ipf_ipidfrag
);
849 for (fp
= &ifs
->ifs_ipfr_ipidlist
; ((fra
= *fp
) != NULL
); ) {
850 if (fra
->ipfr_ttl
> ifs
->ifs_fr_ticks
)
853 fr_fragdelete(fra
, &ifs
->ifs_ipfr_ipidtail
, ifs
);
854 ifs
->ifs_ipfr_stats
.ifs_expire
++;
855 ifs
->ifs_ipfr_inuse
--;
857 RWLOCK_EXIT(&ifs
->ifs_ipf_ipidfrag
);
860 * Same again for the NAT table, except that if the structure also
861 * still points to a NAT structure, and the NAT structure points back
862 * at the one to be free'd, NULL the reference from the NAT struct.
863 * NOTE: We need to grab both mutex's early, and in this order so as
864 * to prevent a deadlock if both try to expire at the same time.
866 WRITE_ENTER(&ifs
->ifs_ipf_nat
);
867 WRITE_ENTER(&ifs
->ifs_ipf_natfrag
);
868 for (fp
= &ifs
->ifs_ipfr_natlist
; ((fra
= *fp
) != NULL
); ) {
869 if (fra
->ipfr_ttl
> ifs
->ifs_fr_ticks
)
871 nat
= fra
->ipfr_data
;
873 if (nat
->nat_data
== fra
)
874 nat
->nat_data
= NULL
;
877 fr_fragdelete(fra
, &ifs
->ifs_ipfr_nattail
, ifs
);
878 ifs
->ifs_ipfr_stats
.ifs_expire
++;
879 ifs
->ifs_ipfr_inuse
--;
881 RWLOCK_EXIT(&ifs
->ifs_ipf_natfrag
);
882 RWLOCK_EXIT(&ifs
->ifs_ipf_nat
);
887 /* ------------------------------------------------------------------------ */
888 /* Function: fr_slowtimer */
890 /* Parameters: Nil */
892 /* Slowly expire held state for fragments. Timeouts are set * in */
893 /* expectation of this being called twice per second. */
894 /* ------------------------------------------------------------------------ */
895 #if !defined(_KERNEL) || (!defined(SOLARIS) && !defined(__hpux) && \
896 !defined(__sgi) && !defined(__osf__) && !defined(linux))
897 # if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi))
898 void fr_slowtimer
__P((void *arg
))
900 int fr_slowtimer(void *arg
)
903 ipf_stack_t
*ifs
= arg
;
905 READ_ENTER(&ifs
->ifs_ipf_global
);
908 fr_timeoutstate(ifs
);
912 if (ifs
->ifs_fr_running
<= 0)
915 # if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000)
916 callout_reset(&fr_slowtimer_ch
, hz
/ 2, fr_slowtimer
, NULL
);
918 # if defined(__OpenBSD__)
919 timeout_add(&fr_slowtimer_ch
, hz
/2);
921 # if (__FreeBSD_version >= 300000)
922 fr_slowtimer_ch
= timeout(fr_slowtimer
, NULL
, hz
/2);
927 timeout(fr_slowtimer
, NULL
, hz
/2);
929 # endif /* FreeBSD */
930 # endif /* OpenBSD */
934 RWLOCK_EXIT(&ifs
->ifs_ipf_global
);
935 # if (BSD < 199103) || !defined(_KERNEL)
939 #endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */
942 int fr_nextfrag(token
, itp
, top
, tail
, lock
, ifs
)
945 ipfr_t
**top
, ***tail
;
949 ipfr_t
*frag
, *next
, zero
;
955 * Retrieve "previous" entry from token and find the next entry.
957 frag
= token
->ipt_data
;
961 next
= frag
->ipfr_next
;
964 * If we found an entry, add reference to it and update token.
965 * Otherwise, zero out data to be returned and NULL out token.
968 ATOMIC_INC(next
->ipfr_ref
);
969 token
->ipt_data
= next
;
971 bzero(&zero
, sizeof(zero
));
973 token
->ipt_data
= NULL
;
977 * Now that we have ref, it's save to give up lock.
982 * Copy out data and clean up references and token as needed.
984 error
= COPYOUT(next
, itp
->igi_data
, sizeof(*next
));
987 if (token
->ipt_data
== NULL
) {
988 ipf_freetoken(token
, ifs
);
991 fr_fragderef(&frag
, lock
, ifs
);
992 if (next
->ipfr_next
== NULL
)
993 ipf_freetoken(token
, ifs
);
999 void fr_fragderef(frp
, lock
, ifs
)
1011 if (fra
->ipfr_ref
<= 0) {
1013 ifs
->ifs_ipfr_stats
.ifs_expire
++;
1014 ifs
->ifs_ipfr_inuse
--;