Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-2.6-block.git] / net / ipv6 / ip6_output.c
index 0c7c03d50dc0342c7caad25c9d24c3931c50438b..906b7e6dd7fb9ff2f557801d109bf81a5b29f9f2 100644 (file)
@@ -56,8 +56,6 @@
 #include <net/checksum.h>
 #include <linux/mroute6.h>
 
-int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
-
 int __ip6_local_out(struct sk_buff *skb)
 {
        int len;
@@ -88,7 +86,8 @@ static int ip6_finish_output2(struct sk_buff *skb)
        struct dst_entry *dst = skb_dst(skb);
        struct net_device *dev = dst->dev;
        struct neighbour *neigh;
-       struct rt6_info *rt;
+       struct in6_addr *nexthop;
+       int ret;
 
        skb->protocol = htons(ETH_P_IPV6);
        skb->dev = dev;
@@ -123,10 +122,17 @@ static int ip6_finish_output2(struct sk_buff *skb)
                                skb->len);
        }
 
-       rt = (struct rt6_info *) dst;
-       neigh = rt->n;
-       if (neigh)
-               return dst_neigh_output(dst, neigh, skb);
+       rcu_read_lock_bh();
+       nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr);
+       neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
+       if (unlikely(!neigh))
+               neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
+       if (!IS_ERR(neigh)) {
+               ret = dst_neigh_output(dst, neigh, skb);
+               rcu_read_unlock_bh();
+               return ret;
+       }
+       rcu_read_unlock_bh();
 
        IP6_INC_STATS_BH(dev_net(dst->dev),
                         ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
@@ -216,7 +222,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
        if (hlimit < 0)
                hlimit = ip6_dst_hoplimit(dst);
 
-       *(__be32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl6->flowlabel;
+       ip6_flow_hdr(hdr, tclass, fl6->flowlabel);
 
        hdr->payload_len = htons(seg_len);
        hdr->nexthdr = proto;
@@ -246,39 +252,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
 
 EXPORT_SYMBOL(ip6_xmit);
 
-/*
- *     To avoid extra problems ND packets are send through this
- *     routine. It's code duplication but I really want to avoid
- *     extra checks since ipv6_build_header is used by TCP (which
- *     is for us performance critical)
- */
-
-int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct net_device *dev,
-              const struct in6_addr *saddr, const struct in6_addr *daddr,
-              int proto, int len)
-{
-       struct ipv6_pinfo *np = inet6_sk(sk);
-       struct ipv6hdr *hdr;
-
-       skb->protocol = htons(ETH_P_IPV6);
-       skb->dev = dev;
-
-       skb_reset_network_header(skb);
-       skb_put(skb, sizeof(struct ipv6hdr));
-       hdr = ipv6_hdr(skb);
-
-       *(__be32*)hdr = htonl(0x60000000);
-
-       hdr->payload_len = htons(len);
-       hdr->nexthdr = proto;
-       hdr->hop_limit = np->hop_limit;
-
-       hdr->saddr = *saddr;
-       hdr->daddr = *daddr;
-
-       return 0;
-}
-
 static int ip6_call_ra_chain(struct sk_buff *skb, int sel)
 {
        struct ip6_ra_chain *ra;
@@ -913,8 +886,12 @@ static int ip6_dst_lookup_tail(struct sock *sk,
         * dst entry of the nexthop router
         */
        rt = (struct rt6_info *) *dst;
-       n = rt->n;
-       if (n && !(n->nud_state & NUD_VALID)) {
+       rcu_read_lock_bh();
+       n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt, &fl6->daddr));
+       err = n && !(n->nud_state & NUD_VALID) ? -EINVAL : 0;
+       rcu_read_unlock_bh();
+
+       if (err) {
                struct inet6_ifaddr *ifp;
                struct flowi6 fl_gw6;
                int redirect;
@@ -1548,9 +1525,7 @@ int ip6_push_pending_frames(struct sock *sk)
        skb_reset_network_header(skb);
        hdr = ipv6_hdr(skb);
 
-       *(__be32*)hdr = fl6->flowlabel |
-                    htonl(0x60000000 | ((int)np->cork.tclass << 20));
-
+       ip6_flow_hdr(hdr, np->cork.tclass, fl6->flowlabel);
        hdr->hop_limit = np->cork.hop_limit;
        hdr->nexthdr = proto;
        hdr->saddr = fl6->saddr;