mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 01:32:21 +02:00
tcp: challenge ACK for non-exact RST in SYN-RECEIVED
The SYN-RECEIVED request-socket path in tcp_check_req() accepts an
in-window RST without requiring SEG.SEQ to exactly match RCV.NXT. A
non-exact RST therefore removes the request instead of eliciting a
challenge ACK.
RFC 9293 section 3.10.7.4 applies the RFC 5961 reset check in
SYN-RECEIVED: an exact RST resets the connection, while a non-exact
in-window RST must trigger a challenge ACK and be dropped.
Apply that check before the ACK-field validation, following the RFC
sequence-number, RST, then ACK processing order. Factor the per-netns
challenge ACK quota out of tcp_send_challenge_ack() so request sockets
can share it. Use the request socket's send_ack() callback and its own
out-of-window ACK timestamp to send and rate-limit the response.
Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
Reported-by: Ao Wang <wangao@seu.edu.cn>
Reported-by: Xuewei Feng <fengxw06@126.com>
Reported-by: Qi Li <qli01@tsinghua.edu.cn>
Reported-by: Ke Xu <xuke@tsinghua.edu.cn>
Fixes: 282f23c6ee ("tcp: implement RFC 5961 3.2")
Cc: stable@vger.kernel.org
Signed-off-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260717081443.809393-2-yangyx22@mails.tsinghua.edu.cn
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
9dfd800b37
commit
a28c4fcbf7
|
|
@ -1974,6 +1974,8 @@ static inline void tcp_fast_path_check(struct sock *sk)
|
|||
|
||||
bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb,
|
||||
int mib_idx, u32 *last_oow_ack_time);
|
||||
void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb,
|
||||
struct request_sock *req);
|
||||
|
||||
static inline void tcp_mib_init(struct net *net)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4038,24 +4038,17 @@ static void tcp_send_ack_reflect_ect(struct sock *sk, bool accecn_reflector)
|
|||
__tcp_send_ack(sk, tp->rcv_nxt, flags);
|
||||
}
|
||||
|
||||
/* RFC 5961 7 [ACK Throttling] */
|
||||
static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector)
|
||||
/* Consume one slot from the per-netns RFC 5961 challenge ACK quota.
|
||||
* Returns true if a challenge ACK may be sent.
|
||||
*/
|
||||
static bool tcp_challenge_ack_allowed(struct net *net)
|
||||
{
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
struct net *net = sock_net(sk);
|
||||
u32 count, now, ack_limit;
|
||||
|
||||
/* First check our per-socket dupack rate limit. */
|
||||
if (__tcp_oow_rate_limited(net,
|
||||
LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
|
||||
&tp->last_oow_ack_time))
|
||||
return;
|
||||
|
||||
ack_limit = READ_ONCE(net->ipv4.sysctl_tcp_challenge_ack_limit);
|
||||
if (ack_limit == INT_MAX)
|
||||
goto send_ack;
|
||||
return true;
|
||||
|
||||
/* Then check host-wide RFC 5961 rate limit. */
|
||||
now = jiffies / HZ;
|
||||
if (now != READ_ONCE(net->ipv4.tcp_challenge_timestamp)) {
|
||||
u32 half = (ack_limit + 1) >> 1;
|
||||
|
|
@ -4067,12 +4060,49 @@ static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector)
|
|||
count = READ_ONCE(net->ipv4.tcp_challenge_count);
|
||||
if (count > 0) {
|
||||
WRITE_ONCE(net->ipv4.tcp_challenge_count, count - 1);
|
||||
send_ack:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* RFC 5961 7 [ACK Throttling] */
|
||||
static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector)
|
||||
{
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
struct net *net = sock_net(sk);
|
||||
|
||||
/* First check our per-socket dupack rate limit. */
|
||||
if (__tcp_oow_rate_limited(net,
|
||||
LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
|
||||
&tp->last_oow_ack_time))
|
||||
return;
|
||||
|
||||
/* Then check the per-netns RFC 5961 rate limit. */
|
||||
if (tcp_challenge_ack_allowed(net)) {
|
||||
NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK);
|
||||
tcp_send_ack_reflect_ect(sk, accecn_reflector);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send a challenge ACK from a SYN-RECEIVED request socket. Uses
|
||||
* __tcp_oow_rate_limited() directly so that an RST carrying payload
|
||||
* cannot bypass the per-request rate limit.
|
||||
*/
|
||||
void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb,
|
||||
struct request_sock *req)
|
||||
{
|
||||
struct net *net = sock_net(sk);
|
||||
|
||||
if (__tcp_oow_rate_limited(net, LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
|
||||
&tcp_rsk(req)->last_oow_ack_time))
|
||||
return;
|
||||
|
||||
if (tcp_challenge_ack_allowed(net)) {
|
||||
NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK);
|
||||
req->rsk_ops->send_ack(sk, skb, req);
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_store_ts_recent(struct tcp_sock *tp)
|
||||
{
|
||||
tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval;
|
||||
|
|
|
|||
|
|
@ -833,7 +833,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
|
|||
* elsewhere and is checked directly against the child socket rather
|
||||
* than req because user data may have been sent out.
|
||||
*/
|
||||
if ((flg & TCP_FLAG_ACK) && !fastopen &&
|
||||
if ((flg & TCP_FLAG_ACK) && !(flg & TCP_FLAG_RST) && !fastopen &&
|
||||
(TCP_SKB_CB(skb)->ack_seq !=
|
||||
tcp_rsk(req)->snt_isn + 1))
|
||||
return sk;
|
||||
|
|
@ -872,6 +872,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
|
|||
flg &= ~TCP_FLAG_SYN;
|
||||
}
|
||||
|
||||
/* RFC 5961 section 3.2, as clarified by RFC 9293 section
|
||||
* 3.10.7.4, requires a challenge ACK for a non-exact
|
||||
* in-window RST in SYN-RECEIVED.
|
||||
*/
|
||||
if ((flg & TCP_FLAG_RST) &&
|
||||
TCP_SKB_CB(skb)->seq != tcp_rsk(req)->rcv_nxt) {
|
||||
tcp_reqsk_send_challenge_ack(sk, skb, req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* RFC793: "second check the RST bit" and
|
||||
* "fourth, check the SYN bit"
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user