Skip to content

Commit ff46e3b

Browse files
luoxuanqiangPaolo Abeni
authored andcommitted
Fix race for duplicate reqsk on identical SYN
When bonding is configured in BOND_MODE_BROADCAST mode, if two identical SYN packets are received at the same time and processed on different CPUs, it can potentially create the same sk (sock) but two different reqsk (request_sock) in tcp_conn_request(). These two different reqsk will respond with two SYNACK packets, and since the generation of the seq (ISN) incorporates a timestamp, the final two SYNACK packets will have different seq values. The consequence is that when the Client receives and replies with an ACK to the earlier SYNACK packet, we will reset(RST) it. ======================================================================== This behavior is consistently reproducible in my local setup, which comprises: | NETA1 ------ NETB1 | PC_A --- bond --- | | --- bond --- PC_B | NETA2 ------ NETB2 | - PC_A is the Server and has two network cards, NETA1 and NETA2. I have bonded these two cards using BOND_MODE_BROADCAST mode and configured them to be handled by different CPU. - PC_B is the Client, also equipped with two network cards, NETB1 and NETB2, which are also bonded and configured in BOND_MODE_BROADCAST mode. If the client attempts a TCP connection to the server, it might encounter a failure. Capturing packets from the server side reveals: 10.10.10.10.45182 > localhost: Flags [S], seq 320236027, 10.10.10.10.45182 > localhost: Flags [S], seq 320236027, localhost > 10.10.10.10.45182: Flags [S.], seq 2967855116, localhost > 10.10.10.10.45182: Flags [S.], seq 2967855123, <== 10.10.10.10.45182 > localhost: Flags [.], ack 4294967290, 10.10.10.10.45182 > localhost: Flags [.], ack 4294967290, localhost > 10.10.10.10.45182: Flags [R], seq 2967855117, <== localhost > 10.10.10.10.45182: Flags [R], seq 2967855117, Two SYNACKs with different seq numbers are sent by localhost, resulting in an anomaly. ======================================================================== The attempted solution is as follows: Add a return value to inet_csk_reqsk_queue_hash_add() to confirm if the ehash insertion is successful (Up to now, the reason for unsuccessful insertion is that a reqsk for the same connection has already been inserted). If the insertion fails, release the reqsk. Due to the refcnt, Kuniyuki suggests also adding a return value check for the DCCP module; if ehash insertion fails, indicating a successful insertion of the same connection, simply release the reqsk as well. Simultaneously, In the reqsk_queue_hash_req(), the start of the req->rsk_timer is adjusted to be after successful insertion. Fixes: 1da177e ("Linux-2.6.12-rc2") Signed-off-by: luoxuanqiang <luoxuanqiang@kylinos.cn> Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Link: https://lore.kernel.org/r/20240621013929.1386815-1-luoxuanqiang@kylinos.cn Signed-off-by: Paolo Abeni <pabeni@redhat.com>
1 parent 0983d28 commit ff46e3b

File tree

5 files changed

+30
-10
lines changed

5 files changed

+30
-10
lines changed

include/net/inet_connection_sock.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ struct dst_entry *inet_csk_route_child_sock(const struct sock *sk,
263263
struct sock *inet_csk_reqsk_queue_add(struct sock *sk,
264264
struct request_sock *req,
265265
struct sock *child);
266-
void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
266+
bool inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
267267
unsigned long timeout);
268268
struct sock *inet_csk_complete_hashdance(struct sock *sk, struct sock *child,
269269
struct request_sock *req,

net/dccp/ipv4.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,8 +657,11 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
657657
if (dccp_v4_send_response(sk, req))
658658
goto drop_and_free;
659659

660-
inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
661-
reqsk_put(req);
660+
if (unlikely(!inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT)))
661+
reqsk_free(req);
662+
else
663+
reqsk_put(req);
664+
662665
return 0;
663666

664667
drop_and_free:

net/dccp/ipv6.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,11 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
400400
if (dccp_v6_send_response(sk, req))
401401
goto drop_and_free;
402402

403-
inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
404-
reqsk_put(req);
403+
if (unlikely(!inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT)))
404+
reqsk_free(req);
405+
else
406+
reqsk_put(req);
407+
405408
return 0;
406409

407410
drop_and_free:

net/ipv4/inet_connection_sock.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,25 +1122,34 @@ static void reqsk_timer_handler(struct timer_list *t)
11221122
inet_csk_reqsk_queue_drop_and_put(oreq->rsk_listener, oreq);
11231123
}
11241124

1125-
static void reqsk_queue_hash_req(struct request_sock *req,
1125+
static bool reqsk_queue_hash_req(struct request_sock *req,
11261126
unsigned long timeout)
11271127
{
1128+
bool found_dup_sk = false;
1129+
1130+
if (!inet_ehash_insert(req_to_sk(req), NULL, &found_dup_sk))
1131+
return false;
1132+
1133+
/* The timer needs to be setup after a successful insertion. */
11281134
timer_setup(&req->rsk_timer, reqsk_timer_handler, TIMER_PINNED);
11291135
mod_timer(&req->rsk_timer, jiffies + timeout);
11301136

1131-
inet_ehash_insert(req_to_sk(req), NULL, NULL);
11321137
/* before letting lookups find us, make sure all req fields
11331138
* are committed to memory and refcnt initialized.
11341139
*/
11351140
smp_wmb();
11361141
refcount_set(&req->rsk_refcnt, 2 + 1);
1142+
return true;
11371143
}
11381144

1139-
void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
1145+
bool inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
11401146
unsigned long timeout)
11411147
{
1142-
reqsk_queue_hash_req(req, timeout);
1148+
if (!reqsk_queue_hash_req(req, timeout))
1149+
return false;
1150+
11431151
inet_csk_reqsk_queue_added(sk);
1152+
return true;
11441153
}
11451154
EXPORT_SYMBOL_GPL(inet_csk_reqsk_queue_hash_add);
11461155

net/ipv4/tcp_input.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7257,7 +7257,12 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
72577257
tcp_rsk(req)->tfo_listener = false;
72587258
if (!want_cookie) {
72597259
req->timeout = tcp_timeout_init((struct sock *)req);
7260-
inet_csk_reqsk_queue_hash_add(sk, req, req->timeout);
7260+
if (unlikely(!inet_csk_reqsk_queue_hash_add(sk, req,
7261+
req->timeout))) {
7262+
reqsk_free(req);
7263+
return 0;
7264+
}
7265+
72617266
}
72627267
af_ops->send_synack(sk, dst, &fl, req, &foc,
72637268
!want_cookie ? TCP_SYNACK_NORMAL :

0 commit comments

Comments
 (0)