Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tcp: Fix bind() regression for v6-only wildcard and v4-mapped-v6 non-wildcard addresses.

Commit 5e07e672412b ("tcp: Use bhash2 for v4-mapped-v6 non-wildcard
address.") introduced bind() regression for v4-mapped-v6 address.

When we bind() the following two addresses on the same port, the 2nd
bind() should succeed but fails now.

1. [::] w/ IPV6_ONLY
2. ::ffff:127.0.0.1

After the chagne, v4-mapped-v6 uses bhash2 instead of bhash to
detect conflict faster, but I forgot to add a necessary change.

During the 2nd bind(), inet_bind2_bucket_match_addr_any() returns
the tb2 bucket of [::], and inet_bhash2_conflict() finally calls
inet_bind_conflict(), which returns true, meaning conflict.

inet_bhash2_addr_any_conflict
|- inet_bind2_bucket_match_addr_any <-- return [::] bucket
`- inet_bhash2_conflict
`- __inet_bhash2_conflict <-- checks IPV6_ONLY for AF_INET
| but not for v4-mapped-v6 address
`- inet_bind_conflict <-- does not check address

inet_bind_conflict() does not check socket addresses because
__inet_bhash2_conflict() is expected to do so.

However, it checks IPV6_V6ONLY attribute only against AF_INET
socket, and not for v4-mapped-v6 address.

As a result, v4-mapped-v6 address conflicts with v6-only wildcard
address.

To avoid that, let's add the missing test to use bhash2 for
v4-mapped-v6 address.

Fixes: 5e07e672412b ("tcp: Use bhash2 for v4-mapped-v6 non-wildcard address.")
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://lore.kernel.org/r/20240326204251.51301-2-kuniyu@amazon.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Kuniyuki Iwashima and committed by
Jakub Kicinski
ea111449 17af4205

+9 -2
+9 -2
net/ipv4/inet_connection_sock.c
··· 203 203 kuid_t sk_uid, bool relax, 204 204 bool reuseport_cb_ok, bool reuseport_ok) 205 205 { 206 - if (sk->sk_family == AF_INET && ipv6_only_sock(sk2)) 207 - return false; 206 + if (ipv6_only_sock(sk2)) { 207 + if (sk->sk_family == AF_INET) 208 + return false; 209 + 210 + #if IS_ENABLED(CONFIG_IPV6) 211 + if (ipv6_addr_v4mapped(&sk->sk_v6_rcv_saddr)) 212 + return false; 213 + #endif 214 + } 208 215 209 216 return inet_bind_conflict(sk, sk2, sk_uid, relax, 210 217 reuseport_cb_ok, reuseport_ok);