ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.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 LOW(n)          (n & 0x00FF)
23
24 #define HOPBYHOP        1
25
26 MODULE_LICENSE("GPL");
27 #if HOPBYHOP
28 MODULE_DESCRIPTION("IPv6 HbH match");
29 #else
30 MODULE_DESCRIPTION("IPv6 DST match");
31 #endif
32 MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
33
34 #if 0
35 #define DEBUGP printk
36 #else
37 #define DEBUGP(format, args...)
38 #endif
39
40 /*
41  * (Type & 0xC0) >> 6
42  *      0       -> ignorable
43  *      1       -> must drop the packet
44  *      2       -> send ICMP PARM PROB regardless and drop packet
45  *      3       -> Send ICMP if not a multicast address and drop packet
46  *  (Type & 0x20) >> 5
47  *      0       -> invariant
48  *      1       -> can change the routing
49  *  (Type & 0x1F) Type
50  *      0       -> PAD0 (only 1 byte!)
51  *      1       -> PAD1 LENGTH info (total length = length + 2)
52  *      C0 | 2  -> JUMBO 4 x x x x ( xxxx > 64k )
53  *      5       -> RTALERT 2 x x
54  */
55
56 static int
57 match(const struct sk_buff *skb,
58       const struct net_device *in,
59       const struct net_device *out,
60       const void *matchinfo,
61       int offset,
62       const void *protohdr,
63       u_int16_t datalen,
64       int *hotdrop)
65 {
66        struct ipv6_opt_hdr *optsh = NULL;
67        const struct ip6t_opts *optinfo = matchinfo;
68        unsigned int temp;
69        unsigned int len;
70        u8 nexthdr;
71        unsigned int ptr;
72        unsigned int hdrlen = 0;
73        unsigned int ret = 0;
74        u_int16_t *optdesc = NULL;
75        
76        /* type of the 1st exthdr */
77        nexthdr = skb->nh.ipv6h->nexthdr;
78        /* pointer to the 1st exthdr */
79        ptr = sizeof(struct ipv6hdr);
80        /* available length */
81        len = skb->len - ptr;
82        temp = 0;
83
84         while (ip6t_ext_hdr(nexthdr)) {
85                struct ipv6_opt_hdr *hdr;
86
87               DEBUGP("ipv6_opts header iteration \n");
88
89               /* Is there enough space for the next ext header? */
90                 if (len < (int)sizeof(struct ipv6_opt_hdr))
91                         return 0;
92               /* No more exthdr -> evaluate */
93                 if (nexthdr == NEXTHDR_NONE) {
94                      break;
95               }
96               /* ESP -> evaluate */
97                 if (nexthdr == NEXTHDR_ESP) {
98                      break;
99               }
100
101               hdr=(void *)(skb->data)+ptr;
102
103               /* Calculate the header length */
104                 if (nexthdr == NEXTHDR_FRAGMENT) {
105                         hdrlen = 8;
106                 } else if (nexthdr == NEXTHDR_AUTH)
107                         hdrlen = (hdr->hdrlen+2)<<2;
108                 else
109                         hdrlen = ipv6_optlen(hdr);
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 = hdr->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        optsh=(void *)(skb->data)+ptr;
164
165        DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen);
166
167        DEBUGP("len %02X %04X %02X ",
168                 optinfo->hdrlen, hdrlen,
169                 (!(optinfo->flags & IP6T_OPTS_LEN) ||
170                            ((optinfo->hdrlen == hdrlen) ^
171                            !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
172
173        ret = (optsh != NULL)
174                 &&
175                 (!(optinfo->flags & IP6T_OPTS_LEN) ||
176                            ((optinfo->hdrlen == hdrlen) ^
177                            !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
178
179        temp = len = 0;
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                         optdesc = (void *)(skb->data)+ptr;
191                         /* Type check */
192                         if ( (unsigned char)*optdesc != 
193                                 (optinfo->opts[temp] & 0xFF00)>>8 ){
194                                 DEBUGP("Tbad %02X %02X\n",
195                                                 (unsigned char)*optdesc,
196                                                 (optinfo->opts[temp] &
197                                                  0xFF00)>>8);
198                                 return 0;
199                         } else {
200                                 DEBUGP("Tok ");
201                         }
202                         /* Length check */
203                         if (((optinfo->opts[temp] & 0x00FF) != 0xFF) &&
204                                 (unsigned char)*optdesc != 0){
205                                 if ( ntohs((u16)*optdesc) != 
206                                                 optinfo->opts[temp] ){
207                                         DEBUGP("Lbad %02X %04X %04X\n",
208                                                         (unsigned char)*optdesc,
209                                                         ntohs((u16)*optdesc),
210                                                         optinfo->opts[temp]);
211                                         return 0;
212                                 } else {
213                                         DEBUGP("Lok ");
214                                 }
215                         }
216                         /* Step to the next */
217                         if ((unsigned char)*optdesc == 0){
218                                 DEBUGP("PAD0 \n");
219                                 ptr++;
220                                 hdrlen--;
221                         } else {
222                                 ptr += LOW(ntohs(*optdesc));
223                                 hdrlen -= LOW(ntohs(*optdesc));
224                                 DEBUGP("len%04X \n", 
225                                         LOW(ntohs(*optdesc)));
226                         }
227                         if (ptr > skb->len || ( !hdrlen && 
228                                 (temp != optinfo->optsnr - 1))) {
229                                 DEBUGP("new pointer is too large! \n");
230                                 break;
231                         }
232                 }
233                 if (temp == optinfo->optsnr)
234                         return ret;
235                 else return 0;
236         }
237
238         return 0;
239 }
240
241 /* Called when user tries to insert an entry of this type. */
242 static int
243 checkentry(const char *tablename,
244           const struct ip6t_ip6 *ip,
245           void *matchinfo,
246           unsigned int matchinfosize,
247           unsigned int hook_mask)
248 {
249        const struct ip6t_opts *optsinfo = matchinfo;
250
251        if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) {
252               DEBUGP("ip6t_opts: matchsize %u != %u\n",
253                       matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts)));
254               return 0;
255        }
256        if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
257               DEBUGP("ip6t_opts: unknown flags %X\n",
258                       optsinfo->invflags);
259               return 0;
260        }
261
262        return 1;
263 }
264
265 static struct ip6t_match opts_match = {
266 #if HOPBYHOP
267         .name           = "hbh",
268 #else
269         .name           = "dst",
270 #endif
271         .match          = &match,
272         .checkentry     = &checkentry,
273         .me             = THIS_MODULE,
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);