fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / ipv6 / ip6_input.c
index 29f7359..ad0b8ab 100644 (file)
@@ -60,17 +60,27 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
 {
        struct ipv6hdr *hdr;
        u32             pkt_len;
+       struct inet6_dev *idev;
 
-       if (skb->pkt_type == PACKET_OTHERHOST)
-               goto drop;
+       if (skb->pkt_type == PACKET_OTHERHOST) {
+               kfree_skb(skb);
+               return 0;
+       }
 
-       IP6_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);
+       rcu_read_lock();
+
+       idev = __in6_dev_get(skb->dev);
+
+       IP6_INC_STATS_BH(idev, IPSTATS_MIB_INRECEIVES);
 
        if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
-               IP6_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
+               IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDISCARDS);
+               rcu_read_unlock();
                goto out;
        }
 
+       memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
+
        /*
         * Store incoming device index. When the packet will
         * be queued, we cannot refer to skb->dev anymore.
@@ -82,16 +92,11 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
         * arrived via the sending interface (ethX), because of the
         * nature of scoping architecture. --yoshfuji
         */
-       IP6CB(skb)->iif = skb->dst ? ((struct rt6_info *)skb->dst)->rt6i_idev->dev->ifindex : dev->ifindex;
+       IP6CB(skb)->iif = skb->dst ? ip6_dst_idev(skb->dst)->dev->ifindex : dev->ifindex;
 
-       if (skb->len < sizeof(struct ipv6hdr))
+       if (unlikely(!pskb_may_pull(skb, sizeof(*hdr))))
                goto err;
 
-       if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) {
-               IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
-               goto drop;
-       }
-
        hdr = skb->nh.ipv6h;
 
        if (hdr->version != 6)
@@ -107,26 +112,29 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
                if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
                        goto truncated;
                if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) {
-                       IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
+                       IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
                        goto drop;
                }
                hdr = skb->nh.ipv6h;
        }
 
        if (hdr->nexthdr == NEXTHDR_HOP) {
-               if (ipv6_parse_hopopts(skb, IP6CB(skb)->nhoff) < 0) {
-                       IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
+               if (ipv6_parse_hopopts(&skb) < 0) {
+                       IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
+                       rcu_read_unlock();
                        return 0;
                }
-               hdr = skb->nh.ipv6h;
        }
 
+       rcu_read_unlock();
+
        return NF_HOOK(PF_INET6,NF_IP6_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish);
 truncated:
-       IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
+       IP6_INC_STATS_BH(idev, IPSTATS_MIB_INTRUNCATEDPKTS);
 err:
-       IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
+       IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
 drop:
+       rcu_read_unlock();
        kfree_skb(skb);
 out:
        return 0;
@@ -144,6 +152,7 @@ static inline int ip6_input_finish(struct sk_buff *skb)
        unsigned int nhoff;
        int nexthdr;
        u8 hash;
+       struct inet6_dev *idev;
 
        /*
         *      Parse extension headers
@@ -151,6 +160,7 @@ static inline int ip6_input_finish(struct sk_buff *skb)
 
        rcu_read_lock();
 resubmit:
+       idev = ip6_dst_idev(skb->dst);
        if (!pskb_pull(skb, skb->h.raw - skb->data))
                goto discard;
        nhoff = IP6CB(skb)->nhoff;
@@ -189,24 +199,24 @@ resubmit:
                if (ret > 0)
                        goto resubmit;
                else if (ret == 0)
-                       IP6_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
+                       IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDELIVERS);
        } else {
                if (!raw_sk) {
                        if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
-                               IP6_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS);
+                               IP6_INC_STATS_BH(idev, IPSTATS_MIB_INUNKNOWNPROTOS);
                                icmpv6_send(skb, ICMPV6_PARAMPROB,
                                            ICMPV6_UNK_NEXTHDR, nhoff,
                                            skb->dev);
                        }
                } else
-                       IP6_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
+                       IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDELIVERS);
                kfree_skb(skb);
        }
        rcu_read_unlock();
        return 0;
 
 discard:
-       IP6_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
+       IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDISCARDS);
        rcu_read_unlock();
        kfree_skb(skb);
        return 0;
@@ -223,7 +233,7 @@ int ip6_mc_input(struct sk_buff *skb)
        struct ipv6hdr *hdr;
        int deliver;
 
-       IP6_INC_STATS_BH(IPSTATS_MIB_INMCASTPKTS);
+       IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INMCASTPKTS);
 
        hdr = skb->nh.ipv6h;
        deliver = likely(!(skb->dev->flags & (IFF_PROMISC|IFF_ALLMULTI))) ||