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