Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / net / ipv6 / ip6_output.c
index 1d5dfeb..4fb47a2 100644 (file)
@@ -28,7 +28,6 @@
  *                             for datagram xmit
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/string.h>
@@ -39,6 +38,7 @@
 #include <linux/in6.h>
 #include <linux/tcp.h>
 #include <linux/route.h>
+#include <linux/module.h>
 
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv6.h>
@@ -229,7 +229,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
        skb->priority = sk->sk_priority;
 
        mtu = dst_mtu(dst);
-       if ((skb->len <= mtu) || ipfragok) {
+       if ((skb->len <= mtu) || ipfragok || skb_is_gso(skb)) {
                IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
                return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev,
                                dst_output);
@@ -356,6 +356,7 @@ int ip6_forward(struct sk_buff *skb)
                skb->dev = dst->dev;
                icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
                            0, skb->dev);
+               IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
 
                kfree_skb(skb);
                return -ETIMEDOUT;
@@ -458,6 +459,7 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        nf_bridge_get(to->nf_bridge);
 #endif
 #endif
+       skb_copy_secmark(to, from);
 }
 
 int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
@@ -488,6 +490,7 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
 
        return offset;
 }
+EXPORT_SYMBOL_GPL(ip6_find_1stfragopt);
 
 static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 {
@@ -593,6 +596,9 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
                        }
                        
                        err = output(skb);
+                       if(!err)
+                               IP6_INC_STATS(IPSTATS_MIB_FRAGCREATES);
+
                        if (err || !frag)
                                break;
 
@@ -704,12 +710,11 @@ slow_path:
                /*
                 *      Put this fragment into the sending queue.
                 */
-
-               IP6_INC_STATS(IPSTATS_MIB_FRAGCREATES);
-
                err = output(frag);
                if (err)
                        goto fail;
+
+               IP6_INC_STATS(IPSTATS_MIB_FRAGCREATES);
        }
        kfree_skb(skb);
        IP6_INC_STATS(IPSTATS_MIB_FRAGOKS);
@@ -721,48 +726,51 @@ fail:
        return err;
 }
 
-int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
+static struct dst_entry *ip6_sk_dst_check(struct sock *sk,
+                                         struct dst_entry *dst,
+                                         struct flowi *fl)
 {
-       int err = 0;
+       struct ipv6_pinfo *np = inet6_sk(sk);
+       struct rt6_info *rt = (struct rt6_info *)dst;
 
-       *dst = NULL;
-       if (sk) {
-               struct ipv6_pinfo *np = inet6_sk(sk);
-       
-               *dst = sk_dst_check(sk, np->dst_cookie);
-               if (*dst) {
-                       struct rt6_info *rt = (struct rt6_info*)*dst;
-       
-                       /* Yes, checking route validity in not connected
-                        * case is not very simple. Take into account,
-                        * that we do not support routing by source, TOS,
-                        * and MSG_DONTROUTE            --ANK (980726)
-                        *
-                        * 1. If route was host route, check that
-                        *    cached destination is current.
-                        *    If it is network route, we still may
-                        *    check its validity using saved pointer
-                        *    to the last used address: daddr_cache.
-                        *    We do not want to save whole address now,
-                        *    (because main consumer of this service
-                        *    is tcp, which has not this problem),
-                        *    so that the last trick works only on connected
-                        *    sockets.
-                        * 2. oif also should be the same.
-                        */
-                       if (((rt->rt6i_dst.plen != 128 ||
-                             !ipv6_addr_equal(&fl->fl6_dst,
-                                              &rt->rt6i_dst.addr))
-                            && (np->daddr_cache == NULL ||
-                                !ipv6_addr_equal(&fl->fl6_dst,
-                                                 np->daddr_cache)))
-                           || (fl->oif && fl->oif != (*dst)->dev->ifindex)) {
-                               dst_release(*dst);
-                               *dst = NULL;
-                       }
-               }
+       if (!dst)
+               goto out;
+
+       /* Yes, checking route validity in not connected
+        * case is not very simple. Take into account,
+        * that we do not support routing by source, TOS,
+        * and MSG_DONTROUTE            --ANK (980726)
+        *
+        * 1. If route was host route, check that
+        *    cached destination is current.
+        *    If it is network route, we still may
+        *    check its validity using saved pointer
+        *    to the last used address: daddr_cache.
+        *    We do not want to save whole address now,
+        *    (because main consumer of this service
+        *    is tcp, which has not this problem),
+        *    so that the last trick works only on connected
+        *    sockets.
+        * 2. oif also should be the same.
+        */
+       if (((rt->rt6i_dst.plen != 128 ||
+             !ipv6_addr_equal(&fl->fl6_dst, &rt->rt6i_dst.addr))
+            && (np->daddr_cache == NULL ||
+                !ipv6_addr_equal(&fl->fl6_dst, np->daddr_cache)))
+           || (fl->oif && fl->oif != dst->dev->ifindex)) {
+               dst_release(dst);
+               dst = NULL;
        }
 
+out:
+       return dst;
+}
+
+static int ip6_dst_lookup_tail(struct sock *sk,
+                              struct dst_entry **dst, struct flowi *fl)
+{
+       int err;
+
        if (*dst == NULL)
                *dst = ip6_route_output(sk, fl);
 
@@ -771,7 +779,6 @@ int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
 
        if (ipv6_addr_any(&fl->fl6_src)) {
                err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
-
                if (err)
                        goto out_err_release;
        }
@@ -784,8 +791,48 @@ out_err_release:
        return err;
 }
 
+/**
+ *     ip6_dst_lookup - perform route lookup on flow
+ *     @sk: socket which provides route info
+ *     @dst: pointer to dst_entry * for result
+ *     @fl: flow to lookup
+ *
+ *     This function performs a route lookup on the given flow.
+ *
+ *     It returns zero on success, or a standard errno code on error.
+ */
+int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
+{
+       *dst = NULL;
+       return ip6_dst_lookup_tail(sk, dst, fl);
+}
 EXPORT_SYMBOL_GPL(ip6_dst_lookup);
 
+/**
+ *     ip6_sk_dst_lookup - perform socket cached route lookup on flow
+ *     @sk: socket which provides the dst cache and route info
+ *     @dst: pointer to dst_entry * for result
+ *     @fl: flow to lookup
+ *
+ *     This function performs a route lookup on the given flow with the
+ *     possibility of using the cached route in the socket if it is valid.
+ *     It will take the socket dst lock when operating on the dst cache.
+ *     As a result, this function can only be used in process context.
+ *
+ *     It returns zero on success, or a standard errno code on error.
+ */
+int ip6_sk_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
+{
+       *dst = NULL;
+       if (sk) {
+               *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie);
+               *dst = ip6_sk_dst_check(sk, *dst, fl);
+       }
+
+       return ip6_dst_lookup_tail(sk, dst, fl);
+}
+EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup);
+
 static inline int ip6_ufo_append_data(struct sock *sk,
                        int getfrag(void *from, char *to, int offset, int len,
                        int odd, struct sk_buff *skb),
@@ -832,7 +879,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                /* specify the length of each IP datagram fragment*/
                skb_shinfo(skb)->gso_size = mtu - fragheaderlen - 
                                            sizeof(struct frag_hdr);
-               skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4;
+               skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
                ipv6_select_ident(skb, &fhdr);
                skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
                __skb_queue_tail(&sk->sk_write_queue, skb);