Skip to content

Commit 88d7fcf

Browse files
iamkafaidavem330
authored andcommitted
net: inet_csk: Fix so_reuseport bind-address cache in tb->fast*
The commit 637bc8b ("inet: reset tb->fastreuseport when adding a reuseport sk") added a bind-address cache in tb->fast*. The tb->fast* caches the address of a sk which has successfully been binded with SO_REUSEPORT ON. The idea is to avoid the expensive conflict search in inet_csk_bind_conflict(). There is an issue with wildcard matching where sk_reuseport_match() should have returned false but it is currently returning true. It ends up hiding bind conflict. For example, bind("[::1]:443"); /* without SO_REUSEPORT. Succeed. */ bind("[::2]:443"); /* with SO_REUSEPORT. Succeed. */ bind("[::]:443"); /* with SO_REUSEPORT. Still Succeed where it shouldn't */ The last bind("[::]:443") with SO_REUSEPORT on should have failed because it should have a conflict with the very first bind("[::1]:443") which has SO_REUSEPORT off. However, the address "[::2]" is cached in tb->fast* in the second bind. In the last bind, the sk_reuseport_match() returns true because the binding sk's wildcard addr "[::]" matches with the "[::2]" cached in tb->fast*. The correct bind conflict is reported by removing the second bind such that tb->fast* cache is not involved and forces the bind("[::]:443") to go through the inet_csk_bind_conflict(): bind("[::1]:443"); /* without SO_REUSEPORT. Succeed. */ bind("[::]:443"); /* with SO_REUSEPORT. -EADDRINUSE */ The expected behavior for sk_reuseport_match() is, it should only allow the "cached" tb->fast* address to be used as a wildcard match but not the address of the binding sk. To do that, the current "bool match_wildcard" arg is split into "bool match_sk1_wildcard" and "bool match_sk2_wildcard". This change only affects the sk_reuseport_match() which is only used by inet_csk (e.g. TCP). The other use cases are calling inet_rcv_saddr_equal() and this patch makes it pass the same "match_wildcard" arg twice to the "ipv[46]_rcv_saddr_equal(..., match_wildcard, match_wildcard)". Cc: Josef Bacik <[email protected]> Fixes: 637bc8b ("inet: reset tb->fastreuseport when adding a reuseport sk") Signed-off-by: Martin KaFai Lau <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent c27a204 commit 88d7fcf

File tree

1 file changed

+24
-19
lines changed

1 file changed

+24
-19
lines changed

net/ipv4/inet_connection_sock.c

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@
2424
#include <net/addrconf.h>
2525

2626
#if IS_ENABLED(CONFIG_IPV6)
27-
/* match_wildcard == true: IPV6_ADDR_ANY equals to any IPv6 addresses if IPv6
28-
* only, and any IPv4 addresses if not IPv6 only
29-
* match_wildcard == false: addresses must be exactly the same, i.e.
30-
* IPV6_ADDR_ANY only equals to IPV6_ADDR_ANY,
31-
* and 0.0.0.0 equals to 0.0.0.0 only
27+
/* match_sk*_wildcard == true: IPV6_ADDR_ANY equals to any IPv6 addresses
28+
* if IPv6 only, and any IPv4 addresses
29+
* if not IPv6 only
30+
* match_sk*_wildcard == false: addresses must be exactly the same, i.e.
31+
* IPV6_ADDR_ANY only equals to IPV6_ADDR_ANY,
32+
* and 0.0.0.0 equals to 0.0.0.0 only
3233
*/
3334
static bool ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6,
3435
const struct in6_addr *sk2_rcv_saddr6,
3536
__be32 sk1_rcv_saddr, __be32 sk2_rcv_saddr,
3637
bool sk1_ipv6only, bool sk2_ipv6only,
37-
bool match_wildcard)
38+
bool match_sk1_wildcard,
39+
bool match_sk2_wildcard)
3840
{
3941
int addr_type = ipv6_addr_type(sk1_rcv_saddr6);
4042
int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED;
@@ -44,20 +46,20 @@ static bool ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6,
4446
if (!sk2_ipv6only) {
4547
if (sk1_rcv_saddr == sk2_rcv_saddr)
4648
return true;
47-
if (!sk1_rcv_saddr || !sk2_rcv_saddr)
48-
return match_wildcard;
49+
return (match_sk1_wildcard && !sk1_rcv_saddr) ||
50+
(match_sk2_wildcard && !sk2_rcv_saddr);
4951
}
5052
return false;
5153
}
5254

5355
if (addr_type == IPV6_ADDR_ANY && addr_type2 == IPV6_ADDR_ANY)
5456
return true;
5557

56-
if (addr_type2 == IPV6_ADDR_ANY && match_wildcard &&
58+
if (addr_type2 == IPV6_ADDR_ANY && match_sk2_wildcard &&
5759
!(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
5860
return true;
5961

60-
if (addr_type == IPV6_ADDR_ANY && match_wildcard &&
62+
if (addr_type == IPV6_ADDR_ANY && match_sk1_wildcard &&
6163
!(sk1_ipv6only && addr_type2 == IPV6_ADDR_MAPPED))
6264
return true;
6365

@@ -69,18 +71,19 @@ static bool ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6,
6971
}
7072
#endif
7173

72-
/* match_wildcard == true: 0.0.0.0 equals to any IPv4 addresses
73-
* match_wildcard == false: addresses must be exactly the same, i.e.
74-
* 0.0.0.0 only equals to 0.0.0.0
74+
/* match_sk*_wildcard == true: 0.0.0.0 equals to any IPv4 addresses
75+
* match_sk*_wildcard == false: addresses must be exactly the same, i.e.
76+
* 0.0.0.0 only equals to 0.0.0.0
7577
*/
7678
static bool ipv4_rcv_saddr_equal(__be32 sk1_rcv_saddr, __be32 sk2_rcv_saddr,
77-
bool sk2_ipv6only, bool match_wildcard)
79+
bool sk2_ipv6only, bool match_sk1_wildcard,
80+
bool match_sk2_wildcard)
7881
{
7982
if (!sk2_ipv6only) {
8083
if (sk1_rcv_saddr == sk2_rcv_saddr)
8184
return true;
82-
if (!sk1_rcv_saddr || !sk2_rcv_saddr)
83-
return match_wildcard;
85+
return (match_sk1_wildcard && !sk1_rcv_saddr) ||
86+
(match_sk2_wildcard && !sk2_rcv_saddr);
8487
}
8588
return false;
8689
}
@@ -96,10 +99,12 @@ bool inet_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
9699
sk2->sk_rcv_saddr,
97100
ipv6_only_sock(sk),
98101
ipv6_only_sock(sk2),
102+
match_wildcard,
99103
match_wildcard);
100104
#endif
101105
return ipv4_rcv_saddr_equal(sk->sk_rcv_saddr, sk2->sk_rcv_saddr,
102-
ipv6_only_sock(sk2), match_wildcard);
106+
ipv6_only_sock(sk2), match_wildcard,
107+
match_wildcard);
103108
}
104109
EXPORT_SYMBOL(inet_rcv_saddr_equal);
105110

@@ -285,10 +290,10 @@ static inline int sk_reuseport_match(struct inet_bind_bucket *tb,
285290
tb->fast_rcv_saddr,
286291
sk->sk_rcv_saddr,
287292
tb->fast_ipv6_only,
288-
ipv6_only_sock(sk), true);
293+
ipv6_only_sock(sk), true, false);
289294
#endif
290295
return ipv4_rcv_saddr_equal(tb->fast_rcv_saddr, sk->sk_rcv_saddr,
291-
ipv6_only_sock(sk), true);
296+
ipv6_only_sock(sk), true, false);
292297
}
293298

294299
/* Obtain a reference to a local port for the given sock,

0 commit comments

Comments
 (0)