Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-2.6-microblaze.git] / net / ipv6 / udp.c
index 649efc2..df71ba0 100644 (file)
@@ -35,7 +35,7 @@
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/slab.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 
 #include <net/addrconf.h>
 #include <net/ndisc.h>
 #include <trace/events/skb.h>
 #include "udp_impl.h"
 
+static bool udp6_lib_exact_dif_match(struct net *net, struct sk_buff *skb)
+{
+#if defined(CONFIG_NET_L3_MASTER_DEV)
+       if (!net->ipv4.sysctl_udp_l3mdev_accept &&
+           skb && ipv6_l3mdev_skb(IP6CB(skb)->flags))
+               return true;
+#endif
+       return false;
+}
+
 static u32 udp6_ehashfn(const struct net *net,
                        const struct in6_addr *laddr,
                        const u16 lport,
@@ -103,7 +113,7 @@ int udp_v6_get_port(struct sock *sk, unsigned short snum)
 
        /* precompute partial secondary hash */
        udp_sk(sk)->udp_portaddr_hash = hash2_partial;
-       return udp_lib_get_port(sk, snum, ipv6_rcv_saddr_equal, hash2_nulladdr);
+       return udp_lib_get_port(sk, snum, hash2_nulladdr);
 }
 
 static void udp_v6_rehash(struct sock *sk)
@@ -118,7 +128,7 @@ static void udp_v6_rehash(struct sock *sk)
 static int compute_score(struct sock *sk, struct net *net,
                         const struct in6_addr *saddr, __be16 sport,
                         const struct in6_addr *daddr, unsigned short hnum,
-                        int dif)
+                        int dif, bool exact_dif)
 {
        int score;
        struct inet_sock *inet;
@@ -149,7 +159,7 @@ static int compute_score(struct sock *sk, struct net *net,
                score++;
        }
 
-       if (sk->sk_bound_dev_if) {
+       if (sk->sk_bound_dev_if || exact_dif) {
                if (sk->sk_bound_dev_if != dif)
                        return -1;
                score++;
@@ -165,7 +175,7 @@ static int compute_score(struct sock *sk, struct net *net,
 static struct sock *udp6_lib_lookup2(struct net *net,
                const struct in6_addr *saddr, __be16 sport,
                const struct in6_addr *daddr, unsigned int hnum, int dif,
-               struct udp_hslot *hslot2,
+               bool exact_dif, struct udp_hslot *hslot2,
                struct sk_buff *skb)
 {
        struct sock *sk, *result;
@@ -176,7 +186,7 @@ static struct sock *udp6_lib_lookup2(struct net *net,
        badness = -1;
        udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) {
                score = compute_score(sk, net, saddr, sport,
-                                     daddr, hnum, dif);
+                                     daddr, hnum, dif, exact_dif);
                if (score > badness) {
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
@@ -212,6 +222,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
        unsigned short hnum = ntohs(dport);
        unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask);
        struct udp_hslot *hslot2, *hslot = &udptable->hash[slot];
+       bool exact_dif = udp6_lib_exact_dif_match(net, skb);
        int score, badness, matches = 0, reuseport = 0;
        u32 hash = 0;
 
@@ -223,7 +234,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
                        goto begin;
 
                result = udp6_lib_lookup2(net, saddr, sport,
-                                         daddr, hnum, dif,
+                                         daddr, hnum, dif, exact_dif,
                                          hslot2, skb);
                if (!result) {
                        unsigned int old_slot2 = slot2;
@@ -239,7 +250,8 @@ struct sock *__udp6_lib_lookup(struct net *net,
 
                        result = udp6_lib_lookup2(net, saddr, sport,
                                                  daddr, hnum, dif,
-                                                 hslot2, skb);
+                                                 exact_dif, hslot2,
+                                                 skb);
                }
                return result;
        }
@@ -247,7 +259,8 @@ begin:
        result = NULL;
        badness = -1;
        sk_for_each_rcu(sk, &hslot->head) {
-               score = compute_score(sk, net, saddr, sport, daddr, hnum, dif);
+               score = compute_score(sk, net, saddr, sport, daddr, hnum, dif,
+                                     exact_dif);
                if (score > badness) {
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
@@ -441,7 +454,7 @@ try_again:
        return err;
 
 csum_copy_err:
-       if (!__sk_queue_drop_skb(sk, skb, flags)) {
+       if (!__sk_queue_drop_skb(sk, skb, flags, udp_skb_destructor)) {
                if (is_udp4) {
                        UDP_INC_STATS(sock_net(sk),
                                      UDP_MIB_CSUMERRORS, is_udplite);
@@ -1295,7 +1308,8 @@ out:
        return err;
 
 do_confirm:
-       dst_confirm(dst);
+       if (msg->msg_flags & MSG_PROBE)
+               dst_confirm_neigh(dst, &fl6.daddr);
        if (!(msg->msg_flags&MSG_PROBE) || len)
                goto back_from_confirm;
        err = 0;