Merge from vendor branch HEIMDAL:
[dragonfly.git] / sys / contrib / ipfilter / netinet / ip_frag.c
1 /*
2  * Copyright (C) 1993-2001 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * @(#)ip_frag.c    1.11 3/24/96 (C) 1993-2000 Darren Reed
7  * @(#)$Id: ip_frag.c,v 2.10.2.24 2002/08/28 12:41:04 darrenr Exp $
8  * $FreeBSD: src/sys/contrib/ipfilter/netinet/ip_frag.c,v 1.15.2.7 2004/07/04 09:24:38 darrenr Exp $
9  * $DragonFly: src/sys/contrib/ipfilter/netinet/ip_frag.c,v 1.7 2004/09/16 23:40:24 joerg Exp $
10  */
11 #if defined(KERNEL) && !defined(_KERNEL)
12 # define      _KERNEL
13 #endif
14
15 #if defined(__sgi) && (IRIX > 602)
16 # include <sys/ptimers.h>
17 #endif
18 #include <sys/errno.h>
19 #include <sys/types.h>
20 #include <sys/param.h>
21 #include <sys/time.h>
22 #include <sys/file.h>
23 #if !defined(_KERNEL) && !defined(KERNEL)
24 # include <stdio.h>
25 # include <string.h>
26 # include <stdlib.h>
27 #endif
28 #if (defined(KERNEL) || defined(_KERNEL)) && (defined(__DragonFly__) || __FreeBSD_version >= 220000)
29 # include <sys/filio.h>
30 # include <sys/fcntl.h>
31 #else
32 # include <sys/ioctl.h>
33 #endif
34 #ifndef linux
35 # include <sys/protosw.h>
36 #endif
37 #include <sys/socket.h>
38 #if defined(_KERNEL) && !defined(linux)
39 # include <sys/systm.h>
40 #endif
41 #if !defined(__SVR4) && !defined(__svr4__)
42 # if defined(_KERNEL) && !defined(__sgi)
43 #  include <sys/kernel.h>
44 # endif
45 # ifndef linux
46 #  include <sys/mbuf.h>
47 # endif
48 #else
49 # include <sys/byteorder.h>
50 # ifdef _KERNEL
51 #  include <sys/dditypes.h>
52 # endif
53 # include <sys/stream.h>
54 # include <sys/kmem.h>
55 #endif
56 #include <net/if.h>
57 #ifdef sun
58 # include <net/af.h>
59 #endif
60 #include <net/route.h>
61 #include <netinet/in.h>
62 #include <netinet/in_systm.h>
63 #include <netinet/ip.h>
64 #ifndef linux
65 # include <netinet/ip_var.h>
66 #endif
67 #include <netinet/tcp.h>
68 #include <netinet/udp.h>
69 #include <netinet/ip_icmp.h>
70 #include "ip_compat.h"
71 #include <netinet/tcpip.h>
72 #include "ip_fil.h"
73 #include "ip_nat.h"
74 #include "ip_frag.h"
75 #include "ip_state.h"
76 #include "ip_auth.h"
77 #if defined(__DragonFly__) || __FreeBSD_version >= 300000
78 # include <sys/malloc.h>
79 # if (defined(KERNEL) || defined(_KERNEL))
80 #  ifndef IPFILTER_LKM
81 #   include <sys/libkern.h>
82 #   include <sys/systm.h>
83 #  endif
84 extern struct callout ipfr_slowtimer_ch;
85 # endif
86 #endif
87 #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000)
88 # include <sys/callout.h>
89 extern struct callout ipfr_slowtimer_ch;
90 #endif
91 #if defined(__OpenBSD__)
92 # include <sys/timeout.h>
93 extern struct timeout ipfr_slowtimer_ch;
94 #endif
95
96 static const char sccsid[] = "@(#)ip_frag.c    1.11 3/24/96 (C) 1993-2000 Darren Reed";
97
98 static ipfr_t   *ipfr_heads[IPFT_SIZE];
99 static ipfr_t   *ipfr_nattab[IPFT_SIZE];
100 static ipfrstat_t ipfr_stats;
101 static int      ipfr_inuse = 0;
102
103 int     fr_ipfrttl = 120;       /* 60 seconds */
104 int     fr_frag_lock = 0;
105
106 #ifdef _KERNEL
107 # if SOLARIS2 >= 7
108 extern  timeout_id_t    ipfr_timer_id;
109 # else
110 extern  int     ipfr_timer_id;
111 # endif
112 #endif
113 #if     (SOLARIS || defined(__sgi)) && defined(_KERNEL)
114 extern  KRWLOCK_T       ipf_frag, ipf_natfrag, ipf_nat, ipf_mutex;
115 # if    SOLARIS
116 extern  KRWLOCK_T       ipf_solaris;
117 # else
118 KRWLOCK_T       ipf_solaris;
119 # endif
120 extern  kmutex_t        ipf_rw;
121 #endif
122
123
124 static ipfr_t *ipfr_new (ip_t *, fr_info_t *, ipfr_t **);
125 static ipfr_t *ipfr_lookup (ip_t *, fr_info_t *, ipfr_t **);
126 static void ipfr_delete (ipfr_t *);
127
128
129 ipfrstat_t *ipfr_fragstats()
130 {
131         ipfr_stats.ifs_table = ipfr_heads;
132         ipfr_stats.ifs_nattab = ipfr_nattab;
133         ipfr_stats.ifs_inuse = ipfr_inuse;
134         return &ipfr_stats;
135 }
136
137
138 /*
139  * add a new entry to the fragment cache, registering it as having come
140  * through this box, with the result of the filter operation.
141  */
142 static ipfr_t *ipfr_new(ip, fin, table)
143 ip_t *ip;
144 fr_info_t *fin;
145 ipfr_t *table[];
146 {
147         ipfr_t **fp, *fra, frag;
148         u_int idx, off;
149
150         if (ipfr_inuse >= IPFT_SIZE)
151                 return NULL;
152
153         if (!(fin->fin_fi.fi_fl & FI_FRAG))
154                 return NULL;
155
156         frag.ipfr_p = ip->ip_p;
157         idx = ip->ip_p;
158         frag.ipfr_id = ip->ip_id;
159         idx += ip->ip_id;
160         frag.ipfr_tos = ip->ip_tos;
161         frag.ipfr_src.s_addr = ip->ip_src.s_addr;
162         idx += ip->ip_src.s_addr;
163         frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
164         idx += ip->ip_dst.s_addr;
165         frag.ipfr_ifp = fin->fin_ifp;
166         idx *= 127;
167         idx %= IPFT_SIZE;
168
169         frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
170         frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
171         frag.ipfr_auth = fin->fin_fi.fi_auth;
172
173         /*
174          * first, make sure it isn't already there...
175          */
176         for (fp = &table[idx]; (fra = *fp); fp = &fra->ipfr_next)
177                 if (!bcmp((char *)&frag.ipfr_src, (char *)&fra->ipfr_src,
178                           IPFR_CMPSZ)) {
179                         ATOMIC_INCL(ipfr_stats.ifs_exists);
180                         return NULL;
181                 }
182
183         /*
184          * allocate some memory, if possible, if not, just record that we
185          * failed to do so.
186          */
187         KMALLOC(fra, ipfr_t *);
188         if (fra == NULL) {
189                 ATOMIC_INCL(ipfr_stats.ifs_nomem);
190                 return NULL;
191         }
192
193         if ((fra->ipfr_rule = fin->fin_fr) != NULL) {
194                 ATOMIC_INC32(fin->fin_fr->fr_ref);
195         }
196
197
198         /*
199          * Insert the fragment into the fragment table, copy the struct used
200          * in the search using bcopy rather than reassign each field.
201          * Set the ttl to the default.
202          */
203         if ((fra->ipfr_next = table[idx]))
204                 table[idx]->ipfr_prev = fra;
205         fra->ipfr_prev = NULL;
206         fra->ipfr_data = NULL;
207         table[idx] = fra;
208         bcopy((char *)&frag.ipfr_src, (char *)&fra->ipfr_src, IPFR_CMPSZ);
209         fra->ipfr_ttl = fr_ipfrttl;
210         /*
211          * Compute the offset of the expected start of the next packet.
212          */
213         off = ip->ip_off & IP_OFFMASK;
214         if (!off)
215                 fra->ipfr_seen0 = 1;
216         fra->ipfr_off = off + (fin->fin_dlen >> 3);
217         ATOMIC_INCL(ipfr_stats.ifs_new);
218         ATOMIC_INC32(ipfr_inuse);
219         return fra;
220 }
221
222
223 int ipfr_newfrag(ip, fin)
224 ip_t *ip;
225 fr_info_t *fin;
226 {
227         ipfr_t  *ipf;
228
229         if ((ip->ip_v != 4) || (fr_frag_lock))
230                 return -1;
231         WRITE_ENTER(&ipf_frag);
232         ipf = ipfr_new(ip, fin, ipfr_heads);
233         RWLOCK_EXIT(&ipf_frag);
234         if (ipf == NULL) {
235                 ATOMIC_INCL(frstats[fin->fin_out].fr_bnfr);
236                 return -1;
237         }
238         ATOMIC_INCL(frstats[fin->fin_out].fr_nfr);
239         return 0;
240 }
241
242
243 int ipfr_nat_newfrag(ip, fin, nat)
244 ip_t *ip;
245 fr_info_t *fin;
246 nat_t *nat;
247 {
248         ipfr_t  *ipf;
249         int off;
250
251         if ((ip->ip_v != 4) || (fr_frag_lock))
252                 return -1;
253
254         off = fin->fin_off;
255         off <<= 3;
256         if ((off + fin->fin_dlen) > 0xffff || (fin->fin_dlen == 0))
257                 return -1;
258
259         WRITE_ENTER(&ipf_natfrag);
260         ipf = ipfr_new(ip, fin, ipfr_nattab);
261         if (ipf != NULL) {
262                 ipf->ipfr_data = nat;
263                 nat->nat_data = ipf;
264         }
265         RWLOCK_EXIT(&ipf_natfrag);
266         return ipf ? 0 : -1;
267 }
268
269
270 /*
271  * check the fragment cache to see if there is already a record of this packet
272  * with its filter result known.
273  */
274 static ipfr_t *ipfr_lookup(ip, fin, table)
275 ip_t *ip;
276 fr_info_t *fin;
277 ipfr_t *table[];
278 {
279         ipfr_t  *f, frag;
280         u_int   idx;
281
282         /*
283          * For fragments, we record protocol, packet id, TOS and both IP#'s
284          * (these should all be the same for all fragments of a packet).
285          *
286          * build up a hash value to index the table with.
287          */
288         frag.ipfr_p = ip->ip_p;
289         idx = ip->ip_p;
290         frag.ipfr_id = ip->ip_id;
291         idx += ip->ip_id;
292         frag.ipfr_tos = ip->ip_tos;
293         frag.ipfr_src.s_addr = ip->ip_src.s_addr;
294         idx += ip->ip_src.s_addr;
295         frag.ipfr_dst.s_addr = ip->ip_dst.s_addr;
296         idx += ip->ip_dst.s_addr;
297         frag.ipfr_ifp = fin->fin_ifp;
298         idx *= 127;
299         idx %= IPFT_SIZE;
300
301         frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY;
302         frag.ipfr_secmsk = fin->fin_fi.fi_secmsk;
303         frag.ipfr_auth = fin->fin_fi.fi_auth;
304
305         /*
306          * check the table, careful to only compare the right amount of data
307          */
308         for (f = table[idx]; f; f = f->ipfr_next)
309                 if (!bcmp((char *)&frag.ipfr_src, (char *)&f->ipfr_src,
310                           IPFR_CMPSZ)) {
311                         u_short atoff, off;
312
313                         off = fin->fin_off;
314
315                         /*
316                          * XXX - We really need to be guarding against the
317                          * retransmission of (src,dst,id,offset-range) here
318                          * because a fragmented packet is never resent with
319                          * the same IP ID#.
320                          */
321                         if (f->ipfr_seen0) {
322                                 if (!off || (fin->fin_fl & FI_SHORT))
323                                         continue;
324                         } else if (!off)
325                                 f->ipfr_seen0 = 1;
326
327                         if (f != table[idx]) {
328                                 /*
329                                  * move fragment info. to the top of the list
330                                  * to speed up searches.
331                                  */
332                                 if ((f->ipfr_prev->ipfr_next = f->ipfr_next))
333                                         f->ipfr_next->ipfr_prev = f->ipfr_prev;
334                                 f->ipfr_next = table[idx];
335                                 table[idx]->ipfr_prev = f;
336                                 f->ipfr_prev = NULL;
337                                 table[idx] = f;
338                         }
339                         atoff = off + (fin->fin_dlen >> 3);
340                         /*
341                          * If we've follwed the fragments, and this is the
342                          * last (in order), shrink expiration time.
343                          */
344                         if (off == f->ipfr_off) {
345                                 if (!(ip->ip_off & IP_MF))
346                                         f->ipfr_ttl = 1;
347                                 else
348                                         f->ipfr_off = atoff;
349                         }
350                         ATOMIC_INCL(ipfr_stats.ifs_hits);
351                         return f;
352                 }
353         return NULL;
354 }
355
356
357 /*
358  * functional interface for NAT lookups of the NAT fragment cache
359  */
360 nat_t *ipfr_nat_knownfrag(ip, fin)
361 ip_t *ip;
362 fr_info_t *fin;
363 {
364         ipfr_t *ipf;
365         nat_t *nat;
366         int off;
367
368         if ((fin->fin_v != 4) || (fr_frag_lock))
369                 return NULL;
370
371         off = fin->fin_off;
372         off <<= 3;
373         if ((off + fin->fin_dlen) > 0xffff || (fin->fin_dlen == 0))
374                 return NULL;
375
376         READ_ENTER(&ipf_natfrag);
377         ipf = ipfr_lookup(ip, fin, ipfr_nattab);
378         if (ipf != NULL) {
379                 nat = ipf->ipfr_data;
380                 /*
381                  * This is the last fragment for this packet.
382                  */
383                 if ((ipf->ipfr_ttl == 1) && (nat != NULL)) {
384                         nat->nat_data = NULL;
385                         ipf->ipfr_data = NULL;
386                 }
387         } else
388                 nat = NULL;
389         RWLOCK_EXIT(&ipf_natfrag);
390         return nat;
391 }
392
393
394 /*
395  * functional interface for normal lookups of the fragment cache
396  */
397 frentry_t *ipfr_knownfrag(ip, fin)
398 ip_t *ip;
399 fr_info_t *fin;
400 {
401         frentry_t *fr;
402         ipfr_t *fra;
403         int off;
404
405         if ((fin->fin_v != 4) || (fr_frag_lock))
406                 return NULL;
407
408         off = fin->fin_off;
409         off <<= 3;
410         if ((off + fin->fin_dlen) > 0xffff || (fin->fin_dlen == 0))
411                 return NULL;
412
413         READ_ENTER(&ipf_frag);
414         fra = ipfr_lookup(ip, fin, ipfr_heads);
415         if (fra != NULL)
416                 fr = fra->ipfr_rule;
417         else
418                 fr = NULL;
419         RWLOCK_EXIT(&ipf_frag);
420         return fr;
421 }
422
423
424 /*
425  * forget any references to this external object.
426  */
427 void ipfr_forget(ptr)
428 void *ptr;
429 {
430         ipfr_t  *fr;
431         int     idx;
432
433         WRITE_ENTER(&ipf_frag);
434         for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
435                 for (fr = ipfr_heads[idx]; fr; fr = fr->ipfr_next)
436                         if (fr->ipfr_data == ptr)
437                                 fr->ipfr_data = NULL;
438
439         RWLOCK_EXIT(&ipf_frag);
440 }
441
442
443 /*
444  * forget any references to this external object.
445  */
446 void ipfr_forgetnat(nat)
447 void *nat;
448 {
449         ipfr_t  *fr;
450         int     idx;
451
452         WRITE_ENTER(&ipf_natfrag);
453         for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
454                 for (fr = ipfr_nattab[idx]; fr; fr = fr->ipfr_next)
455                         if (fr->ipfr_data == nat)
456                                 fr->ipfr_data = NULL;
457
458         RWLOCK_EXIT(&ipf_natfrag);
459 }
460
461
462 static void ipfr_delete(fra)
463 ipfr_t *fra;
464 {
465         frentry_t *fr;
466
467         fr = fra->ipfr_rule;
468         if (fr != NULL) {
469                 ATOMIC_DEC32(fr->fr_ref);
470                 if (fr->fr_ref == 0)
471                         KFREE(fr);
472         }
473         if (fra->ipfr_prev)
474                 fra->ipfr_prev->ipfr_next = fra->ipfr_next;
475         if (fra->ipfr_next)
476                 fra->ipfr_next->ipfr_prev = fra->ipfr_prev;
477         KFREE(fra);
478 }
479
480
481 /*
482  * Free memory in use by fragment state info. kept.
483  */
484 void ipfr_unload()
485 {
486         ipfr_t  **fp, *fra;
487         nat_t   *nat;
488         int     idx;
489
490         WRITE_ENTER(&ipf_frag);
491         for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
492                 for (fp = &ipfr_heads[idx]; (fra = *fp); ) {
493                         *fp = fra->ipfr_next;
494                         ipfr_delete(fra);
495                 }
496         RWLOCK_EXIT(&ipf_frag);
497
498         WRITE_ENTER(&ipf_nat);
499         WRITE_ENTER(&ipf_natfrag);
500         for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
501                 for (fp = &ipfr_nattab[idx]; (fra = *fp); ) {
502                         *fp = fra->ipfr_next;
503                         nat = fra->ipfr_data;
504                         if (nat != NULL) {
505                                 if (nat->nat_data == fra)
506                                         nat->nat_data = NULL;
507                         }
508                         ipfr_delete(fra);
509                 }
510         RWLOCK_EXIT(&ipf_natfrag);
511         RWLOCK_EXIT(&ipf_nat);
512 }
513
514
515 void ipfr_fragexpire()
516 {
517         ipfr_t  **fp, *fra;
518         nat_t   *nat;
519         int     idx;
520 #if defined(_KERNEL)
521 # if !SOLARIS
522         int     s;
523 # endif
524 #endif
525
526         if (fr_frag_lock)
527                 return;
528
529         SPL_NET(s);
530         WRITE_ENTER(&ipf_frag);
531
532         /*
533          * Go through the entire table, looking for entries to expire,
534          * decreasing the ttl by one for each entry.  If it reaches 0,
535          * remove it from the chain and free it.
536          */
537         for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
538                 for (fp = &ipfr_heads[idx]; (fra = *fp); ) {
539                         --fra->ipfr_ttl;
540                         if (fra->ipfr_ttl == 0) {
541                                 *fp = fra->ipfr_next;
542                                 ipfr_delete(fra);
543                                 ATOMIC_INCL(ipfr_stats.ifs_expire);
544                                 ATOMIC_DEC32(ipfr_inuse);
545                         } else
546                                 fp = &fra->ipfr_next;
547                 }
548         RWLOCK_EXIT(&ipf_frag);
549
550         /*
551          * Same again for the NAT table, except that if the structure also
552          * still points to a NAT structure, and the NAT structure points back
553          * at the one to be free'd, NULL the reference from the NAT struct.
554          * NOTE: We need to grab both mutex's early, and in this order so as
555          * to prevent a deadlock if both try to expire at the same time.
556          */
557         WRITE_ENTER(&ipf_nat);
558         WRITE_ENTER(&ipf_natfrag);
559         for (idx = IPFT_SIZE - 1; idx >= 0; idx--)
560                 for (fp = &ipfr_nattab[idx]; (fra = *fp); ) {
561                         --fra->ipfr_ttl;
562                         if (fra->ipfr_ttl == 0) {
563                                 ATOMIC_INCL(ipfr_stats.ifs_expire);
564                                 ATOMIC_DEC32(ipfr_inuse);
565                                 nat = fra->ipfr_data;
566                                 if (nat != NULL) {
567                                         if (nat->nat_data == fra)
568                                                 nat->nat_data = NULL;
569                                 }
570                                 *fp = fra->ipfr_next;
571                                 ipfr_delete(fra);
572                         } else
573                                 fp = &fra->ipfr_next;
574                 }
575         RWLOCK_EXIT(&ipf_natfrag);
576         RWLOCK_EXIT(&ipf_nat);
577         SPL_X(s);
578 }
579
580
581 /*
582  * Slowly expire held state for fragments.  Timeouts are set * in expectation
583  * of this being called twice per second.
584  */
585 #ifdef _KERNEL
586 # if (BSD >= 199306) || SOLARIS || defined(__sgi)
587 #  if defined(SOLARIS2) && (SOLARIS2 < 7)
588 void ipfr_slowtimer()
589 #  else
590 void ipfr_slowtimer (void *ptr)
591 #  endif
592 # else
593 int ipfr_slowtimer()
594 # endif
595 #else
596 void ipfr_slowtimer()
597 #endif
598 {
599 #if defined(_KERNEL) && SOLARIS
600         extern  int     fr_running;
601
602         if (fr_running <= 0) 
603                 return;
604         READ_ENTER(&ipf_solaris);
605 #endif
606
607 #if defined(__sgi) && defined(_KERNEL)
608         ipfilter_sgi_intfsync();
609 #endif
610
611         ipfr_fragexpire();
612         fr_timeoutstate();
613         ip_natexpire();
614         fr_authexpire();
615 #if defined(_KERNEL)
616 # if    SOLARIS
617         ipfr_timer_id = timeout(ipfr_slowtimer, NULL, drv_usectohz(500000));
618         RWLOCK_EXIT(&ipf_solaris);
619 # else
620 #  if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000)
621         callout_reset(&ipfr_slowtimer_ch, hz / 2, ipfr_slowtimer, NULL);
622 #  else
623 #   if defined(__DragonFly__) || __FreeBSD_version >= 300000
624         callout_reset(&ipfr_slowtimer_ch, hz / 2, ipfr_slowtimer, NULL);
625 #   else
626 #    if defined(__OpenBSD__)
627         timeout_add(&ipfr_slowtimer_ch, hz/2);
628 #    else
629         timeout(ipfr_slowtimer, NULL, hz/2);
630 #    endif
631 #   endif
632 #   if (BSD < 199306) && !defined(__sgi)
633         return 0;
634 #   endif /* FreeBSD */
635 #  endif /* NetBSD */
636 # endif /* SOLARIS */
637 #endif /* defined(_KERNEL) */
638 }