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