Merge commit '80d5689f5d4588adc071138e25e9d0d5252d9b55'
[unleashed.git] / kernel / net / ipf / ip_frag.c
blob63c624b06d42a4bdbc82f8492cef1bb0169befa7
1 /*
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.
8 */
10 #if defined(KERNEL) || defined(_KERNEL)
11 # undef KERNEL
12 # undef _KERNEL
13 # define KERNEL 1
14 # define _KERNEL 1
15 #endif
16 #include <sys/errno.h>
17 #include <sys/types.h>
18 #include <sys/param.h>
19 #include <sys/time.h>
20 #include <sys/file.h>
21 #ifdef __hpux
22 # include <sys/timeout.h>
23 #endif
24 #if !defined(_KERNEL)
25 # include <stdio.h>
26 # include <string.h>
27 # include <stdlib.h>
28 # define _KERNEL
29 # ifdef __OpenBSD__
30 struct file;
31 # endif
32 # include <sys/uio.h>
33 # undef _KERNEL
34 #endif
35 #if defined(_KERNEL) && (__FreeBSD_version >= 220000)
36 # include <sys/filio.h>
37 # include <sys/fcntl.h>
38 #else
39 # include <sys/ioctl.h>
40 #endif
41 #if !defined(linux)
42 # include <sys/protosw.h>
43 #endif
44 #include <sys/socket.h>
45 #if defined(_KERNEL)
46 # include <sys/systm.h>
47 # if !defined(__SVR4) && !defined(__svr4__)
48 # include <sys/mbuf.h>
49 # endif
50 #endif
51 #if !defined(__SVR4) && !defined(__svr4__)
52 # if defined(_KERNEL) && !defined(__sgi) && !defined(AIX)
53 # include <sys/kernel.h>
54 # endif
55 #else
56 # include <sys/byteorder.h>
57 # ifdef _KERNEL
58 # include <sys/dditypes.h>
59 # endif
60 # include <sys/stream.h>
61 # include <sys/kmem.h>
62 #endif
63 #include <net/if.h>
64 #ifdef sun
65 # include <net/af.h>
66 #endif
67 #include <net/route.h>
68 #include <netinet/in.h>
69 #include <netinet/in_systm.h>
70 #include <netinet/ip.h>
71 #if !defined(linux)
72 # include <netinet/ip_var.h>
73 #endif
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>
87 # if defined(_KERNEL)
88 # ifndef IPFILTER_LKM
89 # include <sys/libkern.h>
90 # include <sys/systm.h>
91 # endif
92 extern struct callout_handle fr_slowtimer_ch;
93 # endif
94 #endif
95 #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000)
96 # include <sys/callout.h>
97 extern struct callout fr_slowtimer_ch;
98 #endif
99 #if defined(__OpenBSD__)
100 # include <sys/timeout.h>
101 extern struct timeout fr_slowtimer_ch;
102 #endif
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 */
117 /* */
118 /* Initialise the hash tables for the fragment cache lookups. */
119 /* ------------------------------------------------------------------------ */
120 int fr_fraginit(ifs)
121 ipf_stack_t *ifs;
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)
133 return -1;
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)
140 return -1;
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)
147 return -1;
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;
160 return 0;
164 /* ------------------------------------------------------------------------ */
165 /* Function: fr_fragunload */
166 /* Returns: Nil */
167 /* Parameters: Nil */
168 /* */
169 /* Free all memory allocated whilst running and from initialisation. */
170 /* ------------------------------------------------------------------------ */
171 void fr_fragunload(ifs)
172 ipf_stack_t *ifs;
174 if (ifs->ifs_fr_frag_init == 1) {
175 fr_fragclear(ifs);
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 */
205 /* */
206 /* Updates ipfr_stats with current information and returns a pointer to it */
207 /* ------------------------------------------------------------------------ */
208 ipfrstat_t *fr_fragstats(ifs)
209 ipf_stack_t *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 */
223 /* */
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)
228 fr_info_t *fin;
229 ipfr_t *frag;
231 u_int idx;
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.
240 #ifdef USE_INET6
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;
249 } else
250 #endif
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;
271 idx = frag->ipfr_p;
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];
281 idx *= 127;
282 idx %= IPFT_SIZE;
284 return idx;
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 */
293 /* */
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)
298 fr_info_t *fin;
299 u_32_t pass;
300 ipfr_t *table[];
302 ipfr_t *fra, frag;
303 u_int idx, off;
304 ipf_stack_t *ifs = fin->fin_ifs;
306 if (ifs->ifs_ipfr_inuse >= ifs->ifs_ipfr_size)
307 return NULL;
309 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
310 return NULL;
312 if (pass & FR_FRSTRICT)
313 if (fin->fin_off != 0)
314 return NULL;
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,
323 IPFR_CMPSZ)) {
324 ifs->ifs_ipfr_stats.ifs_exists++;
325 return NULL;
329 * allocate some memory, if possible, if not, just record that we
330 * failed to do so.
332 KMALLOC(fra, ipfr_t *);
333 if (fra == NULL) {
334 ifs->ifs_ipfr_stats.ifs_nomem++;
335 return NULL;
338 fra->ipfr_rule = fin->fin_fr;
339 if (fra->ipfr_rule != NULL) {
341 frentry_t *fr;
343 fr = fin->fin_fr;
344 MUTEX_ENTER(&fr->fr_lock);
345 fr->fr_ref++;
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;
358 table[idx] = fra;
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;
366 if (off == 0) {
367 fra->ipfr_seen0 = 1;
368 } else {
369 fra->ipfr_seen0 = 0;
371 fra->ipfr_off = off + fin->fin_dlen;
372 fra->ipfr_pass = pass;
373 fra->ipfr_ref = 1;
374 ifs->ifs_ipfr_stats.ifs_new++;
375 ifs->ifs_ipfr_inuse++;
376 return fra;
380 /* ------------------------------------------------------------------------ */
381 /* Function: fr_newfrag */
382 /* Returns: int - 0 == success, -1 == error */
383 /* Parameters: fin(I) - pointer to packet information */
384 /* */
385 /* Add a new entry to the fragment cache table based on the current packet */
386 /* ------------------------------------------------------------------------ */
387 int fr_newfrag(fin, pass)
388 u_32_t pass;
389 fr_info_t *fin;
391 ipfr_t *fra;
392 ipf_stack_t *ifs = fin->fin_ifs;
394 if (ifs->ifs_fr_frag_lock != 0)
395 return -1;
397 WRITE_ENTER(&ifs->ifs_ipf_frag);
398 fra = ipfr_newfrag(fin, pass, ifs->ifs_ipfr_heads);
399 if (fra != NULL) {
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);
408 return fra ? 0 : -1;
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 */
417 /* */
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)
422 fr_info_t *fin;
423 u_32_t pass;
424 nat_t *nat;
426 ipfr_t *fra;
427 ipf_stack_t *ifs = fin->fin_ifs;
429 if (ifs->ifs_fr_frag_lock != 0)
430 return 0;
432 WRITE_ENTER(&ifs->ifs_ipf_natfrag);
433 fra = ipfr_newfrag(fin, pass, ifs->ifs_ipfr_nattab);
434 if (fra != NULL) {
435 fra->ipfr_data = nat;
436 nat->nat_data = fra;
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);
443 return fra ? 0 : -1;
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 */
452 /* */
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)
457 fr_info_t *fin;
458 u_32_t ipid;
460 ipfr_t *fra;
461 ipf_stack_t *ifs = fin->fin_ifs;
463 if (ifs->ifs_fr_frag_lock)
464 return 0;
466 WRITE_ENTER(&ifs->ifs_ipf_ipidfrag);
467 fra = ipfr_newfrag(fin, 0, ifs->ifs_ipfr_ipidtab);
468 if (fra != NULL) {
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);
476 return fra ? 0 : -1;
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 */
486 /* */
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)
491 fr_info_t *fin;
492 ipfr_t *table[];
494 ipfr_t *f, frag;
495 u_int idx;
496 ipf_stack_t *ifs = fin->fin_ifs;
498 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
499 return NULL;
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,
514 IPFR_CMPSZ)) {
515 u_short off;
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);
529 continue;
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;
539 if (f->ipfr_seen0) {
540 if (off == 0) {
541 ATOMIC_INCL(ifs->ifs_ipfr_stats.ifs_retrans0);
542 continue;
544 } else if (off == 0) {
545 f->ipfr_seen0 = 1;
548 if (f != table[idx]) {
549 ipfr_t **fp;
552 * Move fragment info. to the top of the list
553 * to speed up searches. First, delink...
555 fp = f->ipfr_hprev;
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;
565 table[idx] = f;
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)
577 continue;
578 ATOMIC_INCL(ifs->ifs_ipfr_stats.ifs_hits);
579 return f;
581 return NULL;
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 */
590 /* */
591 /* Functional interface for NAT lookups of the NAT fragment cache */
592 /* ------------------------------------------------------------------------ */
593 nat_t *fr_nat_knownfrag(fin)
594 fr_info_t *fin;
596 nat_t *nat;
597 ipfr_t *ipf;
598 ipf_stack_t *ifs = fin->fin_ifs;
600 if (ifs->ifs_fr_frag_lock || !ifs->ifs_ipfr_natlist)
601 return NULL;
602 READ_ENTER(&ifs->ifs_ipf_natfrag);
603 ipf = fr_fraglookup(fin, ifs->ifs_ipfr_nattab);
604 if (ipf != NULL) {
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;
613 } else
614 nat = NULL;
615 RWLOCK_EXIT(&ifs->ifs_ipf_natfrag);
616 return nat;
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 */
625 /* */
626 /* Functional interface for IP ID lookups of the IP ID fragment cache */
627 /* ------------------------------------------------------------------------ */
628 u_32_t fr_ipid_knownfrag(fin)
629 fr_info_t *fin;
631 ipfr_t *ipf;
632 u_32_t id;
633 ipf_stack_t *ifs = fin->fin_ifs;
635 if (ifs->ifs_fr_frag_lock || !ifs->ifs_ipfr_ipidlist)
636 return 0xffffffff;
638 READ_ENTER(&ifs->ifs_ipf_ipidfrag);
639 ipf = fr_fraglookup(fin, ifs->ifs_ipfr_ipidtab);
640 if (ipf != NULL)
641 id = (u_32_t)(uintptr_t)ipf->ipfr_data;
642 else
643 id = 0xffffffff;
644 RWLOCK_EXIT(&ifs->ifs_ipf_ipidfrag);
645 return id;
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 */
655 /* */
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)
661 fr_info_t *fin;
662 u_32_t *passp;
664 frentry_t *fr = NULL;
665 ipfr_t *fra;
666 u_32_t pass, oflx;
667 ipf_stack_t *ifs = fin->fin_ifs;
669 if (ifs->ifs_fr_frag_lock || (ifs->ifs_ipfr_list == NULL))
670 return NULL;
672 READ_ENTER(&ifs->ifs_ipf_frag);
673 oflx = fin->fin_flx;
674 fra = fr_fraglookup(fin, ifs->ifs_ipfr_heads);
675 if (fra != NULL) {
676 fr = fra->ipfr_rule;
677 fin->fin_fr = fr;
678 if (fr != NULL) {
679 pass = fr->fr_flags;
680 if ((pass & FR_LOGFIRST) != 0)
681 pass &= ~(FR_LOGFIRST|FR_LOG);
682 *passp = pass;
685 if (!(oflx & FI_BAD) && (fin->fin_flx & FI_BAD)) {
686 *passp &= ~FR_CMDMASK;
687 *passp |= FR_BLOCK;
688 fr = &ifs->ifs_frblock;
690 RWLOCK_EXIT(&ifs->ifs_ipf_frag);
691 return fr;
695 /* ------------------------------------------------------------------------ */
696 /* Function: fr_forget */
697 /* Returns: Nil */
698 /* Parameters: ptr(I) - pointer to data structure */
699 /* */
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)
704 void *ptr;
705 ipf_stack_t *ifs;
707 ipfr_t *fr;
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 */
719 /* Returns: Nil */
720 /* Parameters: ptr(I) - pointer to data structure */
721 /* */
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)
726 void *ptr;
727 ipf_stack_t *ifs;
729 ipfr_t *fr;
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 */
741 /* Returns: Nil */
742 /* Parameters: fra(I) - pointer to fragment structure to delete */
743 /* tail(IO) - pointer to the pointer to the tail of the frag */
744 /* list */
745 /* */
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;
752 ipf_stack_t *ifs;
754 frentry_t *fr;
756 fr = fra->ipfr_rule;
757 if (fr != NULL)
758 (void)fr_derefrule(&fr, ifs);
760 if (fra->ipfr_next)
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;
766 if (fra->ipfr_hnext)
767 fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev;
768 *fra->ipfr_hprev = fra->ipfr_hnext;
770 if (fra->ipfr_ref <= 0)
771 KFREE(fra);
775 /* ------------------------------------------------------------------------ */
776 /* Function: fr_fragclear */
777 /* Returns: Nil */
778 /* Parameters: Nil */
779 /* */
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)
784 ipf_stack_t *ifs;
786 ipfr_t *fra;
787 nat_t *nat;
789 WRITE_ENTER(&ifs->ifs_ipf_frag);
790 while ((fra = ifs->ifs_ipfr_list) != NULL) {
791 fra->ipfr_ref--;
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;
801 if (nat != NULL) {
802 if (nat->nat_data == fra)
803 nat->nat_data = NULL;
805 fra->ipfr_ref--;
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 */
816 /* Returns: Nil */
817 /* Parameters: Nil */
818 /* */
819 /* Expire entries in the fragment cache table that have been there too long */
820 /* ------------------------------------------------------------------------ */
821 void fr_fragexpire(ifs)
822 ipf_stack_t *ifs;
824 ipfr_t **fp, *fra;
825 nat_t *nat;
826 SPL_INT(s);
828 if (ifs->ifs_fr_frag_lock)
829 return;
831 SPL_NET(s);
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
836 * ifs_fr_ticks.
838 for (fp = &ifs->ifs_ipfr_list; ((fra = *fp) != NULL); ) {
839 if (fra->ipfr_ttl > ifs->ifs_fr_ticks)
840 break;
841 fra->ipfr_ref--;
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)
851 break;
852 fra->ipfr_ref--;
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)
870 break;
871 nat = fra->ipfr_data;
872 if (nat != NULL) {
873 if (nat->nat_data == fra)
874 nat->nat_data = NULL;
876 fra->ipfr_ref--;
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);
883 SPL_X(s);
887 /* ------------------------------------------------------------------------ */
888 /* Function: fr_slowtimer */
889 /* Returns: Nil */
890 /* Parameters: Nil */
891 /* */
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))
899 # else
900 int fr_slowtimer(void *arg)
901 # endif
903 ipf_stack_t *ifs = arg;
905 READ_ENTER(&ifs->ifs_ipf_global);
907 fr_fragexpire(ifs);
908 fr_timeoutstate(ifs);
909 fr_natexpire(ifs);
910 fr_authexpire(ifs);
911 ifs->ifs_fr_ticks++;
912 if (ifs->ifs_fr_running <= 0)
913 goto done;
914 # ifdef _KERNEL
915 # if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000)
916 callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL);
917 # else
918 # if defined(__OpenBSD__)
919 timeout_add(&fr_slowtimer_ch, hz/2);
920 # else
921 # if (__FreeBSD_version >= 300000)
922 fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2);
923 # else
924 # ifdef linux
926 # else
927 timeout(fr_slowtimer, NULL, hz/2);
928 # endif
929 # endif /* FreeBSD */
930 # endif /* OpenBSD */
931 # endif /* NetBSD */
932 # endif
933 done:
934 RWLOCK_EXIT(&ifs->ifs_ipf_global);
935 # if (BSD < 199103) || !defined(_KERNEL)
936 return 0;
937 # endif
939 #endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */
941 /*ARGSUSED*/
942 int fr_nextfrag(token, itp, top, tail, lock, ifs)
943 ipftoken_t *token;
944 ipfgeniter_t *itp;
945 ipfr_t **top, ***tail;
946 ipfrwlock_t *lock;
947 ipf_stack_t *ifs;
949 ipfr_t *frag, *next, zero;
950 int error = 0;
952 READ_ENTER(lock);
955 * Retrieve "previous" entry from token and find the next entry.
957 frag = token->ipt_data;
958 if (frag == NULL)
959 next = *top;
960 else
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.
967 if (next != NULL) {
968 ATOMIC_INC(next->ipfr_ref);
969 token->ipt_data = next;
970 } else {
971 bzero(&zero, sizeof(zero));
972 next = &zero;
973 token->ipt_data = NULL;
977 * Now that we have ref, it's save to give up lock.
979 RWLOCK_EXIT(lock);
982 * Copy out data and clean up references and token as needed.
984 error = COPYOUT(next, itp->igi_data, sizeof(*next));
985 if (error != 0)
986 error = EFAULT;
987 if (token->ipt_data == NULL) {
988 ipf_freetoken(token, ifs);
989 } else {
990 if (frag != NULL)
991 fr_fragderef(&frag, lock, ifs);
992 if (next->ipfr_next == NULL)
993 ipf_freetoken(token, ifs);
995 return error;
999 void fr_fragderef(frp, lock, ifs)
1000 ipfr_t **frp;
1001 ipfrwlock_t *lock;
1002 ipf_stack_t *ifs;
1004 ipfr_t *fra;
1006 fra = *frp;
1007 *frp = NULL;
1009 WRITE_ENTER(lock);
1010 fra->ipfr_ref--;
1011 if (fra->ipfr_ref <= 0) {
1012 KFREE(fra);
1013 ifs->ifs_ipfr_stats.ifs_expire++;
1014 ifs->ifs_ipfr_inuse--;
1016 RWLOCK_EXIT(lock);