ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / net / ipv6 / netfilter / ip6t_rt.c
1 /* Kernel module to match ROUTING 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_rt.h>
21
22 MODULE_LICENSE("GPL");
23 MODULE_DESCRIPTION("IPv6 RT 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 /* Returns 1 if the id is matched by the range, 0 otherwise */
33 static inline int
34 segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, int invert)
35 {
36        int r=0;
37        DEBUGP("rt segsleft_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
38               min,id,max);
39        r=(id >= min && id <= max) ^ invert;
40        DEBUGP(" result %s\n",r? "PASS" : "FAILED");
41        return r;
42 }
43
44 static int
45 match(const struct sk_buff *skb,
46       const struct net_device *in,
47       const struct net_device *out,
48       const void *matchinfo,
49       int offset,
50       const void *protohdr,
51       u_int16_t datalen,
52       int *hotdrop)
53 {
54        struct ipv6_rt_hdr *route = NULL;
55        const struct ip6t_rt *rtinfo = matchinfo;
56        unsigned int temp;
57        unsigned int len;
58        u8 nexthdr;
59        unsigned int ptr;
60        unsigned int hdrlen = 0;
61        unsigned int ret = 0;
62
63        /* type of the 1st exthdr */
64        nexthdr = skb->nh.ipv6h->nexthdr;
65        /* pointer to the 1st exthdr */
66        ptr = sizeof(struct ipv6hdr);
67        /* available length */
68        len = skb->len - ptr;
69        temp = 0;
70
71         while (ip6t_ext_hdr(nexthdr)) {
72                struct ipv6_opt_hdr *hdr;
73
74               DEBUGP("ipv6_rt header iteration \n");
75
76               /* Is there enough space for the next ext header? */
77                 if (len < (int)sizeof(struct ipv6_opt_hdr))
78                         return 0;
79               /* No more exthdr -> evaluate */
80                 if (nexthdr == NEXTHDR_NONE) {
81                      break;
82               }
83               /* ESP -> evaluate */
84                 if (nexthdr == NEXTHDR_ESP) {
85                      break;
86               }
87
88               hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
89
90               /* Calculate the header length */
91                 if (nexthdr == NEXTHDR_FRAGMENT) {
92                         hdrlen = 8;
93                 } else if (nexthdr == NEXTHDR_AUTH)
94                         hdrlen = (hdr->hdrlen+2)<<2;
95                 else
96                         hdrlen = ipv6_optlen(hdr);
97
98               /* ROUTING -> evaluate */
99                 if (nexthdr == NEXTHDR_ROUTING) {
100                      temp |= MASK_ROUTING;
101                      break;
102               }
103
104
105               /* set the flag */
106               switch (nexthdr){
107                      case NEXTHDR_HOP:
108                      case NEXTHDR_ROUTING:
109                      case NEXTHDR_FRAGMENT:
110                      case NEXTHDR_AUTH:
111                      case NEXTHDR_DEST:
112                             break;
113                      default:
114                             DEBUGP("ipv6_rt match: unknown nextheader %u\n",nexthdr);
115                             return 0;
116                             break;
117               }
118
119                 nexthdr = hdr->nexthdr;
120                 len -= hdrlen;
121                 ptr += hdrlen;
122                 if ( ptr > skb->len ) {
123                         DEBUGP("ipv6_rt: new pointer is too large! \n");
124                         break;
125                 }
126         }
127
128        /* ROUTING header not found */
129        if ( temp != MASK_ROUTING ) return 0;
130
131        if (len < (int)sizeof(struct ipv6_rt_hdr)){
132                *hotdrop = 1;
133                 return 0;
134        }
135
136        if (len < hdrlen){
137                /* Pcket smaller than its length field */
138                 return 0;
139        }
140
141        route = (struct ipv6_rt_hdr *) (skb->data + ptr);
142
143        DEBUGP("IPv6 RT LEN %u %u ", hdrlen, route->hdrlen);
144        DEBUGP("TYPE %04X ", route->type);
145        DEBUGP("SGS_LEFT %u %08X\n", ntohl(route->segments_left), ntohl(route->segments_left));
146
147        DEBUGP("IPv6 RT segsleft %02X ",
148                 (segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1],
149                            ntohl(route->segments_left),
150                            !!(rtinfo->invflags & IP6T_RT_INV_SGS))));
151        DEBUGP("type %02X %02X %02X ",
152                 rtinfo->rt_type, route->type, 
153                 (!(rtinfo->flags & IP6T_RT_TYP) ||
154                            ((rtinfo->rt_type == route->type) ^
155                            !!(rtinfo->invflags & IP6T_RT_INV_TYP))));
156        DEBUGP("len %02X %04X %02X ",
157                 rtinfo->hdrlen, hdrlen,
158                 (!(rtinfo->flags & IP6T_RT_LEN) ||
159                            ((rtinfo->hdrlen == hdrlen) ^
160                            !!(rtinfo->invflags & IP6T_RT_INV_LEN))));
161        DEBUGP("res %02X %02X %02X ", 
162                 (rtinfo->flags & IP6T_RT_RES), ((struct rt0_hdr *)route)->bitmap,
163                 !((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->bitmap)));
164
165        ret = (route != NULL)
166                 &&
167                 (segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1],
168                            ntohl(route->segments_left),
169                            !!(rtinfo->invflags & IP6T_RT_INV_SGS)))
170                 &&
171                 (!(rtinfo->flags & IP6T_RT_LEN) ||
172                            ((rtinfo->hdrlen == hdrlen) ^
173                            !!(rtinfo->invflags & IP6T_RT_INV_LEN)))
174                 &&
175                 (!(rtinfo->flags & IP6T_RT_TYP) ||
176                            ((rtinfo->rt_type == route->type) ^
177                            !!(rtinfo->invflags & IP6T_RT_INV_TYP)))
178                 &&
179                 !((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->bitmap));
180
181         DEBUGP("#%d ",rtinfo->addrnr);
182        temp = len = ptr = 0;
183        if ( !(rtinfo->flags & IP6T_RT_FST) ){
184                return ret;
185         } else if (rtinfo->flags & IP6T_RT_FST_NSTRICT) {
186                 DEBUGP("Not strict ");
187                 if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){
188                         DEBUGP("There isn't enough space\n");
189                         return 0;
190                 } else {
191                         DEBUGP("#%d ",rtinfo->addrnr);
192                         ptr = 0;
193                         for(temp=0; temp<(unsigned int)((hdrlen-8)/16); temp++){
194                                 len = 0;
195                                 while ((u8)(((struct rt0_hdr *)route)->
196                                                 addr[temp].s6_addr[len]) ==
197                                         (u8)(rtinfo->addrs[ptr].s6_addr[len])){
198                                         DEBUGP("%02X?%02X ",
199                 (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
200                                         (u8)(rtinfo->addrs[ptr].s6_addr[len]));
201                                         len++;
202                                         if ( len == 16 ) break;
203                                 }
204                                 if (len==16) {
205                                         DEBUGP("ptr=%d temp=%d;\n",ptr,temp);
206                                         ptr++;
207                                 } else {
208                                         DEBUGP("%02X?%02X ",
209                 (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
210                                         (u8)(rtinfo->addrs[ptr].s6_addr[len]));
211                                         DEBUGP("!ptr=%d temp=%d;\n",ptr,temp);
212                                 }
213                                 if (ptr==rtinfo->addrnr) break;
214                         }
215                         DEBUGP("ptr=%d len=%d #%d\n",ptr,len, rtinfo->addrnr);
216                         if ( (len == 16) && (ptr == rtinfo->addrnr))
217                                 return ret;
218                         else return 0;
219                 }
220         } else {
221                 DEBUGP("Strict ");
222                 if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){
223                         DEBUGP("There isn't enough space\n");
224                         return 0;
225                 } else {
226                         DEBUGP("#%d ",rtinfo->addrnr);
227                         for(temp=0; temp<rtinfo->addrnr; temp++){
228                                 len = 0;
229                                 while ((u8)(((struct rt0_hdr *)route)->
230                                                 addr[temp].s6_addr[len]) ==
231                                         (u8)(rtinfo->addrs[temp].s6_addr[len])){
232                                         DEBUGP("%02X?%02X ",
233                 (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
234                                         (u8)(rtinfo->addrs[temp].s6_addr[len]));
235                                         len++;
236                                         if ( len == 16 ) break;
237                                 }
238                                 if (len!=16) {
239                                         DEBUGP("%02X?%02X ",
240                 (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
241                                         (u8)(rtinfo->addrs[temp].s6_addr[len]));
242                                         DEBUGP("!len=%d temp=%d;\n",len,temp);
243                                         break;
244                                 }
245                         }
246                         DEBUGP("temp=%d len=%d #%d\n",temp,len,rtinfo->addrnr);
247                         if ( (len == 16) && (temp == rtinfo->addrnr) && (temp == (unsigned int)((hdrlen-8)/16)))
248                                 return ret;
249                         else return 0;
250                 }
251         }
252
253         return 0;
254 }
255
256 /* Called when user tries to insert an entry of this type. */
257 static int
258 checkentry(const char *tablename,
259           const struct ip6t_ip6 *ip,
260           void *matchinfo,
261           unsigned int matchinfosize,
262           unsigned int hook_mask)
263 {
264        const struct ip6t_rt *rtinfo = matchinfo;
265
266        if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_rt))) {
267               DEBUGP("ip6t_rt: matchsize %u != %u\n",
268                       matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_rt)));
269               return 0;
270        }
271        if (rtinfo->invflags & ~IP6T_RT_INV_MASK) {
272               DEBUGP("ip6t_rt: unknown flags %X\n",
273                       rtinfo->invflags);
274               return 0;
275        }
276        if ( (rtinfo->flags & (IP6T_RT_RES|IP6T_RT_FST_MASK)) && 
277                        (!(rtinfo->flags & IP6T_RT_TYP) || 
278                        (rtinfo->rt_type != 0) || 
279                        (rtinfo->invflags & IP6T_RT_INV_TYP)) ) {
280               DEBUGP("`--rt-type 0' required before `--rt-0-*'");
281               return 0;
282        }
283
284        return 1;
285 }
286
287 static struct ip6t_match rt_match = {
288         .name           = "rt",
289         .match          = &match,
290         .checkentry     = &checkentry,
291         .me             = THIS_MODULE,
292 };
293
294 static int __init init(void)
295 {
296        return ip6t_register_match(&rt_match);
297 }
298
299 static void __exit cleanup(void)
300 {
301        ip6t_unregister_match(&rt_match);
302 }
303
304 module_init(init);
305 module_exit(cleanup);