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