Auto-update from upstream
[pandora-kernel.git] / net / ipv6 / netfilter / ip6t_hbh.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        1
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 ptr;
67        unsigned int hdrlen = 0;
68        unsigned int ret = 0;
69        u8 _opttype, *tp = NULL;
70        u8 _optlen, *lp = NULL;
71        unsigned int optlen;
72        
73 #if HOPBYHOP
74         if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP, NULL) < 0)
75 #else
76         if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST, NULL) < 0)
77 #endif
78                 return 0;
79
80        oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
81        if (oh == NULL){
82                *hotdrop = 1;
83                 return 0;
84        }
85
86        hdrlen = ipv6_optlen(oh);
87        if (skb->len - ptr < hdrlen){
88                /* Packet smaller than it's length field */
89                 return 0;
90        }
91
92        DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen);
93
94        DEBUGP("len %02X %04X %02X ",
95                 optinfo->hdrlen, hdrlen,
96                 (!(optinfo->flags & IP6T_OPTS_LEN) ||
97                            ((optinfo->hdrlen == hdrlen) ^
98                            !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
99
100        ret = (oh != NULL)
101                 &&
102                 (!(optinfo->flags & IP6T_OPTS_LEN) ||
103                            ((optinfo->hdrlen == hdrlen) ^
104                            !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
105
106        ptr += 2;
107        hdrlen -= 2;
108        if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){
109                return ret;
110         } else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
111                 DEBUGP("Not strict - not implemented");
112         } else {
113                 DEBUGP("Strict ");
114                 DEBUGP("#%d ",optinfo->optsnr);
115                 for(temp=0; temp<optinfo->optsnr; temp++){
116                         /* type field exists ? */
117                         if (hdrlen < 1)
118                                 break;
119                         tp = skb_header_pointer(skb, ptr, sizeof(_opttype),
120                                                 &_opttype);
121                         if (tp == NULL)
122                                 break;
123
124                         /* Type check */
125                         if (*tp != (optinfo->opts[temp] & 0xFF00)>>8){
126                                 DEBUGP("Tbad %02X %02X\n",
127                                        *tp,
128                                        (optinfo->opts[temp] & 0xFF00)>>8);
129                                 return 0;
130                         } else {
131                                 DEBUGP("Tok ");
132                         }
133                         /* Length check */
134                         if (*tp) {
135                                 u16 spec_len;
136
137                                 /* length field exists ? */
138                                 if (hdrlen < 2)
139                                         break;
140                                 lp = skb_header_pointer(skb, ptr + 1,
141                                                         sizeof(_optlen),
142                                                         &_optlen);
143                                 if (lp == NULL)
144                                         break;
145                                 spec_len = optinfo->opts[temp] & 0x00FF;
146
147                                 if (spec_len != 0x00FF && spec_len != *lp) {
148                                         DEBUGP("Lbad %02X %04X\n", *lp,
149                                                spec_len);
150                                         return 0;
151                                 }
152                                 DEBUGP("Lok ");
153                                 optlen = *lp + 2;
154                         } else {
155                                 DEBUGP("Pad1\n");
156                                 optlen = 1;
157                         }
158
159                         /* Step to the next */
160                         DEBUGP("len%04X \n", optlen);
161
162                         if ((ptr > skb->len - optlen || hdrlen < optlen) &&
163                             (temp < optinfo->optsnr - 1)) {
164                                 DEBUGP("new pointer is too large! \n");
165                                 break;
166                         }
167                         ptr += optlen;
168                         hdrlen -= optlen;
169                 }
170                 if (temp == optinfo->optsnr)
171                         return ret;
172                 else return 0;
173         }
174
175         return 0;
176 }
177
178 /* Called when user tries to insert an entry of this type. */
179 static int
180 checkentry(const char *tablename,
181           const struct ip6t_ip6 *ip,
182           void *matchinfo,
183           unsigned int matchinfosize,
184           unsigned int hook_mask)
185 {
186        const struct ip6t_opts *optsinfo = matchinfo;
187
188        if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) {
189               DEBUGP("ip6t_opts: matchsize %u != %u\n",
190                       matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts)));
191               return 0;
192        }
193        if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
194               DEBUGP("ip6t_opts: unknown flags %X\n",
195                       optsinfo->invflags);
196               return 0;
197        }
198
199        return 1;
200 }
201
202 static struct ip6t_match opts_match = {
203 #if HOPBYHOP
204         .name           = "hbh",
205 #else
206         .name           = "dst",
207 #endif
208         .match          = &match,
209         .checkentry     = &checkentry,
210         .me             = THIS_MODULE,
211 };
212
213 static int __init init(void)
214 {
215        return ip6t_register_match(&opts_match);
216 }
217
218 static void __exit cleanup(void)
219 {
220        ip6t_unregister_match(&opts_match);
221 }
222
223 module_init(init);
224 module_exit(cleanup);