From: Paolo Abeni <[email protected]>

The CI reports sporadic failures of the fastclose self-tests. The root
cause is a duplicate reset, not carrying the relevant MPTCP option.
In the failing scenario the bad reset is received by the peer before
the fastclose one, preventing the reception of the latter.

Indeed there is window of opportunity at fastclose time for the
following race:

  mptcp_do_fastclose
    __mptcp_close_ssk
      __tcp_close()
        tcp_set_state() [1]
        tcp_send_active_reset() [2]

After [1] the stack will send reset to in-flight data reaching the now
closed port. Such reset may race with [2].

Address the issue explicitly sending a single reset on fastclose before
explicitly moving the subflow to close status.

Fixes: d21f83485518 ("mptcp: use fastclose on more edge scenarios")
Cc: [email protected]
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/596
Signed-off-by: Paolo Abeni <[email protected]>
Reviewed-by: Geliang Tang <[email protected]>
Reviewed-by: Matthieu Baerts (NGI0) <[email protected]>
Signed-off-by: Matthieu Baerts (NGI0) <[email protected]>
---
 net/mptcp/protocol.c | 36 +++++++++++++++++++++++-------------
 1 file changed, 23 insertions(+), 13 deletions(-)

diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index c59246c1fde6..a70267a74e3c 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -2409,7 +2409,6 @@ bool __mptcp_retransmit_pending_data(struct sock *sk)
 
 /* flags for __mptcp_close_ssk() */
 #define MPTCP_CF_PUSH          BIT(1)
-#define MPTCP_CF_FASTCLOSE     BIT(2)
 
 /* be sure to send a reset only if the caller asked for it, also
  * clean completely the subflow status when the subflow reaches
@@ -2420,7 +2419,7 @@ static void __mptcp_subflow_disconnect(struct sock *ssk,
                                       unsigned int flags)
 {
        if (((1 << ssk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) ||
-           (flags & MPTCP_CF_FASTCLOSE)) {
+           subflow->send_fastclose) {
                /* The MPTCP code never wait on the subflow sockets, TCP-level
                 * disconnect should never fail
                 */
@@ -2467,14 +2466,8 @@ static void __mptcp_close_ssk(struct sock *sk, struct 
sock *ssk,
 
        lock_sock_nested(ssk, SINGLE_DEPTH_NESTING);
 
-       if ((flags & MPTCP_CF_FASTCLOSE) && !__mptcp_check_fallback(msk)) {
-               /* be sure to force the tcp_close path
-                * to generate the egress reset
-                */
-               ssk->sk_lingertime = 0;
-               sock_set_flag(ssk, SOCK_LINGER);
-               subflow->send_fastclose = 1;
-       }
+       if (subflow->send_fastclose && ssk->sk_state != TCP_CLOSE)
+               tcp_set_state(ssk, TCP_CLOSE);
 
        need_push = (flags & MPTCP_CF_PUSH) && 
__mptcp_retransmit_pending_data(sk);
        if (!dispose_it) {
@@ -2779,9 +2772,26 @@ static void mptcp_do_fastclose(struct sock *sk)
        struct mptcp_sock *msk = mptcp_sk(sk);
 
        mptcp_set_state(sk, TCP_CLOSE);
-       mptcp_for_each_subflow_safe(msk, subflow, tmp)
-               __mptcp_close_ssk(sk, mptcp_subflow_tcp_sock(subflow),
-                                 subflow, MPTCP_CF_FASTCLOSE);
+
+       /* Explicitly send the fastclose reset as need */
+       if (__mptcp_check_fallback(msk))
+               return;
+
+       mptcp_for_each_subflow_safe(msk, subflow, tmp) {
+               struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+
+               lock_sock(ssk);
+
+               /* Some subflow socket states don't allow/need a reset.*/
+               if ((1 << ssk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))
+                       goto unlock;
+
+               subflow->send_fastclose = 1;
+               tcp_send_active_reset(ssk, ssk->sk_allocation,
+                                     SK_RST_REASON_TCP_ABORT_ON_CLOSE);
+unlock:
+               release_sock(ssk);
+       }
 }
 
 static void mptcp_worker(struct work_struct *work)

-- 
2.51.0


Reply via email to