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