Merge tag 'v5.7-rc7' into perf/core, to pick up fixes
[linux-2.6-microblaze.git] / net / ipv4 / inet_connection_sock.c
index d545fb9..65c29f2 100644 (file)
 #include <net/addrconf.h>
 
 #if IS_ENABLED(CONFIG_IPV6)
-/* match_wildcard == true:  IPV6_ADDR_ANY equals to any IPv6 addresses if IPv6
- *                          only, and any IPv4 addresses if not IPv6 only
- * match_wildcard == false: addresses must be exactly the same, i.e.
- *                          IPV6_ADDR_ANY only equals to IPV6_ADDR_ANY,
- *                          and 0.0.0.0 equals to 0.0.0.0 only
+/* match_sk*_wildcard == true:  IPV6_ADDR_ANY equals to any IPv6 addresses
+ *                             if IPv6 only, and any IPv4 addresses
+ *                             if not IPv6 only
+ * match_sk*_wildcard == false: addresses must be exactly the same, i.e.
+ *                             IPV6_ADDR_ANY only equals to IPV6_ADDR_ANY,
+ *                             and 0.0.0.0 equals to 0.0.0.0 only
  */
 static bool ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6,
                                 const struct in6_addr *sk2_rcv_saddr6,
                                 __be32 sk1_rcv_saddr, __be32 sk2_rcv_saddr,
                                 bool sk1_ipv6only, bool sk2_ipv6only,
-                                bool match_wildcard)
+                                bool match_sk1_wildcard,
+                                bool match_sk2_wildcard)
 {
        int addr_type = ipv6_addr_type(sk1_rcv_saddr6);
        int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED;
@@ -44,8 +46,8 @@ static bool ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6,
                if (!sk2_ipv6only) {
                        if (sk1_rcv_saddr == sk2_rcv_saddr)
                                return true;
-                       if (!sk1_rcv_saddr || !sk2_rcv_saddr)
-                               return match_wildcard;
+                       return (match_sk1_wildcard && !sk1_rcv_saddr) ||
+                               (match_sk2_wildcard && !sk2_rcv_saddr);
                }
                return false;
        }
@@ -53,11 +55,11 @@ static bool ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6,
        if (addr_type == IPV6_ADDR_ANY && addr_type2 == IPV6_ADDR_ANY)
                return true;
 
-       if (addr_type2 == IPV6_ADDR_ANY && match_wildcard &&
+       if (addr_type2 == IPV6_ADDR_ANY && match_sk2_wildcard &&
            !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
                return true;
 
-       if (addr_type == IPV6_ADDR_ANY && match_wildcard &&
+       if (addr_type == IPV6_ADDR_ANY && match_sk1_wildcard &&
            !(sk1_ipv6only && addr_type2 == IPV6_ADDR_MAPPED))
                return true;
 
@@ -69,18 +71,19 @@ static bool ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6,
 }
 #endif
 
-/* match_wildcard == true:  0.0.0.0 equals to any IPv4 addresses
- * match_wildcard == false: addresses must be exactly the same, i.e.
- *                          0.0.0.0 only equals to 0.0.0.0
+/* match_sk*_wildcard == true:  0.0.0.0 equals to any IPv4 addresses
+ * match_sk*_wildcard == false: addresses must be exactly the same, i.e.
+ *                             0.0.0.0 only equals to 0.0.0.0
  */
 static bool ipv4_rcv_saddr_equal(__be32 sk1_rcv_saddr, __be32 sk2_rcv_saddr,
-                                bool sk2_ipv6only, bool match_wildcard)
+                                bool sk2_ipv6only, bool match_sk1_wildcard,
+                                bool match_sk2_wildcard)
 {
        if (!sk2_ipv6only) {
                if (sk1_rcv_saddr == sk2_rcv_saddr)
                        return true;
-               if (!sk1_rcv_saddr || !sk2_rcv_saddr)
-                       return match_wildcard;
+               return (match_sk1_wildcard && !sk1_rcv_saddr) ||
+                       (match_sk2_wildcard && !sk2_rcv_saddr);
        }
        return false;
 }
@@ -96,10 +99,12 @@ bool inet_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
                                            sk2->sk_rcv_saddr,
                                            ipv6_only_sock(sk),
                                            ipv6_only_sock(sk2),
+                                           match_wildcard,
                                            match_wildcard);
 #endif
        return ipv4_rcv_saddr_equal(sk->sk_rcv_saddr, sk2->sk_rcv_saddr,
-                                   ipv6_only_sock(sk2), match_wildcard);
+                                   ipv6_only_sock(sk2), match_wildcard,
+                                   match_wildcard);
 }
 EXPORT_SYMBOL(inet_rcv_saddr_equal);
 
@@ -131,7 +136,7 @@ static int inet_csk_bind_conflict(const struct sock *sk,
 {
        struct sock *sk2;
        bool reuse = sk->sk_reuse;
-       bool reuseport = !!sk->sk_reuseport && reuseport_ok;
+       bool reuseport = !!sk->sk_reuseport;
        kuid_t uid = sock_i_uid((struct sock *)sk);
 
        /*
@@ -146,17 +151,21 @@ static int inet_csk_bind_conflict(const struct sock *sk,
                    (!sk->sk_bound_dev_if ||
                     !sk2->sk_bound_dev_if ||
                     sk->sk_bound_dev_if == sk2->sk_bound_dev_if)) {
-                       if ((!reuse || !sk2->sk_reuse ||
-                           sk2->sk_state == TCP_LISTEN) &&
-                           (!reuseport || !sk2->sk_reuseport ||
-                            rcu_access_pointer(sk->sk_reuseport_cb) ||
-                            (sk2->sk_state != TCP_TIME_WAIT &&
-                            !uid_eq(uid, sock_i_uid(sk2))))) {
-                               if (inet_rcv_saddr_equal(sk, sk2, true))
-                                       break;
-                       }
-                       if (!relax && reuse && sk2->sk_reuse &&
+                       if (reuse && sk2->sk_reuse &&
                            sk2->sk_state != TCP_LISTEN) {
+                               if ((!relax ||
+                                    (!reuseport_ok &&
+                                     reuseport && sk2->sk_reuseport &&
+                                     !rcu_access_pointer(sk->sk_reuseport_cb) &&
+                                     (sk2->sk_state == TCP_TIME_WAIT ||
+                                      uid_eq(uid, sock_i_uid(sk2))))) &&
+                                   inet_rcv_saddr_equal(sk, sk2, true))
+                                       break;
+                       } else if (!reuseport_ok ||
+                                  !reuseport || !sk2->sk_reuseport ||
+                                  rcu_access_pointer(sk->sk_reuseport_cb) ||
+                                  (sk2->sk_state != TCP_TIME_WAIT &&
+                                   !uid_eq(uid, sock_i_uid(sk2)))) {
                                if (inet_rcv_saddr_equal(sk, sk2, true))
                                        break;
                        }
@@ -176,12 +185,14 @@ inet_csk_find_open_port(struct sock *sk, struct inet_bind_bucket **tb_ret, int *
        int port = 0;
        struct inet_bind_hashbucket *head;
        struct net *net = sock_net(sk);
+       bool relax = false;
        int i, low, high, attempt_half;
        struct inet_bind_bucket *tb;
        u32 remaining, offset;
        int l3mdev;
 
        l3mdev = inet_sk_bound_l3mdev(sk);
+ports_exhausted:
        attempt_half = (sk->sk_reuse == SK_CAN_REUSE) ? 1 : 0;
 other_half_scan:
        inet_get_local_port_range(net, &low, &high);
@@ -219,7 +230,7 @@ other_parity_scan:
                inet_bind_bucket_for_each(tb, &head->chain)
                        if (net_eq(ib_net(tb), net) && tb->l3mdev == l3mdev &&
                            tb->port == port) {
-                               if (!inet_csk_bind_conflict(sk, tb, false, false))
+                               if (!inet_csk_bind_conflict(sk, tb, relax, false))
                                        goto success;
                                goto next_port;
                        }
@@ -239,6 +250,12 @@ next_port:
                attempt_half = 2;
                goto other_half_scan;
        }
+
+       if (net->ipv4.sysctl_ip_autobind_reuse && !relax) {
+               /* We still have a chance to connect to different destinations */
+               relax = true;
+               goto ports_exhausted;
+       }
        return NULL;
 success:
        *port_ret = port;
@@ -273,10 +290,10 @@ static inline int sk_reuseport_match(struct inet_bind_bucket *tb,
                                            tb->fast_rcv_saddr,
                                            sk->sk_rcv_saddr,
                                            tb->fast_ipv6_only,
-                                           ipv6_only_sock(sk), true);
+                                           ipv6_only_sock(sk), true, false);
 #endif
        return ipv4_rcv_saddr_equal(tb->fast_rcv_saddr, sk->sk_rcv_saddr,
-                                   ipv6_only_sock(sk), true);
+                                   ipv6_only_sock(sk), true, false);
 }
 
 /* Obtain a reference to a local port for the given sock,