]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/netfilter/ip6_tables.c
Merge tag 'spi-for-linus' of git://git.secretlab.ca/git/linux-2.6
[~andy/linux] / net / ipv6 / netfilter / ip6_tables.c
index d4e350f72bbb09dd98d1d668e326fe8536a72a58..d7cb04506c3dac8266780c30a74e1a3fa1320ade 100644 (file)
@@ -133,7 +133,7 @@ ip6_packet_match(const struct sk_buff *skb,
                int protohdr;
                unsigned short _frag_off;
 
-               protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
+               protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off, NULL);
                if (protohdr < 0) {
                        if (_frag_off == 0)
                                *hotdrop = true;
@@ -181,8 +181,7 @@ ip6_checkentry(const struct ip6t_ip6 *ipv6)
 static unsigned int
 ip6t_error(struct sk_buff *skb, const struct xt_action_param *par)
 {
-       if (net_ratelimit())
-               pr_info("error: `%s'\n", (const char *)par->targinfo);
+       net_info_ratelimited("error: `%s'\n", (const char *)par->targinfo);
 
        return NF_DROP;
 }
@@ -362,6 +361,7 @@ ip6t_do_table(struct sk_buff *skb,
                const struct xt_entry_match *ematch;
 
                IP_NF_ASSERT(e);
+               acpar.thoff = 0;
                if (!ip6_packet_match(skb, indev, outdev, &e->ipv6,
                    &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) {
  no_match:
@@ -2278,6 +2278,10 @@ static void __exit ip6_tables_fini(void)
  * if target < 0. "last header" is transport protocol header, ESP, or
  * "No next header".
  *
+ * Note that *offset is used as input/output parameter. an if it is not zero,
+ * then it must be a valid offset to an inner IPv6 header. This can be used
+ * to explore inner IPv6 header, eg. ICMPv6 error messages.
+ *
  * If target header is found, its offset is set in *offset and return protocol
  * number. Otherwise, return -1.
  *
@@ -2289,17 +2293,33 @@ static void __exit ip6_tables_fini(void)
  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
  * isn't NULL.
  *
+ * if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG
+ * will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and
+ * target < 0, then this function will stop at the AH header.
  */
 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
-                 int target, unsigned short *fragoff)
+                 int target, unsigned short *fragoff, int *flags)
 {
        unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
        u8 nexthdr = ipv6_hdr(skb)->nexthdr;
-       unsigned int len = skb->len - start;
+       unsigned int len;
 
        if (fragoff)
                *fragoff = 0;
 
+       if (*offset) {
+               struct ipv6hdr _ip6, *ip6;
+
+               ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
+               if (!ip6 || (ip6->version != 6)) {
+                       printk(KERN_ERR "IPv6 header not found\n");
+                       return -EBADMSG;
+               }
+               start = *offset + sizeof(struct ipv6hdr);
+               nexthdr = ip6->nexthdr;
+       }
+       len = skb->len - start;
+
        while (nexthdr != target) {
                struct ipv6_opt_hdr _hdr, *hp;
                unsigned int hdrlen;
@@ -2316,6 +2336,9 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                if (nexthdr == NEXTHDR_FRAGMENT) {
                        unsigned short _frag_off;
                        __be16 *fp;
+
+                       if (flags)      /* Indicate that this is a fragment */
+                               *flags |= IP6T_FH_F_FRAG;
                        fp = skb_header_pointer(skb,
                                                start+offsetof(struct frag_hdr,
                                                               frag_off),
@@ -2336,9 +2359,11 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                                return -ENOENT;
                        }
                        hdrlen = 8;
-               } else if (nexthdr == NEXTHDR_AUTH)
+               } else if (nexthdr == NEXTHDR_AUTH) {
+                       if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0))
+                               break;
                        hdrlen = (hp->hdrlen + 2) << 2;
-               else
+               else
                        hdrlen = ipv6_optlen(hp);
 
                nexthdr = hp->nexthdr;