xref: /freebsd/sys/netpfil/ipfilter/netinet/ip_frag.c (revision 5d3e7166)
1 /*	$FreeBSD$	*/
2 
3 /*
4  * Copyright (C) 2012 by Darren Reed.
5  *
6  * See the IPFILTER.LICENCE file for details on licencing.
7  */
8 #if defined(KERNEL) || defined(_KERNEL)
9 # undef KERNEL
10 # undef _KERNEL
11 # define        KERNEL	1
12 # define        _KERNEL	1
13 #endif
14 #include <sys/errno.h>
15 #include <sys/types.h>
16 #include <sys/param.h>
17 #include <sys/time.h>
18 #include <sys/file.h>
19 #if !defined(_KERNEL)
20 # include <stdio.h>
21 # include <string.h>
22 # include <stdlib.h>
23 # define _KERNEL
24 # include <sys/uio.h>
25 # undef _KERNEL
26 #endif
27 #if defined(_KERNEL) && defined(__FreeBSD__)
28 # include <sys/filio.h>
29 # include <sys/fcntl.h>
30 #else
31 # include <sys/ioctl.h>
32 #endif
33 # include <sys/protosw.h>
34 #include <sys/socket.h>
35 #if defined(_KERNEL)
36 # include <sys/systm.h>
37 # if !defined(__SVR4)
38 #  include <sys/mbuf.h>
39 # endif
40 #endif
41 #if !defined(__SVR4)
42 # if defined(_KERNEL)
43 #  include <sys/kernel.h>
44 # endif
45 #else
46 # include <sys/byteorder.h>
47 # ifdef _KERNEL
48 #  include <sys/dditypes.h>
49 # endif
50 # include <sys/stream.h>
51 # include <sys/kmem.h>
52 #endif
53 #include <net/if.h>
54 #ifdef sun
55 # include <net/af.h>
56 #endif
57 #include <netinet/in.h>
58 #include <netinet/in_systm.h>
59 #include <netinet/ip.h>
60 # include <netinet/ip_var.h>
61 #include <netinet/tcp.h>
62 #include <netinet/udp.h>
63 #include <netinet/ip_icmp.h>
64 #include "netinet/ip_compat.h"
65 #include <netinet/tcpip.h>
66 #include "netinet/ip_fil.h"
67 #include "netinet/ip_nat.h"
68 #include "netinet/ip_frag.h"
69 #include "netinet/ip_state.h"
70 #include "netinet/ip_auth.h"
71 #include "netinet/ip_lookup.h"
72 #include "netinet/ip_proxy.h"
73 #include "netinet/ip_sync.h"
74 /* END OF INCLUDES */
75 
76 #if !defined(lint)
77 static const char sccsid[] = "@(#)ip_frag.c	1.11 3/24/96 (C) 1993-2000 Darren Reed";
78 static const char rcsid[] = "@(#)$FreeBSD$";
79 /* static const char rcsid[] = "@(#)$Id: ip_frag.c,v 2.77.2.12 2007/09/20 12:51:51 darrenr Exp $"; */
80 #endif
81 
82 
83 #ifdef USE_MUTEXES
84 static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *,
85 				  fr_info_t *, u_32_t, ipfr_t **,
86 				  ipfrwlock_t *);
87 static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **, ipfrwlock_t *);
88 static void ipf_frag_deref(void *, ipfr_t **, ipfrwlock_t *);
89 static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *,
90 			      ipfr_t **, ipfrwlock_t *);
91 #else
92 static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *,
93 				  fr_info_t *, u_32_t, ipfr_t **);
94 static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **);
95 static void ipf_frag_deref(void *, ipfr_t **);
96 static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *,
97 			      ipfr_t **);
98 #endif
99 static void ipf_frag_delete(ipf_main_softc_t *, ipfr_t *, ipfr_t ***);
100 static void ipf_frag_free(ipf_frag_softc_t *, ipfr_t *);
101 
102 static frentry_t ipfr_block;
103 
104 static ipftuneable_t ipf_frag_tuneables[] = {
105 	{ { (void *)offsetof(ipf_frag_softc_t, ipfr_size) },
106 		"frag_size",		1,	0x7fffffff,
107 		stsizeof(ipf_frag_softc_t, ipfr_size),
108 		IPFT_WRDISABLED,	NULL,	NULL },
109 	{ { (void *)offsetof(ipf_frag_softc_t, ipfr_ttl) },
110 		"frag_ttl",		1,	0x7fffffff,
111 		stsizeof(ipf_frag_softc_t, ipfr_ttl),
112 		0,			NULL,	NULL },
113 	{ { NULL },
114 		NULL,			0,	0,
115 		0,
116 		0,			NULL,	NULL }
117 };
118 
119 #define	FBUMP(x)	softf->ipfr_stats.x++
120 #define	FBUMPD(x)	do { softf->ipfr_stats.x++; DT(x); } while (0)
121 
122 
123 /* ------------------------------------------------------------------------ */
124 /* Function:    ipf_frag_main_load                                          */
125 /* Returns:     int - 0 == success, -1 == error                             */
126 /* Parameters:  Nil                                                         */
127 /*                                                                          */
128 /* Initialise the filter rule associted with blocked packets - everyone can */
129 /* use it.                                                                  */
130 /* ------------------------------------------------------------------------ */
131 int
132 ipf_frag_main_load(void)
133 {
134 	bzero((char *)&ipfr_block, sizeof(ipfr_block));
135 	ipfr_block.fr_flags = FR_BLOCK|FR_QUICK;
136 	ipfr_block.fr_ref = 1;
137 
138 	return (0);
139 }
140 
141 
142 /* ------------------------------------------------------------------------ */
143 /* Function:    ipf_frag_main_unload                                        */
144 /* Returns:     int - 0 == success, -1 == error                             */
145 /* Parameters:  Nil                                                         */
146 /*                                                                          */
147 /* A null-op function that exists as a placeholder so that the flow in      */
148 /* other functions is obvious.                                              */
149 /* ------------------------------------------------------------------------ */
150 int
151 ipf_frag_main_unload(void)
152 {
153 	return (0);
154 }
155 
156 
157 /* ------------------------------------------------------------------------ */
158 /* Function:    ipf_frag_soft_create                                        */
159 /* Returns:     void *   - NULL = failure, else pointer to local context    */
160 /* Parameters:  softc(I) - pointer to soft context main structure           */
161 /*                                                                          */
162 /* Allocate a new soft context structure to track fragment related info.    */
163 /* ------------------------------------------------------------------------ */
164 /*ARGSUSED*/
165 void *
166 ipf_frag_soft_create(ipf_main_softc_t *softc)
167 {
168 	ipf_frag_softc_t *softf;
169 
170 	KMALLOC(softf, ipf_frag_softc_t *);
171 	if (softf == NULL)
172 		return (NULL);
173 
174 	bzero((char *)softf, sizeof(*softf));
175 
176 	RWLOCK_INIT(&softf->ipfr_ipidfrag, "frag ipid lock");
177 	RWLOCK_INIT(&softf->ipfr_frag, "ipf fragment rwlock");
178 	RWLOCK_INIT(&softf->ipfr_natfrag, "ipf NAT fragment rwlock");
179 
180 	softf->ipf_frag_tune = ipf_tune_array_copy(softf,
181 						   sizeof(ipf_frag_tuneables),
182 						   ipf_frag_tuneables);
183 	if (softf->ipf_frag_tune == NULL) {
184 		ipf_frag_soft_destroy(softc, softf);
185 		return (NULL);
186 	}
187 	if (ipf_tune_array_link(softc, softf->ipf_frag_tune) == -1) {
188 		ipf_frag_soft_destroy(softc, softf);
189 		return (NULL);
190 	}
191 
192 	softf->ipfr_size = IPFT_SIZE;
193 	softf->ipfr_ttl = IPF_TTLVAL(60);
194 	softf->ipfr_lock = 1;
195 	softf->ipfr_tail = &softf->ipfr_list;
196 	softf->ipfr_nattail = &softf->ipfr_natlist;
197 	softf->ipfr_ipidtail = &softf->ipfr_ipidlist;
198 
199 	return (softf);
200 }
201 
202 
203 /* ------------------------------------------------------------------------ */
204 /* Function:    ipf_frag_soft_destroy                                       */
205 /* Returns:     Nil                                                         */
206 /* Parameters:  softc(I) - pointer to soft context main structure           */
207 /*              arg(I)   - pointer to local context to use                  */
208 /*                                                                          */
209 /* Initialise the hash tables for the fragment cache lookups.               */
210 /* ------------------------------------------------------------------------ */
211 void
212 ipf_frag_soft_destroy(ipf_main_softc_t *softc, void *arg)
213 {
214 	ipf_frag_softc_t *softf = arg;
215 
216 	RW_DESTROY(&softf->ipfr_ipidfrag);
217 	RW_DESTROY(&softf->ipfr_frag);
218 	RW_DESTROY(&softf->ipfr_natfrag);
219 
220 	if (softf->ipf_frag_tune != NULL) {
221 		ipf_tune_array_unlink(softc, softf->ipf_frag_tune);
222 		KFREES(softf->ipf_frag_tune, sizeof(ipf_frag_tuneables));
223 		softf->ipf_frag_tune = NULL;
224 	}
225 
226 	KFREE(softf);
227 }
228 
229 
230 /* ------------------------------------------------------------------------ */
231 /* Function:    ipf_frag_soft_init                                          */
232 /* Returns:     int      - 0 == success, -1 == error                        */
233 /* Parameters:  softc(I) - pointer to soft context main structure           */
234 /*              arg(I)   - pointer to local context to use                  */
235 /*                                                                          */
236 /* Initialise the hash tables for the fragment cache lookups.               */
237 /* ------------------------------------------------------------------------ */
238 /*ARGSUSED*/
239 int
240 ipf_frag_soft_init(ipf_main_softc_t *softc, void *arg)
241 {
242 	ipf_frag_softc_t *softf = arg;
243 
244 	KMALLOCS(softf->ipfr_heads, ipfr_t **,
245 		 softf->ipfr_size * sizeof(ipfr_t *));
246 	if (softf->ipfr_heads == NULL)
247 		return (-1);
248 
249 	bzero((char *)softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *));
250 
251 	KMALLOCS(softf->ipfr_nattab, ipfr_t **,
252 		 softf->ipfr_size * sizeof(ipfr_t *));
253 	if (softf->ipfr_nattab == NULL)
254 		return (-2);
255 
256 	bzero((char *)softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *));
257 
258 	KMALLOCS(softf->ipfr_ipidtab, ipfr_t **,
259 		 softf->ipfr_size * sizeof(ipfr_t *));
260 	if (softf->ipfr_ipidtab == NULL)
261 		return (-3);
262 
263 	bzero((char *)softf->ipfr_ipidtab,
264 	      softf->ipfr_size * sizeof(ipfr_t *));
265 
266 	softf->ipfr_lock = 0;
267 	softf->ipfr_inited = 1;
268 
269 	return (0);
270 }
271 
272 
273 /* ------------------------------------------------------------------------ */
274 /* Function:    ipf_frag_soft_fini                                          */
275 /* Returns:     int      - 0 == success, -1 == error                        */
276 /* Parameters:  softc(I) - pointer to soft context main structure           */
277 /*              arg(I)   - pointer to local context to use                  */
278 /*                                                                          */
279 /* Free all memory allocated whilst running and from initialisation.        */
280 /* ------------------------------------------------------------------------ */
281 int
282 ipf_frag_soft_fini(ipf_main_softc_t *softc, void *arg)
283 {
284 	ipf_frag_softc_t *softf = arg;
285 
286 	softf->ipfr_lock = 1;
287 
288 	if (softf->ipfr_inited == 1) {
289 		ipf_frag_clear(softc);
290 
291 		softf->ipfr_inited = 0;
292 	}
293 
294 	if (softf->ipfr_heads != NULL)
295 		KFREES(softf->ipfr_heads,
296 		       softf->ipfr_size * sizeof(ipfr_t *));
297 	softf->ipfr_heads = NULL;
298 
299 	if (softf->ipfr_nattab != NULL)
300 		KFREES(softf->ipfr_nattab,
301 		       softf->ipfr_size * sizeof(ipfr_t *));
302 	softf->ipfr_nattab = NULL;
303 
304 	if (softf->ipfr_ipidtab != NULL)
305 		KFREES(softf->ipfr_ipidtab,
306 		       softf->ipfr_size * sizeof(ipfr_t *));
307 	softf->ipfr_ipidtab = NULL;
308 
309 	return (0);
310 }
311 
312 
313 /* ------------------------------------------------------------------------ */
314 /* Function:    ipf_frag_set_lock                                           */
315 /* Returns:     Nil                                                         */
316 /* Parameters:  arg(I) - pointer to local context to use                    */
317 /*              tmp(I) - new value for lock                                 */
318 /*                                                                          */
319 /* Stub function that allows for external manipulation of ipfr_lock         */
320 /* ------------------------------------------------------------------------ */
321 void
322 ipf_frag_setlock(void *arg, int tmp)
323 {
324 	ipf_frag_softc_t *softf = arg;
325 
326 	softf->ipfr_lock = tmp;
327 }
328 
329 
330 /* ------------------------------------------------------------------------ */
331 /* Function:    ipf_frag_stats                                              */
332 /* Returns:     ipfrstat_t* - pointer to struct with current frag stats     */
333 /* Parameters:  arg(I) - pointer to local context to use                    */
334 /*                                                                          */
335 /* Updates ipfr_stats with current information and returns a pointer to it  */
336 /* ------------------------------------------------------------------------ */
337 ipfrstat_t *
338 ipf_frag_stats(void *arg)
339 {
340 	ipf_frag_softc_t *softf = arg;
341 
342 	softf->ipfr_stats.ifs_table = softf->ipfr_heads;
343 	softf->ipfr_stats.ifs_nattab = softf->ipfr_nattab;
344 	return (&softf->ipfr_stats);
345 }
346 
347 
348 /* ------------------------------------------------------------------------ */
349 /* Function:    ipfr_frag_new                                               */
350 /* Returns:     ipfr_t * - pointer to fragment cache state info or NULL     */
351 /* Parameters:  fin(I)   - pointer to packet information                    */
352 /*              table(I) - pointer to frag table to add to                  */
353 /*              lock(I)  - pointer to lock to get a write hold of           */
354 /*                                                                          */
355 /* Add a new entry to the fragment cache, registering it as having come     */
356 /* through this box, with the result of the filter operation.               */
357 /*                                                                          */
358 /* If this function succeeds, it returns with a write lock held on "lock".  */
359 /* If it fails, no lock is held on return.                                  */
360 /* ------------------------------------------------------------------------ */
361 static ipfr_t *
362 ipfr_frag_new(ipf_main_softc_t *softc, ipf_frag_softc_t *softf,
363 	fr_info_t *fin, u_32_t pass, ipfr_t *table[]
364 #ifdef USE_MUTEXES
365 , ipfrwlock_t *lock
366 #endif
367 )
368 {
369 	ipfr_t *fra, frag, *fran;
370 	u_int idx, off;
371 	frentry_t *fr;
372 
373 	if (softf->ipfr_stats.ifs_inuse >= softf->ipfr_size) {
374 		FBUMPD(ifs_maximum);
375 		return (NULL);
376 	}
377 
378 	if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) {
379 		FBUMPD(ifs_newbad);
380 		return (NULL);
381 	}
382 
383 	if (pass & FR_FRSTRICT) {
384 		if (fin->fin_off != 0) {
385 			FBUMPD(ifs_newrestrictnot0);
386 			return (NULL);
387 		}
388 	}
389 
390 	memset(&frag, 0, sizeof(frag));
391 	frag.ipfr_v = fin->fin_v;
392 	idx = fin->fin_v;
393 	frag.ipfr_p = fin->fin_p;
394 	idx += fin->fin_p;
395 	frag.ipfr_id = fin->fin_id;
396 	idx += fin->fin_id;
397 	frag.ipfr_source = fin->fin_fi.fi_src;
398 	idx += frag.ipfr_src.s_addr;
399 	frag.ipfr_dest = fin->fin_fi.fi_dst;
400 	idx += frag.ipfr_dst.s_addr;
401 	frag.ipfr_ifp = fin->fin_ifp;
402 	idx *= 127;
403 	idx %= softf->ipfr_size;
404 
405 	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
406 	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
407 	frag.ipfr_auth = fin->fin_fi.fi_auth;
408 
409 	off = fin->fin_off >> 3;
410 	if (off == 0) {
411 		char *ptr;
412 		int end;
413 
414 #ifdef USE_INET6
415 		if (fin->fin_v == 6) {
416 
417 			ptr = (char *)fin->fin_fraghdr +
418 			      sizeof(struct ip6_frag);
419 		} else
420 #endif
421 		{
422 			ptr = fin->fin_dp;
423 		}
424 		end = fin->fin_plen - (ptr - (char *)fin->fin_ip);
425 		frag.ipfr_firstend = end >> 3;
426 	} else {
427 		frag.ipfr_firstend = 0;
428 	}
429 
430 	/*
431 	 * allocate some memory, if possible, if not, just record that we
432 	 * failed to do so.
433 	 */
434 	KMALLOC(fran, ipfr_t *);
435 	if (fran == NULL) {
436 		FBUMPD(ifs_nomem);
437 		return (NULL);
438 	}
439 	memset(fran, 0, sizeof(*fran));
440 
441 	WRITE_ENTER(lock);
442 
443 	/*
444 	 * first, make sure it isn't already there...
445 	 */
446 	for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext)
447 		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp,
448 			  IPFR_CMPSZ)) {
449 			RWLOCK_EXIT(lock);
450 			FBUMPD(ifs_exists);
451 			KFREE(fran);
452 			return (NULL);
453 		}
454 
455 	fra = fran;
456 	fran = NULL;
457 	fr = fin->fin_fr;
458 	fra->ipfr_rule = fr;
459 	if (fr != NULL) {
460 		MUTEX_ENTER(&fr->fr_lock);
461 		fr->fr_ref++;
462 		MUTEX_EXIT(&fr->fr_lock);
463 	}
464 
465 	/*
466 	 * Insert the fragment into the fragment table, copy the struct used
467 	 * in the search using bcopy rather than reassign each field.
468 	 * Set the ttl to the default.
469 	 */
470 	if ((fra->ipfr_hnext = table[idx]) != NULL)
471 		table[idx]->ipfr_hprev = &fra->ipfr_hnext;
472 	fra->ipfr_hprev = table + idx;
473 	fra->ipfr_data = NULL;
474 	table[idx] = fra;
475 	bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ);
476 	fra->ipfr_v = fin->fin_v;
477 	fra->ipfr_p = fin->fin_p;
478 	fra->ipfr_ttl = softc->ipf_ticks + softf->ipfr_ttl;
479 	fra->ipfr_firstend = frag.ipfr_firstend;
480 
481 	/*
482 	 * Compute the offset of the expected start of the next packet.
483 	 */
484 	if (off == 0)
485 		fra->ipfr_seen0 = 1;
486 	fra->ipfr_off = off + (fin->fin_dlen >> 3);
487 	fra->ipfr_pass = pass;
488 	fra->ipfr_ref = 1;
489 	fra->ipfr_pkts = 1;
490 	fra->ipfr_bytes = fin->fin_plen;
491 	FBUMP(ifs_inuse);
492 	FBUMP(ifs_new);
493 	return (fra);
494 }
495 
496 
497 /* ------------------------------------------------------------------------ */
498 /* Function:    ipf_frag_new                                                */
499 /* Returns:     int - 0 == success, -1 == error                             */
500 /* Parameters:  fin(I)  - pointer to packet information                     */
501 /*                                                                          */
502 /* Add a new entry to the fragment cache table based on the current packet  */
503 /* ------------------------------------------------------------------------ */
504 int
505 ipf_frag_new(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass)
506 {
507 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
508 	ipfr_t	*fra;
509 
510 	if (softf->ipfr_lock != 0)
511 		return (-1);
512 
513 #ifdef USE_MUTEXES
514 	fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads, &softc->ipf_frag);
515 #else
516 	fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads);
517 #endif
518 	if (fra != NULL) {
519 		*softf->ipfr_tail = fra;
520 		fra->ipfr_prev = softf->ipfr_tail;
521 		softf->ipfr_tail = &fra->ipfr_next;
522 		fra->ipfr_next = NULL;
523 		RWLOCK_EXIT(&softc->ipf_frag);
524 	}
525 	return (fra ? 0 : -1);
526 }
527 
528 
529 /* ------------------------------------------------------------------------ */
530 /* Function:    ipf_frag_natnew                                             */
531 /* Returns:     int - 0 == success, -1 == error                             */
532 /* Parameters:  fin(I)  - pointer to packet information                     */
533 /*              nat(I)  - pointer to NAT structure                          */
534 /*                                                                          */
535 /* Create a new NAT fragment cache entry based on the current packet and    */
536 /* the NAT structure for this "session".                                    */
537 /* ------------------------------------------------------------------------ */
538 int
539 ipf_frag_natnew(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass,
540 	nat_t *nat)
541 {
542 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
543 	ipfr_t	*fra;
544 
545 	if (softf->ipfr_lock != 0)
546 		return (0);
547 
548 #ifdef USE_MUTEXES
549 	fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab,
550 			    &softf->ipfr_natfrag);
551 #else
552 	fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab);
553 #endif
554 	if (fra != NULL) {
555 		fra->ipfr_data = nat;
556 		nat->nat_data = fra;
557 		*softf->ipfr_nattail = fra;
558 		fra->ipfr_prev = softf->ipfr_nattail;
559 		softf->ipfr_nattail = &fra->ipfr_next;
560 		fra->ipfr_next = NULL;
561 		RWLOCK_EXIT(&softf->ipfr_natfrag);
562 		return (0);
563 	}
564 	return (-1);
565 }
566 
567 
568 /* ------------------------------------------------------------------------ */
569 /* Function:    ipf_frag_ipidnew                                            */
570 /* Returns:     int - 0 == success, -1 == error                             */
571 /* Parameters:  fin(I)  - pointer to packet information                     */
572 /*              ipid(I) - new IP ID for this fragmented packet              */
573 /*                                                                          */
574 /* Create a new fragment cache entry for this packet and store, as a data   */
575 /* pointer, the new IP ID value.                                            */
576 /* ------------------------------------------------------------------------ */
577 int
578 ipf_frag_ipidnew(fr_info_t *fin, u_32_t ipid)
579 {
580 	ipf_main_softc_t *softc = fin->fin_main_soft;
581 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
582 	ipfr_t	*fra;
583 
584 	if (softf->ipfr_lock)
585 		return (0);
586 
587 #ifdef USE_MUTEXES
588 	fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag);
589 #else
590 	fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab);
591 #endif
592 	if (fra != NULL) {
593 		fra->ipfr_data = (void *)(intptr_t)ipid;
594 		*softf->ipfr_ipidtail = fra;
595 		fra->ipfr_prev = softf->ipfr_ipidtail;
596 		softf->ipfr_ipidtail = &fra->ipfr_next;
597 		fra->ipfr_next = NULL;
598 		RWLOCK_EXIT(&softf->ipfr_ipidfrag);
599 	}
600 	return (fra ? 0 : -1);
601 }
602 
603 
604 /* ------------------------------------------------------------------------ */
605 /* Function:    ipf_frag_lookup                                             */
606 /* Returns:     ipfr_t * - pointer to ipfr_t structure if there's a         */
607 /*                         matching entry in the frag table, else NULL      */
608 /* Parameters:  fin(I)   - pointer to packet information                    */
609 /*              table(I) - pointer to fragment cache table to search        */
610 /*                                                                          */
611 /* Check the fragment cache to see if there is already a record of this     */
612 /* packet with its filter result known.                                     */
613 /*                                                                          */
614 /* If this function succeeds, it returns with a write lock held on "lock".  */
615 /* If it fails, no lock is held on return.                                  */
616 /* ------------------------------------------------------------------------ */
617 static ipfr_t *
618 ipf_frag_lookup(ipf_main_softc_t *softc, ipf_frag_softc_t *softf,
619 	fr_info_t *fin, ipfr_t *table[]
620 #ifdef USE_MUTEXES
621 , ipfrwlock_t *lock
622 #endif
623 )
624 {
625 	ipfr_t *f, frag;
626 	u_int idx;
627 
628 	/*
629 	 * We don't want to let short packets match because they could be
630 	 * compromising the security of other rules that want to match on
631 	 * layer 4 fields (and can't because they have been fragmented off.)
632 	 * Why do this check here?  The counter acts as an indicator of this
633 	 * kind of attack, whereas if it was elsewhere, it wouldn't know if
634 	 * other matching packets had been seen.
635 	 */
636 	if (fin->fin_flx & FI_SHORT) {
637 		FBUMPD(ifs_short);
638 		return (NULL);
639 	}
640 
641 	if ((fin->fin_flx & FI_BAD) != 0) {
642 		FBUMPD(ifs_bad);
643 		return (NULL);
644 	}
645 
646 	/*
647 	 * For fragments, we record protocol, packet id, TOS and both IP#'s
648 	 * (these should all be the same for all fragments of a packet).
649 	 *
650 	 * build up a hash value to index the table with.
651 	 */
652 	memset(&frag, 0, sizeof(frag));
653 	frag.ipfr_v = fin->fin_v;
654 	idx = fin->fin_v;
655 	frag.ipfr_p = fin->fin_p;
656 	idx += fin->fin_p;
657 	frag.ipfr_id = fin->fin_id;
658 	idx += fin->fin_id;
659 	frag.ipfr_source = fin->fin_fi.fi_src;
660 	idx += frag.ipfr_src.s_addr;
661 	frag.ipfr_dest = fin->fin_fi.fi_dst;
662 	idx += frag.ipfr_dst.s_addr;
663 	frag.ipfr_ifp = fin->fin_ifp;
664 	idx *= 127;
665 	idx %= softf->ipfr_size;
666 
667 	frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
668 	frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
669 	frag.ipfr_auth = fin->fin_fi.fi_auth;
670 
671 	READ_ENTER(lock);
672 
673 	/*
674 	 * check the table, careful to only compare the right amount of data
675 	 */
676 	for (f = table[idx]; f; f = f->ipfr_hnext) {
677 		if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp,
678 			  IPFR_CMPSZ)) {
679 			u_short	off;
680 
681 			/*
682 			 * XXX - We really need to be guarding against the
683 			 * retransmission of (src,dst,id,offset-range) here
684 			 * because a fragmented packet is never resent with
685 			 * the same IP ID# (or shouldn't).
686 			 */
687 			off = fin->fin_off >> 3;
688 			if (f->ipfr_seen0) {
689 				if (off == 0) {
690 					FBUMPD(ifs_retrans0);
691 					continue;
692 				}
693 
694 				/*
695 				 * Case 3. See comment for frpr_fragment6.
696 				 */
697 				if ((f->ipfr_firstend != 0) &&
698 				    (off < f->ipfr_firstend)) {
699 					FBUMP(ifs_overlap);
700 					DT2(ifs_overlap, u_short, off,
701 					    ipfr_t *, f);
702 					DT3(ipf_fi_bad_ifs_overlap, fr_info_t *, fin, u_short, off,
703 					    ipfr_t *, f);
704 					fin->fin_flx |= FI_BAD;
705 					break;
706 				}
707 			} else if (off == 0)
708 				f->ipfr_seen0 = 1;
709 
710 			if (f != table[idx] && MUTEX_TRY_UPGRADE(lock)) {
711 				ipfr_t **fp;
712 
713 				/*
714 				 * Move fragment info. to the top of the list
715 				 * to speed up searches.  First, delink...
716 				 */
717 				fp = f->ipfr_hprev;
718 				(*fp) = f->ipfr_hnext;
719 				if (f->ipfr_hnext != NULL)
720 					f->ipfr_hnext->ipfr_hprev = fp;
721 				/*
722 				 * Then put back at the top of the chain.
723 				 */
724 				f->ipfr_hnext = table[idx];
725 				table[idx]->ipfr_hprev = &f->ipfr_hnext;
726 				f->ipfr_hprev = table + idx;
727 				table[idx] = f;
728 				MUTEX_DOWNGRADE(lock);
729 			}
730 
731 			/*
732 			 * If we've follwed the fragments, and this is the
733 			 * last (in order), shrink expiration time.
734 			 */
735 			if (off == f->ipfr_off) {
736 				f->ipfr_off = (fin->fin_dlen >> 3) + off;
737 
738 				/*
739 				 * Well, we could shrink the expiration time
740 				 * but only if every fragment has been seen
741 				 * in order upto this, the last. ipfr_badorder
742 				 * is used here to count those out of order
743 				 * and if it equals 0 when we get to the last
744 				 * fragment then we can assume all of the
745 				 * fragments have been seen and in order.
746 				 */
747 #if 0
748 				/*
749 				 * Doing this properly requires moving it to
750 				 * the head of the list which is infesible.
751 				 */
752 				if ((more == 0) && (f->ipfr_badorder == 0))
753 					f->ipfr_ttl = softc->ipf_ticks + 1;
754 #endif
755 			} else {
756 				f->ipfr_badorder++;
757 				FBUMPD(ifs_unordered);
758 				if (f->ipfr_pass & FR_FRSTRICT) {
759 					FBUMPD(ifs_strict);
760 					continue;
761 				}
762 			}
763 			f->ipfr_pkts++;
764 			f->ipfr_bytes += fin->fin_plen;
765 			FBUMP(ifs_hits);
766 			return (f);
767 		}
768 	}
769 
770 	RWLOCK_EXIT(lock);
771 	FBUMP(ifs_miss);
772 	return (NULL);
773 }
774 
775 
776 /* ------------------------------------------------------------------------ */
777 /* Function:    ipf_frag_natknown                                           */
778 /* Returns:     nat_t* - pointer to 'parent' NAT structure if frag table    */
779 /*                       match found, else NULL                             */
780 /* Parameters:  fin(I)  - pointer to packet information                     */
781 /*                                                                          */
782 /* Functional interface for NAT lookups of the NAT fragment cache           */
783 /* ------------------------------------------------------------------------ */
784 nat_t *
785 ipf_frag_natknown(fr_info_t *fin)
786 {
787 	ipf_main_softc_t *softc = fin->fin_main_soft;
788 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
789 	nat_t	*nat;
790 	ipfr_t	*ipf;
791 
792 	if ((softf->ipfr_lock) || !softf->ipfr_natlist)
793 		return (NULL);
794 #ifdef USE_MUTEXES
795 	ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab,
796 			      &softf->ipfr_natfrag);
797 #else
798 	ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab);
799 #endif
800 	if (ipf != NULL) {
801 		nat = ipf->ipfr_data;
802 		/*
803 		 * This is the last fragment for this packet.
804 		 */
805 		if ((ipf->ipfr_ttl == softc->ipf_ticks + 1) && (nat != NULL)) {
806 			nat->nat_data = NULL;
807 			ipf->ipfr_data = NULL;
808 		}
809 		RWLOCK_EXIT(&softf->ipfr_natfrag);
810 	} else
811 		nat = NULL;
812 	return (nat);
813 }
814 
815 
816 /* ------------------------------------------------------------------------ */
817 /* Function:    ipf_frag_ipidknown                                          */
818 /* Returns:     u_32_t - IPv4 ID for this packet if match found, else       */
819 /*                       return 0xfffffff to indicate no match.             */
820 /* Parameters:  fin(I) - pointer to packet information                      */
821 /*                                                                          */
822 /* Functional interface for IP ID lookups of the IP ID fragment cache       */
823 /* ------------------------------------------------------------------------ */
824 u_32_t
825 ipf_frag_ipidknown(fr_info_t *fin)
826 {
827 	ipf_main_softc_t *softc = fin->fin_main_soft;
828 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
829 	ipfr_t	*ipf;
830 	u_32_t	id;
831 
832 	if (softf->ipfr_lock || !softf->ipfr_ipidlist)
833 		return (0xffffffff);
834 
835 #ifdef USE_MUTEXES
836 	ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab,
837 			      &softf->ipfr_ipidfrag);
838 #else
839 	ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab);
840 #endif
841 	if (ipf != NULL) {
842 		id = (u_32_t)(intptr_t)ipf->ipfr_data;
843 		RWLOCK_EXIT(&softf->ipfr_ipidfrag);
844 	} else
845 		id = 0xffffffff;
846 	return (id);
847 }
848 
849 
850 /* ------------------------------------------------------------------------ */
851 /* Function:    ipf_frag_known                                              */
852 /* Returns:     frentry_t* - pointer to filter rule if a match is found in  */
853 /*                           the frag cache table, else NULL.               */
854 /* Parameters:  fin(I)   - pointer to packet information                    */
855 /*              passp(O) - pointer to where to store rule flags resturned   */
856 /*                                                                          */
857 /* Functional interface for normal lookups of the fragment cache.  If a     */
858 /* match is found, return the rule pointer and flags from the rule, except  */
859 /* that if FR_LOGFIRST is set, reset FR_LOG.                                */
860 /* ------------------------------------------------------------------------ */
861 frentry_t *
862 ipf_frag_known(fr_info_t *fin, u_32_t *passp)
863 {
864 	ipf_main_softc_t *softc = fin->fin_main_soft;
865 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
866 	frentry_t *fr = NULL;
867 	ipfr_t	*fra;
868 	u_32_t pass;
869 
870 	if ((softf->ipfr_lock) || (softf->ipfr_list == NULL))
871 		return (NULL);
872 
873 #ifdef USE_MUTEXES
874 	fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads,
875 			      &softc->ipf_frag);
876 #else
877 	fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads);
878 #endif
879 	if (fra != NULL) {
880 		if (fin->fin_flx & FI_BAD) {
881 			fr = &ipfr_block;
882 			fin->fin_reason = FRB_BADFRAG;
883 			DT2(ipf_frb_badfrag, fr_info_t *, fin, uint, fra);
884 		} else {
885 			fr = fra->ipfr_rule;
886 		}
887 		fin->fin_fr = fr;
888 		if (fr != NULL) {
889 			pass = fr->fr_flags;
890 			if ((pass & FR_KEEPSTATE) != 0) {
891 				fin->fin_flx |= FI_STATE;
892 				/*
893 				 * Reset the keep state flag here so that we
894 				 * don't try and add a new state entry because
895 				 * of a match here. That leads to blocking of
896 				 * the packet later because the add fails.
897 				 */
898 				pass &= ~FR_KEEPSTATE;
899 			}
900 			if ((pass & FR_LOGFIRST) != 0)
901 				pass &= ~(FR_LOGFIRST|FR_LOG);
902 			*passp = pass;
903 		}
904 		RWLOCK_EXIT(&softc->ipf_frag);
905 	}
906 	return (fr);
907 }
908 
909 
910 /* ------------------------------------------------------------------------ */
911 /* Function:    ipf_frag_natforget                                          */
912 /* Returns:     Nil                                                         */
913 /* Parameters:  softc(I) - pointer to soft context main structure           */
914 /*              ptr(I) - pointer to data structure                          */
915 /*                                                                          */
916 /* Search through all of the fragment cache entries for NAT and wherever a  */
917 /* pointer  is found to match ptr, reset it to NULL.                        */
918 /* ------------------------------------------------------------------------ */
919 void
920 ipf_frag_natforget(ipf_main_softc_t *softc, void *ptr)
921 {
922 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
923 	ipfr_t	*fr;
924 
925 	WRITE_ENTER(&softf->ipfr_natfrag);
926 	for (fr = softf->ipfr_natlist; fr; fr = fr->ipfr_next)
927 		if (fr->ipfr_data == ptr)
928 			fr->ipfr_data = NULL;
929 	RWLOCK_EXIT(&softf->ipfr_natfrag);
930 }
931 
932 
933 /* ------------------------------------------------------------------------ */
934 /* Function:    ipf_frag_delete                                             */
935 /* Returns:     Nil                                                         */
936 /* Parameters:  softc(I) - pointer to soft context main structure           */
937 /*              fra(I)   - pointer to fragment structure to delete          */
938 /*              tail(IO) - pointer to the pointer to the tail of the frag   */
939 /*                         list                                             */
940 /*                                                                          */
941 /* Remove a fragment cache table entry from the table & list.  Also free    */
942 /* the filter rule it is associated with it if it is no longer used as a    */
943 /* result of decreasing the reference count.                                */
944 /* ------------------------------------------------------------------------ */
945 static void
946 ipf_frag_delete(ipf_main_softc_t *softc, ipfr_t *fra, ipfr_t ***tail)
947 {
948 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
949 
950 	if (fra->ipfr_next)
951 		fra->ipfr_next->ipfr_prev = fra->ipfr_prev;
952 	*fra->ipfr_prev = fra->ipfr_next;
953 	if (*tail == &fra->ipfr_next)
954 		*tail = fra->ipfr_prev;
955 
956 	if (fra->ipfr_hnext)
957 		fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev;
958 	*fra->ipfr_hprev = fra->ipfr_hnext;
959 
960 	if (fra->ipfr_rule != NULL) {
961 		(void) ipf_derefrule(softc, &fra->ipfr_rule);
962 	}
963 
964 	if (fra->ipfr_ref <= 0)
965 		ipf_frag_free(softf, fra);
966 }
967 
968 
969 /* ------------------------------------------------------------------------ */
970 /* Function:    ipf_frag_free                                               */
971 /* Returns:     Nil                                                         */
972 /* Parameters:  softf(I) - pointer to fragment context information          */
973 /*              fra(I)   - pointer to fragment structure to free            */
974 /*                                                                          */
975 /* Free up a fragment cache entry and bump relevent statistics.             */
976 /* ------------------------------------------------------------------------ */
977 static void
978 ipf_frag_free(ipf_frag_softc_t *softf, ipfr_t *fra)
979 {
980 	KFREE(fra);
981 	FBUMP(ifs_expire);
982 	softf->ipfr_stats.ifs_inuse--;
983 }
984 
985 
986 /* ------------------------------------------------------------------------ */
987 /* Function:    ipf_frag_clear                                              */
988 /* Returns:     Nil                                                         */
989 /* Parameters:  softc(I) - pointer to soft context main structure           */
990 /*                                                                          */
991 /* Free memory in use by fragment state information kept.  Do the normal    */
992 /* fragment state stuff first and then the NAT-fragment table.              */
993 /* ------------------------------------------------------------------------ */
994 void
995 ipf_frag_clear(ipf_main_softc_t *softc)
996 {
997 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
998 	ipfr_t	*fra;
999 	nat_t	*nat;
1000 
1001 	WRITE_ENTER(&softc->ipf_frag);
1002 	while ((fra = softf->ipfr_list) != NULL) {
1003 		fra->ipfr_ref--;
1004 		ipf_frag_delete(softc, fra, &softf->ipfr_tail);
1005 	}
1006 	softf->ipfr_tail = &softf->ipfr_list;
1007 	RWLOCK_EXIT(&softc->ipf_frag);
1008 
1009 	WRITE_ENTER(&softc->ipf_nat);
1010 	WRITE_ENTER(&softf->ipfr_natfrag);
1011 	while ((fra = softf->ipfr_natlist) != NULL) {
1012 		nat = fra->ipfr_data;
1013 		if (nat != NULL) {
1014 			if (nat->nat_data == fra)
1015 				nat->nat_data = NULL;
1016 		}
1017 		fra->ipfr_ref--;
1018 		ipf_frag_delete(softc, fra, &softf->ipfr_nattail);
1019 	}
1020 	softf->ipfr_nattail = &softf->ipfr_natlist;
1021 	RWLOCK_EXIT(&softf->ipfr_natfrag);
1022 	RWLOCK_EXIT(&softc->ipf_nat);
1023 }
1024 
1025 
1026 /* ------------------------------------------------------------------------ */
1027 /* Function:    ipf_frag_expire                                             */
1028 /* Returns:     Nil                                                         */
1029 /* Parameters:  softc(I) - pointer to soft context main structure           */
1030 /*                                                                          */
1031 /* Expire entries in the fragment cache table that have been there too long */
1032 /* ------------------------------------------------------------------------ */
1033 void
1034 ipf_frag_expire(ipf_main_softc_t *softc)
1035 {
1036 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
1037 	ipfr_t	**fp, *fra;
1038 	nat_t	*nat;
1039 	SPL_INT(s);
1040 
1041 	if (softf->ipfr_lock)
1042 		return;
1043 
1044 	SPL_NET(s);
1045 	WRITE_ENTER(&softc->ipf_frag);
1046 	/*
1047 	 * Go through the entire table, looking for entries to expire,
1048 	 * which is indicated by the ttl being less than or equal to ipf_ticks.
1049 	 */
1050 	for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) {
1051 		if (fra->ipfr_ttl > softc->ipf_ticks)
1052 			break;
1053 		fra->ipfr_ref--;
1054 		ipf_frag_delete(softc, fra, &softf->ipfr_tail);
1055 	}
1056 	RWLOCK_EXIT(&softc->ipf_frag);
1057 
1058 	WRITE_ENTER(&softf->ipfr_ipidfrag);
1059 	for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) {
1060 		if (fra->ipfr_ttl > softc->ipf_ticks)
1061 			break;
1062 		fra->ipfr_ref--;
1063 		ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail);
1064 	}
1065 	RWLOCK_EXIT(&softf->ipfr_ipidfrag);
1066 
1067 	/*
1068 	 * Same again for the NAT table, except that if the structure also
1069 	 * still points to a NAT structure, and the NAT structure points back
1070 	 * at the one to be free'd, NULL the reference from the NAT struct.
1071 	 * NOTE: We need to grab both mutex's early, and in this order so as
1072 	 * to prevent a deadlock if both try to expire at the same time.
1073 	 * The extra if() statement here is because it locks out all NAT
1074 	 * operations - no need to do that if there are no entries in this
1075 	 * list, right?
1076 	 */
1077 	if (softf->ipfr_natlist != NULL) {
1078 		WRITE_ENTER(&softc->ipf_nat);
1079 		WRITE_ENTER(&softf->ipfr_natfrag);
1080 		for (fp = &softf->ipfr_natlist; ((fra = *fp) != NULL); ) {
1081 			if (fra->ipfr_ttl > softc->ipf_ticks)
1082 				break;
1083 			nat = fra->ipfr_data;
1084 			if (nat != NULL) {
1085 				if (nat->nat_data == fra)
1086 					nat->nat_data = NULL;
1087 			}
1088 			fra->ipfr_ref--;
1089 			ipf_frag_delete(softc, fra, &softf->ipfr_nattail);
1090 		}
1091 		RWLOCK_EXIT(&softf->ipfr_natfrag);
1092 		RWLOCK_EXIT(&softc->ipf_nat);
1093 	}
1094 	SPL_X(s);
1095 }
1096 
1097 
1098 /* ------------------------------------------------------------------------ */
1099 /* Function:    ipf_frag_pkt_next                                           */
1100 /* Returns:     int      - 0 == success, else error                         */
1101 /* Parameters:  softc(I) - pointer to soft context main structure           */
1102 /*              token(I) - pointer to token information for this caller     */
1103 /*              itp(I)   - pointer to generic iterator from caller          */
1104 /*                                                                          */
1105 /* This function is used to step through the fragment cache list used for   */
1106 /* filter rules. The hard work is done by the more generic ipf_frag_next.   */
1107 /* ------------------------------------------------------------------------ */
1108 int
1109 ipf_frag_pkt_next(ipf_main_softc_t *softc, ipftoken_t *token,
1110 	ipfgeniter_t *itp)
1111 {
1112 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
1113 
1114 #ifdef USE_MUTEXES
1115 	return (ipf_frag_next(softc, token, itp, &softf->ipfr_list,
1116 			     &softf->ipfr_frag));
1117 #else
1118 	return (ipf_frag_next(softc, token, itp, &softf->ipfr_list));
1119 #endif
1120 }
1121 
1122 
1123 /* ------------------------------------------------------------------------ */
1124 /* Function:    ipf_frag_nat_next                                           */
1125 /* Returns:     int      - 0 == success, else error                         */
1126 /* Parameters:  softc(I) - pointer to soft context main structure           */
1127 /*              token(I) - pointer to token information for this caller     */
1128 /*              itp(I)   - pointer to generic iterator from caller          */
1129 /*                                                                          */
1130 /* This function is used to step through the fragment cache list used for   */
1131 /* NAT. The hard work is done by the more generic ipf_frag_next.            */
1132 /* ------------------------------------------------------------------------ */
1133 int
1134 ipf_frag_nat_next(ipf_main_softc_t *softc, ipftoken_t *token,
1135 	ipfgeniter_t *itp)
1136 {
1137 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
1138 
1139 #ifdef USE_MUTEXES
1140 	return (ipf_frag_next(softc, token, itp, &softf->ipfr_natlist,
1141 			     &softf->ipfr_natfrag));
1142 #else
1143 	return (ipf_frag_next(softc, token, itp, &softf->ipfr_natlist));
1144 #endif
1145 }
1146 
1147 /* ------------------------------------------------------------------------ */
1148 /* Function:    ipf_frag_next                                               */
1149 /* Returns:     int      - 0 == success, else error                         */
1150 /* Parameters:  softc(I) - pointer to soft context main structure           */
1151 /*              token(I) - pointer to token information for this caller     */
1152 /*              itp(I)   - pointer to generic iterator from caller          */
1153 /*              top(I)   - top of the fragment list                         */
1154 /*              lock(I)  - fragment cache lock                              */
1155 /*                                                                          */
1156 /* This function is used to interate through the list of entries in the     */
1157 /* fragment cache.  It increases the reference count on the one currently   */
1158 /* being returned so that the caller can come back and resume from it later.*/
1159 /*                                                                          */
1160 /* This function is used for both the NAT fragment cache as well as the ipf */
1161 /* fragment cache - hence the reason for passing in top and lock.           */
1162 /* ------------------------------------------------------------------------ */
1163 static int
1164 ipf_frag_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp,
1165 	ipfr_t **top
1166 #ifdef USE_MUTEXES
1167 , ipfrwlock_t *lock
1168 #endif
1169 )
1170 {
1171 	ipfr_t *frag, *next, zero;
1172 	int error = 0;
1173 
1174 	if (itp->igi_data == NULL) {
1175 		IPFERROR(20001);
1176 		return (EFAULT);
1177 	}
1178 
1179 	if (itp->igi_nitems != 1) {
1180 		IPFERROR(20003);
1181 		return (EFAULT);
1182 	}
1183 
1184 	frag = token->ipt_data;
1185 
1186 	READ_ENTER(lock);
1187 
1188 	if (frag == NULL)
1189 		next = *top;
1190 	else
1191 		next = frag->ipfr_next;
1192 
1193 	if (next != NULL) {
1194 		ATOMIC_INC(next->ipfr_ref);
1195 		token->ipt_data = next;
1196 	} else {
1197 		bzero(&zero, sizeof(zero));
1198 		next = &zero;
1199 		token->ipt_data = NULL;
1200 	}
1201 	if (next->ipfr_next == NULL)
1202 		ipf_token_mark_complete(token);
1203 
1204 	RWLOCK_EXIT(lock);
1205 
1206 	error = COPYOUT(next, itp->igi_data, sizeof(*next));
1207 	if (error != 0)
1208 		IPFERROR(20002);
1209 
1210 	if (frag != NULL) {
1211 #ifdef USE_MUTEXES
1212 		ipf_frag_deref(softc, &frag, lock);
1213 #else
1214 		ipf_frag_deref(softc, &frag);
1215 #endif
1216 	}
1217 	return (error);
1218 }
1219 
1220 
1221 /* ------------------------------------------------------------------------ */
1222 /* Function:    ipf_frag_pkt_deref                                          */
1223 /* Returns:     Nil                                                         */
1224 /* Parameters:  softc(I) - pointer to soft context main structure           */
1225 /*              data(I)  - pointer to frag cache pointer                    */
1226 /*                                                                          */
1227 /* This function is the external interface for dropping a reference to a    */
1228 /* fragment cache entry used by filter rules.                               */
1229 /* ------------------------------------------------------------------------ */
1230 void
1231 ipf_frag_pkt_deref(ipf_main_softc_t *softc, void *data)
1232 {
1233 	ipfr_t **frp = data;
1234 
1235 #ifdef USE_MUTEXES
1236 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
1237 
1238 	ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_frag);
1239 #else
1240 	ipf_frag_deref(softc->ipf_frag_soft, frp);
1241 #endif
1242 }
1243 
1244 
1245 /* ------------------------------------------------------------------------ */
1246 /* Function:    ipf_frag_nat_deref                                          */
1247 /* Returns:     Nil                                                         */
1248 /* Parameters:  softc(I) - pointer to soft context main structure           */
1249 /*              data(I)  - pointer to frag cache pointer                    */
1250 /*                                                                          */
1251 /* This function is the external interface for dropping a reference to a    */
1252 /* fragment cache entry used by NAT table entries.                          */
1253 /* ------------------------------------------------------------------------ */
1254 void
1255 ipf_frag_nat_deref(ipf_main_softc_t *softc, void *data)
1256 {
1257 	ipfr_t **frp = data;
1258 
1259 #ifdef USE_MUTEXES
1260 	ipf_frag_softc_t *softf = softc->ipf_frag_soft;
1261 
1262 	ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_natfrag);
1263 #else
1264 	ipf_frag_deref(softc->ipf_frag_soft, frp);
1265 #endif
1266 }
1267 
1268 
1269 /* ------------------------------------------------------------------------ */
1270 /* Function:    ipf_frag_deref                                              */
1271 /* Returns:     Nil                                                         */
1272 /* Parameters:  frp(IO) - pointer to fragment structure to deference        */
1273 /*              lock(I) - lock associated with the fragment                 */
1274 /*                                                                          */
1275 /* This function dereferences a fragment structure (ipfr_t).  The pointer   */
1276 /* passed in will always be reset back to NULL, even if the structure is    */
1277 /* not freed, to enforce the notion that the caller is no longer entitled   */
1278 /* to use the pointer it is dropping the reference to.                      */
1279 /* ------------------------------------------------------------------------ */
1280 static void
1281 ipf_frag_deref(void *arg, ipfr_t **frp
1282 #ifdef USE_MUTEXES
1283 , ipfrwlock_t *lock
1284 #endif
1285 )
1286 {
1287 	ipf_frag_softc_t *softf = arg;
1288 	ipfr_t *fra;
1289 
1290 	fra = *frp;
1291 	*frp = NULL;
1292 
1293 	WRITE_ENTER(lock);
1294 	fra->ipfr_ref--;
1295 	if (fra->ipfr_ref <= 0)
1296 		ipf_frag_free(softf, fra);
1297 	RWLOCK_EXIT(lock);
1298 }
1299