udp: Unhash auto-bound connected sk from 4-tuple hash table when disconnected.

Let's say we bind() an UDP socket to the wildcard address with a
non-zero port, connect() it to an address, and disconnect it from
the address.

bind() sets SOCK_BINDPORT_LOCK on sk->sk_userlocks (but not
SOCK_BINDADDR_LOCK), and connect() calls udp_lib_hash4() to put
the socket into the 4-tuple hash table.

Then, __udp_disconnect() calls sk->sk_prot->rehash(sk).

It computes a new hash based on the wildcard address and moves
the socket to a new slot in the 4-tuple hash table, leaving a
garbage in the chain that no packet hits.

Let's remove such a socket from 4-tuple hash table when disconnected.

Note that udp_sk(sk)->udp_portaddr_hash needs to be udpated after
udp_hash4_dec(hslot2) in udp_unhash4().

Fixes: 78c91ae2c6 ("ipv4/udp: Add 4-tuple hash for connected socket")
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260227035547.3321327-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Kuniyuki Iwashima 2026-02-27 03:55:35 +00:00 committed by Jakub Kicinski
parent dabffd0854
commit 6996a2d2d0

View File

@ -2287,7 +2287,6 @@ void udp_lib_rehash(struct sock *sk, u16 newhash, u16 newhash4)
udp_sk(sk)->udp_port_hash); udp_sk(sk)->udp_port_hash);
hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash); hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
nhslot2 = udp_hashslot2(udptable, newhash); nhslot2 = udp_hashslot2(udptable, newhash);
udp_sk(sk)->udp_portaddr_hash = newhash;
if (hslot2 != nhslot2 || if (hslot2 != nhslot2 ||
rcu_access_pointer(sk->sk_reuseport_cb)) { rcu_access_pointer(sk->sk_reuseport_cb)) {
@ -2321,19 +2320,25 @@ void udp_lib_rehash(struct sock *sk, u16 newhash, u16 newhash4)
if (udp_hashed4(sk)) { if (udp_hashed4(sk)) {
spin_lock_bh(&hslot->lock); spin_lock_bh(&hslot->lock);
udp_rehash4(udptable, sk, newhash4); if (inet_rcv_saddr_any(sk)) {
if (hslot2 != nhslot2) { udp_unhash4(udptable, sk);
spin_lock(&hslot2->lock); } else {
udp_hash4_dec(hslot2); udp_rehash4(udptable, sk, newhash4);
spin_unlock(&hslot2->lock); if (hslot2 != nhslot2) {
spin_lock(&hslot2->lock);
udp_hash4_dec(hslot2);
spin_unlock(&hslot2->lock);
spin_lock(&nhslot2->lock); spin_lock(&nhslot2->lock);
udp_hash4_inc(nhslot2); udp_hash4_inc(nhslot2);
spin_unlock(&nhslot2->lock); spin_unlock(&nhslot2->lock);
}
} }
spin_unlock_bh(&hslot->lock); spin_unlock_bh(&hslot->lock);
} }
udp_sk(sk)->udp_portaddr_hash = newhash;
} }
} }
EXPORT_IPV6_MOD(udp_lib_rehash); EXPORT_IPV6_MOD(udp_lib_rehash);