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