1/* $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_frag.c 145522 2005-04-25 18:43:14Z darrenr $ */
2
3/*
4 * Copyright (C) 1993-2003 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__
28struct file;
29# endif
30# include <sys/uio.h>
31# undef _KERNEL
32#endif
33#if defined(_KERNEL) && (__FreeBSD_version >= 220000)
34# include <sys/filio.h>
35# include <sys/fcntl.h>
36#else
37# include <sys/ioctl.h>
38#endif
39#if !defined(linux)
40# include <sys/protosw.h>
41#endif
42#include <sys/socket.h>
43#if defined(_KERNEL)
44# include <sys/systm.h>
45# if !defined(__SVR4) && !defined(__svr4__)
46# include <sys/mbuf.h>
47# endif
48#endif
49#if !defined(__SVR4) && !defined(__svr4__)
50# if defined(_KERNEL) && !defined(__sgi)
51# include <sys/kernel.h>
52# endif
53#else
54# include <sys/byteorder.h>
55# ifdef _KERNEL
56# include <sys/dditypes.h>
57# endif
58# include <sys/stream.h>
59# include <sys/kmem.h>
60#endif
61#include <net/if.h>
62#ifdef sun
63# include <net/af.h>
64#endif
65#include <net/route.h>
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_proxy.h"
83#if (__FreeBSD_version >= 300000)
84# include <sys/malloc.h>
85# if defined(_KERNEL)
86# ifndef IPFILTER_LKM
87# include <sys/libkern.h>
88# include <sys/systm.h>
89# endif
90extern struct callout_handle fr_slowtimer_ch;
91# endif
92#endif
93#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000)
94# include <sys/callout.h>
95extern struct callout fr_slowtimer_ch;
96#endif
97#if defined(__OpenBSD__)
98# include <sys/timeout.h>
99extern struct timeout fr_slowtimer_ch;
100#endif
101/* END OF INCLUDES */
102
103#if !defined(lint)
104static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed";
105static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_frag.c 145522 2005-04-25 18:43:14Z darrenr $";
106static const char rcsid[] = "@(#)Id: ip_frag.c,v 2.77 2004/01/27 00:24:54 darrenr Exp";
107#endif
108
109
110static ipfr_t *ipfr_list = NULL;
111static ipfr_t **ipfr_tail = &ipfr_list;
112static ipfr_t **ipfr_heads;
113
114static ipfr_t *ipfr_natlist = NULL;
115static ipfr_t **ipfr_nattail = &ipfr_natlist;
116static ipfr_t **ipfr_nattab;
117
118static ipfr_t *ipfr_ipidlist = NULL;
119static ipfr_t **ipfr_ipidtail = &ipfr_ipidlist;
120static ipfr_t **ipfr_ipidtab;
121
122static ipfrstat_t ipfr_stats;
123static int ipfr_inuse = 0;
124int ipfr_size = IPFT_SIZE;
125
126int fr_ipfrttl = 120; /* 60 seconds */
127int fr_frag_lock = 0;
128int fr_frag_init = 0;
129u_long fr_ticks = 0;
130
131
132static ipfr_t *ipfr_newfrag __P((fr_info_t *, u_32_t, ipfr_t **));
133static ipfr_t *fr_fraglookup __P((fr_info_t *, ipfr_t **));
134static void fr_fragdelete __P((ipfr_t *, ipfr_t ***));
135
136
137/* ------------------------------------------------------------------------ */
138/* Function: fr_fraginit */
139/* Returns: int - 0 == success, -1 == error */
140/* Parameters: Nil */
141/* */
142/* Initialise the hash tables for the fragment cache lookups. */
143/* ------------------------------------------------------------------------ */
144int fr_fraginit()
145{
146 KMALLOCS(ipfr_heads, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
147 if (ipfr_heads == NULL)
148 return -1;
149 bzero((char *)ipfr_heads, ipfr_size * sizeof(ipfr_t *));
150
151 KMALLOCS(ipfr_nattab, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
152 if (ipfr_nattab == NULL)
153 return -1;
154 bzero((char *)ipfr_nattab, ipfr_size * sizeof(ipfr_t *));
155
156 KMALLOCS(ipfr_ipidtab, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
157 if (ipfr_ipidtab == NULL)
158 return -1;
159 bzero((char *)ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *));
160
161 RWLOCK_INIT(&ipf_frag, "ipf fragment rwlock");
162 fr_frag_init = 1;
163
164 return 0;
165}
166
167
168/* ------------------------------------------------------------------------ */
169/* Function: fr_fragunload */
170/* Returns: Nil */
171/* Parameters: Nil */
172/* */
173/* Free all memory allocated whilst running and from initialisation. */
174/* ------------------------------------------------------------------------ */
175void fr_fragunload()
176{
177 if (fr_frag_init == 1) {
178 fr_fragclear();
179
180 RW_DESTROY(&ipf_frag);
181 fr_frag_init = 0;
182 }
183
184 if (ipfr_heads != NULL)
185 KFREES(ipfr_heads, ipfr_size * sizeof(ipfr_t *));
186 ipfr_heads = NULL;
187
188 if (ipfr_nattab != NULL)
189 KFREES(ipfr_nattab, ipfr_size * sizeof(ipfr_t *));
190 ipfr_nattab = NULL;
191
192 if (ipfr_ipidtab != NULL)
193 KFREES(ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *));
194 ipfr_ipidtab = NULL;
195}
196
197
198/* ------------------------------------------------------------------------ */
199/* Function: fr_fragstats */
200/* Returns: ipfrstat_t* - pointer to struct with current frag stats */
201/* Parameters: Nil */
202/* */
203/* Updates ipfr_stats with current information and returns a pointer to it */
204/* ------------------------------------------------------------------------ */
205ipfrstat_t *fr_fragstats()
206{
207 ipfr_stats.ifs_table = ipfr_heads;
208 ipfr_stats.ifs_nattab = ipfr_nattab;
209 ipfr_stats.ifs_inuse = ipfr_inuse;
210 return &ipfr_stats;
211}
212
213
214/* ------------------------------------------------------------------------ */
215/* Function: ipfr_newfrag */
216/* Returns: ipfr_t * - pointer to fragment cache state info or NULL */
217/* Parameters: fin(I) - pointer to packet information */
218/* table(I) - pointer to frag table to add to */
219/* */
220/* Add a new entry to the fragment cache, registering it as having come */
221/* through this box, with the result of the filter operation. */
222/* ------------------------------------------------------------------------ */
223static ipfr_t *ipfr_newfrag(fin, pass, table)
224fr_info_t *fin;
225u_32_t pass;
226ipfr_t *table[];
227{
228 ipfr_t *fra, frag;
229 u_int idx, off;
230 ip_t *ip;
231
232 if (ipfr_inuse >= IPFT_SIZE)
233 return NULL;
234
235 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
236 return NULL;
237
238 ip = fin->fin_ip;
239
240 if (pass & FR_FRSTRICT)
241 if ((ip->ip_off & IP_OFFMASK) != 0)
242 return NULL;
243
244 frag.ipfr_p = ip->ip_p;
245 idx = ip->ip_p;
246 frag.ipfr_id = ip->ip_id;
247 idx += ip->ip_id;
248 frag.ipfr_tos = ip->ip_tos;
249 frag.ipfr_src.s_addr = ip->ip_src.s_addr;
250 idx += ip->ip_src.s_addr;
251 frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
252 idx += ip->ip_dst.s_addr;
253 frag.ipfr_ifp = fin->fin_ifp;
254 idx *= 127;
255 idx %= IPFT_SIZE;
256
257 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
258 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
259 frag.ipfr_auth = fin->fin_fi.fi_auth;
260
261 /*
262 * first, make sure it isn't already there...
263 */
264 for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext)
265 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp,
266 IPFR_CMPSZ)) {
267 ipfr_stats.ifs_exists++;
268 return NULL;
269 }
270
271 /*
272 * allocate some memory, if possible, if not, just record that we
273 * failed to do so.
274 */
275 KMALLOC(fra, ipfr_t *);
276 if (fra == NULL) {
277 ipfr_stats.ifs_nomem++;
278 return NULL;
279 }
280
281 if ((fra->ipfr_rule = fin->fin_fr) != NULL)
282 fin->fin_fr->fr_ref++;
283
284 /*
285 * Insert the fragment into the fragment table, copy the struct used
286 * in the search using bcopy rather than reassign each field.
287 * Set the ttl to the default.
288 */
289 if ((fra->ipfr_hnext = table[idx]) != NULL)
290 table[idx]->ipfr_hprev = &fra->ipfr_hnext;
291 fra->ipfr_hprev = table + idx;
292 fra->ipfr_data = NULL;
293 table[idx] = fra;
294 bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ);
295 fra->ipfr_ttl = fr_ticks + fr_ipfrttl;
296
297 /*
298 * Compute the offset of the expected start of the next packet.
299 */
300 off = ip->ip_off & IP_OFFMASK;
301 if (off == 0)
302 fra->ipfr_seen0 = 1;
303 fra->ipfr_off = off + (fin->fin_dlen >> 3);
304 fra->ipfr_pass = pass;
305 ipfr_stats.ifs_new++;
306 ipfr_inuse++;
307 return fra;
308}
309
310
311/* ------------------------------------------------------------------------ */
312/* Function: fr_newfrag */
313/* Returns: int - 0 == success, -1 == error */
314/* Parameters: fin(I) - pointer to packet information */
315/* */
316/* Add a new entry to the fragment cache table based on the current packet */
317/* ------------------------------------------------------------------------ */
318int fr_newfrag(fin, pass)
319u_32_t pass;
320fr_info_t *fin;
321{
322 ipfr_t *fra;
323
324 if ((fin->fin_v != 4) || (fr_frag_lock != 0))
325 return -1;
326
327 WRITE_ENTER(&ipf_frag);
328 fra = ipfr_newfrag(fin, pass, ipfr_heads);
329 if (fra != NULL) {
330 *ipfr_tail = fra;
331 fra->ipfr_prev = ipfr_tail;
332 ipfr_tail = &fra->ipfr_next;
333 if (ipfr_list == NULL)
334 ipfr_list = fra;
335 fra->ipfr_next = NULL;
336 }
337 RWLOCK_EXIT(&ipf_frag);
338 return fra ? 0 : -1;
339}
340
341
342/* ------------------------------------------------------------------------ */
343/* Function: fr_nat_newfrag */
344/* Returns: int - 0 == success, -1 == error */
345/* Parameters: fin(I) - pointer to packet information */
346/* nat(I) - pointer to NAT structure */
347/* */
348/* Create a new NAT fragment cache entry based on the current packet and */
349/* the NAT structure for this "session". */
350/* ------------------------------------------------------------------------ */
351int fr_nat_newfrag(fin, pass, nat)
352fr_info_t *fin;
353u_32_t pass;
354nat_t *nat;
355{
356 ipfr_t *fra;
357
358 if ((fin->fin_v != 4) || (fr_frag_lock != 0))
359 return 0;
360
361 WRITE_ENTER(&ipf_natfrag);
362 fra = ipfr_newfrag(fin, pass, ipfr_nattab);
363 if (fra != NULL) {
364 fra->ipfr_data = nat;
365 nat->nat_data = fra;
366 *ipfr_nattail = fra;
367 fra->ipfr_prev = ipfr_nattail;
368 ipfr_nattail = &fra->ipfr_next;
369 fra->ipfr_next = NULL;
370 }
371 RWLOCK_EXIT(&ipf_natfrag);
372 return fra ? 0 : -1;
373}
374
375
376/* ------------------------------------------------------------------------ */
377/* Function: fr_ipid_newfrag */
378/* Returns: int - 0 == success, -1 == error */
379/* Parameters: fin(I) - pointer to packet information */
380/* ipid(I) - new IP ID for this fragmented packet */
381/* */
382/* Create a new fragment cache entry for this packet and store, as a data */
383/* pointer, the new IP ID value. */
384/* ------------------------------------------------------------------------ */
385int fr_ipid_newfrag(fin, ipid)
386fr_info_t *fin;
387u_32_t ipid;
388{
389 ipfr_t *fra;
390
391 if ((fin->fin_v != 4) || (fr_frag_lock))
392 return 0;
393
394 WRITE_ENTER(&ipf_ipidfrag);
395 fra = ipfr_newfrag(fin, 0, ipfr_ipidtab);
396 if (fra != NULL) {
397 fra->ipfr_data = (void *)ipid;
398 *ipfr_ipidtail = fra;
399 fra->ipfr_prev = ipfr_ipidtail;
400 ipfr_ipidtail = &fra->ipfr_next;
401 fra->ipfr_next = NULL;
402 }
403 RWLOCK_EXIT(&ipf_ipidfrag);
404 return fra ? 0 : -1;
405}
406
407
408/* ------------------------------------------------------------------------ */
409/* Function: fr_fraglookup */
410/* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */
411/* matching entry in the frag table, else NULL */
412/* Parameters: fin(I) - pointer to packet information */
413/* table(I) - pointer to fragment cache table to search */
414/* */
415/* Check the fragment cache to see if there is already a record of this */
416/* packet with its filter result known. */
417/* ------------------------------------------------------------------------ */
418static ipfr_t *fr_fraglookup(fin, table)
419fr_info_t *fin;
420ipfr_t *table[];
421{
422 ipfr_t *f, frag;
423 u_int idx;
424 ip_t *ip;
425
426 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
427 return NULL;
428
429 /*
430 * For fragments, we record protocol, packet id, TOS and both IP#'s
431 * (these should all be the same for all fragments of a packet).
432 *
433 * build up a hash value to index the table with.
434 */
435 ip = fin->fin_ip;
436 frag.ipfr_p = ip->ip_p;
437 idx = ip->ip_p;
438 frag.ipfr_id = ip->ip_id;
439 idx += ip->ip_id;
440 frag.ipfr_tos = ip->ip_tos;
441 frag.ipfr_src.s_addr = ip->ip_src.s_addr;
442 idx += ip->ip_src.s_addr;
443 frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
444 idx += ip->ip_dst.s_addr;
445 frag.ipfr_ifp = fin->fin_ifp;
446 idx *= 127;
447 idx %= IPFT_SIZE;
448
449 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
450 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
451 frag.ipfr_auth = fin->fin_fi.fi_auth;
452
453 /*
454 * check the table, careful to only compare the right amount of data
455 */
456 for (f = table[idx]; f; f = f->ipfr_hnext)
457 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp,
458 IPFR_CMPSZ)) {
459 u_short off;
460
461 /*
462 * We don't want to let short packets match because
463 * they could be compromising the security of other
464 * rules that want to match on layer 4 fields (and
465 * can't because they have been fragmented off.)
466 * Why do this check here? The counter acts as an
467 * indicator of this kind of attack, whereas if it was
468 * elsewhere, it wouldn't know if other matching
469 * packets had been seen.
470 */
471 if (fin->fin_flx & FI_SHORT) {
472 ATOMIC_INCL(ipfr_stats.ifs_short);
473 continue;
474 }
475
476 /*
477 * XXX - We really need to be guarding against the
478 * retransmission of (src,dst,id,offset-range) here
479 * because a fragmented packet is never resent with
480 * the same IP ID# (or shouldn't).
481 */
482 off = ip->ip_off & IP_OFFMASK;
483 if (f->ipfr_seen0) {
484 if (off == 0) {
485 ATOMIC_INCL(ipfr_stats.ifs_retrans0);
486 continue;
487 }
488 } else if (off == 0)
489 f->ipfr_seen0 = 1;
490
491 if (f != table[idx]) {
492 ipfr_t **fp;
493
494 /*
495 * Move fragment info. to the top of the list
496 * to speed up searches. First, delink...
497 */
498 fp = f->ipfr_hprev;
499 (*fp) = f->ipfr_hnext;
500 if (f->ipfr_hnext != NULL)
501 f->ipfr_hnext->ipfr_hprev = fp;
502 /*
503 * Then put back at the top of the chain.
504 */
505 f->ipfr_hnext = table[idx];
506 table[idx]->ipfr_hprev = &f->ipfr_hnext;
507 f->ipfr_hprev = table + idx;
508 table[idx] = f;
509 }
510
511 /*
512 * If we've follwed the fragments, and this is the
513 * last (in order), shrink expiration time.
514 */
515 if (off == f->ipfr_off) {
516 if (!(ip->ip_off & IP_MF))
517 f->ipfr_ttl = fr_ticks + 1;
518 f->ipfr_off = (fin->fin_dlen >> 3) + off;
519 } else if (f->ipfr_pass & FR_FRSTRICT)
520 continue;
521 ATOMIC_INCL(ipfr_stats.ifs_hits);
522 return f;
523 }
524 return NULL;
525}
526
527
528/* ------------------------------------------------------------------------ */
529/* Function: fr_nat_knownfrag */
530/* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */
531/* match found, else NULL */
532/* Parameters: fin(I) - pointer to packet information */
533/* */
534/* Functional interface for NAT lookups of the NAT fragment cache */
535/* ------------------------------------------------------------------------ */
536nat_t *fr_nat_knownfrag(fin)
537fr_info_t *fin;
538{
539 nat_t *nat;
540 ipfr_t *ipf;
541
542 if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_natlist)
543 return NULL;
544 READ_ENTER(&ipf_natfrag);
545 ipf = fr_fraglookup(fin, ipfr_nattab);
546 if (ipf != NULL) {
547 nat = ipf->ipfr_data;
548 /*
549 * This is the last fragment for this packet.
550 */
551 if ((ipf->ipfr_ttl == fr_ticks + 1) && (nat != NULL)) {
552 nat->nat_data = NULL;
553 ipf->ipfr_data = NULL;
554 }
555 } else
556 nat = NULL;
557 RWLOCK_EXIT(&ipf_natfrag);
558 return nat;
559}
560
561
562/* ------------------------------------------------------------------------ */
563/* Function: fr_ipid_knownfrag */
564/* Returns: u_32_t - IPv4 ID for this packet if match found, else */
565/* return 0xfffffff to indicate no match. */
566/* Parameters: fin(I) - pointer to packet information */
567/* */
568/* Functional interface for IP ID lookups of the IP ID fragment cache */
569/* ------------------------------------------------------------------------ */
570u_32_t fr_ipid_knownfrag(fin)
571fr_info_t *fin;
572{
573 ipfr_t *ipf;
574 u_32_t id;
575
576 if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_ipidlist)
577 return 0xffffffff;
578
579 READ_ENTER(&ipf_ipidfrag);
580 ipf = fr_fraglookup(fin, ipfr_ipidtab);
581 if (ipf != NULL)
582 id = (u_32_t)ipf->ipfr_data;
583 else
584 id = 0xffffffff;
585 RWLOCK_EXIT(&ipf_ipidfrag);
586 return id;
587}
588
589
590/* ------------------------------------------------------------------------ */
591/* Function: fr_knownfrag */
592/* Returns: frentry_t* - pointer to filter rule if a match is found in */
593/* the frag cache table, else NULL. */
594/* Parameters: fin(I) - pointer to packet information */
595/* passp(O) - pointer to where to store rule flags resturned */
596/* */
597/* Functional interface for normal lookups of the fragment cache. If a */
598/* match is found, return the rule pointer and flags from the rule, except */
599/* that if FR_LOGFIRST is set, reset FR_LOG. */
600/* ------------------------------------------------------------------------ */
601frentry_t *fr_knownfrag(fin, passp)
602fr_info_t *fin;
603u_32_t *passp;
604{
605 frentry_t *fr = NULL;
606 ipfr_t *fra;
607 u_32_t pass;
608
609 if ((fin->fin_v != 4) || (fr_frag_lock) || (ipfr_list == NULL))
610 return NULL;
611
612 READ_ENTER(&ipf_frag);
613 fra = fr_fraglookup(fin, ipfr_heads);
614 if (fra != NULL) {
615 fr = fra->ipfr_rule;
616 fin->fin_fr = fr;
617 if (fr != NULL) {
618 pass = fr->fr_flags;
619 if ((pass & FR_LOGFIRST) != 0)
620 pass &= ~(FR_LOGFIRST|FR_LOG);
621 *passp = pass;
622 }
623 }
624 RWLOCK_EXIT(&ipf_frag);
625 return fr;
626}
627
628
629/* ------------------------------------------------------------------------ */
630/* Function: fr_forget */
631/* Returns: Nil */
632/* Parameters: ptr(I) - pointer to data structure */
633/* */
634/* Search through all of the fragment cache entries and wherever a pointer */
635/* is found to match ptr, reset it to NULL. */
636/* ------------------------------------------------------------------------ */
637void fr_forget(ptr)
638void *ptr;
639{
640 ipfr_t *fr;
641
642 WRITE_ENTER(&ipf_frag);
643 for (fr = ipfr_list; fr; fr = fr->ipfr_next)
644 if (fr->ipfr_data == ptr)
645 fr->ipfr_data = NULL;
646 RWLOCK_EXIT(&ipf_frag);
647}
648
649
650/* ------------------------------------------------------------------------ */
651/* Function: fr_forgetnat */
652/* Returns: Nil */
653/* Parameters: ptr(I) - pointer to data structure */
654/* */
655/* Search through all of the fragment cache entries for NAT and wherever a */
656/* pointer is found to match ptr, reset it to NULL. */
657/* ------------------------------------------------------------------------ */
658void fr_forgetnat(ptr)
659void *ptr;
660{
661 ipfr_t *fr;
662
663 WRITE_ENTER(&ipf_natfrag);
664 for (fr = ipfr_natlist; fr; fr = fr->ipfr_next)
665 if (fr->ipfr_data == ptr)
666 fr->ipfr_data = NULL;
667 RWLOCK_EXIT(&ipf_natfrag);
668}
669
670
671/* ------------------------------------------------------------------------ */
672/* Function: fr_fragdelete */
673/* Returns: Nil */
674/* Parameters: fra(I) - pointer to fragment structure to delete */
675/* tail(IO) - pointer to the pointer to the tail of the frag */
676/* list */
677/* */
678/* Remove a fragment cache table entry from the table & list. Also free */
679/* the filter rule it is associated with it if it is no longer used as a */
680/* result of decreasing the reference count. */
681/* ------------------------------------------------------------------------ */
682static void fr_fragdelete(fra, tail)
683ipfr_t *fra, ***tail;
684{
685 frentry_t *fr;
686
687 fr = fra->ipfr_rule;
688 if (fr != NULL)
689 (void)fr_derefrule(&fr);
690
691 if (fra->ipfr_next)
692 fra->ipfr_next->ipfr_prev = fra->ipfr_prev;
693 *fra->ipfr_prev = fra->ipfr_next;
694 if (*tail == &fra->ipfr_next)
695 *tail = fra->ipfr_prev;
696
697 if (fra->ipfr_hnext)
698 fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev;
699 *fra->ipfr_hprev = fra->ipfr_hnext;
700 KFREE(fra);
701}
702
703
704/* ------------------------------------------------------------------------ */
705/* Function: fr_fragclear */
706/* Returns: Nil */
707/* Parameters: Nil */
708/* */
709/* Free memory in use by fragment state information kept. Do the normal */
710/* fragment state stuff first and then the NAT-fragment table. */
711/* ------------------------------------------------------------------------ */
712void fr_fragclear()
713{
714 ipfr_t *fra;
715 nat_t *nat;
716
717 WRITE_ENTER(&ipf_frag);
718 while ((fra = ipfr_list) != NULL)
719 fr_fragdelete(fra, &ipfr_tail);
720 ipfr_tail = &ipfr_list;
721 RWLOCK_EXIT(&ipf_frag);
722
723 WRITE_ENTER(&ipf_nat);
724 WRITE_ENTER(&ipf_natfrag);
725 while ((fra = ipfr_natlist) != NULL) {
726 nat = fra->ipfr_data;
727 if (nat != NULL) {
728 if (nat->nat_data == fra)
729 nat->nat_data = NULL;
730 }
731 fr_fragdelete(fra, &ipfr_nattail);
732 }
733 ipfr_nattail = &ipfr_natlist;
734 RWLOCK_EXIT(&ipf_natfrag);
735 RWLOCK_EXIT(&ipf_nat);
736}
737
738
739/* ------------------------------------------------------------------------ */
740/* Function: fr_fragexpire */
741/* Returns: Nil */
742/* Parameters: Nil */
743/* */
744/* Expire entries in the fragment cache table that have been there too long */
745/* ------------------------------------------------------------------------ */
746void fr_fragexpire()
747{
748 ipfr_t **fp, *fra;
749 nat_t *nat;
750#if defined(USE_SPL) && defined(_KERNEL)
751 int s;
752#endif
753
754 if (fr_frag_lock)
755 return;
756
757 SPL_NET(s);
758 WRITE_ENTER(&ipf_frag);
759 /*
760 * Go through the entire table, looking for entries to expire,
761 * which is indicated by the ttl being less than or equal to fr_ticks.
762 */
763 for (fp = &ipfr_list; ((fra = *fp) != NULL); ) {
764 if (fra->ipfr_ttl > fr_ticks)
765 break;
766 fr_fragdelete(fra, &ipfr_tail);
767 ipfr_stats.ifs_expire++;
768 ipfr_inuse--;
769 }
770 RWLOCK_EXIT(&ipf_frag);
771
772 WRITE_ENTER(&ipf_ipidfrag);
773 for (fp = &ipfr_ipidlist; ((fra = *fp) != NULL); ) {
774 if (fra->ipfr_ttl > fr_ticks)
775 break;
776 fr_fragdelete(fra, &ipfr_ipidtail);
777 ipfr_stats.ifs_expire++;
778 ipfr_inuse--;
779 }
780 RWLOCK_EXIT(&ipf_ipidfrag);
781
782 /*
783 * Same again for the NAT table, except that if the structure also
784 * still points to a NAT structure, and the NAT structure points back
785 * at the one to be free'd, NULL the reference from the NAT struct.
786 * NOTE: We need to grab both mutex's early, and in this order so as
787 * to prevent a deadlock if both try to expire at the same time.
788 */
789 WRITE_ENTER(&ipf_nat);
790 WRITE_ENTER(&ipf_natfrag);
791 for (fp = &ipfr_natlist; ((fra = *fp) != NULL); ) {
792 if (fra->ipfr_ttl > fr_ticks)
793 break;
794 nat = fra->ipfr_data;
795 if (nat != NULL) {
796 if (nat->nat_data == fra)
797 nat->nat_data = NULL;
798 }
799 fr_fragdelete(fra, &ipfr_nattail);
800 ipfr_stats.ifs_expire++;
801 ipfr_inuse--;
802 }
803 RWLOCK_EXIT(&ipf_natfrag);
804 RWLOCK_EXIT(&ipf_nat);
805 SPL_X(s);
806}
807
808
809/* ------------------------------------------------------------------------ */
810/* Function: fr_slowtimer */
811/* Returns: Nil */
812/* Parameters: Nil */
813/* */
814/* Slowly expire held state for fragments. Timeouts are set * in */
815/* expectation of this being called twice per second. */
816/* ------------------------------------------------------------------------ */
817#if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \
818 !defined(__osf__))
819# if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi))
820void fr_slowtimer __P((void *ptr))
821# else
822int fr_slowtimer()
823# endif
824{
825 READ_ENTER(&ipf_global);
826
827 fr_fragexpire();
828 fr_timeoutstate();
829 fr_natexpire();
830 fr_authexpire();
831 fr_ticks++;
832 if (fr_running <= 0)
833 goto done;
834# ifdef _KERNEL
835# if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000)
836 callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL);
837# else
838# if defined(__OpenBSD__)
839 timeout_add(&fr_slowtimer_ch, hz/2);
840# else
841# if (__FreeBSD_version >= 300000)
842 fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2);
843# else
844# ifdef linux
845 ;
846# else
847 timeout(fr_slowtimer, NULL, hz/2);
848# endif
849# endif /* FreeBSD */
850# endif /* OpenBSD */
851# endif /* NetBSD */
852# endif
853done:
854 RWLOCK_EXIT(&ipf_global);
855# if (BSD < 199103) || !defined(_KERNEL)
856 return 0;
857# endif
858}
859#endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */
2
3/*
4 * Copyright (C) 1993-2003 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__
28struct file;
29# endif
30# include <sys/uio.h>
31# undef _KERNEL
32#endif
33#if defined(_KERNEL) && (__FreeBSD_version >= 220000)
34# include <sys/filio.h>
35# include <sys/fcntl.h>
36#else
37# include <sys/ioctl.h>
38#endif
39#if !defined(linux)
40# include <sys/protosw.h>
41#endif
42#include <sys/socket.h>
43#if defined(_KERNEL)
44# include <sys/systm.h>
45# if !defined(__SVR4) && !defined(__svr4__)
46# include <sys/mbuf.h>
47# endif
48#endif
49#if !defined(__SVR4) && !defined(__svr4__)
50# if defined(_KERNEL) && !defined(__sgi)
51# include <sys/kernel.h>
52# endif
53#else
54# include <sys/byteorder.h>
55# ifdef _KERNEL
56# include <sys/dditypes.h>
57# endif
58# include <sys/stream.h>
59# include <sys/kmem.h>
60#endif
61#include <net/if.h>
62#ifdef sun
63# include <net/af.h>
64#endif
65#include <net/route.h>
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_proxy.h"
83#if (__FreeBSD_version >= 300000)
84# include <sys/malloc.h>
85# if defined(_KERNEL)
86# ifndef IPFILTER_LKM
87# include <sys/libkern.h>
88# include <sys/systm.h>
89# endif
90extern struct callout_handle fr_slowtimer_ch;
91# endif
92#endif
93#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000)
94# include <sys/callout.h>
95extern struct callout fr_slowtimer_ch;
96#endif
97#if defined(__OpenBSD__)
98# include <sys/timeout.h>
99extern struct timeout fr_slowtimer_ch;
100#endif
101/* END OF INCLUDES */
102
103#if !defined(lint)
104static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed";
105static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_frag.c 145522 2005-04-25 18:43:14Z darrenr $";
106static const char rcsid[] = "@(#)Id: ip_frag.c,v 2.77 2004/01/27 00:24:54 darrenr Exp";
107#endif
108
109
110static ipfr_t *ipfr_list = NULL;
111static ipfr_t **ipfr_tail = &ipfr_list;
112static ipfr_t **ipfr_heads;
113
114static ipfr_t *ipfr_natlist = NULL;
115static ipfr_t **ipfr_nattail = &ipfr_natlist;
116static ipfr_t **ipfr_nattab;
117
118static ipfr_t *ipfr_ipidlist = NULL;
119static ipfr_t **ipfr_ipidtail = &ipfr_ipidlist;
120static ipfr_t **ipfr_ipidtab;
121
122static ipfrstat_t ipfr_stats;
123static int ipfr_inuse = 0;
124int ipfr_size = IPFT_SIZE;
125
126int fr_ipfrttl = 120; /* 60 seconds */
127int fr_frag_lock = 0;
128int fr_frag_init = 0;
129u_long fr_ticks = 0;
130
131
132static ipfr_t *ipfr_newfrag __P((fr_info_t *, u_32_t, ipfr_t **));
133static ipfr_t *fr_fraglookup __P((fr_info_t *, ipfr_t **));
134static void fr_fragdelete __P((ipfr_t *, ipfr_t ***));
135
136
137/* ------------------------------------------------------------------------ */
138/* Function: fr_fraginit */
139/* Returns: int - 0 == success, -1 == error */
140/* Parameters: Nil */
141/* */
142/* Initialise the hash tables for the fragment cache lookups. */
143/* ------------------------------------------------------------------------ */
144int fr_fraginit()
145{
146 KMALLOCS(ipfr_heads, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
147 if (ipfr_heads == NULL)
148 return -1;
149 bzero((char *)ipfr_heads, ipfr_size * sizeof(ipfr_t *));
150
151 KMALLOCS(ipfr_nattab, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
152 if (ipfr_nattab == NULL)
153 return -1;
154 bzero((char *)ipfr_nattab, ipfr_size * sizeof(ipfr_t *));
155
156 KMALLOCS(ipfr_ipidtab, ipfr_t **, ipfr_size * sizeof(ipfr_t *));
157 if (ipfr_ipidtab == NULL)
158 return -1;
159 bzero((char *)ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *));
160
161 RWLOCK_INIT(&ipf_frag, "ipf fragment rwlock");
162 fr_frag_init = 1;
163
164 return 0;
165}
166
167
168/* ------------------------------------------------------------------------ */
169/* Function: fr_fragunload */
170/* Returns: Nil */
171/* Parameters: Nil */
172/* */
173/* Free all memory allocated whilst running and from initialisation. */
174/* ------------------------------------------------------------------------ */
175void fr_fragunload()
176{
177 if (fr_frag_init == 1) {
178 fr_fragclear();
179
180 RW_DESTROY(&ipf_frag);
181 fr_frag_init = 0;
182 }
183
184 if (ipfr_heads != NULL)
185 KFREES(ipfr_heads, ipfr_size * sizeof(ipfr_t *));
186 ipfr_heads = NULL;
187
188 if (ipfr_nattab != NULL)
189 KFREES(ipfr_nattab, ipfr_size * sizeof(ipfr_t *));
190 ipfr_nattab = NULL;
191
192 if (ipfr_ipidtab != NULL)
193 KFREES(ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *));
194 ipfr_ipidtab = NULL;
195}
196
197
198/* ------------------------------------------------------------------------ */
199/* Function: fr_fragstats */
200/* Returns: ipfrstat_t* - pointer to struct with current frag stats */
201/* Parameters: Nil */
202/* */
203/* Updates ipfr_stats with current information and returns a pointer to it */
204/* ------------------------------------------------------------------------ */
205ipfrstat_t *fr_fragstats()
206{
207 ipfr_stats.ifs_table = ipfr_heads;
208 ipfr_stats.ifs_nattab = ipfr_nattab;
209 ipfr_stats.ifs_inuse = ipfr_inuse;
210 return &ipfr_stats;
211}
212
213
214/* ------------------------------------------------------------------------ */
215/* Function: ipfr_newfrag */
216/* Returns: ipfr_t * - pointer to fragment cache state info or NULL */
217/* Parameters: fin(I) - pointer to packet information */
218/* table(I) - pointer to frag table to add to */
219/* */
220/* Add a new entry to the fragment cache, registering it as having come */
221/* through this box, with the result of the filter operation. */
222/* ------------------------------------------------------------------------ */
223static ipfr_t *ipfr_newfrag(fin, pass, table)
224fr_info_t *fin;
225u_32_t pass;
226ipfr_t *table[];
227{
228 ipfr_t *fra, frag;
229 u_int idx, off;
230 ip_t *ip;
231
232 if (ipfr_inuse >= IPFT_SIZE)
233 return NULL;
234
235 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
236 return NULL;
237
238 ip = fin->fin_ip;
239
240 if (pass & FR_FRSTRICT)
241 if ((ip->ip_off & IP_OFFMASK) != 0)
242 return NULL;
243
244 frag.ipfr_p = ip->ip_p;
245 idx = ip->ip_p;
246 frag.ipfr_id = ip->ip_id;
247 idx += ip->ip_id;
248 frag.ipfr_tos = ip->ip_tos;
249 frag.ipfr_src.s_addr = ip->ip_src.s_addr;
250 idx += ip->ip_src.s_addr;
251 frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
252 idx += ip->ip_dst.s_addr;
253 frag.ipfr_ifp = fin->fin_ifp;
254 idx *= 127;
255 idx %= IPFT_SIZE;
256
257 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
258 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
259 frag.ipfr_auth = fin->fin_fi.fi_auth;
260
261 /*
262 * first, make sure it isn't already there...
263 */
264 for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext)
265 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp,
266 IPFR_CMPSZ)) {
267 ipfr_stats.ifs_exists++;
268 return NULL;
269 }
270
271 /*
272 * allocate some memory, if possible, if not, just record that we
273 * failed to do so.
274 */
275 KMALLOC(fra, ipfr_t *);
276 if (fra == NULL) {
277 ipfr_stats.ifs_nomem++;
278 return NULL;
279 }
280
281 if ((fra->ipfr_rule = fin->fin_fr) != NULL)
282 fin->fin_fr->fr_ref++;
283
284 /*
285 * Insert the fragment into the fragment table, copy the struct used
286 * in the search using bcopy rather than reassign each field.
287 * Set the ttl to the default.
288 */
289 if ((fra->ipfr_hnext = table[idx]) != NULL)
290 table[idx]->ipfr_hprev = &fra->ipfr_hnext;
291 fra->ipfr_hprev = table + idx;
292 fra->ipfr_data = NULL;
293 table[idx] = fra;
294 bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ);
295 fra->ipfr_ttl = fr_ticks + fr_ipfrttl;
296
297 /*
298 * Compute the offset of the expected start of the next packet.
299 */
300 off = ip->ip_off & IP_OFFMASK;
301 if (off == 0)
302 fra->ipfr_seen0 = 1;
303 fra->ipfr_off = off + (fin->fin_dlen >> 3);
304 fra->ipfr_pass = pass;
305 ipfr_stats.ifs_new++;
306 ipfr_inuse++;
307 return fra;
308}
309
310
311/* ------------------------------------------------------------------------ */
312/* Function: fr_newfrag */
313/* Returns: int - 0 == success, -1 == error */
314/* Parameters: fin(I) - pointer to packet information */
315/* */
316/* Add a new entry to the fragment cache table based on the current packet */
317/* ------------------------------------------------------------------------ */
318int fr_newfrag(fin, pass)
319u_32_t pass;
320fr_info_t *fin;
321{
322 ipfr_t *fra;
323
324 if ((fin->fin_v != 4) || (fr_frag_lock != 0))
325 return -1;
326
327 WRITE_ENTER(&ipf_frag);
328 fra = ipfr_newfrag(fin, pass, ipfr_heads);
329 if (fra != NULL) {
330 *ipfr_tail = fra;
331 fra->ipfr_prev = ipfr_tail;
332 ipfr_tail = &fra->ipfr_next;
333 if (ipfr_list == NULL)
334 ipfr_list = fra;
335 fra->ipfr_next = NULL;
336 }
337 RWLOCK_EXIT(&ipf_frag);
338 return fra ? 0 : -1;
339}
340
341
342/* ------------------------------------------------------------------------ */
343/* Function: fr_nat_newfrag */
344/* Returns: int - 0 == success, -1 == error */
345/* Parameters: fin(I) - pointer to packet information */
346/* nat(I) - pointer to NAT structure */
347/* */
348/* Create a new NAT fragment cache entry based on the current packet and */
349/* the NAT structure for this "session". */
350/* ------------------------------------------------------------------------ */
351int fr_nat_newfrag(fin, pass, nat)
352fr_info_t *fin;
353u_32_t pass;
354nat_t *nat;
355{
356 ipfr_t *fra;
357
358 if ((fin->fin_v != 4) || (fr_frag_lock != 0))
359 return 0;
360
361 WRITE_ENTER(&ipf_natfrag);
362 fra = ipfr_newfrag(fin, pass, ipfr_nattab);
363 if (fra != NULL) {
364 fra->ipfr_data = nat;
365 nat->nat_data = fra;
366 *ipfr_nattail = fra;
367 fra->ipfr_prev = ipfr_nattail;
368 ipfr_nattail = &fra->ipfr_next;
369 fra->ipfr_next = NULL;
370 }
371 RWLOCK_EXIT(&ipf_natfrag);
372 return fra ? 0 : -1;
373}
374
375
376/* ------------------------------------------------------------------------ */
377/* Function: fr_ipid_newfrag */
378/* Returns: int - 0 == success, -1 == error */
379/* Parameters: fin(I) - pointer to packet information */
380/* ipid(I) - new IP ID for this fragmented packet */
381/* */
382/* Create a new fragment cache entry for this packet and store, as a data */
383/* pointer, the new IP ID value. */
384/* ------------------------------------------------------------------------ */
385int fr_ipid_newfrag(fin, ipid)
386fr_info_t *fin;
387u_32_t ipid;
388{
389 ipfr_t *fra;
390
391 if ((fin->fin_v != 4) || (fr_frag_lock))
392 return 0;
393
394 WRITE_ENTER(&ipf_ipidfrag);
395 fra = ipfr_newfrag(fin, 0, ipfr_ipidtab);
396 if (fra != NULL) {
397 fra->ipfr_data = (void *)ipid;
398 *ipfr_ipidtail = fra;
399 fra->ipfr_prev = ipfr_ipidtail;
400 ipfr_ipidtail = &fra->ipfr_next;
401 fra->ipfr_next = NULL;
402 }
403 RWLOCK_EXIT(&ipf_ipidfrag);
404 return fra ? 0 : -1;
405}
406
407
408/* ------------------------------------------------------------------------ */
409/* Function: fr_fraglookup */
410/* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */
411/* matching entry in the frag table, else NULL */
412/* Parameters: fin(I) - pointer to packet information */
413/* table(I) - pointer to fragment cache table to search */
414/* */
415/* Check the fragment cache to see if there is already a record of this */
416/* packet with its filter result known. */
417/* ------------------------------------------------------------------------ */
418static ipfr_t *fr_fraglookup(fin, table)
419fr_info_t *fin;
420ipfr_t *table[];
421{
422 ipfr_t *f, frag;
423 u_int idx;
424 ip_t *ip;
425
426 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG)
427 return NULL;
428
429 /*
430 * For fragments, we record protocol, packet id, TOS and both IP#'s
431 * (these should all be the same for all fragments of a packet).
432 *
433 * build up a hash value to index the table with.
434 */
435 ip = fin->fin_ip;
436 frag.ipfr_p = ip->ip_p;
437 idx = ip->ip_p;
438 frag.ipfr_id = ip->ip_id;
439 idx += ip->ip_id;
440 frag.ipfr_tos = ip->ip_tos;
441 frag.ipfr_src.s_addr = ip->ip_src.s_addr;
442 idx += ip->ip_src.s_addr;
443 frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
444 idx += ip->ip_dst.s_addr;
445 frag.ipfr_ifp = fin->fin_ifp;
446 idx *= 127;
447 idx %= IPFT_SIZE;
448
449 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
450 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
451 frag.ipfr_auth = fin->fin_fi.fi_auth;
452
453 /*
454 * check the table, careful to only compare the right amount of data
455 */
456 for (f = table[idx]; f; f = f->ipfr_hnext)
457 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp,
458 IPFR_CMPSZ)) {
459 u_short off;
460
461 /*
462 * We don't want to let short packets match because
463 * they could be compromising the security of other
464 * rules that want to match on layer 4 fields (and
465 * can't because they have been fragmented off.)
466 * Why do this check here? The counter acts as an
467 * indicator of this kind of attack, whereas if it was
468 * elsewhere, it wouldn't know if other matching
469 * packets had been seen.
470 */
471 if (fin->fin_flx & FI_SHORT) {
472 ATOMIC_INCL(ipfr_stats.ifs_short);
473 continue;
474 }
475
476 /*
477 * XXX - We really need to be guarding against the
478 * retransmission of (src,dst,id,offset-range) here
479 * because a fragmented packet is never resent with
480 * the same IP ID# (or shouldn't).
481 */
482 off = ip->ip_off & IP_OFFMASK;
483 if (f->ipfr_seen0) {
484 if (off == 0) {
485 ATOMIC_INCL(ipfr_stats.ifs_retrans0);
486 continue;
487 }
488 } else if (off == 0)
489 f->ipfr_seen0 = 1;
490
491 if (f != table[idx]) {
492 ipfr_t **fp;
493
494 /*
495 * Move fragment info. to the top of the list
496 * to speed up searches. First, delink...
497 */
498 fp = f->ipfr_hprev;
499 (*fp) = f->ipfr_hnext;
500 if (f->ipfr_hnext != NULL)
501 f->ipfr_hnext->ipfr_hprev = fp;
502 /*
503 * Then put back at the top of the chain.
504 */
505 f->ipfr_hnext = table[idx];
506 table[idx]->ipfr_hprev = &f->ipfr_hnext;
507 f->ipfr_hprev = table + idx;
508 table[idx] = f;
509 }
510
511 /*
512 * If we've follwed the fragments, and this is the
513 * last (in order), shrink expiration time.
514 */
515 if (off == f->ipfr_off) {
516 if (!(ip->ip_off & IP_MF))
517 f->ipfr_ttl = fr_ticks + 1;
518 f->ipfr_off = (fin->fin_dlen >> 3) + off;
519 } else if (f->ipfr_pass & FR_FRSTRICT)
520 continue;
521 ATOMIC_INCL(ipfr_stats.ifs_hits);
522 return f;
523 }
524 return NULL;
525}
526
527
528/* ------------------------------------------------------------------------ */
529/* Function: fr_nat_knownfrag */
530/* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */
531/* match found, else NULL */
532/* Parameters: fin(I) - pointer to packet information */
533/* */
534/* Functional interface for NAT lookups of the NAT fragment cache */
535/* ------------------------------------------------------------------------ */
536nat_t *fr_nat_knownfrag(fin)
537fr_info_t *fin;
538{
539 nat_t *nat;
540 ipfr_t *ipf;
541
542 if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_natlist)
543 return NULL;
544 READ_ENTER(&ipf_natfrag);
545 ipf = fr_fraglookup(fin, ipfr_nattab);
546 if (ipf != NULL) {
547 nat = ipf->ipfr_data;
548 /*
549 * This is the last fragment for this packet.
550 */
551 if ((ipf->ipfr_ttl == fr_ticks + 1) && (nat != NULL)) {
552 nat->nat_data = NULL;
553 ipf->ipfr_data = NULL;
554 }
555 } else
556 nat = NULL;
557 RWLOCK_EXIT(&ipf_natfrag);
558 return nat;
559}
560
561
562/* ------------------------------------------------------------------------ */
563/* Function: fr_ipid_knownfrag */
564/* Returns: u_32_t - IPv4 ID for this packet if match found, else */
565/* return 0xfffffff to indicate no match. */
566/* Parameters: fin(I) - pointer to packet information */
567/* */
568/* Functional interface for IP ID lookups of the IP ID fragment cache */
569/* ------------------------------------------------------------------------ */
570u_32_t fr_ipid_knownfrag(fin)
571fr_info_t *fin;
572{
573 ipfr_t *ipf;
574 u_32_t id;
575
576 if ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_ipidlist)
577 return 0xffffffff;
578
579 READ_ENTER(&ipf_ipidfrag);
580 ipf = fr_fraglookup(fin, ipfr_ipidtab);
581 if (ipf != NULL)
582 id = (u_32_t)ipf->ipfr_data;
583 else
584 id = 0xffffffff;
585 RWLOCK_EXIT(&ipf_ipidfrag);
586 return id;
587}
588
589
590/* ------------------------------------------------------------------------ */
591/* Function: fr_knownfrag */
592/* Returns: frentry_t* - pointer to filter rule if a match is found in */
593/* the frag cache table, else NULL. */
594/* Parameters: fin(I) - pointer to packet information */
595/* passp(O) - pointer to where to store rule flags resturned */
596/* */
597/* Functional interface for normal lookups of the fragment cache. If a */
598/* match is found, return the rule pointer and flags from the rule, except */
599/* that if FR_LOGFIRST is set, reset FR_LOG. */
600/* ------------------------------------------------------------------------ */
601frentry_t *fr_knownfrag(fin, passp)
602fr_info_t *fin;
603u_32_t *passp;
604{
605 frentry_t *fr = NULL;
606 ipfr_t *fra;
607 u_32_t pass;
608
609 if ((fin->fin_v != 4) || (fr_frag_lock) || (ipfr_list == NULL))
610 return NULL;
611
612 READ_ENTER(&ipf_frag);
613 fra = fr_fraglookup(fin, ipfr_heads);
614 if (fra != NULL) {
615 fr = fra->ipfr_rule;
616 fin->fin_fr = fr;
617 if (fr != NULL) {
618 pass = fr->fr_flags;
619 if ((pass & FR_LOGFIRST) != 0)
620 pass &= ~(FR_LOGFIRST|FR_LOG);
621 *passp = pass;
622 }
623 }
624 RWLOCK_EXIT(&ipf_frag);
625 return fr;
626}
627
628
629/* ------------------------------------------------------------------------ */
630/* Function: fr_forget */
631/* Returns: Nil */
632/* Parameters: ptr(I) - pointer to data structure */
633/* */
634/* Search through all of the fragment cache entries and wherever a pointer */
635/* is found to match ptr, reset it to NULL. */
636/* ------------------------------------------------------------------------ */
637void fr_forget(ptr)
638void *ptr;
639{
640 ipfr_t *fr;
641
642 WRITE_ENTER(&ipf_frag);
643 for (fr = ipfr_list; fr; fr = fr->ipfr_next)
644 if (fr->ipfr_data == ptr)
645 fr->ipfr_data = NULL;
646 RWLOCK_EXIT(&ipf_frag);
647}
648
649
650/* ------------------------------------------------------------------------ */
651/* Function: fr_forgetnat */
652/* Returns: Nil */
653/* Parameters: ptr(I) - pointer to data structure */
654/* */
655/* Search through all of the fragment cache entries for NAT and wherever a */
656/* pointer is found to match ptr, reset it to NULL. */
657/* ------------------------------------------------------------------------ */
658void fr_forgetnat(ptr)
659void *ptr;
660{
661 ipfr_t *fr;
662
663 WRITE_ENTER(&ipf_natfrag);
664 for (fr = ipfr_natlist; fr; fr = fr->ipfr_next)
665 if (fr->ipfr_data == ptr)
666 fr->ipfr_data = NULL;
667 RWLOCK_EXIT(&ipf_natfrag);
668}
669
670
671/* ------------------------------------------------------------------------ */
672/* Function: fr_fragdelete */
673/* Returns: Nil */
674/* Parameters: fra(I) - pointer to fragment structure to delete */
675/* tail(IO) - pointer to the pointer to the tail of the frag */
676/* list */
677/* */
678/* Remove a fragment cache table entry from the table & list. Also free */
679/* the filter rule it is associated with it if it is no longer used as a */
680/* result of decreasing the reference count. */
681/* ------------------------------------------------------------------------ */
682static void fr_fragdelete(fra, tail)
683ipfr_t *fra, ***tail;
684{
685 frentry_t *fr;
686
687 fr = fra->ipfr_rule;
688 if (fr != NULL)
689 (void)fr_derefrule(&fr);
690
691 if (fra->ipfr_next)
692 fra->ipfr_next->ipfr_prev = fra->ipfr_prev;
693 *fra->ipfr_prev = fra->ipfr_next;
694 if (*tail == &fra->ipfr_next)
695 *tail = fra->ipfr_prev;
696
697 if (fra->ipfr_hnext)
698 fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev;
699 *fra->ipfr_hprev = fra->ipfr_hnext;
700 KFREE(fra);
701}
702
703
704/* ------------------------------------------------------------------------ */
705/* Function: fr_fragclear */
706/* Returns: Nil */
707/* Parameters: Nil */
708/* */
709/* Free memory in use by fragment state information kept. Do the normal */
710/* fragment state stuff first and then the NAT-fragment table. */
711/* ------------------------------------------------------------------------ */
712void fr_fragclear()
713{
714 ipfr_t *fra;
715 nat_t *nat;
716
717 WRITE_ENTER(&ipf_frag);
718 while ((fra = ipfr_list) != NULL)
719 fr_fragdelete(fra, &ipfr_tail);
720 ipfr_tail = &ipfr_list;
721 RWLOCK_EXIT(&ipf_frag);
722
723 WRITE_ENTER(&ipf_nat);
724 WRITE_ENTER(&ipf_natfrag);
725 while ((fra = ipfr_natlist) != NULL) {
726 nat = fra->ipfr_data;
727 if (nat != NULL) {
728 if (nat->nat_data == fra)
729 nat->nat_data = NULL;
730 }
731 fr_fragdelete(fra, &ipfr_nattail);
732 }
733 ipfr_nattail = &ipfr_natlist;
734 RWLOCK_EXIT(&ipf_natfrag);
735 RWLOCK_EXIT(&ipf_nat);
736}
737
738
739/* ------------------------------------------------------------------------ */
740/* Function: fr_fragexpire */
741/* Returns: Nil */
742/* Parameters: Nil */
743/* */
744/* Expire entries in the fragment cache table that have been there too long */
745/* ------------------------------------------------------------------------ */
746void fr_fragexpire()
747{
748 ipfr_t **fp, *fra;
749 nat_t *nat;
750#if defined(USE_SPL) && defined(_KERNEL)
751 int s;
752#endif
753
754 if (fr_frag_lock)
755 return;
756
757 SPL_NET(s);
758 WRITE_ENTER(&ipf_frag);
759 /*
760 * Go through the entire table, looking for entries to expire,
761 * which is indicated by the ttl being less than or equal to fr_ticks.
762 */
763 for (fp = &ipfr_list; ((fra = *fp) != NULL); ) {
764 if (fra->ipfr_ttl > fr_ticks)
765 break;
766 fr_fragdelete(fra, &ipfr_tail);
767 ipfr_stats.ifs_expire++;
768 ipfr_inuse--;
769 }
770 RWLOCK_EXIT(&ipf_frag);
771
772 WRITE_ENTER(&ipf_ipidfrag);
773 for (fp = &ipfr_ipidlist; ((fra = *fp) != NULL); ) {
774 if (fra->ipfr_ttl > fr_ticks)
775 break;
776 fr_fragdelete(fra, &ipfr_ipidtail);
777 ipfr_stats.ifs_expire++;
778 ipfr_inuse--;
779 }
780 RWLOCK_EXIT(&ipf_ipidfrag);
781
782 /*
783 * Same again for the NAT table, except that if the structure also
784 * still points to a NAT structure, and the NAT structure points back
785 * at the one to be free'd, NULL the reference from the NAT struct.
786 * NOTE: We need to grab both mutex's early, and in this order so as
787 * to prevent a deadlock if both try to expire at the same time.
788 */
789 WRITE_ENTER(&ipf_nat);
790 WRITE_ENTER(&ipf_natfrag);
791 for (fp = &ipfr_natlist; ((fra = *fp) != NULL); ) {
792 if (fra->ipfr_ttl > fr_ticks)
793 break;
794 nat = fra->ipfr_data;
795 if (nat != NULL) {
796 if (nat->nat_data == fra)
797 nat->nat_data = NULL;
798 }
799 fr_fragdelete(fra, &ipfr_nattail);
800 ipfr_stats.ifs_expire++;
801 ipfr_inuse--;
802 }
803 RWLOCK_EXIT(&ipf_natfrag);
804 RWLOCK_EXIT(&ipf_nat);
805 SPL_X(s);
806}
807
808
809/* ------------------------------------------------------------------------ */
810/* Function: fr_slowtimer */
811/* Returns: Nil */
812/* Parameters: Nil */
813/* */
814/* Slowly expire held state for fragments. Timeouts are set * in */
815/* expectation of this being called twice per second. */
816/* ------------------------------------------------------------------------ */
817#if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \
818 !defined(__osf__))
819# if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi))
820void fr_slowtimer __P((void *ptr))
821# else
822int fr_slowtimer()
823# endif
824{
825 READ_ENTER(&ipf_global);
826
827 fr_fragexpire();
828 fr_timeoutstate();
829 fr_natexpire();
830 fr_authexpire();
831 fr_ticks++;
832 if (fr_running <= 0)
833 goto done;
834# ifdef _KERNEL
835# if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000)
836 callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL);
837# else
838# if defined(__OpenBSD__)
839 timeout_add(&fr_slowtimer_ch, hz/2);
840# else
841# if (__FreeBSD_version >= 300000)
842 fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2);
843# else
844# ifdef linux
845 ;
846# else
847 timeout(fr_slowtimer, NULL, hz/2);
848# endif
849# endif /* FreeBSD */
850# endif /* OpenBSD */
851# endif /* NetBSD */
852# endif
853done:
854 RWLOCK_EXIT(&ipf_global);
855# if (BSD < 199103) || !defined(_KERNEL)
856 return 0;
857# endif
858}
859#endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */