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