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