[GFS2] Add nanosecond timestamp feature
[safe/jmp/linux-2.6] / net / ipv6 / ip6_output.c
index 0f4434e..4704b5f 100644 (file)
@@ -137,9 +137,17 @@ static int ip6_output2(struct sk_buff *skb)
        return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish);
 }
 
+static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
+{
+       struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
+
+       return (np && np->pmtudisc == IPV6_PMTUDISC_PROBE) ?
+              skb->dst->dev->mtu : dst_mtu(skb->dst);
+}
+
 int ip6_output(struct sk_buff *skb)
 {
-       if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
+       if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
                                dst_allfrag(skb->dst))
                return ip6_fragment(skb, ip6_output2);
        else
@@ -263,7 +271,8 @@ int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct net_device *dev,
 
        totlen = len + sizeof(struct ipv6hdr);
 
-       skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr));
+       skb_reset_network_header(skb);
+       skb_put(skb, sizeof(struct ipv6hdr));
        hdr = ipv6_hdr(skb);
 
        *(__be32*)hdr = htonl(0x60000000);
@@ -377,7 +386,7 @@ int ip6_forward(struct sk_buff *skb)
                goto drop;
        }
 
-       skb->ip_summed = CHECKSUM_NONE;
+       skb_forward_csum(skb);
 
        /*
         *      We DO NOT make any processing on
@@ -454,10 +463,17 @@ int ip6_forward(struct sk_buff *skb)
                 */
                if (xrlim_allow(dst, 1*HZ))
                        ndisc_send_redirect(skb, n, target);
-       } else if (ipv6_addr_type(&hdr->saddr)&(IPV6_ADDR_MULTICAST|IPV6_ADDR_LOOPBACK
-                                               |IPV6_ADDR_LINKLOCAL)) {
+       } else {
+               int addrtype = ipv6_addr_type(&hdr->saddr);
+
                /* This check is security critical. */
-               goto error;
+               if (addrtype & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LOOPBACK))
+                       goto error;
+               if (addrtype & IPV6_ADDR_LINKLOCAL) {
+                       icmpv6_send(skb, ICMPV6_DEST_UNREACH,
+                               ICMPV6_NOT_NEIGHBOUR, 0, skb->dev);
+                       goto error;
+               }
        }
 
        if (skb->len > dst_mtu(dst)) {
@@ -504,23 +520,7 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
 #ifdef CONFIG_NET_SCHED
        to->tc_index = from->tc_index;
 #endif
-#ifdef CONFIG_NETFILTER
-       /* Connection association is same as pre-frag packet */
-       nf_conntrack_put(to->nfct);
-       to->nfct = from->nfct;
-       nf_conntrack_get(to->nfct);
-       to->nfctinfo = from->nfctinfo;
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
-       nf_conntrack_put_reasm(to->nfct_reasm);
-       to->nfct_reasm = from->nfct_reasm;
-       nf_conntrack_get_reasm(to->nfct_reasm);
-#endif
-#ifdef CONFIG_BRIDGE_NETFILTER
-       nf_bridge_put(to->nf_bridge);
-       to->nf_bridge = from->nf_bridge;
-       nf_bridge_get(to->nf_bridge);
-#endif
-#endif
+       nf_copy(to, from);
        skb_copy_secmark(to, from);
 }
 
@@ -529,7 +529,7 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
        u16 offset = sizeof(struct ipv6hdr);
        struct ipv6_opt_hdr *exthdr =
                                (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
-       unsigned int packet_len = skb->tail - skb_network_header(skb);
+       unsigned int packet_len = skb->tail - skb->network_header;
        int found_rhdr = 0;
        *nexthdr = &ipv6_hdr(skb)->nexthdr;
 
@@ -581,7 +581,20 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
        hlen = ip6_find_1stfragopt(skb, &prevhdr);
        nexthdr = *prevhdr;
 
-       mtu = dst_mtu(&rt->u.dst);
+       mtu = ip6_skb_dst_mtu(skb);
+
+       /* We must not fragment if the socket is set to force MTU discovery
+        * or if the skb it not generated by a local socket.  (This last
+        * check should be redundant, but it's free.)
+        */
+       if (!np || np->pmtudisc >= IPV6_PMTUDISC_DO) {
+               skb->dev = skb->dst->dev;
+               icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
+               IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
+               kfree_skb(skb);
+               return -EMSGSIZE;
+       }
+
        if (np && np->frag_size < mtu) {
                if (np->frag_size)
                        mtu = np->frag_size;
@@ -748,7 +761,8 @@ slow_path:
                skb_put(frag, len + hlen + sizeof(struct frag_hdr));
                skb_reset_network_header(frag);
                fh = (struct frag_hdr *)(skb_network_header(frag) + hlen);
-               frag->h.raw = frag->nh.raw + hlen + sizeof(struct frag_hdr);
+               frag->transport_header = (frag->network_header + hlen +
+                                         sizeof(struct frag_hdr));
 
                /*
                 *      Charge the memory for the fragment to any owner
@@ -760,7 +774,7 @@ slow_path:
                /*
                 *      Copy the packet header into the new buffer.
                 */
-               memcpy(skb_network_header(frag), skb->data, hlen);
+               skb_copy_from_linear_data(skb, skb_network_header(frag), hlen);
 
                /*
                 *      Build fragment header.
@@ -776,7 +790,7 @@ slow_path:
                /*
                 *      Copy a block of the IP datagram.
                 */
-               if (skb_copy_bits(skb, ptr, frag->h.raw, len))
+               if (skb_copy_bits(skb, ptr, skb_transport_header(skb), len))
                        BUG();
                left -= len;
 
@@ -991,7 +1005,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                skb_reset_network_header(skb);
 
                /* initialize protocol header pointer */
-               skb->h.raw = skb->nh.raw + fragheaderlen;
+               skb->transport_header = skb->network_header + fragheaderlen;
 
                skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum = 0;
@@ -1064,7 +1078,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                inet->cork.fl = *fl;
                np->cork.hop_limit = hlimit;
                np->cork.tclass = tclass;
-               mtu = dst_mtu(rt->u.dst.path);
+               mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+                     rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);
                if (np->frag_size < mtu) {
                        if (np->frag_size)
                                mtu = np->frag_size;
@@ -1213,8 +1228,8 @@ alloc_new_skb:
                        data = skb_put(skb, fraglen);
                        skb_set_network_header(skb, exthdrlen);
                        data += fragheaderlen;
-                       skb->h.raw = skb->nh.raw + fragheaderlen;
-
+                       skb->transport_header = (skb->network_header +
+                                                fragheaderlen);
                        if (fraggap) {
                                skb->csum = skb_copy_and_csum_bits(
                                        skb_prev, maxfraglen,
@@ -1340,7 +1355,7 @@ int ip6_push_pending_frames(struct sock *sk)
        if (skb->data < skb_network_header(skb))
                __skb_pull(skb, skb_network_offset(skb));
        while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
-               __skb_pull(tmp_skb, skb->h.raw - skb->nh.raw);
+               __skb_pull(tmp_skb, skb_network_header_len(skb));
                *tail_skb = tmp_skb;
                tail_skb = &(tmp_skb->next);
                skb->len += tmp_skb->len;
@@ -1352,7 +1367,7 @@ int ip6_push_pending_frames(struct sock *sk)
        }
 
        ipv6_addr_copy(final_dst, &fl->fl6_dst);
-       __skb_pull(skb, skb->h.raw - skb->nh.raw);
+       __skb_pull(skb, skb_network_header_len(skb));
        if (opt && opt->opt_flen)
                ipv6_push_frag_opts(skb, opt, &proto);
        if (opt && opt->opt_nflen)