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 <[email protected]>
Reported-by: Yizhou Zhao <[email protected]>
Reported-by: Ao Wang <[email protected]>
Reported-by: Xuewei Feng <[email protected]>
Reported-by: Qi Li <[email protected]>
Reported-by: Ke Xu <[email protected]>
Fixes: 282f23c6ee34 ("tcp: implement RFC 5961 3.2")
Cc: [email protected]
Signed-off-by: Yuxiang Yang <[email protected]>
---
 include/net/tcp.h        |  2 ++
 net/ipv4/tcp_input.c     | 56 ++++++++++++++++++++++++++++++----------
 net/ipv4/tcp_minisocks.c | 12 ++++++++-
 3 files changed, 56 insertions(+), 14 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 6d376ea4d..2c5b88953 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -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)
 {
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 61045a888..daff93d51 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -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;
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index ddc4b17a8..6ab3e3a0b 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -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"
         */
-- 
2.34.1


Reply via email to