ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / net / ipv6 / netfilter / ip6t_frag.c
1 /* Kernel module to match FRAG parameters. */
2
3 /* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  */
9
10 #include <linux/module.h>
11 #include <linux/skbuff.h>
12 #include <linux/ipv6.h>
13 #include <linux/types.h>
14 #include <net/checksum.h>
15 #include <net/ipv6.h>
16
17 #include <asm/byteorder.h>
18
19 #include <linux/netfilter_ipv6/ip6_tables.h>
20 #include <linux/netfilter_ipv6/ip6t_frag.h>
21
22 MODULE_LICENSE("GPL");
23 MODULE_DESCRIPTION("IPv6 FRAG match");
24 MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
25
26 #if 0
27 #define DEBUGP printk
28 #else
29 #define DEBUGP(format, args...)
30 #endif
31
32 #if 0
33 #if     BYTE_ORDER == BIG_ENDIAN
34 #define IP6F_OFF_MASK       0xfff8  /* mask out offset from _offlg */
35 #define IP6F_RESERVED_MASK  0x0006  /* reserved bits in ip6f_offlg */
36 #define IP6F_MORE_FRAG      0x0001  /* more-fragments flag */
37 #else   /* BYTE_ORDER == LITTLE_ENDIAN */
38 #define IP6F_OFF_MASK       0xf8ff  /* mask out offset from _offlg */
39 #define IP6F_RESERVED_MASK  0x0600  /* reserved bits in ip6f_offlg */
40 #define IP6F_MORE_FRAG      0x0100  /* more-fragments flag */
41 #endif
42 #endif
43
44 #define IP6F_OFF_MASK       0xf8ff  /* mask out offset from _offlg */
45 #define IP6F_RESERVED_MASK  0x0600  /* reserved bits in ip6f_offlg */
46 #define IP6F_MORE_FRAG      0x0100  /* more-fragments flag */
47
48 struct fraghdr {
49        __u8    nexthdr;
50        __u8    hdrlen;
51        __u16   info;
52        __u32   id;
53 };
54
55 /* Returns 1 if the id is matched by the range, 0 otherwise */
56 static inline int
57 id_match(u_int32_t min, u_int32_t max, u_int32_t id, int invert)
58 {
59        int r=0;
60        DEBUGP("frag id_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
61               min,id,max);
62        r=(id >= min && id <= max) ^ invert;
63        DEBUGP(" result %s\n",r? "PASS" : "FAILED");
64        return r;
65 }
66
67 static int
68 match(const struct sk_buff *skb,
69       const struct net_device *in,
70       const struct net_device *out,
71       const void *matchinfo,
72       int offset,
73       const void *protohdr,
74       u_int16_t datalen,
75       int *hotdrop)
76 {
77        struct fraghdr *frag = NULL;
78        const struct ip6t_frag *fraginfo = matchinfo;
79        unsigned int temp;
80        int len;
81        u8 nexthdr;
82        unsigned int ptr;
83        unsigned int hdrlen = 0;
84
85        /* type of the 1st exthdr */
86        nexthdr = skb->nh.ipv6h->nexthdr;
87        /* pointer to the 1st exthdr */
88        ptr = sizeof(struct ipv6hdr);
89        /* available length */
90        len = skb->len - ptr;
91        temp = 0;
92
93         while (ip6t_ext_hdr(nexthdr)) {
94                struct ipv6_opt_hdr *hdr;
95
96               DEBUGP("ipv6_frag header iteration \n");
97
98               /* Is there enough space for the next ext header? */
99                 if (len < (int)sizeof(struct ipv6_opt_hdr))
100                         return 0;
101               /* No more exthdr -> evaluate */
102                 if (nexthdr == NEXTHDR_NONE) {
103                      break;
104               }
105               /* ESP -> evaluate */
106                 if (nexthdr == NEXTHDR_ESP) {
107                      break;
108               }
109
110               hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
111
112               /* Calculate the header length */
113                 if (nexthdr == NEXTHDR_FRAGMENT) {
114                         hdrlen = 8;
115                 } else if (nexthdr == NEXTHDR_AUTH)
116                         hdrlen = (hdr->hdrlen+2)<<2;
117                 else
118                         hdrlen = ipv6_optlen(hdr);
119
120               /* FRAG -> evaluate */
121                 if (nexthdr == NEXTHDR_FRAGMENT) {
122                      temp |= MASK_FRAGMENT;
123                      break;
124               }
125
126
127               /* set the flag */
128               switch (nexthdr){
129                      case NEXTHDR_HOP:
130                      case NEXTHDR_ROUTING:
131                      case NEXTHDR_FRAGMENT:
132                      case NEXTHDR_AUTH:
133                      case NEXTHDR_DEST:
134                             break;
135                      default:
136                             DEBUGP("ipv6_frag match: unknown nextheader %u\n",nexthdr);
137                             return 0;
138                             break;
139               }
140
141                 nexthdr = hdr->nexthdr;
142                 len -= hdrlen;
143                 ptr += hdrlen;
144                 if ( ptr > skb->len ) {
145                         DEBUGP("ipv6_frag: new pointer too large! \n");
146                         break;
147                 }
148         }
149
150        /* FRAG header not found */
151        if ( temp != MASK_FRAGMENT ) return 0;
152
153        if (len < (int)sizeof(struct fraghdr)){
154                *hotdrop = 1;
155                 return 0;
156        }
157
158        frag = (struct fraghdr *) (skb->data + ptr);
159
160        DEBUGP("IPv6 FRAG LEN %u %u ", hdrlen, frag->hdrlen);
161        DEBUGP("INFO %04X ", frag->info);
162        DEBUGP("OFFSET %04X ", frag->info & IP6F_OFF_MASK);
163        DEBUGP("RES %04X ", frag->info & IP6F_RESERVED_MASK);
164        DEBUGP("MF %04X ", frag->info & IP6F_MORE_FRAG);
165        DEBUGP("ID %u %08X\n", ntohl(frag->id), ntohl(frag->id));
166
167        DEBUGP("IPv6 FRAG id %02X ",
168                 (id_match(fraginfo->ids[0], fraginfo->ids[1],
169                            ntohl(frag->id),
170                            !!(fraginfo->invflags & IP6T_FRAG_INV_IDS))));
171        DEBUGP("len %02X %04X %02X ",
172                 fraginfo->hdrlen, hdrlen,
173                 (!fraginfo->hdrlen ||
174                            (fraginfo->hdrlen == hdrlen) ^
175                            !!(fraginfo->invflags & IP6T_FRAG_INV_LEN)));
176        DEBUGP("res %02X %02X %02X ", 
177                 (fraginfo->flags & IP6T_FRAG_RES), frag->info & IP6F_RESERVED_MASK,
178                 !((fraginfo->flags & IP6T_FRAG_RES) && (frag->info & IP6F_RESERVED_MASK)));
179        DEBUGP("first %02X %02X %02X ", 
180                 (fraginfo->flags & IP6T_FRAG_FST), frag->info & IP6F_OFF_MASK,
181                 !((fraginfo->flags & IP6T_FRAG_FST) && (frag->info & IP6F_OFF_MASK)));
182        DEBUGP("mf %02X %02X %02X ", 
183                 (fraginfo->flags & IP6T_FRAG_MF), frag->info & IP6F_MORE_FRAG,
184                 !((fraginfo->flags & IP6T_FRAG_MF) && !((frag->info & IP6F_MORE_FRAG))));
185        DEBUGP("last %02X %02X %02X\n", 
186                 (fraginfo->flags & IP6T_FRAG_NMF), frag->info & IP6F_MORE_FRAG,
187                 !((fraginfo->flags & IP6T_FRAG_NMF) && (frag->info & IP6F_MORE_FRAG)));
188
189        return (frag != NULL)
190                 &&
191                 (id_match(fraginfo->ids[0], fraginfo->ids[1],
192                            ntohl(frag->id),
193                            !!(fraginfo->invflags & IP6T_FRAG_INV_IDS)))
194                 &&
195                 (!fraginfo->hdrlen ||
196                            (fraginfo->hdrlen == hdrlen) ^
197                            !!(fraginfo->invflags & IP6T_FRAG_INV_LEN))
198                 &&
199                 !((fraginfo->flags & IP6T_FRAG_RES) && (frag->info & IP6F_RESERVED_MASK))
200                 &&
201                 !((fraginfo->flags & IP6T_FRAG_FST) && (frag->info & IP6F_OFF_MASK))
202                 &&
203                 !((fraginfo->flags & IP6T_FRAG_MF) && !((frag->info & IP6F_MORE_FRAG)))
204                 &&
205                 !((fraginfo->flags & IP6T_FRAG_NMF) && (frag->info & IP6F_MORE_FRAG));
206 }
207
208 /* Called when user tries to insert an entry of this type. */
209 static int
210 checkentry(const char *tablename,
211           const struct ip6t_ip6 *ip,
212           void *matchinfo,
213           unsigned int matchinfosize,
214           unsigned int hook_mask)
215 {
216        const struct ip6t_frag *fraginfo = matchinfo;
217
218        if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_frag))) {
219               DEBUGP("ip6t_frag: matchsize %u != %u\n",
220                       matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_frag)));
221               return 0;
222        }
223        if (fraginfo->invflags & ~IP6T_FRAG_INV_MASK) {
224               DEBUGP("ip6t_frag: unknown flags %X\n",
225                       fraginfo->invflags);
226               return 0;
227        }
228
229        return 1;
230 }
231
232 static struct ip6t_match frag_match = {
233         .name           = "frag",
234         .match          = &match,
235         .checkentry     = &checkentry,
236         .me             = THIS_MODULE,
237 };
238
239 static int __init init(void)
240 {
241        return ip6t_register_match(&frag_match);
242 }
243
244 static void __exit cleanup(void)
245 {
246        ip6t_unregister_match(&frag_match);
247 }
248
249 module_init(init);
250 module_exit(cleanup);