diff mbox series

[v2,2/3] mptcp: avoid data stream corruption on allocation failure.

Message ID 86739a0eaa1063a30d25eb7744daeb86db4a78e1.1575376296.git.pabeni@redhat.com
State Superseded, archived
Headers show
Series None | expand

Commit Message

Paolo Abeni Dec. 3, 2019, 12:35 p.m. UTC
Currently, when if mpext allocation fails, the data stream
is corrupted, as we can add the required DSS nor roll-back
the TCP status.

Allocate a skb_ext before the actually skb_buff creation/TCP
status update: in case of memory allocation failure we can
bail early, avoiding the above condition.

Additionally, if the ext is unused to to skb collapsing,
cache the ext in the msk for the next send.

Squash-to: "mptcp: Write MPTCP DSS headers to outgoing data packets"
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
v1 -> v2:
 - properly convert to bool mptcp_ext_cache_refill() return value

Note: a rebased tree including DATA_FIN, v1 and this patches is
avaliable here:

https://github.com/pabeni/mptcp/tree/mptcp_net-next_FIN_v1_recvmsg
---
 net/mptcp/protocol.c | 39 +++++++++++++++++++++++++--------------
 net/mptcp/protocol.h |  1 +
 2 files changed, 26 insertions(+), 14 deletions(-)
diff mbox series

Patch

diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 3180202e56b4..baa5f84c7278 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -55,6 +55,14 @@  static struct sock *mptcp_subflow_get(const struct mptcp_sock *msk)
 	return NULL;
 }
 
+static bool mptcp_ext_cache_refill(struct mptcp_sock *msk)
+{
+	if (!msk->cached_ext)
+		msk->cached_ext = skb_ext_alloc();
+
+	return !!msk->cached_ext;
+}
+
 static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,
 			      struct msghdr *msg, long *timeo)
 {
@@ -69,7 +77,8 @@  static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,
 	 * from one substream to another, but do per subflow memory accounting
 	 */
 	pfrag = sk_page_frag(sk);
-	while (!sk_page_frag_refill(ssk, pfrag)) {
+	while (!sk_page_frag_refill(ssk, pfrag) ||
+	       !mptcp_ext_cache_refill(msk)) {
 		ret = sk_stream_wait_memory(ssk, timeo);
 		if (ret)
 			return ret;
@@ -103,19 +112,19 @@  static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,
 		iov_iter_revert(&msg->msg_iter, psize - ret);
 
 	skb = tcp_write_queue_tail(ssk);
-	mpext = skb_ext_add(skb, SKB_EXT_MPTCP);
-	if (mpext) {
-		memset(mpext, 0, sizeof(*mpext));
-		mpext->data_seq = msk->write_seq;
-		mpext->subflow_seq = mptcp_subflow_ctx(ssk)->rel_write_seq;
-		mpext->data_len = ret;
-		mpext->use_map = 1;
-		mpext->dsn64 = 1;
-
-		pr_debug("data_seq=%llu subflow_seq=%u data_len=%u dsn64=%d",
-			 mpext->data_seq, mpext->subflow_seq, mpext->data_len,
-			 mpext->dsn64);
-	} /* TODO: else fallback */
+	mpext = __skb_ext_add(skb, SKB_EXT_MPTCP, msk->cached_ext);
+	msk->cached_ext = NULL;
+
+	memset(mpext, 0, sizeof(*mpext));
+	mpext->data_seq = msk->write_seq;
+	mpext->subflow_seq = mptcp_subflow_ctx(ssk)->rel_write_seq;
+	mpext->data_len = ret;
+	mpext->use_map = 1;
+	mpext->dsn64 = 1;
+
+	pr_debug("data_seq=%llu subflow_seq=%u data_len=%u dsn64=%d",
+		 mpext->data_seq, mpext->subflow_seq, mpext->data_len,
+		 mpext->dsn64);
 
 	pfrag->offset += ret;
 	msk->write_seq += ret;
@@ -242,6 +251,8 @@  static void mptcp_close(struct sock *sk, long timeout)
 		sock_release(mptcp_subflow_tcp_socket(subflow));
 	}
 
+	if (msk->cached_ext)
+		__skb_ext_put(msk->cached_ext);
 	release_sock(sk);
 	sk_common_release(sk);
 }
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 412718c1ae5c..3b8d1548dda1 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -58,6 +58,7 @@  struct mptcp_sock {
 	u64		write_seq;
 	u64		ack_seq;
 	u32		token;
+	struct	skb_ext *cached_ext;	/* for the next sendmsg */
 	struct list_head conn_list;
 	struct socket	*subflow; /* outgoing connect/listener/!mp_capable */
 };