Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / net / ipv6 / ip6_input.c
index 0be5945..aceee25 100644 (file)
@@ -48,7 +48,7 @@
 
 
 
-static inline int ip6_rcv_finish( struct sk_buff *skb) 
+inline int ip6_rcv_finish( struct sk_buff *skb) 
 {
        if (skb->dst == NULL)
                ip6_route_input(skb);
@@ -56,7 +56,7 @@ static inline int ip6_rcv_finish( struct sk_buff *skb)
        return dst_input(skb);
 }
 
-int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
+int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
 {
        struct ipv6hdr *hdr;
        u32             pkt_len;
@@ -64,23 +64,31 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
        if (skb->pkt_type == PACKET_OTHERHOST)
                goto drop;
 
-       IP6_INC_STATS_BH(InReceives);
+       IP6_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);
 
        if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
-               IP6_INC_STATS_BH(InDiscards);
+               IP6_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
                goto out;
        }
 
-       /* Store incoming device index. When the packet will
-          be queued, we cannot refer to skb->dev anymore.
+       /*
+        * Store incoming device index. When the packet will
+        * be queued, we cannot refer to skb->dev anymore.
+        *
+        * BTW, when we send a packet for our own local address on a
+        * non-loopback interface (e.g. ethX), it is being delivered
+        * via the loopback interface (lo) here; skb->dev = &loopback_dev.
+        * It, however, should be considered as if it is being
+        * arrived via the sending interface (ethX), because of the
+        * nature of scoping architecture. --yoshfuji
         */
-       IP6CB(skb)->iif = dev->ifindex;
+       IP6CB(skb)->iif = skb->dst ? ((struct rt6_info *)skb->dst)->rt6i_idev->dev->ifindex : dev->ifindex;
 
        if (skb->len < sizeof(struct ipv6hdr))
                goto err;
 
        if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) {
-               IP6_INC_STATS_BH(InHdrErrors);
+               IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
                goto drop;
        }
 
@@ -89,37 +97,34 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
        if (hdr->version != 6)
                goto err;
 
+       skb->h.raw = (u8 *)(hdr + 1);
+       IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
+
        pkt_len = ntohs(hdr->payload_len);
 
        /* pkt_len may be zero if Jumbo payload option is present */
        if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
                if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
                        goto truncated;
-               if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
-                       if (__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr))){
-                               IP6_INC_STATS_BH(InHdrErrors);
-                               goto drop;
-                       }
-                       hdr = skb->nh.ipv6h;
-                       if (skb->ip_summed == CHECKSUM_HW)
-                               skb->ip_summed = CHECKSUM_NONE;
+               if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) {
+                       IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
+                       goto drop;
                }
+               hdr = skb->nh.ipv6h;
        }
 
        if (hdr->nexthdr == NEXTHDR_HOP) {
-               skb->h.raw = (u8*)(hdr+1);
-               if (ipv6_parse_hopopts(skb, offsetof(struct ipv6hdr, nexthdr)) < 0) {
-                       IP6_INC_STATS_BH(InHdrErrors);
+               if (ipv6_parse_hopopts(skb) < 0) {
+                       IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
                        return 0;
                }
-               hdr = skb->nh.ipv6h;
        }
 
        return NF_HOOK(PF_INET6,NF_IP6_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish);
 truncated:
-       IP6_INC_STATS_BH(InTruncatedPkts);
+       IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
 err:
-       IP6_INC_STATS_BH(InHdrErrors);
+       IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
 drop:
        kfree_skb(skb);
 out:
@@ -138,47 +143,36 @@ static inline int ip6_input_finish(struct sk_buff *skb)
        unsigned int nhoff;
        int nexthdr;
        u8 hash;
-       int cksum_sub = 0;
-
-       skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
 
        /*
         *      Parse extension headers
         */
 
-       nexthdr = skb->nh.ipv6h->nexthdr;
-       nhoff = offsetof(struct ipv6hdr, nexthdr);
-
-       /* Skip hop-by-hop options, they are already parsed. */
-       if (nexthdr == NEXTHDR_HOP) {
-               nhoff = sizeof(struct ipv6hdr);
-               nexthdr = skb->h.raw[0];
-               skb->h.raw += (skb->h.raw[1]+1)<<3;
-       }
-
        rcu_read_lock();
 resubmit:
        if (!pskb_pull(skb, skb->h.raw - skb->data))
                goto discard;
+       nhoff = IP6CB(skb)->nhoff;
        nexthdr = skb->nh.raw[nhoff];
 
        raw_sk = sk_head(&raw_v6_htable[nexthdr & (MAX_INET_PROTOS - 1)]);
-       if (raw_sk)
-               ipv6_raw_deliver(skb, nexthdr);
+       if (raw_sk && !ipv6_raw_deliver(skb, nexthdr))
+               raw_sk = NULL;
 
        hash = nexthdr & (MAX_INET_PROTOS - 1);
-       if ((ipprot = inet6_protos[hash]) != NULL) {
+       if ((ipprot = rcu_dereference(inet6_protos[hash])) != NULL) {
                int ret;
                
-               smp_read_barrier_depends();
                if (ipprot->flags & INET6_PROTO_FINAL) {
                        struct ipv6hdr *hdr;    
 
-                       if (!cksum_sub && skb->ip_summed == CHECKSUM_HW) {
-                               skb->csum = csum_sub(skb->csum,
-                                                    csum_partial(skb->nh.raw, skb->h.raw-skb->nh.raw, 0));
-                               cksum_sub++;
-                       }
+                       /* Free reference early: we don't need it any more,
+                          and it may hold ip_conntrack module loaded
+                          indefinitely. */
+                       nf_reset(skb);
+
+                       skb_postpull_rcsum(skb, skb->nh.raw,
+                                          skb->h.raw - skb->nh.raw);
                        hdr = skb->nh.ipv6h;
                        if (ipv6_addr_is_multicast(&hdr->daddr) &&
                            !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr,
@@ -190,27 +184,28 @@ resubmit:
                    !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) 
                        goto discard;
                
-               ret = ipprot->handler(&skb, &nhoff);
+               ret = ipprot->handler(&skb);
                if (ret > 0)
                        goto resubmit;
                else if (ret == 0)
-                       IP6_INC_STATS_BH(InDelivers);
+                       IP6_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
        } else {
                if (!raw_sk) {
                        if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
-                               IP6_INC_STATS_BH(InUnknownProtos);
-                               icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhoff);
+                               IP6_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS);
+                               icmpv6_send(skb, ICMPV6_PARAMPROB,
+                                           ICMPV6_UNK_NEXTHDR, nhoff,
+                                           skb->dev);
                        }
-               } else {
-                       IP6_INC_STATS_BH(InDelivers);
-                       kfree_skb(skb);
-               }
+               } else
+                       IP6_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
+               kfree_skb(skb);
        }
        rcu_read_unlock();
        return 0;
 
 discard:
-       IP6_INC_STATS_BH(InDiscards);
+       IP6_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
        rcu_read_unlock();
        kfree_skb(skb);
        return 0;
@@ -227,7 +222,7 @@ int ip6_mc_input(struct sk_buff *skb)
        struct ipv6hdr *hdr;
        int deliver;
 
-       IP6_INC_STATS_BH(InMcastPkts);
+       IP6_INC_STATS_BH(IPSTATS_MIB_INMCASTPKTS);
 
        hdr = skb->nh.ipv6h;
        deliver = likely(!(skb->dev->flags & (IFF_PROMISC|IFF_ALLMULTI))) ||