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