| From 4f06dacef3a5b9868dce9d9cecb2fcd7bae9833e Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 17 May 2022 11:02:12 -0700 |
| Subject: mptcp: Do TCP fallback on early DSS checksum failure |
| |
| From: Mat Martineau <mathew.j.martineau@linux.intel.com> |
| |
| [ Upstream commit ae66fb2ba6c3dcaf8b9612b65aa949a1a4bed150 ] |
| |
| RFC 8684 section 3.7 describes several opportunities for a MPTCP |
| connection to "fall back" to regular TCP early in the connection |
| process, before it has been confirmed that MPTCP options can be |
| successfully propagated on all SYN, SYN/ACK, and data packets. If a peer |
| acknowledges the first received data packet with a regular TCP header |
| (no MPTCP options), fallback is allowed. |
| |
| If the recipient of that first data packet finds a MPTCP DSS checksum |
| error, this provides an opportunity to fail gracefully with a TCP |
| fallback rather than resetting the connection (as might happen if a |
| checksum failure were detected later). |
| |
| This commit modifies the checksum failure code to attempt fallback on |
| the initial subflow of a MPTCP connection, only if it's a failure in the |
| first data mapping. In cases where the peer initiates the connection, |
| requests checksums, is the first to send data, and the peer is sending |
| incorrect checksums (see |
| https://github.com/multipath-tcp/mptcp_net-next/issues/275), this allows |
| the connection to proceed as TCP rather than reset. |
| |
| Fixes: dd8bcd1768ff ("mptcp: validate the data checksum") |
| Acked-by: Paolo Abeni <pabeni@redhat.com> |
| Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/mptcp/protocol.h | 3 ++- |
| net/mptcp/subflow.c | 21 ++++++++++++++++++--- |
| 2 files changed, 20 insertions(+), 4 deletions(-) |
| |
| diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h |
| index 8d70e491139a..62ad31482644 100644 |
| --- a/net/mptcp/protocol.h |
| +++ b/net/mptcp/protocol.h |
| @@ -437,7 +437,8 @@ struct mptcp_subflow_context { |
| can_ack : 1, /* only after processing the remote a key */ |
| disposable : 1, /* ctx can be free at ulp release time */ |
| stale : 1, /* unable to snd/rcv data, do not use for xmit */ |
| - local_id_valid : 1; /* local_id is correctly initialized */ |
| + local_id_valid : 1, /* local_id is correctly initialized */ |
| + valid_csum_seen : 1; /* at least one csum validated */ |
| enum mptcp_data_avail data_avail; |
| u32 remote_nonce; |
| u64 thmac; |
| diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c |
| index 204dfb82f697..c52a824c0669 100644 |
| --- a/net/mptcp/subflow.c |
| +++ b/net/mptcp/subflow.c |
| @@ -958,11 +958,14 @@ static enum mapping_status validate_data_csum(struct sock *ssk, struct sk_buff * |
| subflow->map_data_csum); |
| if (unlikely(csum)) { |
| MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_DATACSUMERR); |
| - subflow->send_mp_fail = 1; |
| - MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_MPFAILTX); |
| + if (subflow->mp_join || subflow->valid_csum_seen) { |
| + subflow->send_mp_fail = 1; |
| + MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_MPFAILTX); |
| + } |
| return subflow->mp_join ? MAPPING_INVALID : MAPPING_DUMMY; |
| } |
| |
| + subflow->valid_csum_seen = 1; |
| return MAPPING_OK; |
| } |
| |
| @@ -1144,6 +1147,18 @@ static void subflow_sched_work_if_closed(struct mptcp_sock *msk, struct sock *ss |
| } |
| } |
| |
| +static bool subflow_can_fallback(struct mptcp_subflow_context *subflow) |
| +{ |
| + struct mptcp_sock *msk = mptcp_sk(subflow->conn); |
| + |
| + if (subflow->mp_join) |
| + return false; |
| + else if (READ_ONCE(msk->csum_enabled)) |
| + return !subflow->valid_csum_seen; |
| + else |
| + return !subflow->fully_established; |
| +} |
| + |
| static bool subflow_check_data_avail(struct sock *ssk) |
| { |
| struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); |
| @@ -1221,7 +1236,7 @@ static bool subflow_check_data_avail(struct sock *ssk) |
| return true; |
| } |
| |
| - if (subflow->mp_join || subflow->fully_established) { |
| + if (!subflow_can_fallback(subflow)) { |
| /* fatal protocol error, close the socket. |
| * subflow_error_report() will introduce the appropriate barriers |
| */ |
| -- |
| 2.35.1 |
| |