mptcp: Track received DATA_FIN sequence number and add related helpers
authorMat Martineau <mathew.j.martineau@linux.intel.com>
Tue, 28 Jul 2020 22:12:03 +0000 (15:12 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 29 Jul 2020 00:02:42 +0000 (17:02 -0700)
Incoming DATA_FIN headers need to propagate the presence of the DATA_FIN
bit and the associated sequence number to the MPTCP layer, even when
arriving on a bare ACK that does not get added to the receive queue. Add
structure members to store the DATA_FIN information and helpers to set
and check those values.

Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/mptcp/options.c
net/mptcp/protocol.c
net/mptcp/protocol.h

index f157cb7..38583d1 100644 (file)
@@ -782,6 +782,22 @@ static void update_una(struct mptcp_sock *msk,
        }
 }
 
+bool mptcp_update_rcv_data_fin(struct mptcp_sock *msk, u64 data_fin_seq)
+{
+       /* Skip if DATA_FIN was already received.
+        * If updating simultaneously with the recvmsg loop, values
+        * should match. If they mismatch, the peer is misbehaving and
+        * we will prefer the most recent information.
+        */
+       if (READ_ONCE(msk->rcv_data_fin) || !READ_ONCE(msk->first))
+               return false;
+
+       WRITE_ONCE(msk->rcv_data_fin_seq, data_fin_seq);
+       WRITE_ONCE(msk->rcv_data_fin, 1);
+
+       return true;
+}
+
 static bool add_addr_hmac_valid(struct mptcp_sock *msk,
                                struct mptcp_options_received *mp_opt)
 {
index dd403ba..e1c71bf 100644 (file)
@@ -16,6 +16,7 @@
 #include <net/inet_hashtables.h>
 #include <net/protocol.h>
 #include <net/tcp.h>
+#include <net/tcp_states.h>
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
 #include <net/transp_v6.h>
 #endif
@@ -163,6 +164,101 @@ static bool mptcp_subflow_dsn_valid(const struct mptcp_sock *msk,
        return mptcp_subflow_data_available(ssk);
 }
 
+static bool mptcp_pending_data_fin(struct sock *sk, u64 *seq)
+{
+       struct mptcp_sock *msk = mptcp_sk(sk);
+
+       if (READ_ONCE(msk->rcv_data_fin) &&
+           ((1 << sk->sk_state) &
+            (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2))) {
+               u64 rcv_data_fin_seq = READ_ONCE(msk->rcv_data_fin_seq);
+
+               if (msk->ack_seq == rcv_data_fin_seq) {
+                       if (seq)
+                               *seq = rcv_data_fin_seq;
+
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static void mptcp_set_timeout(const struct sock *sk, const struct sock *ssk)
+{
+       long tout = ssk && inet_csk(ssk)->icsk_pending ?
+                                     inet_csk(ssk)->icsk_timeout - jiffies : 0;
+
+       if (tout <= 0)
+               tout = mptcp_sk(sk)->timer_ival;
+       mptcp_sk(sk)->timer_ival = tout > 0 ? tout : TCP_RTO_MIN;
+}
+
+static void mptcp_check_data_fin(struct sock *sk)
+{
+       struct mptcp_sock *msk = mptcp_sk(sk);
+       u64 rcv_data_fin_seq;
+
+       if (__mptcp_check_fallback(msk) || !msk->first)
+               return;
+
+       /* Need to ack a DATA_FIN received from a peer while this side
+        * of the connection is in ESTABLISHED, FIN_WAIT1, or FIN_WAIT2.
+        * msk->rcv_data_fin was set when parsing the incoming options
+        * at the subflow level and the msk lock was not held, so this
+        * is the first opportunity to act on the DATA_FIN and change
+        * the msk state.
+        *
+        * If we are caught up to the sequence number of the incoming
+        * DATA_FIN, send the DATA_ACK now and do state transition.  If
+        * not caught up, do nothing and let the recv code send DATA_ACK
+        * when catching up.
+        */
+
+       if (mptcp_pending_data_fin(sk, &rcv_data_fin_seq)) {
+               struct mptcp_subflow_context *subflow;
+
+               msk->ack_seq++;
+               WRITE_ONCE(msk->rcv_data_fin, 0);
+
+               sk->sk_shutdown |= RCV_SHUTDOWN;
+
+               switch (sk->sk_state) {
+               case TCP_ESTABLISHED:
+                       inet_sk_state_store(sk, TCP_CLOSE_WAIT);
+                       break;
+               case TCP_FIN_WAIT1:
+                       inet_sk_state_store(sk, TCP_CLOSING);
+                       break;
+               case TCP_FIN_WAIT2:
+                       inet_sk_state_store(sk, TCP_CLOSE);
+                       // @@ Close subflows now?
+                       break;
+               default:
+                       /* Other states not expected */
+                       WARN_ON_ONCE(1);
+                       break;
+               }
+
+               mptcp_set_timeout(sk, NULL);
+               mptcp_for_each_subflow(msk, subflow) {
+                       struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+
+                       lock_sock(ssk);
+                       tcp_send_ack(ssk);
+                       release_sock(ssk);
+               }
+
+               sk->sk_state_change(sk);
+
+               if (sk->sk_shutdown == SHUTDOWN_MASK ||
+                   sk->sk_state == TCP_CLOSE)
+                       sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_HUP);
+               else
+                       sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
+       }
+}
+
 static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,
                                           struct sock *ssk,
                                           unsigned int *bytes)
@@ -303,16 +399,6 @@ static void __mptcp_flush_join_list(struct mptcp_sock *msk)
        spin_unlock_bh(&msk->join_list_lock);
 }
 
-static void mptcp_set_timeout(const struct sock *sk, const struct sock *ssk)
-{
-       long tout = ssk && inet_csk(ssk)->icsk_pending ?
-                                     inet_csk(ssk)->icsk_timeout - jiffies : 0;
-
-       if (tout <= 0)
-               tout = mptcp_sk(sk)->timer_ival;
-       mptcp_sk(sk)->timer_ival = tout > 0 ? tout : TCP_RTO_MIN;
-}
-
 static bool mptcp_timer_pending(struct sock *sk)
 {
        return timer_pending(&inet_csk(sk)->icsk_retransmit_timer);
index 3f49cc1..beb34b8 100644 (file)
@@ -193,12 +193,14 @@ struct mptcp_sock {
        u64             remote_key;
        u64             write_seq;
        u64             ack_seq;
+       u64             rcv_data_fin_seq;
        atomic64_t      snd_una;
        unsigned long   timer_ival;
        u32             token;
        unsigned long   flags;
        bool            can_ack;
        bool            fully_established;
+       bool            rcv_data_fin;
        bool            snd_data_fin_enable;
        spinlock_t      join_list_lock;
        struct work_struct work;
@@ -385,6 +387,7 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk);
 bool mptcp_finish_join(struct sock *sk);
 void mptcp_data_acked(struct sock *sk);
 void mptcp_subflow_eof(struct sock *sk);
+bool mptcp_update_rcv_data_fin(struct mptcp_sock *msk, u64 data_fin_seq);
 
 void __init mptcp_token_init(void);
 static inline void mptcp_token_init_request(struct request_sock *req)