Skip to content

Commit 644f910

Browse files
Philo LuPaolo Abeni
authored andcommitted
udp: Make rehash4 independent in udp_lib_rehash()
As discussed in [0], rehash4 could be missed in udp_lib_rehash() when udp hash4 changes while hash2 doesn't change. This patch fixes this by moving rehash4 codes out of rehash2 checking, and then rehash2 and rehash4 are done separately. By doing this, we no longer need to call rehash4 explicitly in udp_lib_hash4(), as the rehash callback in __ip4_datagram_connect takes it. Thus, now udp_lib_hash4() returns directly if the sk is already hashed. Note that uhash4 may fail to work under consecutive connect(<dst address>) calls because rehash() is not called with every connect(). To overcome this, connect(<AF_UNSPEC>) needs to be called after the next connect to a new destination. [0] https://lore.kernel.org/all/4761e466ab9f7542c68cdc95f248987d127044d2.1733499715.git.pabeni@redhat.com/ Fixes: 78c91ae ("ipv4/udp: Add 4-tuple hash for connected socket") Suggested-by: Paolo Abeni <[email protected]> Signed-off-by: Philo Lu <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent 1f691a1 commit 644f910

File tree

1 file changed

+27
-19
lines changed

1 file changed

+27
-19
lines changed

net/ipv4/udp.c

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ static struct sock *udp4_lib_lookup4(const struct net *net,
533533
return NULL;
534534
}
535535

536-
/* In hash4, rehash can happen in connect(), where hash4_cnt keeps unchanged. */
536+
/* udp_rehash4() only checks hslot4, and hash4_cnt is not processed. */
537537
static void udp_rehash4(struct udp_table *udptable, struct sock *sk,
538538
u16 newhash4)
539539
{
@@ -582,15 +582,13 @@ void udp_lib_hash4(struct sock *sk, u16 hash)
582582
struct net *net = sock_net(sk);
583583
struct udp_table *udptable;
584584

585-
/* Connected udp socket can re-connect to another remote address,
586-
* so rehash4 is needed.
585+
/* Connected udp socket can re-connect to another remote address, which
586+
* will be handled by rehash. Thus no need to redo hash4 here.
587587
*/
588-
udptable = net->ipv4.udp_table;
589-
if (udp_hashed4(sk)) {
590-
udp_rehash4(udptable, sk, hash);
588+
if (udp_hashed4(sk))
591589
return;
592-
}
593590

591+
udptable = net->ipv4.udp_table;
594592
hslot = udp_hashslot(udptable, net, udp_sk(sk)->udp_port_hash);
595593
hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
596594
hslot4 = udp_hashslot4(udptable, hash);
@@ -2173,14 +2171,14 @@ void udp_lib_rehash(struct sock *sk, u16 newhash, u16 newhash4)
21732171
struct udp_table *udptable = udp_get_table_prot(sk);
21742172
struct udp_hslot *hslot, *hslot2, *nhslot2;
21752173

2174+
hslot = udp_hashslot(udptable, sock_net(sk),
2175+
udp_sk(sk)->udp_port_hash);
21762176
hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
21772177
nhslot2 = udp_hashslot2(udptable, newhash);
21782178
udp_sk(sk)->udp_portaddr_hash = newhash;
21792179

21802180
if (hslot2 != nhslot2 ||
21812181
rcu_access_pointer(sk->sk_reuseport_cb)) {
2182-
hslot = udp_hashslot(udptable, sock_net(sk),
2183-
udp_sk(sk)->udp_port_hash);
21842182
/* we must lock primary chain too */
21852183
spin_lock_bh(&hslot->lock);
21862184
if (rcu_access_pointer(sk->sk_reuseport_cb))
@@ -2199,19 +2197,29 @@ void udp_lib_rehash(struct sock *sk, u16 newhash, u16 newhash4)
21992197
spin_unlock(&nhslot2->lock);
22002198
}
22012199

2202-
if (udp_hashed4(sk)) {
2203-
udp_rehash4(udptable, sk, newhash4);
2200+
spin_unlock_bh(&hslot->lock);
2201+
}
2202+
2203+
/* Now process hash4 if necessary:
2204+
* (1) update hslot4;
2205+
* (2) update hslot2->hash4_cnt.
2206+
* Note that hslot2/hslot4 should be checked separately, as
2207+
* either of them may change with the other unchanged.
2208+
*/
2209+
if (udp_hashed4(sk)) {
2210+
spin_lock_bh(&hslot->lock);
22042211

2205-
if (hslot2 != nhslot2) {
2206-
spin_lock(&hslot2->lock);
2207-
udp_hash4_dec(hslot2);
2208-
spin_unlock(&hslot2->lock);
2212+
udp_rehash4(udptable, sk, newhash4);
2213+
if (hslot2 != nhslot2) {
2214+
spin_lock(&hslot2->lock);
2215+
udp_hash4_dec(hslot2);
2216+
spin_unlock(&hslot2->lock);
22092217

2210-
spin_lock(&nhslot2->lock);
2211-
udp_hash4_inc(nhslot2);
2212-
spin_unlock(&nhslot2->lock);
2213-
}
2218+
spin_lock(&nhslot2->lock);
2219+
udp_hash4_inc(nhslot2);
2220+
spin_unlock(&nhslot2->lock);
22142221
}
2222+
22152223
spin_unlock_bh(&hslot->lock);
22162224
}
22172225
}

0 commit comments

Comments
 (0)