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