Merge branch 'master'
[pandora-kernel.git] / net / ipv6 / icmp.c
index 34a3322..21eb725 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/net.h>
 #include <linux/skbuff.h>
 #include <linux/init.h>
+#include <linux/netfilter.h>
 
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
@@ -79,7 +80,7 @@ DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics) __read_mostly;
 static DEFINE_PER_CPU(struct socket *, __icmpv6_socket) = NULL;
 #define icmpv6_socket  __get_cpu_var(__icmpv6_socket)
 
-static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp);
+static int icmpv6_rcv(struct sk_buff **pskb);
 
 static struct inet6_protocol icmpv6_protocol = {
        .handler        =       icmpv6_rcv,
@@ -255,6 +256,7 @@ out:
 struct icmpv6_msg {
        struct sk_buff  *skb;
        int             offset;
+       uint8_t         type;
 };
 
 static int icmpv6_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb)
@@ -266,6 +268,8 @@ static int icmpv6_getfrag(void *from, char *to, int offset, int len, int odd, st
        csum = skb_copy_and_csum_bits(org_skb, msg->offset + offset,
                                      to, len, csum);
        skb->csum = csum_block_add(skb->csum, csum, odd);
+       if (!(msg->type & ICMPV6_INFOMSG_MASK))
+               nf_ct_attach(skb, org_skb);
        return 0;
 }
 
@@ -328,8 +332,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
                iif = skb->dev->ifindex;
 
        /*
-        *      Must not send if we know that source is Anycast also.
-        *      for now we don't know that.
+        *      Must not send error if the source does not uniquely
+        *      identify a single node (RFC2463 Section 2.4).
+        *      We check unspecified / multicast addresses here,
+        *      and anycast addresses will be checked later.
         */
        if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
                LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: addr_any/mcast source\n");
@@ -373,6 +379,16 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
        err = ip6_dst_lookup(sk, &dst, &fl);
        if (err)
                goto out;
+
+       /*
+        * We won't send icmp if the destination is known
+        * anycast.
+        */
+       if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) {
+               LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n");
+               goto out_dst_release;
+       }
+
        if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
                goto out;
 
@@ -391,6 +407,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
 
        msg.skb = skb;
        msg.offset = skb->nh.raw - skb->data;
+       msg.type = type;
 
        len = skb->len - msg.offset;
        len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) -sizeof(struct icmp6hdr));
@@ -488,6 +505,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
 
        msg.skb = skb;
        msg.offset = 0;
+       msg.type = ICMPV6_ECHO_REPLY;
 
        err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr),
                                sizeof(struct icmp6hdr), hlimit, tclass, NULL, &fl,
@@ -569,7 +587,7 @@ static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info)
  *     Handle icmp messages
  */
 
-static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
+static int icmpv6_rcv(struct sk_buff **pskb)
 {
        struct sk_buff *skb = *pskb;
        struct net_device *dev = skb->dev;
@@ -595,7 +613,7 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
                skb->csum = ~csum_ipv6_magic(saddr, daddr, skb->len,
                                             IPPROTO_ICMPV6, 0);
                if (__skb_checksum_complete(skb)) {
-                       LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+                       LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [" NIP6_FMT " > " NIP6_FMT "]\n",
                                       NIP6(*saddr), NIP6(*daddr));
                        goto discard_it;
                }