vserver 1.9.5.x5
[linux-2.6.git] / net / ipv6 / netfilter / ip6t_dst.c
index 2d38503..540925e 100644 (file)
@@ -7,7 +7,6 @@
  * published by the Free Software Foundation.
  */
 
-
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/ipv6.h>
@@ -20,8 +19,6 @@
 #include <linux/netfilter_ipv6/ip6_tables.h>
 #include <linux/netfilter_ipv6/ip6t_opts.h>
 
-#define LOW(n)         (n & 0x00FF)
-
 #define HOPBYHOP       0
 
 MODULE_LICENSE("GPL");
@@ -48,8 +45,8 @@ MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
  *     0       -> invariant
  *     1       -> can change the routing
  *  (Type & 0x1F) Type
- *      0      -> PAD0 (only 1 byte!)
- *      1      -> PAD1 LENGTH info (total length = length + 2)
+ *      0      -> Pad1 (only 1 byte!)
+ *      1      -> PadN LENGTH info (total length = length + 2)
  *      C0 | 2 -> JUMBO 4 x x x x ( xxxx > 64k )
  *      5      -> RTALERT 2 x x
  */
@@ -60,11 +57,10 @@ match(const struct sk_buff *skb,
       const struct net_device *out,
       const void *matchinfo,
       int offset,
-      const void *protohdr,
-      u_int16_t datalen,
+      unsigned int protoff,
       int *hotdrop)
 {
-       struct ipv6_opt_hdr *optsh = NULL;
+       struct ipv6_opt_hdr _optsh, *oh;
        const struct ip6t_opts *optinfo = matchinfo;
        unsigned int temp;
        unsigned int len;
@@ -72,7 +68,9 @@ match(const struct sk_buff *skb,
        unsigned int ptr;
        unsigned int hdrlen = 0;
        unsigned int ret = 0;
-       u_int16_t *optdesc = NULL;
+       u8 _opttype, *tp = NULL;
+       u8 _optlen, *lp = NULL;
+       unsigned int optlen;
        
        /* type of the 1st exthdr */
        nexthdr = skb->nh.ipv6h->nexthdr;
@@ -83,7 +81,7 @@ match(const struct sk_buff *skb,
        temp = 0;
 
         while (ip6t_ext_hdr(nexthdr)) {
-               struct ipv6_opt_hdr *hdr;
+               struct ipv6_opt_hdr _hdr, *hp;
 
               DEBUGP("ipv6_opts header iteration \n");
 
@@ -99,15 +97,16 @@ match(const struct sk_buff *skb,
                      break;
               }
 
-              hdr=(void *)(skb->data)+ptr;
+             hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
+             BUG_ON(hp == NULL);
 
               /* Calculate the header length */
                 if (nexthdr == NEXTHDR_FRAGMENT) {
                         hdrlen = 8;
                 } else if (nexthdr == NEXTHDR_AUTH)
-                        hdrlen = (hdr->hdrlen+2)<<2;
+                        hdrlen = (hp->hdrlen+2)<<2;
                 else
-                        hdrlen = ipv6_optlen(hdr);
+                        hdrlen = ipv6_optlen(hp);
 
               /* OPTS -> evaluate */
 #if HOPBYHOP
@@ -135,7 +134,7 @@ match(const struct sk_buff *skb,
                             break;
               }
 
-                nexthdr = hdr->nexthdr;
+                nexthdr = hp->nexthdr;
                 len -= hdrlen;
                 ptr += hdrlen;
                if ( ptr > skb->len ) {
@@ -161,9 +160,10 @@ match(const struct sk_buff *skb,
                        return 0;
        }
 
-       optsh=(void *)(skb->data)+ptr;
+       oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
+       BUG_ON(oh == NULL);
 
-       DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen);
+       DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen);
 
        DEBUGP("len %02X %04X %02X ",
                        optinfo->hdrlen, hdrlen,
@@ -171,13 +171,12 @@ match(const struct sk_buff *skb,
                            ((optinfo->hdrlen == hdrlen) ^
                            !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
 
-       ret = (optsh != NULL)
+       ret = (oh != NULL)
                        &&
                (!(optinfo->flags & IP6T_OPTS_LEN) ||
                            ((optinfo->hdrlen == hdrlen) ^
                            !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
 
-       temp = len = 0;
        ptr += 2;
        hdrlen -= 2;
        if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){
@@ -188,48 +187,59 @@ match(const struct sk_buff *skb,
                DEBUGP("Strict ");
                DEBUGP("#%d ",optinfo->optsnr);
                for(temp=0; temp<optinfo->optsnr; temp++){
-                       optdesc = (void *)(skb->data)+ptr;
+                       /* type field exists ? */
+                       if (hdrlen < 1)
+                               break;
+                       tp = skb_header_pointer(skb, ptr, sizeof(_opttype),
+                                               &_opttype);
+                       if (tp == NULL)
+                               break;
+
                        /* Type check */
-                       if ( (unsigned char)*optdesc != 
-                               (optinfo->opts[temp] & 0xFF00)>>8 ){
+                       if (*tp != (optinfo->opts[temp] & 0xFF00)>>8){
                                DEBUGP("Tbad %02X %02X\n",
-                                               (unsigned char)*optdesc,
-                                               (optinfo->opts[temp] &
-                                                0xFF00)>>8);
+                                      *tp,
+                                      (optinfo->opts[temp] & 0xFF00)>>8);
                                return 0;
                        } else {
                                DEBUGP("Tok ");
                        }
                        /* Length check */
-                       if (((optinfo->opts[temp] & 0x00FF) != 0xFF) &&
-                               (unsigned char)*optdesc != 0){
-                               if ( ntohs((u16)*optdesc) != 
-                                               optinfo->opts[temp] ){
-                                       DEBUGP("Lbad %02X %04X %04X\n",
-                                                       (unsigned char)*optdesc,
-                                                       ntohs((u16)*optdesc),
-                                                       optinfo->opts[temp]);
+                       if (*tp) {
+                               u16 spec_len;
+
+                               /* length field exists ? */
+                               if (hdrlen < 2)
+                                       break;
+                               lp = skb_header_pointer(skb, ptr + 1,
+                                                       sizeof(_optlen),
+                                                       &_optlen);
+                               if (lp == NULL)
+                                       break;
+                               spec_len = optinfo->opts[temp] & 0x00FF;
+
+                               if (spec_len != 0x00FF && spec_len != *lp) {
+                                       DEBUGP("Lbad %02X %04X\n", *lp,
+                                              spec_len);
                                        return 0;
-                               } else {
-                                       DEBUGP("Lok ");
                                }
-                       }
-                       /* Step to the next */
-                       if ((unsigned char)*optdesc == 0){
-                               DEBUGP("PAD0 \n");
-                               ptr++;
-                               hdrlen--;
+                               DEBUGP("Lok ");
+                               optlen = *lp + 2;
                        } else {
-                               ptr += LOW(ntohs(*optdesc));
-                               hdrlen -= LOW(ntohs(*optdesc));
-                               DEBUGP("len%04X \n", 
-                                       LOW(ntohs(*optdesc)));
+                               DEBUGP("Pad1\n");
+                               optlen = 1;
                        }
-                       if (ptr > skb->len || ( !hdrlen && 
-                               (temp != optinfo->optsnr - 1))) {
+
+                       /* Step to the next */
+                       DEBUGP("len%04X \n", optlen);
+
+                       if ((ptr > skb->len - optlen || hdrlen < optlen) &&
+                           (temp < optinfo->optsnr - 1)) {
                                DEBUGP("new pointer is too large! \n");
                                break;
                        }
+                       ptr += optlen;
+                       hdrlen -= optlen;
                }
                if (temp == optinfo->optsnr)
                        return ret;
@@ -271,6 +281,7 @@ static struct ip6t_match opts_match = {
 #endif
        .match          = &match,
        .checkentry     = &checkentry,
+       .me             = THIS_MODULE,
 };
 
 static int __init init(void)