v4 GRE: Add TCP segmentation offload for GRE
[firefly-linux-kernel-4.4.55.git] / net / ipv4 / udp.c
index 1f4d405eafba746d2b8085132a740b17a6658c80..39a5e7a9a77fe2e0043cf518939b0f91b5814edf 100644 (file)
@@ -139,6 +139,7 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
 {
        struct sock *sk2;
        struct hlist_nulls_node *node;
+       kuid_t uid = sock_i_uid(sk);
 
        sk_nulls_for_each(sk2, node, &hslot->head)
                if (net_eq(sock_net(sk2), net) &&
@@ -147,6 +148,8 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
                    (!sk2->sk_reuse || !sk->sk_reuse) &&
                    (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if ||
                     sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
+                   (!sk2->sk_reuseport || !sk->sk_reuseport ||
+                     !uid_eq(uid, sock_i_uid(sk2))) &&
                    (*saddr_comp)(sk, sk2)) {
                        if (bitmap)
                                __set_bit(udp_sk(sk2)->udp_port_hash >> log,
@@ -169,6 +172,7 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
 {
        struct sock *sk2;
        struct hlist_nulls_node *node;
+       kuid_t uid = sock_i_uid(sk);
        int res = 0;
 
        spin_lock(&hslot2->lock);
@@ -179,6 +183,8 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
                    (!sk2->sk_reuse || !sk->sk_reuse) &&
                    (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if ||
                     sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
+                   (!sk2->sk_reuseport || !sk->sk_reuseport ||
+                     !uid_eq(uid, sock_i_uid(sk2))) &&
                    (*saddr_comp)(sk, sk2)) {
                        res = 1;
                        break;
@@ -337,26 +343,26 @@ static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr,
                        !ipv6_only_sock(sk)) {
                struct inet_sock *inet = inet_sk(sk);
 
-               score = (sk->sk_family == PF_INET ? 1 : 0);
+               score = (sk->sk_family == PF_INET ? 2 : 1);
                if (inet->inet_rcv_saddr) {
                        if (inet->inet_rcv_saddr != daddr)
                                return -1;
-                       score += 2;
+                       score += 4;
                }
                if (inet->inet_daddr) {
                        if (inet->inet_daddr != saddr)
                                return -1;
-                       score += 2;
+                       score += 4;
                }
                if (inet->inet_dport) {
                        if (inet->inet_dport != sport)
                                return -1;
-                       score += 2;
+                       score += 4;
                }
                if (sk->sk_bound_dev_if) {
                        if (sk->sk_bound_dev_if != dif)
                                return -1;
-                       score += 2;
+                       score += 4;
                }
        }
        return score;
@@ -365,7 +371,6 @@ static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr,
 /*
  * In this second variant, we check (daddr, dport) matches (inet_rcv_sadd, inet_num)
  */
-#define SCORE2_MAX (1 + 2 + 2 + 2)
 static inline int compute_score2(struct sock *sk, struct net *net,
                                 __be32 saddr, __be16 sport,
                                 __be32 daddr, unsigned int hnum, int dif)
@@ -380,21 +385,21 @@ static inline int compute_score2(struct sock *sk, struct net *net,
                if (inet->inet_num != hnum)
                        return -1;
 
-               score = (sk->sk_family == PF_INET ? 1 : 0);
+               score = (sk->sk_family == PF_INET ? 2 : 1);
                if (inet->inet_daddr) {
                        if (inet->inet_daddr != saddr)
                                return -1;
-                       score += 2;
+                       score += 4;
                }
                if (inet->inet_dport) {
                        if (inet->inet_dport != sport)
                                return -1;
-                       score += 2;
+                       score += 4;
                }
                if (sk->sk_bound_dev_if) {
                        if (sk->sk_bound_dev_if != dif)
                                return -1;
-                       score += 2;
+                       score += 4;
                }
        }
        return score;
@@ -409,19 +414,29 @@ static struct sock *udp4_lib_lookup2(struct net *net,
 {
        struct sock *sk, *result;
        struct hlist_nulls_node *node;
-       int score, badness;
+       int score, badness, matches = 0, reuseport = 0;
+       u32 hash = 0;
 
 begin:
        result = NULL;
-       badness = -1;
+       badness = 0;
        udp_portaddr_for_each_entry_rcu(sk, node, &hslot2->head) {
                score = compute_score2(sk, net, saddr, sport,
                                      daddr, hnum, dif);
                if (score > badness) {
                        result = sk;
                        badness = score;
-                       if (score == SCORE2_MAX)
-                               goto exact_match;
+                       reuseport = sk->sk_reuseport;
+                       if (reuseport) {
+                               hash = inet_ehashfn(net, daddr, hnum,
+                                                   saddr, htons(sport));
+                               matches = 1;
+                       }
+               } else if (score == badness && reuseport) {
+                       matches++;
+                       if (((u64)hash * matches) >> 32 == 0)
+                               result = sk;
+                       hash = next_pseudo_random32(hash);
                }
        }
        /*
@@ -431,9 +446,7 @@ begin:
         */
        if (get_nulls_value(node) != slot2)
                goto begin;
-
        if (result) {
-exact_match:
                if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
                        result = NULL;
                else if (unlikely(compute_score2(result, net, saddr, sport,
@@ -457,7 +470,8 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
        unsigned short hnum = ntohs(dport);
        unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask);
        struct udp_hslot *hslot2, *hslot = &udptable->hash[slot];
-       int score, badness;
+       int score, badness, matches = 0, reuseport = 0;
+       u32 hash = 0;
 
        rcu_read_lock();
        if (hslot->count > 10) {
@@ -486,13 +500,24 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
        }
 begin:
        result = NULL;
-       badness = -1;
+       badness = 0;
        sk_nulls_for_each_rcu(sk, node, &hslot->head) {
                score = compute_score(sk, net, saddr, hnum, sport,
                                      daddr, dport, dif);
                if (score > badness) {
                        result = sk;
                        badness = score;
+                       reuseport = sk->sk_reuseport;
+                       if (reuseport) {
+                               hash = inet_ehashfn(net, daddr, hnum,
+                                                   saddr, htons(sport));
+                               matches = 1;
+                       }
+               } else if (score == badness && reuseport) {
+                       matches++;
+                       if (((u64)hash * matches) >> 32 == 0)
+                               result = sk;
+                       hash = next_pseudo_random32(hash);
                }
        }
        /*
@@ -971,7 +996,7 @@ back_from_confirm:
                                  sizeof(struct udphdr), &ipc, &rt,
                                  msg->msg_flags);
                err = PTR_ERR(skb);
-               if (skb && !IS_ERR(skb))
+               if (!IS_ERR_OR_NULL(skb))
                        err = udp_send_skb(skb, fl4);
                goto out;
        }
@@ -2280,7 +2305,8 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
                /* Packet is from an untrusted source, reset gso_segs. */
                int type = skb_shinfo(skb)->gso_type;
 
-               if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
+               if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
+                                     SKB_GSO_GRE) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;