vserver 1.9.5.x5
[linux-2.6.git] / net / ipv6 / netfilter / ip6t_dst.c
1 /* Kernel module to match Hop-by-Hop and Destination 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_opts.h>
21
22 #define HOPBYHOP        0
23
24 MODULE_LICENSE("GPL");
25 #if HOPBYHOP
26 MODULE_DESCRIPTION("IPv6 HbH match");
27 #else
28 MODULE_DESCRIPTION("IPv6 DST match");
29 #endif
30 MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
31
32 #if 0
33 #define DEBUGP printk
34 #else
35 #define DEBUGP(format, args...)
36 #endif
37
38 /*
39  * (Type & 0xC0) >> 6
40  *      0       -> ignorable
41  *      1       -> must drop the packet
42  *      2       -> send ICMP PARM PROB regardless and drop packet
43  *      3       -> Send ICMP if not a multicast address and drop packet
44  *  (Type & 0x20) >> 5
45  *      0       -> invariant
46  *      1       -> can change the routing
47  *  (Type & 0x1F) Type
48  *      0       -> Pad1 (only 1 byte!)
49  *      1       -> PadN LENGTH info (total length = length + 2)
50  *      C0 | 2  -> JUMBO 4 x x x x ( xxxx > 64k )
51  *      5       -> RTALERT 2 x x
52  */
53
54 static int
55 match(const struct sk_buff *skb,
56       const struct net_device *in,
57       const struct net_device *out,
58       const void *matchinfo,
59       int offset,
60       unsigned int protoff,
61       int *hotdrop)
62 {
63        struct ipv6_opt_hdr _optsh, *oh;
64        const struct ip6t_opts *optinfo = matchinfo;
65        unsigned int temp;
66        unsigned int len;
67        u8 nexthdr;
68        unsigned int ptr;
69        unsigned int hdrlen = 0;
70        unsigned int ret = 0;
71        u8 _opttype, *tp = NULL;
72        u8 _optlen, *lp = NULL;
73        unsigned int optlen;
74        
75        /* type of the 1st exthdr */
76        nexthdr = skb->nh.ipv6h->nexthdr;
77        /* pointer to the 1st exthdr */
78        ptr = sizeof(struct ipv6hdr);
79        /* available length */
80        len = skb->len - ptr;
81        temp = 0;
82
83         while (ip6t_ext_hdr(nexthdr)) {
84                struct ipv6_opt_hdr _hdr, *hp;
85
86               DEBUGP("ipv6_opts header iteration \n");
87
88               /* Is there enough space for the next ext header? */
89                 if (len < (int)sizeof(struct ipv6_opt_hdr))
90                         return 0;
91               /* No more exthdr -> evaluate */
92                 if (nexthdr == NEXTHDR_NONE) {
93                      break;
94               }
95               /* ESP -> evaluate */
96                 if (nexthdr == NEXTHDR_ESP) {
97                      break;
98               }
99
100               hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
101               BUG_ON(hp == NULL);
102
103               /* Calculate the header length */
104                 if (nexthdr == NEXTHDR_FRAGMENT) {
105                         hdrlen = 8;
106                 } else if (nexthdr == NEXTHDR_AUTH)
107                         hdrlen = (hp->hdrlen+2)<<2;
108                 else
109                         hdrlen = ipv6_optlen(hp);
110
111               /* OPTS -> evaluate */
112 #if HOPBYHOP
113                 if (nexthdr == NEXTHDR_HOP) {
114                      temp |= MASK_HOPOPTS;
115 #else
116                 if (nexthdr == NEXTHDR_DEST) {
117                      temp |= MASK_DSTOPTS;
118 #endif
119                      break;
120               }
121
122
123               /* set the flag */
124               switch (nexthdr){
125                      case NEXTHDR_HOP:
126                      case NEXTHDR_ROUTING:
127                      case NEXTHDR_FRAGMENT:
128                      case NEXTHDR_AUTH:
129                      case NEXTHDR_DEST:
130                             break;
131                      default:
132                             DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr);
133                             return 0;
134                             break;
135               }
136
137                 nexthdr = hp->nexthdr;
138                 len -= hdrlen;
139                 ptr += hdrlen;
140                 if ( ptr > skb->len ) {
141                         DEBUGP("ipv6_opts: new pointer is too large! \n");
142                         break;
143                 }
144         }
145
146        /* OPTIONS header not found */
147 #if HOPBYHOP
148        if ( temp != MASK_HOPOPTS ) return 0;
149 #else
150        if ( temp != MASK_DSTOPTS ) return 0;
151 #endif
152
153        if (len < (int)sizeof(struct ipv6_opt_hdr)){
154                *hotdrop = 1;
155                 return 0;
156        }
157
158        if (len < hdrlen){
159                /* Packet smaller than it's length field */
160                 return 0;
161        }
162
163        oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
164        BUG_ON(oh == NULL);
165
166        DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen);
167
168        DEBUGP("len %02X %04X %02X ",
169                 optinfo->hdrlen, hdrlen,
170                 (!(optinfo->flags & IP6T_OPTS_LEN) ||
171                            ((optinfo->hdrlen == hdrlen) ^
172                            !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
173
174        ret = (oh != NULL)
175                 &&
176                 (!(optinfo->flags & IP6T_OPTS_LEN) ||
177                            ((optinfo->hdrlen == hdrlen) ^
178                            !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
179
180        ptr += 2;
181        hdrlen -= 2;
182        if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){
183                return ret;
184         } else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
185                 DEBUGP("Not strict - not implemented");
186         } else {
187                 DEBUGP("Strict ");
188                 DEBUGP("#%d ",optinfo->optsnr);
189                 for(temp=0; temp<optinfo->optsnr; temp++){
190                         /* type field exists ? */
191                         if (hdrlen < 1)
192                                 break;
193                         tp = skb_header_pointer(skb, ptr, sizeof(_opttype),
194                                                 &_opttype);
195                         if (tp == NULL)
196                                 break;
197
198                         /* Type check */
199                         if (*tp != (optinfo->opts[temp] & 0xFF00)>>8){
200                                 DEBUGP("Tbad %02X %02X\n",
201                                        *tp,
202                                        (optinfo->opts[temp] & 0xFF00)>>8);
203                                 return 0;
204                         } else {
205                                 DEBUGP("Tok ");
206                         }
207                         /* Length check */
208                         if (*tp) {
209                                 u16 spec_len;
210
211                                 /* length field exists ? */
212                                 if (hdrlen < 2)
213                                         break;
214                                 lp = skb_header_pointer(skb, ptr + 1,
215                                                         sizeof(_optlen),
216                                                         &_optlen);
217                                 if (lp == NULL)
218                                         break;
219                                 spec_len = optinfo->opts[temp] & 0x00FF;
220
221                                 if (spec_len != 0x00FF && spec_len != *lp) {
222                                         DEBUGP("Lbad %02X %04X\n", *lp,
223                                                spec_len);
224                                         return 0;
225                                 }
226                                 DEBUGP("Lok ");
227                                 optlen = *lp + 2;
228                         } else {
229                                 DEBUGP("Pad1\n");
230                                 optlen = 1;
231                         }
232
233                         /* Step to the next */
234                         DEBUGP("len%04X \n", optlen);
235
236                         if ((ptr > skb->len - optlen || hdrlen < optlen) &&
237                             (temp < optinfo->optsnr - 1)) {
238                                 DEBUGP("new pointer is too large! \n");
239                                 break;
240                         }
241                         ptr += optlen;
242                         hdrlen -= optlen;
243                 }
244                 if (temp == optinfo->optsnr)
245                         return ret;
246                 else return 0;
247         }
248
249         return 0;
250 }
251
252 /* Called when user tries to insert an entry of this type. */
253 static int
254 checkentry(const char *tablename,
255           const struct ip6t_ip6 *ip,
256           void *matchinfo,
257           unsigned int matchinfosize,
258           unsigned int hook_mask)
259 {
260        const struct ip6t_opts *optsinfo = matchinfo;
261
262        if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) {
263               DEBUGP("ip6t_opts: matchsize %u != %u\n",
264                       matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts)));
265               return 0;
266        }
267        if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
268               DEBUGP("ip6t_opts: unknown flags %X\n",
269                       optsinfo->invflags);
270               return 0;
271        }
272
273        return 1;
274 }
275
276 static struct ip6t_match opts_match = {
277 #if HOPBYHOP
278         .name           = "hbh",
279 #else
280         .name           = "dst",
281 #endif
282         .match          = &match,
283         .checkentry     = &checkentry,
284         .me             = THIS_MODULE,
285 };
286
287 static int __init init(void)
288 {
289        return ip6t_register_match(&opts_match);
290 }
291
292 static void __exit cleanup(void)
293 {
294        ip6t_unregister_match(&opts_match);
295 }
296
297 module_init(init);
298 module_exit(cleanup);