From nobody Thu Nov 5 07:28:13 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.50 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793695972; b=slBhLaFfZdz3ozzxIkQpkxdsNMvCtAwSaU2czKrPDbBynb9AEy/SD3qNLHd/qUefsnHg oNMQQVKfoZDuJatitU+DdgF+reU3M9YktbVd6dcCyy2VQfUo3gXGtedYU5+YLT07iJBn8 LbOf2wiWy+/48Rt6TkYJpMUJXozTSl/X6joEUOMsR9cP11j5V25xxKsuW+2hHaWA5VytV dYypsmKC6WET/TGryR7YtHLIw2yXxO/qJuUfGnR4hMQn1xAfnRcRHn4Lm9iLEp/rRIrA5 5kGLAcWulRN8olF9L+XOIGr2Ikn8bzYDoBpW2+UGAm2oB5YERvt2phfLHp7mG+UM87Q== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793695972; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:From:To:Cc:Subject: Date:Message-Id:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=lvLN24gtIiDD108HbQ1hU9mpKtt6mQ07/LEzVwndxzA=; b=ueh7rgwLwUHQ5vps3wPRnMIXYZHbbftPQTlsu3HTxMH2Ibl2elLDMEOZ2wbV9veWiw5q vgPhGNN+IOo6035JF1Jdu3kr2xJsBhyIV+NFLuOFtB92Xp6xGlsLgP1dhgap26TmA0F9o N7OjskOksJd35T8piCfSL+uOdL2l42PU7pjao2Qo4ctyNTrg1Rm9/Kj/VZ+Z/g3D3ZRLx mrd13gHdnGkoGMgWRm8305GbjIsYzIBmy3IxUmyYXRaOxeUzU46Lz7crMC54/jU/VLK3D af4x53joQ+mj3waYXd9ydRHTUOPhbpwWC/zOPQQr29FsjZkbmXlD+0JvSYTEzK/biKQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=hYZOyvwe; arc=none smtp.remote-ip=74.125.82.50 Received: from mail-dl1-f50.google.com (mail-dl1-f50.google.com [74.125.82.50]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B741E606A327 for ; Tue, 3 Nov 2026 08:52:51 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org B741E606A327 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=hYZOyvwe Received: by mail-dl1-f50.google.com with SMTP id a92af1059eb24-1a2af4c8d00so5672986c88.0 for ; Tue, 03 Nov 2026 00:52:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793695971; x=1794300771; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=lvLN24gtIiDD108HbQ1hU9mpKtt6mQ07/LEzVwndxzA=; b=hYZOyvwe/OszIfGtr2qcAyigMLFdA7ZiZM0tYPUrBqII4G0viQJrsnKyDuElGHR//X FqM+ImVVRRl90xdPJHczUzUB5Gr+ZMGIy4zs5YDngODDfpS6i1TSt1rR4IQFI94S+rbS ApRygquADhQkxTWwRzLmDG5MGzTf41axYRTT6X29JtLiCo+TrClGA4C9iIMAFIaZLmIz 4rhEUooBcnoayN4hEJ3l6OB+LacVAGJ+zBf0iOiM2sv2qlATl7+X/sSuefIJSwClUIIs gTiznilubLu8i3W2VV0hhgwzNU9lM2vG6mEbbGWUQe/tJYruVsbiDwLWybVEKlho2uYd lozQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793695971; x=1794300771; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=lvLN24gtIiDD108HbQ1hU9mpKtt6mQ07/LEzVwndxzA=; b=YdzxYbMAVINsfIbR6fAxgnx2eqxGSHUs8mh9m6HduDXYw7LRXMOZLnuTq6P/COYO2y UV3MxXY8+hjLieJ2EN8d4v0KKkXKS03dTaWawlD0JE0/ifxcu9eTzAtwd+OrIUfBo8By nGL559nXndNF1/rTHLuBP4F2GLeG2zcD01NqvaUvZu6szNd7hPwoHLplmUO9eWPANLZU oJa7zi0SIdVPCaR6Jm2fNuC/0oil2VYF15oJ8/XUvmYwAEXgFPOggDUj1ZWqx87TVXI3 0Ml50L5v3M9q4laRj99UKJ01LEvO5QsUqFDDgC1I9PR7gWanO7isn7VgEY/Lo3sMRKwd E0+w== X-Gm-Message-State: APuU4Z/LmEoAPmi/dnnAQzCb6F4UiebSdL4AC0srzGCUxoGgMV5Ba6oG +Tj6CwRKgaaqG8N8fM04ggH85bmyVJmrsaNWvw25+Chiv8uFOa9YGwQHAEcfwA== X-Gm-Gg: AYBFou0Oh61PIlAIb7VycJTqAx8pqIHWZq+q2dntkFgbL8169CrnEBXMXYPXadNdBi1 5cxb5np5W+lUyhcMSwOZow3ZhrmmacXMNlJ9E3HKoId+6XCCqOUhJhx02aIcQAn7eBO9set9c6Z viTWdVXE9EgL0PxhH0sYNHXV533vvYxaaB0KR0JwxX8GgLH4LKeVGLm0YMIbaSnNiWSZQX9HaC9 QwU8/zeIfFRVdjjTmhohyNwkh7O+fsBQzfk+kn1/NsNs/h8tSnmoYoA+GV1Z5cBlfn+G1dO0z7T 13f+ydyoOQr1GhSm/MpVDLtQB0rmBkwTOeF5vRIVTEuP1mdrXRkaHg+rsn7gbzW4vgqa9P21Msi PJRMxkXj9yZ0mhJlBixhqYGmcXrqkUTTs3AtEOetjmrnGhAGrcgHQUvwD2wVJHFaXDBckMm2O8Y ulRKLe/yizAkY8pdKFoC1enWIjmUu4YNrtv944C+JN9sntOrodby/cUxC4sbb8GKQKYeMiTjJeW JBIUIg1GFuSzrs/yUbo9uGDoQRtKNXKZ6OFZAhkR9hglhhlcB7a5GNRNwNbNkbZMmEo9OCEnDEM uTGDKHtTdawlcDWoT1rSmNHY5JEOCj0BuBbrh068Men9DHlrUqMAM2ydHw8UtdvDSImYCviGiCc 9mR7VNwHorA7hNJHeNYqdZn4/HdvulZvBxLLIg+rEQkSZk6AfFWCilf07dfPBWRjqz/tRauTBwr o4J4YjJ9Ul6jFXClh6ZyI= X-Received: by 2002:a05:7301:3d06:b0:358:eccb:af7c with SMTP id 5a478bee46e88-36a42404840mr28348368eec.33.1793695970615; Tue, 03 Nov 2026 00:52:50 -0800 (PST) Received: from ramen-dev-security-2.asia-south1-c.c.ramen-dev-345017.internal (90.156.200.35.bc.googleusercontent.com. [35.200.156.90]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-36a42a6f7f9sm53065635eec.25.2026.11.03.00.52.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Nov 2026 00:52:50 -0800 (PST) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v15 1/7] selftests: mptcp: connect: retry write() on EAGAIN Date: Tue, 3 Nov 2026 08:52:38 +0000 Message-Id: <20261103085244.23064-2-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261103085244.23064-1-anilkaushikwireless@gmail.com> References: <20261103085244.23064-1-anilkaushikwireless@gmail.com> X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" mptcp_connect treats any write() error as fatal. But write() can fail with EAGAIN even after poll() said the socket was writable: the MPTCP send buffer shrinks when a subflow is closed. The tool then exits and the transfer fails on both sides. Instead, go back to poll() and wait for more space. If a poll timeout is set, still fail after as many 1 ms retries as the timeout in ms: poll() does not check every cause of EAGAIN. Fixes: 048d19d444be ("mptcp: add basic kselftest for mptcp") Signed-off-by: Anil Kaushik --- Notes: v13: - say how many retries are done with a poll timeout v6: - the comment says the limit only applies with a poll timeout (netdev Sashiko) v5: - no change v4: - count the EAGAIN retries, not ms (Sashiko) - no limit when there is no poll timeout, -t 0 (Sashiko) v3: - do not wait forever on EAGAIN (Sashiko) - wrap lines over 80 columns v2: - Fixes tag .../selftests/net/mptcp/mptcp_connect.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/test= ing/selftests/net/mptcp/mptcp_connect.c index 178d98d91fea..49017d00ab69 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.c +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c @@ -638,6 +638,7 @@ static int copyfd_io_poll(int infd, int peerfd, int out= fd, .events =3D POLLIN | POLLOUT, }; unsigned int total_wlen =3D 0, total_rlen =3D 0; + int eagain_cnt =3D 0; =20 set_nonblock(peerfd, true); =20 @@ -711,6 +712,22 @@ static int copyfd_io_poll(int infd, int peerfd, int ou= tfd, =20 bw =3D do_rnd_write(peerfd, winfo->buf + winfo->off, winfo->len); if (bw < 0) { + /* POLLOUT does not guarantee that + * the next write will not block: + * the MPTCP send buffer can shrink + * in between, e.g. when a subflow + * is closed. Wait for more space, + * but not forever if a poll timeout + * is set: poll() does not check + * every cause of EAGAIN. + */ + if (errno =3D=3D EAGAIN && + (poll_timeout < 0 || + ++eagain_cnt <=3D poll_timeout)) { + usleep(1000); + continue; + } + /* expected reset, continue to read */ if (cfg_rcv_trunc && (errno =3D=3D ECONNRESET || @@ -723,6 +740,7 @@ static int copyfd_io_poll(int infd, int peerfd, int out= fd, return 111; } =20 + eagain_cnt =3D 0; winfo->off +=3D bw; winfo->len -=3D bw; total_wlen +=3D bw; --=20 2.25.1 From nobody Thu Nov 5 07:28:13 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.174 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793695974; b=bWFgQQFKgPV2oNOAhleLWzckXwqfNNdiWdMTu/tNFUMNCSUkr2Ifm8k4HZCElS41DJ/9 HSyPLx6YLF55x2YMUb22xw2OzmNz5vFeFiCYOU20vxri64mX9kzPSN3u+8ZG9m6ElVe6U uljZzOWWZIQpmNGoG5JTJW9isxg8fNgpW0Dx6VGYVj3mlNfzWZ/RwLTsi0tcuzM+PYoaS LBmEgxBX+AvC2xYHm5TWlI1ErJXiD2YelSWN8m/8kWDfKN9LaI+DbcfEz2zRDjiKJuTq+ DlwBV7zAjQNW8d908ppfLbSD1fbWvJ/uqg0F+wyIsDBuouR2hk51VrgNq+jkla9JNGg== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793695974; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:From:To:Cc:Subject: Date:Message-Id:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=eRt2kWbGCAz58A6g57CpIHgRsmg4UizgVAJ55qsS1ik=; b=M7Q0cN+fqi4O3R+IzlRfb/HXlIWHJZN8IygTI0Y46/PouNlRNVQCv5oI2c5pSLqQO+xc Ga2f+DA/pfI8N3UBUFt4KCVFaDPwlCXBi0VsHRqfwHdBTtFzFVvOy/E+J9gIhRpOgTwcz ArFAY1F8Bozr85w7JJZlFpkAda2wLE1e3r/egNF5ZqAy5TabYpu67SXILWiY6tVRZFd3l i/0+2FUf2qB7y4ZiMIXoZ+7Z/AEics4zCwQFFAxrDK3LCa0oNZMhUrQ3WyEP+J3JizTRk 1t+rC3IWRAIaG/sL3pyresqfIF2O16sp6tDtytP91REoV0LdBZKmjMpCSOb8M1SUHFg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=Oi4NCHrw; arc=none smtp.remote-ip=74.125.82.174 Received: from mail-dy1-f174.google.com (mail-dy1-f174.google.com [74.125.82.174]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9F8AF606A329 for ; Tue, 3 Nov 2026 08:52:53 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 9F8AF606A329 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=Oi4NCHrw Received: by mail-dy1-f174.google.com with SMTP id 5a478bee46e88-36a1947498bso3754516eec.1 for ; Tue, 03 Nov 2026 00:52:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793695973; x=1794300773; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=eRt2kWbGCAz58A6g57CpIHgRsmg4UizgVAJ55qsS1ik=; b=Oi4NCHrw+aEesg2YY1E0pJqU2ad2BFMHUxFIuzeMQ75z+8QjmIbjG30LYVNwzH6fkQ H9UB//Y7ZMlCLHWIiMVKg1I8LIs9JN8v7yjaoF09JEPLbS3X524oF/FsUt6H6ACJLXGM wsAFKia+2woXQHuquwr7nDPdJu8am706Qhol3bea5YZU+grkroJRU0OAB/VY1bfkobIX +kc5FN45JlDQEL507lFyvAb/5PXzjVlu/m8OanwsC8pKy4jKdqNvW1KjlKgKzdivD41V eSrCtyG3DRbsVEKTuPwUIkkq4vWU8q32Y8cc/1yS4ZNp/07Fg9R8aIyKcIsrsLSHcl+s t42w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793695973; x=1794300773; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=eRt2kWbGCAz58A6g57CpIHgRsmg4UizgVAJ55qsS1ik=; b=xXQZ34yWHTa6gNGrAKNjVcrKyh4dm/kyXRKT6kPEi152odzfnQZL4mw+RUf7QwuRvf ZUvxOOZfWst57RtDwJYUD/EcmFAMnM27z54TdyS04S1tMd/pOwivGLCKdSVCCcDb0sWm vVL1eZcaiinzdVPuRABj7YbqPlaTmhRcGwm7CZWu3zb6j/RRp1Sk4FiUYwlqEgygwvMi EaqIi803h7kEvYr3IkNd+wIPvaSD1GK6C4PpQp2SBK6sJCcyaq7J0hdCc/FZ5u2JrPZE VGRAgPkeRDNuxPOm6Ra9ufskS4bl2LtrgyhhVvoQCOfj4eXstABh8paTdThPHudzscyy TRlg== X-Gm-Message-State: APuU4Z9kZ8YGpCQIdfghkxZVJbB8M2JvY7R8CdfeK2IsDGt7QRd7PyHa jDb+f3EwC0/lzyOMrSnyrTsPyhoJ4mOSY8JWqX7vb40bVmtIxZ8IaZYh0qA4Zg== X-Gm-Gg: AYBFou2XdMf60oNor3NAF+/2x7LvoRHJE9B84hTFYJ5ZQvJiWXSRLD9fgoZpDpAi0dj iZPTtniZO0IVQB0w46UaNKLNpTa9HSW6u2tS64xgJoVRZFrwYqHtMdLDHf/8R55aVgithZaOI5+ rMWL5z3vd9LMgcUJOemsjZCrkbfMmBE/0R8ZVwuggJiaQ805St6hrwB7uHYOnmMqgrM0GCFwk1w cfdmuv9cQ8pAARxbfcEIufQRxUNPRJ5Gq+/+sBCN6T932EvLW1zjNRx1Nl5UyzmTgPZy96iJHx3 hFJ8yAu157P15ICWk/twha+OJKNVpgpvmXjSndDyehDFEEdv4VsMlVHe+0p20G2GeG8Z68r/rcB CDiv1ob+3VGBBy1vXNqLIVfKxP5S8UaIZ4In6y4T6OCSmM4Tw9X3+V3TbuDu0QW4NNWaXOh84bE gstIYARWeyX3Orn2MiI9IrMJe9fS126AFKDltWXuL5QOt3dyuQ5qLB81MGyZsv5xL7H/n9tQwpJ b74ON2ANSYeCjp13CSWwkChcKTZ6sTk5U+eCWGXWFdC2Wj2jYwSjQAqlCHXjx1NvdB9bYtJudqz KaS5/RZd30Wywh4Ud83j/Z/ax2pHKSRqER3ZJ3Gbbp10PjTC++JitFtT7awwFgDK9ezJCKqS+gT cMVfIvjWhhMc1agbH66TBKRvqdWCpeqEpxG/lHsvFVTVxTAxID++6R2RKWONLON+fNDKJJq/Txw == X-Received: by 2002:a05:7300:c00e:20b0:36a:12bb:7f8e with SMTP id 5a478bee46e88-36a4232ede1mr21045852eec.13.1793695972485; Tue, 03 Nov 2026 00:52:52 -0800 (PST) Received: from ramen-dev-security-2.asia-south1-c.c.ramen-dev-345017.internal (90.156.200.35.bc.googleusercontent.com. [35.200.156.90]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-36a42a6f7f9sm53065635eec.25.2026.11.03.00.52.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Nov 2026 00:52:52 -0800 (PST) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v15 2/7] mptcp: pm: in-kernel: check the limits with the netlink policy Date: Tue, 3 Nov 2026 08:52:39 +0000 Message-Id: <20261103085244.23064-3-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261103085244.23064-1-anilkaushikwireless@gmail.com> References: <20261103085244.23064-1-anilkaushikwireless@gmail.com> X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The maximum of the rcv-add-addrs and subflows limits is checked by parse_limit(). The netlink policy can do that: add the maximum to the spec, and drop this helper. The next limit will do the same. The maximum is unchanged, but a value above it now gets ERANGE from the policy, instead of EINVAL. The uapi header is not changed. The generated files are updated with ynl-regen. Signed-off-by: Anil Kaushik --- Notes: v15: new patch, all the limits checked the same way (Matthieu on v7) Documentation/netlink/specs/mptcp_pm.yaml | 4 +++ net/mptcp/mptcp_pm_gen.c | 8 ++--- net/mptcp/pm_kernel.c | 39 +++++------------------ 3 files changed, 16 insertions(+), 35 deletions(-) diff --git a/Documentation/netlink/specs/mptcp_pm.yaml b/Documentation/netl= ink/specs/mptcp_pm.yaml index 39f3facc38e5..f25f00759ffa 100644 --- a/Documentation/netlink/specs/mptcp_pm.yaml +++ b/Documentation/netlink/specs/mptcp_pm.yaml @@ -183,9 +183,13 @@ attribute-sets: - name: rcv-add-addrs type: u32 + checks: + max: 64 - name: subflows type: u32 + checks: + max: 64 - name: token type: u32 diff --git a/net/mptcp/mptcp_pm_gen.c b/net/mptcp/mptcp_pm_gen.c index c180930a8e0e..59d57aebf968 100644 --- a/net/mptcp/mptcp_pm_gen.c +++ b/net/mptcp/mptcp_pm_gen.c @@ -45,14 +45,14 @@ const struct nla_policy mptcp_pm_flush_addrs_nl_policy[= MPTCP_PM_ENDPOINT_ADDR + =20 /* MPTCP_PM_CMD_SET_LIMITS - do */ const struct nla_policy mptcp_pm_set_limits_nl_policy[MPTCP_PM_ATTR_SUBFLO= WS + 1] =3D { - [MPTCP_PM_ATTR_RCV_ADD_ADDRS] =3D { .type =3D NLA_U32, }, - [MPTCP_PM_ATTR_SUBFLOWS] =3D { .type =3D NLA_U32, }, + [MPTCP_PM_ATTR_RCV_ADD_ADDRS] =3D NLA_POLICY_MAX(NLA_U32, 64), + [MPTCP_PM_ATTR_SUBFLOWS] =3D NLA_POLICY_MAX(NLA_U32, 64), }; =20 /* MPTCP_PM_CMD_GET_LIMITS - do */ const struct nla_policy mptcp_pm_get_limits_nl_policy[MPTCP_PM_ATTR_SUBFLO= WS + 1] =3D { - [MPTCP_PM_ATTR_RCV_ADD_ADDRS] =3D { .type =3D NLA_U32, }, - [MPTCP_PM_ATTR_SUBFLOWS] =3D { .type =3D NLA_U32, }, + [MPTCP_PM_ATTR_RCV_ADD_ADDRS] =3D NLA_POLICY_MAX(NLA_U32, 64), + [MPTCP_PM_ATTR_SUBFLOWS] =3D NLA_POLICY_MAX(NLA_U32, 64), }; =20 /* MPTCP_PM_CMD_SET_FLAGS - do */ diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c index d3014bf57bf3..08a36191b5c0 100644 --- a/net/mptcp/pm_kernel.c +++ b/net/mptcp/pm_kernel.c @@ -30,7 +30,6 @@ struct pm_nl_pernet { }; =20 #define MPTCP_PM_ADDR_MAX 8 -#define MPTCP_PM_SUBFLOWS_MAX 64 =20 static struct pm_nl_pernet *pm_nl_get_pernet(const struct net *net) { @@ -1399,46 +1398,24 @@ int mptcp_pm_nl_dump_addr(struct sk_buff *msg, return msg->len; } =20 -static int parse_limit(struct genl_info *info, int id, unsigned int *limit) -{ - struct nlattr *attr =3D info->attrs[id]; - - if (!attr) - return 0; - - *limit =3D nla_get_u32(attr); - if (*limit > MPTCP_PM_SUBFLOWS_MAX) { - NL_SET_ERR_MSG_ATTR_FMT(info->extack, attr, - "limit greater than maximum (%u)", - MPTCP_PM_SUBFLOWS_MAX); - return -EINVAL; - } - return 0; -} - int mptcp_pm_nl_set_limits_doit(struct sk_buff *skb, struct genl_info *inf= o) { struct pm_nl_pernet *pernet =3D genl_info_pm_nl(info); + struct nlattr **attrs =3D info->attrs; unsigned int rcv_addrs, subflows; - int ret; =20 + /* the maximums are checked by the policy */ spin_lock_bh(&pernet->lock); - rcv_addrs =3D pernet->limit_add_addr_accepted; - ret =3D parse_limit(info, MPTCP_PM_ATTR_RCV_ADD_ADDRS, &rcv_addrs); - if (ret) - goto unlock; - - subflows =3D pernet->limit_extra_subflows; - ret =3D parse_limit(info, MPTCP_PM_ATTR_SUBFLOWS, &subflows); - if (ret) - goto unlock; + rcv_addrs =3D nla_get_u32_default(attrs[MPTCP_PM_ATTR_RCV_ADD_ADDRS], + pernet->limit_add_addr_accepted); + subflows =3D nla_get_u32_default(attrs[MPTCP_PM_ATTR_SUBFLOWS], + pernet->limit_extra_subflows); =20 WRITE_ONCE(pernet->limit_add_addr_accepted, rcv_addrs); WRITE_ONCE(pernet->limit_extra_subflows, subflows); - -unlock: spin_unlock_bh(&pernet->lock); - return ret; + + return 0; } =20 int mptcp_pm_nl_get_limits_doit(struct sk_buff *skb, struct genl_info *inf= o) --=20 2.25.1 From nobody Thu Nov 5 07:28:13 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.169 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793695975; b=zrAnC+QtpZd8TE1emfBcrBUTaVcNEzErABasy/nHmlfg2fTuulf2Jjrwn+R6Y2CwaZLx 5wsEZMCmeIGl1HdvIQFioCeUiMTXe3Laty+tOuAQmTweRVNL5swbQzgq1xRObnDi6K3QY nj+T0tzksPP10xrF4dsWsPgKLPmLvLKWNZhCqFE+K83T9PdvcZ2/XT3MjEiqcaRkEIMsA f/5cte5L2yIMvZUMab7JuUOrMUFaocB/gZemvPBt9/mKM5VPza65tfYHod0pE7sSubg+Q nllteeSaANQ1q4KkJdosjMuVUjX8TqhHPUBoIsPm+OBKb6Uhzc1zplAvMFgU7Iu/GEA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793695975; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:From:To:Cc:Subject: Date:Message-Id:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=VbM06yFn2Jh2tq6JZ2lNHsgrv3CcW4NZaAIdbeX3CqM=; b=KLbkmmgqzkGTiV/MVtavqGF3WRoclkfu6qfVMrwIu4ng3VNWwZIJ0t8NqSCNf1nVSsJB 5zhzmllo1Z4pZXZ/h7O+nLaTZKdHSTj96FpzAQfOic3kL+DxIPT0D+DruUPnpbwXXM+c5 13K87jjmmW1ed6BCD7ZISB3r6LnSF8VWHN3Bl3dpw5Q12TBD0QDaRZUApDZwaihKEAZku YLLNxJMDPM8SIrUUaXpMy0IgNooDGgBtc/IJp9hnF6cvzTTPyGj0LO49wk46X8ZXXHRYA X5GM1CaFhLiiniCV8W+CeDR6+qXZfnYqK9eY9w9r7Xv11r1Ldjh4KfTblGxtXo+OUPw== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=nKeBTBLo; arc=none smtp.remote-ip=74.125.82.169 Received: from mail-dy1-f169.google.com (mail-dy1-f169.google.com [74.125.82.169]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8645B606A32D for ; Tue, 3 Nov 2026 08:52:55 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 8645B606A32D Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=nKeBTBLo Received: by mail-dy1-f169.google.com with SMTP id 5a478bee46e88-36a18fc7a38so7424538eec.1 for ; Tue, 03 Nov 2026 00:52:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793695975; x=1794300775; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=VbM06yFn2Jh2tq6JZ2lNHsgrv3CcW4NZaAIdbeX3CqM=; b=nKeBTBLoXoyqqap1SDEtZ2rDWLrjxflfx7/GrFVefAlWeFjk7AC7xKbUvbcTAnCGhZ S9us3GheagjK1edSQK6gqIkR8Zg57MkHXFU9iFs+Reb+WHetTlz4C9lQZNPOI322Y3Iq kSPXAK49UQJt2Ad9QefCckU/7FwFPvIkISGwXeVoDEModpQiSO9pVrXZ1xsvkS31oCR3 RsZQLFvWlrO8PIhqiBllhOkYglzS6leV/J15QhBFPt2k4c03frmnhtRKJXXpRQBz/LFR 3+lbOt3NG8YA92uyKfBWx8mCbt2jg+UIXaE7TOGJ+Pp14J+d6xXDJMnTzdkBoXj3/NjI Je2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793695975; x=1794300775; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=VbM06yFn2Jh2tq6JZ2lNHsgrv3CcW4NZaAIdbeX3CqM=; b=HP7aTNSLTVLMZanOvtE70YFgBpVdhM44SLaNIJUZy/xqPBxbQc911k7MS8oExdr5DX xuSwQtJLuAldR95kvHbxC3VRQ5MGgLIoqPJir0hbrIJc9m2pSrpcX6PuBX17TO9VbCzY F68KMhTYt6Gb9ezCRh5Hv4Q+Ngyba/HgE5lbol0DaMkjlZNKkUVvBKeMmYfvNpbDHhsQ 4VmwQUa999Q940LM1q9ZihH80fvXcDkVICrXcbdbzmxvqmzxG0klZ0NeD3fFy/1TvTQu miLEiPs/LBB0hP95UchwmpZJX0T+fBBKecUhZd2puS83Obq8/vXTqEj+UQALk9u7GITt HwLA== X-Gm-Message-State: APuU4Z/SOMeiA1jQAjMWH5djrV2z9d80qt6Wk2NLhLeYyk1ly9QcdTfJ tCbS+Ui1juEPYKrP5Ls3k3ZYqDysT0uDRuTT7FeBpWKH+xeg1xLUnM/obhs30g== X-Gm-Gg: AYBFou3U2OZp4BSsxXrMiWUorvfNprDrmIs5BOsyK3IPfW8w76DUV8u+iUDG26dZyGR iyMPbRsG4UxkhwTHKgu1h4jIsNJjnaxQeLuhAdoi1iiFYlz/LIGCfGAcpZwgLtnSRSXWmSE0mJt Ni7g8HpRD4rCelNj90WdwuduVluKNOu5T3wp4Q/uuajYF/ZXf1CxB7Bk180/W9JHNK85kSrdsmH 0ShnDcgYTZCF0/xrRCRqd4k5BbdDGnQeJY7nqNBJxn1qqBF4RadH4VnAK6lGBTSvsvr5J4eMY48 JQ6+4uIkfa3XoRQ6n2yq0ySRUoWYlyuk4zrV7nQd+5CgRolDsQFD/NFqh9mDAvdyBZNSLt/+XXO lVBGxuwonNfyBpLUECn1iAwNDMR8uJHCHJEJo0PrFZED4MBzHcsWnWZ7KPQTr3IaOGpmHzfun4s TeS1wYzGgQTLJ1Pgbi40nsnMV2NzPVdnN+3gMKQTB5tceEJU80j64RA8DNY+2uzdieccS2e1ozm BK++Z9M6Cckm+LYBG7WDC5UeHfaixFMtLXbZ+AdSh3TeGSSXmefgpakAEXh6pkwpK/45UqDOmon +Ev8/H01huWxOcJSZXnOGSrUgm7wyLOvp1mjHxTX5QiLbJRPS5OsyltC8qpYuSUAWpbtFs5z/lh p8WXXkyMISwvCnbkouFhuuwNiBgn5wn3AdbfMEgjJ1waO+fTqvHJj3q2EksgwAxj/CItBPbk+Ov g4ztqKJBP9Owgb02jUHbU= X-Received: by 2002:a05:693c:895c:10b0:369:f05d:6c5c with SMTP id 5a478bee46e88-36a4240f66emr20228928eec.28.1793695974343; Tue, 03 Nov 2026 00:52:54 -0800 (PST) Received: from ramen-dev-security-2.asia-south1-c.c.ramen-dev-345017.internal (90.156.200.35.bc.googleusercontent.com. [35.200.156.90]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-36a42a6f7f9sm53065635eec.25.2026.11.03.00.52.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Nov 2026 00:52:53 -0800 (PST) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v15 3/7] netlink: specs: mptcp: add the subflow-retries attribute Date: Tue, 3 Nov 2026 08:52:40 +0000 Message-Id: <20261103085244.23064-4-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261103085244.23064-1-anilkaushikwireless@gmail.com> References: <20261103085244.23064-1-anilkaushikwireless@gmail.com> X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" It will be used by the in-kernel PM to set the number of SYN retransmissions of a subflow re-created after a timeout, with the other limits: add it to the set-limits and get-limits commands. The uapi header and the generated files are updated with ynl-regen. Signed-off-by: Anil Kaushik --- Notes: v9: - after a timeout, not any error, and about 4 hours with the default RTO settings v8: - the default is the maximum, MAX_TCP_SYNCNT (127) (Matthieu) v7: - the doc gives the default value (netdev Sashiko) v6 (netdev Sashiko): - the policy checks the maximum value, 127 (MAX_TCP_SYNCNT) - the doc says the value works like TCP_SYNCNT v5: - new, split from the retry patch (Matthieu) Documentation/netlink/specs/mptcp_pm.yaml | 13 +++++++++++++ include/uapi/linux/mptcp_pm.h | 1 + net/mptcp/mptcp_pm_gen.c | 10 ++++++---- net/mptcp/mptcp_pm_gen.h | 4 ++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Documentation/netlink/specs/mptcp_pm.yaml b/Documentation/netl= ink/specs/mptcp_pm.yaml index f25f00759ffa..68734a23c4e2 100644 --- a/Documentation/netlink/specs/mptcp_pm.yaml +++ b/Documentation/netlink/specs/mptcp_pm.yaml @@ -200,6 +200,16 @@ attribute-sets: name: addr-remote type: nest nested-attributes: address + - + name: subflow-retries + type: u32 + checks: + max: 127 + doc: | + SYN retries of a subflow re-created after a timeout, like the + TCP_SYNCNT socket option, so with tcp_syn_linear_timeouts on top. + The default is the maximum, MAX_TCP_SYNCNT (127), about 4 hours + with the default RTO settings. 0 to not re-create it. - name: event-attr enum-name: mptcp-event-attr @@ -331,6 +341,7 @@ operations: attributes: - rcv-add-addrs - subflows + - subflow-retries - name: get-limits doc: Get protocol limits @@ -341,10 +352,12 @@ operations: attributes: - rcv-add-addrs - subflows + - subflow-retries reply: attributes: - rcv-add-addrs - subflows + - subflow-retries - name: set-flags doc: Change endpoint flags diff --git a/include/uapi/linux/mptcp_pm.h b/include/uapi/linux/mptcp_pm.h index fe9863d75350..43e9643deda9 100644 --- a/include/uapi/linux/mptcp_pm.h +++ b/include/uapi/linux/mptcp_pm.h @@ -102,6 +102,7 @@ enum { MPTCP_PM_ATTR_TOKEN, MPTCP_PM_ATTR_LOC_ID, MPTCP_PM_ATTR_ADDR_REMOTE, + MPTCP_PM_ATTR_SUBFLOW_RETRIES, =20 __MPTCP_ATTR_AFTER_LAST }; diff --git a/net/mptcp/mptcp_pm_gen.c b/net/mptcp/mptcp_pm_gen.c index 59d57aebf968..8ff103f5be37 100644 --- a/net/mptcp/mptcp_pm_gen.c +++ b/net/mptcp/mptcp_pm_gen.c @@ -44,15 +44,17 @@ const struct nla_policy mptcp_pm_flush_addrs_nl_policy[= MPTCP_PM_ENDPOINT_ADDR + }; =20 /* MPTCP_PM_CMD_SET_LIMITS - do */ -const struct nla_policy mptcp_pm_set_limits_nl_policy[MPTCP_PM_ATTR_SUBFLO= WS + 1] =3D { +const struct nla_policy mptcp_pm_set_limits_nl_policy[MPTCP_PM_ATTR_SUBFLO= W_RETRIES + 1] =3D { [MPTCP_PM_ATTR_RCV_ADD_ADDRS] =3D NLA_POLICY_MAX(NLA_U32, 64), [MPTCP_PM_ATTR_SUBFLOWS] =3D NLA_POLICY_MAX(NLA_U32, 64), + [MPTCP_PM_ATTR_SUBFLOW_RETRIES] =3D NLA_POLICY_MAX(NLA_U32, 127), }; =20 /* MPTCP_PM_CMD_GET_LIMITS - do */ -const struct nla_policy mptcp_pm_get_limits_nl_policy[MPTCP_PM_ATTR_SUBFLO= WS + 1] =3D { +const struct nla_policy mptcp_pm_get_limits_nl_policy[MPTCP_PM_ATTR_SUBFLO= W_RETRIES + 1] =3D { [MPTCP_PM_ATTR_RCV_ADD_ADDRS] =3D NLA_POLICY_MAX(NLA_U32, 64), [MPTCP_PM_ATTR_SUBFLOWS] =3D NLA_POLICY_MAX(NLA_U32, 64), + [MPTCP_PM_ATTR_SUBFLOW_RETRIES] =3D NLA_POLICY_MAX(NLA_U32, 127), }; =20 /* MPTCP_PM_CMD_SET_FLAGS - do */ @@ -127,7 +129,7 @@ const struct genl_ops mptcp_pm_nl_ops[11] =3D { .validate =3D GENL_DONT_VALIDATE_STRICT, .doit =3D mptcp_pm_nl_set_limits_doit, .policy =3D mptcp_pm_set_limits_nl_policy, - .maxattr =3D MPTCP_PM_ATTR_SUBFLOWS, + .maxattr =3D MPTCP_PM_ATTR_SUBFLOW_RETRIES, .flags =3D GENL_UNS_ADMIN_PERM, }, { @@ -135,7 +137,7 @@ const struct genl_ops mptcp_pm_nl_ops[11] =3D { .validate =3D GENL_DONT_VALIDATE_STRICT, .doit =3D mptcp_pm_nl_get_limits_doit, .policy =3D mptcp_pm_get_limits_nl_policy, - .maxattr =3D MPTCP_PM_ATTR_SUBFLOWS, + .maxattr =3D MPTCP_PM_ATTR_SUBFLOW_RETRIES, }, { .cmd =3D MPTCP_PM_CMD_SET_FLAGS, diff --git a/net/mptcp/mptcp_pm_gen.h b/net/mptcp/mptcp_pm_gen.h index b9280bcb43f5..5b4dee8a0a34 100644 --- a/net/mptcp/mptcp_pm_gen.h +++ b/net/mptcp/mptcp_pm_gen.h @@ -23,9 +23,9 @@ extern const struct nla_policy mptcp_pm_get_addr_nl_polic= y[MPTCP_PM_ATTR_TOKEN + =20 extern const struct nla_policy mptcp_pm_flush_addrs_nl_policy[MPTCP_PM_END= POINT_ADDR + 1]; =20 -extern const struct nla_policy mptcp_pm_set_limits_nl_policy[MPTCP_PM_ATTR= _SUBFLOWS + 1]; +extern const struct nla_policy mptcp_pm_set_limits_nl_policy[MPTCP_PM_ATTR= _SUBFLOW_RETRIES + 1]; =20 -extern const struct nla_policy mptcp_pm_get_limits_nl_policy[MPTCP_PM_ATTR= _SUBFLOWS + 1]; +extern const struct nla_policy mptcp_pm_get_limits_nl_policy[MPTCP_PM_ATTR= _SUBFLOW_RETRIES + 1]; =20 extern const struct nla_policy mptcp_pm_set_flags_nl_policy[MPTCP_PM_ATTR_= ADDR_REMOTE + 1]; =20 --=20 2.25.1 From nobody Thu Nov 5 07:28:13 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.172 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793695978; b=GlpxQCTzmSNxCpcwiyImUDZdATV1DSK1zSZaSspWdDjNXBJGKdT65t/rQRPSHKr/QoZz QgC6xzsKU70PuU1kb/qMxBg4W4NkLHpKg4wQjPcAIWiVEXT2UUgGmPvIoS22yltp67vhK jISHpjHr33JMKBTzXb7wL+M3qcFFGonMDK1oIMmlKxzIse41tOD6ZuAzU7U3dDvzY0RRq o11C5ByrFKbRb/uq3xUcXhIPxj9ExgV514CRu2a3VnOOYbYExKlgzShCVM7kdX8imhmIe Ix6zljtW0lAbHK+qkm0LjP54KpxG+/X4Q+5Wb/0RLZCJXQ1TXaSdLQynHyUsIb9+QvA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793695978; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:From:To:Cc:Subject: Date:Message-Id:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=6PB4DCzsc9cKJM/W4Yiq3L1K4+NCR5yt0/X2juO9xzk=; b=2DPhinkkg/syipgFQa6CvdNPnUcD4meMDXEEXAuKCprqyznIG9UaHsbyfeQgebzff6od 1NoXP71TlqEejxddbQ7IjFwCNNkta/H1lk8jeucagk61Kt+bOu/6LfXnjuNvJGf3xe5b2 k8Pb/qcaL75gSts7pwN654pAspdB1yoHoMWPkIm7j6xgAEioPZtBJOFo3mZ7/Mzg6KY8I 6LU/9oQ4FhYL1YVtNASQrEm3c3qnz6A1lkyUgghGmbR4Mm8w9z0T+ICBOMMbtSFAzL9r6 hFEFgVq80wsCT81T8IL1JsrVe0atVmidEomKuiXhErNxrOWOFgiYS9d7mBFIw1zTxqQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=m4YoZga7; arc=none smtp.remote-ip=74.125.82.172 Received: from mail-dy1-f172.google.com (mail-dy1-f172.google.com [74.125.82.172]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9E365606A324 for ; Tue, 3 Nov 2026 08:52:57 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 9E365606A324 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=m4YoZga7 Received: by mail-dy1-f172.google.com with SMTP id 5a478bee46e88-35cd4e8bdbdso4922865eec.1 for ; Tue, 03 Nov 2026 00:52:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793695977; x=1794300777; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=6PB4DCzsc9cKJM/W4Yiq3L1K4+NCR5yt0/X2juO9xzk=; b=m4YoZga713ZtCK6YUyWoohQaK+rxFPcJZy8fQlOEGHAKIenFzQdl932BFuazNxwCxu w0ZbJHyqeJY3xyhtMRk3k2RR26FBH+78SKsJ8y9JRFsX9p3+D1IZIbxY8++wQ2ah+KRh 6EJ95LOQ+q1QlwGaheU+06Kn9Wx1QTJUKvESAwk5j3e0QhhzxRIpj93jrcCrMxcEHpgI MGvi8Du/1Acs107aiNTybMwvyq+ubuvc6U/8FZsVbnmpLtPQGjWG4JsjnXEjPuXTprvh l8Ud21EcsoFO9XTbFmg03DjJ6tO2QY7U86rWG5Vtkm73uz6eVpYg7I3LbGoQj93940Ql 9hXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793695977; x=1794300777; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=6PB4DCzsc9cKJM/W4Yiq3L1K4+NCR5yt0/X2juO9xzk=; b=WcjhvuQW2HLFwgxm/BbI8N8YFXzgDqEMVrOnpmYHljA52WQ4nda+Y6gzwBZGH5tfgX NhXkbv1eLHrmM8cBgc1IqPl2P18Fa8KI//o0/QBB/V82SgHOFg7+6NxCTjGqiY0fHREm MxBcdTRUOYs48czG/5VM0XwUlWrmP3XwJ8zT1zkGrs3JYyyczZLLc7y2Aa5qJX4mcQKn 9vBLEBwb2Kl5iRnaMWolzQPLo6V49spJS+Ji5HL5wVunPBPEEHGTtI/K5nnLvA1OZwIq 7223su1gRmTyHrtlXSgRDV7eN/hpXS0VqUMzmgP2xhMASARPqVrQymZ2LSUQa9snZWi6 UOnQ== X-Gm-Message-State: APuU4Z/V8ycqbcDh206MXKFLRsDY8F6Dck+7u1occp8o/azDwKp2PMJM mGCV016OU4ydoJarVK2Cx7G5tJRjpDD8Z8LmbbxZJ1UlD88t1ZZEVeH9tzdiww== X-Gm-Gg: AYBFou0FpGNBTeVpzdO6BVep2GLE2xjEnZRmWnFM8C19wKG6T7pDKfJgf/KpdLVmmZz qhhh9H8Z9uiGkVDdR4i1W4LP6LWPgTqWlDFYqiIMlPPklpSwOpEA6YNsV5RTjHLbxpTEEm3ttIZ jfglWmAD+LxCYiYX16Ira2kTtiIzWTa2gLae3HpmWCpyCtA027poyh7wCUC6ugDzZQIroQtihai lMk1bPJTRdi8M4tS2lmCNXCO41H6jdpmiAWSD3tws+fwWw67i6kR6KfItu9ucT1or9yzNGP5DCQ GetonY0+wtIfwhKYEYzGxpFUc1CQ64E8X+0wAZGJ5wAgC1ieOeMdmMvaczF8jDGfNB/j/UaAp9L vCBXVeb5M3rJ1dRVeU2zPLWaFa7b0ak6bUuKyyCvP4hkrCT17UjkorxtdlsF5pekG0UmGFB605X qH99VQYNUbuQwGrFrij8LPfx1aZ5J2fIqoKbJ2RkunfK8jFq7nB8jGsEoVt2N/lfbbYaXrytFpi MusYPJSX1+kZKTl5yVakk03afwWlOfHVB7PGiYXOzedBDSgHnD/p1KqYjLUpppd+eKQQsza7wr0 3NqXqE88peDZFL1sKRGXmg1q0QYL/wweR2lCgxrwD26auxg6oUtf0kNt5JvBK6C5gF867pWKGn4 yZBa0RMOdOPeLqA/Ui8npIiBGMk95ycCo4d5ZlPxRtKgn9I3Hch9nzJI3o16Dql57PGPvSJ7RJQ == X-Received: by 2002:a05:7301:77c1:b0:35f:b0f:d15c with SMTP id 5a478bee46e88-36a42452cf1mr19348280eec.39.1793695976478; Tue, 03 Nov 2026 00:52:56 -0800 (PST) Received: from ramen-dev-security-2.asia-south1-c.c.ramen-dev-345017.internal (90.156.200.35.bc.googleusercontent.com. [35.200.156.90]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-36a42a6f7f9sm53065635eec.25.2026.11.03.00.52.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Nov 2026 00:52:56 -0800 (PST) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v15 4/7] mptcp: pm: in-kernel: add the subflow-retries limit Date: Tue, 3 Nov 2026 08:52:41 +0000 Message-Id: <20261103085244.23064-5-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261103085244.23064-1-anilkaushikwireless@gmail.com> References: <20261103085244.23064-1-anilkaushikwireless@gmail.com> X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A new limit for the in-kernel PM, set and read with the other limits: the number of SYN retries of a subflow re-created after a timeout, like the TCP_SYNCNT socket option. The default is the maximum, MAX_TCP_SYNCNT (127), about 4 hours, and 0 to not re-create it. The netlink policy checks this maximum value. It is also exposed in struct mptcp_info, in place of the reserved field. The next patch uses it. Signed-off-by: Anil Kaushik --- Notes: v15: - no export for the new getter: it has no module user - the limit is read like the two other ones, new patch 2 v9: - MAX_TCP_SYNCNT instead of 127 for the default - after a timeout, not any error v8: - the default is the maximum, 127, about 4 hours (Matthieu) v6: - the netlink policy checks the maximum, no more parse_limit() change (netdev Sashiko) - the limit works like TCP_SYNCNT, say so (netdev Sashiko) v5: - new, split from the retry patch (Matthieu) - the limit is also in struct mptcp_info (Matthieu) include/uapi/linux/mptcp.h | 2 +- net/mptcp/pm_kernel.c | 19 ++++++++++++++++++- net/mptcp/protocol.h | 1 + net/mptcp/sockopt.c | 2 ++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h index 72a5d030154e..d7e3f359773d 100644 --- a/include/uapi/linux/mptcp.h +++ b/include/uapi/linux/mptcp.h @@ -72,7 +72,7 @@ struct mptcp_info { __u8 mptcpi_subflows_total; __u8 mptcpi_endp_laminar_max; __u8 mptcpi_endp_fullmesh_max; - __u8 reserved; + __u8 mptcpi_limit_subflow_retries; __u32 mptcpi_last_data_sent; __u32 mptcpi_last_data_recv; __u32 mptcpi_last_ack_recv; diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c index 08a36191b5c0..07ce4c7887e1 100644 --- a/net/mptcp/pm_kernel.c +++ b/net/mptcp/pm_kernel.c @@ -25,6 +25,7 @@ struct pm_nl_pernet { u8 endp_fullmesh_max; u8 limit_add_addr_accepted; u8 limit_extra_subflows; + u8 limit_subflow_retries; u8 next_id; DECLARE_BITMAP(id_bitmap, MPTCP_PM_MAX_ADDR_ID + 1); }; @@ -95,6 +96,13 @@ u8 mptcp_pm_get_limit_extra_subflows(const struct mptcp_= sock *msk) } EXPORT_SYMBOL_GPL(mptcp_pm_get_limit_extra_subflows); =20 +u8 mptcp_pm_get_limit_subflow_retries(const struct mptcp_sock *msk) +{ + struct pm_nl_pernet *pernet =3D pm_nl_get_pernet_from_msk(msk); + + return READ_ONCE(pernet->limit_subflow_retries); +} + static bool has_subflow_daddr(const struct mptcp_sock *msk, const struct mptcp_addr_info *daddr) { @@ -1401,8 +1409,8 @@ int mptcp_pm_nl_dump_addr(struct sk_buff *msg, int mptcp_pm_nl_set_limits_doit(struct sk_buff *skb, struct genl_info *inf= o) { struct pm_nl_pernet *pernet =3D genl_info_pm_nl(info); + unsigned int rcv_addrs, subflows, retries; struct nlattr **attrs =3D info->attrs; - unsigned int rcv_addrs, subflows; =20 /* the maximums are checked by the policy */ spin_lock_bh(&pernet->lock); @@ -1410,9 +1418,12 @@ int mptcp_pm_nl_set_limits_doit(struct sk_buff *skb,= struct genl_info *info) pernet->limit_add_addr_accepted); subflows =3D nla_get_u32_default(attrs[MPTCP_PM_ATTR_SUBFLOWS], pernet->limit_extra_subflows); + retries =3D nla_get_u32_default(attrs[MPTCP_PM_ATTR_SUBFLOW_RETRIES], + pernet->limit_subflow_retries); =20 WRITE_ONCE(pernet->limit_add_addr_accepted, rcv_addrs); WRITE_ONCE(pernet->limit_extra_subflows, subflows); + WRITE_ONCE(pernet->limit_subflow_retries, retries); spin_unlock_bh(&pernet->lock); =20 return 0; @@ -1441,6 +1452,10 @@ int mptcp_pm_nl_get_limits_doit(struct sk_buff *skb,= struct genl_info *info) READ_ONCE(pernet->limit_extra_subflows))) goto fail; =20 + if (nla_put_u32(msg, MPTCP_PM_ATTR_SUBFLOW_RETRIES, + READ_ONCE(pernet->limit_subflow_retries))) + goto fail; + genlmsg_end(msg, reply); return genlmsg_reply(msg, info); =20 @@ -1595,6 +1610,8 @@ static int __net_init pm_nl_init_net(struct net *net) =20 /* Cit. 2 subflows ought to be enough for anybody. */ pernet->limit_extra_subflows =3D 2; + /* the maximum: about 4 hours with the default RTOs */ + pernet->limit_subflow_retries =3D MAX_TCP_SYNCNT; pernet->next_id =3D 1; spin_lock_init(&pernet->lock); =20 diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 3be60da12924..4aa4aedb2ba7 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -1266,6 +1266,7 @@ u8 mptcp_pm_get_endp_laminar_max(const struct mptcp_s= ock *msk); u8 mptcp_pm_get_endp_fullmesh_max(const struct mptcp_sock *msk); u8 mptcp_pm_get_limit_add_addr_accepted(const struct mptcp_sock *msk); u8 mptcp_pm_get_limit_extra_subflows(const struct mptcp_sock *msk); +u8 mptcp_pm_get_limit_subflow_retries(const struct mptcp_sock *msk); =20 /* called under PM lock */ static inline void __mptcp_pm_close_subflow(struct mptcp_sock *msk) diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index 922f6ae5c80c..d3c4abf59ec5 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -1075,6 +1075,8 @@ void mptcp_diag_fill_info(struct mptcp_sock *msk, str= uct mptcp_info *info) mptcp_pm_get_endp_laminar_max(msk); info->mptcpi_endp_fullmesh_max =3D mptcp_pm_get_endp_fullmesh_max(msk); + info->mptcpi_limit_subflow_retries =3D + mptcp_pm_get_limit_subflow_retries(msk); } =20 if (__mptcp_check_fallback(msk)) --=20 2.25.1 From nobody Thu Nov 5 07:28:13 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.173 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793695980; b=Gm2c6/dAuiYSSHPFOO/DSpisLYxhgb8WW7hmJyYu7WO2+9RjS162e/lr1O3Z2fut5+Ly +sK6FONcR9zUseRHdvdBI+CT6eZRrrOprpfc2iAqqSpW7W+pS6KFzwIOsG3rC+E/HGsjK NdQVFQcocD1s3pCiFM0Lr1fJdViFPIesVY7JaHM8XhlGe3+4+aw6LODLRW7VKTr8B7qhx 8wbmOw/YGtsDpz7gO8erNElPc9kBcz8E9pP48wfU5kB9zBTHbpAf6qS9t+2r5HGghgf2S /SzeycWx3VRVGmfHht841nHq4hV3lQjZPSMG1DOo+MPYFozR61Bvr8QuM9NZeaKEcGw== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793695980; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:From:To:Cc:Subject: Date:Message-Id:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=vBzEdLYYc9lNiqXQdHQjIfAxVfSh/RDfR3GzW8RAhgs=; b=BB7E50d0/YUxDDg9dbHnhmHddMEg+Zsb6FM6bhcNMEeWIKzqoKROh/+h01p0ZL5wDneH HMNYtMRXE1drelgMfkFfl/S8SGbwq8pyU7y9PkzBLZFibf9bUCAXde8h2qqDGsLvOmaa2 lyMl2PJzzD2hZtQ8meQtQOxrUhlByLlv5OBw1oghCRCqZKIT3SGA6uZzfpUIy3O49yiAe 5ydvMvsj1xRR9lPLtlz2XsGW2BkEOyUwKHYwuXC2A1fW4NPcj8Z1Pkb63XdxjkNbpkpxr GLhuF4x6Mavom4TfuO3+km0tJGoPck7fmKmPb74RWZS6BFn7aGfSYFwnE7DyHoS3Q+Q== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=Gig9qzO3; arc=none smtp.remote-ip=74.125.82.173 Received: from mail-dy1-f173.google.com (mail-dy1-f173.google.com [74.125.82.173]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0D298606A324 for ; Tue, 3 Nov 2026 08:52:59 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 0D298606A324 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=Gig9qzO3 Received: by mail-dy1-f173.google.com with SMTP id 5a478bee46e88-3514b90bb89so8110238eec.1 for ; Tue, 03 Nov 2026 00:52:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793695979; x=1794300779; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=vBzEdLYYc9lNiqXQdHQjIfAxVfSh/RDfR3GzW8RAhgs=; b=Gig9qzO3sAsw+8k7digxchUYRMHA35OtBso2nZC442y3iCDAAoO4PNMLjAj0mLi88s +Gmyx1nu2lWuJfCWE2LtrGFLrKhl/wmaoabqi+HLL644mKlL9sYtg8LZ9yIrWwL4CIba 1PBv/eFasHVK3a5potsVzNo/ixKNkkVR0cwNUlxg5YYk7BXWelHFescyqfUVTfNTmCS/ UT3NhosRsQrwxFHxtIYLn9ohpJSiLZsksEj7fYkdzSkI748ve234CouqPdWfXmEEg9lT CZjOFP5UB4RwFVfYUVWe7pK2mFype5+kKUeaonZsfwG7Mw8+HcKZUGijL6ew6ggeM1Nm 8qww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793695979; x=1794300779; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=vBzEdLYYc9lNiqXQdHQjIfAxVfSh/RDfR3GzW8RAhgs=; b=JRl5ykHXqZEIp0d6e/TnTKRnbRffRJrsgfdYbalJjM3yA0Rf3wvmnDANrrb4sw0FZe lYIWH1pfA5xNPlRE6WL3wJHBsKOuHrsZ3fMg49W+b9AhE+a06Veg+9aY/ComEFnmCnkJ nCn253XshIv5UufWu/dZN7whMAZ0+kUsdrOHINWWWm6ixMxjedvIHqxgGjPMXAznoAGH PZgW59yL1yDEHdtX9jdR38lAMumWcZl4uiddbeEMSuPBZymXYQojdegpkPa+AqMyTIMM Kjualm3LKVO9ylugZWIWI4ETdOHmJoK+5893urqynwNM7lcXf7yPTHAClqDxraSHN3Wa HFDw== X-Gm-Message-State: APuU4Z+atqw4XJHDUb2J31Ry+seni9aDJOOnUD6dWyUccEVR4txDM/7k UG1rckoPh4I9RWkLDdsXN9Kv3RmiiqNqbx/w6AoKEX8eHG2n/Iun4BQyaUWNgA== X-Gm-Gg: AYBFou2bWS7VRgEk81yy3+ZKQxfJANDsmXCwQykXgBTBCMnQ5gYmuc1WT834oaz3SKh KfGsLuk7BqrYu5J4+3oaN7cfO6af0XWa4MXvpQLSAvnH4hLVC39QdRNucD757D1l8BFStqvIzg+ iOwdG4u7SxvA/hk+hhxgiOEX11VfX+laISawVWIZc8y6w8kaoj01jJu+ILBYPmNoJR48F0JqVC+ kM3NHmv0Vfu4SAVuada2c+qWM+R55ZDcw4Bbh8X8tkW23yJr5ziN3UAWyI5x88+FDriby2oD/Sw DBDQIQtmQt2wn5+VLMje3YOiDFBIJ+Ipr/6sK7/yAw2DUhhv+cCZuev/y/5iWz1IsEHxx/a1Kkp MXnD1260kr6B9Iei9Db1cKYffnNUivMfBQZEyxAWVp5XnKhuKYH+6FQpwGCmnrYXCNrcwDIsr/A F1C3ER903L4Zbmwb4p4zgHTdzc9bDtg+Laptl5emcRycS6WtXiprbcceY24ehE9B00ebbEtUIpq CONeXoaINNy+Ng/knpoNgLkvVfoFzN6qzzL4qlj/52b6gCLGdmBLFf4lUGutRUS8kB/QV3C1bT2 IBetyfUN4kBlZuyDUcVfIssONOJFfULdF6jUkKOgQ5Cmgbz9m5eFJAmStCt/HvNbgo8LORvqWHz Kz1XQeVGo0SNAAMQJYB+EJeWOWdD0mGJtEHlWPksemeC9i0KPQkMa/yYWcYTYB/4a+XgJItml3g == X-Received: by 2002:a05:693c:41d2:20b0:369:f961:ccf8 with SMTP id 5a478bee46e88-36a423a9233mr19468591eec.19.1793695978523; Tue, 03 Nov 2026 00:52:58 -0800 (PST) Received: from ramen-dev-security-2.asia-south1-c.c.ramen-dev-345017.internal (90.156.200.35.bc.googleusercontent.com. [35.200.156.90]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-36a42a6f7f9sm53065635eec.25.2026.11.03.00.52.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Nov 2026 00:52:58 -0800 (PST) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v15 5/7] mptcp: pm: in-kernel: retry subflows closed after an error Date: Tue, 3 Nov 2026 08:52:42 +0000 Message-Id: <20261103085244.23064-6-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261103085244.23064-1-anilkaushikwireless@gmail.com> References: <20261103085244.23064-1-anilkaushikwireless@gmail.com> X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" With the in-kernel PM, a subflow closed after an error, e.g. a timeout when a path silently drops packets, is never re-created, even once the path works again. For example, with a main link and a backup link, connections move to the backup after an outage but never come back. When TCP gives up on an established subflow created by this side, after a retransmission or keepalive timeout, whatever ICMP error was received from the path before, the PM now creates a new one with the same addresses. Its SYN retries, set like TCP_SYNCNT by the subflow-retries limit, space out the attempts. This is a single connection attempt: an ICMP error, or a local error when connecting, ends it, and it is not retried again. It is also closed if the MPTCP socket is closed in the meantime. This is only done while the connection is established and another subflow can send data: otherwise the connection is closed after the close timeout, as before. The re-created subflows not fully established yet are closed when no other subflow can send data anymore, when the connection is no longer established, e.g. after a DATA_FIN from the peer, or when the MPTCP socket is shut down for writing or closed, unless they got fully established in between. A closed one gets an MPTCP_EVENT_SUB_CLOSED event while the MPTCP socket is established, like a join that fails. Once it is established, it is handled like any other subflow. Re-creating the initial subflow uses one of the additional subflows allowed by the limits. A subflow reset by the peer, or by anything on the path, is not re-created: the path works, and re-creating it right away could loop. A subflow that never got established is not re-created either: the PM keeps trying the next endpoints, as before. A re-created subflow takes the place of the closed one in the subflows limit: while it is connecting, an endpoint not used yet is only tried if the limit allows one more subflow, or if the new subflow cannot be created. Subflows aborted locally, also by MPTCP, or using an address that has been removed are not retried. TCP only closes a subflow after a retransmission timeout set by tcp_retries2, about 15 minutes by default. A subflow that only receives data never gets an error: the application has to enable TCP keepalive on the MPTCP socket, which MPTCP applies to all the subflows. Both are documented in mptcp.rst. So is the case of a peer that only receives data: it can still count the old subflow, and if its subflows limit is then reached, it rejects the new one, which is not retried. Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/440 Signed-off-by: Anil Kaushik --- Notes: v15: - say that a peer that only receives data can reject the new subflow, and the comment says any soft error, not only an ICMP one v14: - keep the slot of a closed additional subflow for the new one, so a join from the peer cannot take it in between (netdev Sashiko) - if the new subflow cannot be created, e.g. a local error when connecting, the PM tries the next endpoints, and a slot kept while the limit was lowered is released - the commit message and mptcp.rst say the subflows are only re-created while the connection is established, and that the ones being re-created are closed once it is no longer established - the commit message says the SUB_CLOSED event is only sent while the MPTCP socket is established, and that tcp_retries2 sets a timeout, not a number of retransmissions, also in mptcp.rst (netdev Sashiko) v13: - close again all the re-created subflows not fully established when no other subflow can send data, not only the ones still connecting: a joined one waiting for the 4th ACK could keep the connection open forever (sashiko.dev) - __mptcp_close_ssk() checks fully_established again under the ssk lock, and keeps a subflow that got fully established in between (netdev Sashiko on v11) - say that a re-created subflow keeps its place in the subflows limit, before an endpoint not used yet (netdev Sashiko on v12) - __mptcp_close_ssk() cleans the msk backlog only with MPTCP_CF_PUSH again: a re-created subflow it closes is not fully established, so it has no data there - say that only a shutdown for writing closes them, and that they get a SUB_CLOSED event v12: - when no other subflow can send data, only close the re-created subflows still connecting: a joined one is fully established soon, and stop once one got established (netdev Sashiko) - a subflow in CLOSE_WAIT does not keep the re-created ones open either: one helper for both checks (netdev Sashiko) - mptcp.rst: the initial subflow is not re-created if the peer does not accept joins to its initial address (netdev Sashiko) v11: - re-create a subflow only while another subflow can send data: the close timeout is then handled as before, no more re-arming nor stopping it (sashiko.dev) - close the re-created subflows not established yet from the worker, or when the msk is shut down or closed, never while iterating on the subflows: mptcp_close_ssk() only schedules the worker (sashiko.dev) - also close them once the msk is no longer established, whatever their TCP state, also when a DATA_FIN is handled outside the worker (sashiko.dev) - SUB_CLOSED event for them while the msk is established, like mptcp_close_ssk(): they can have been joined already - another subflow in CLOSE_WAIT, being closed, does not count as one that can send data v10: - keep the connection when the close timeout expires if a re-created subflow connected in the meantime: an active join does not stop the timer, mptcp_finish_join() returns early for it (sashiko.dev) - close the re-created subflows still connecting once the msk is no longer established, after a DATA_FIN or a shutdown(), when no other subflow is left: they cannot join anymore, and would keep the msk around (sashiko.dev) v9: - every timeout is retried, whatever ICMP error TCP reports for it, the last one received, see tcp_write_err(): resets, local aborts and MPTCP protocol errors are excluded instead of listing the timeout errors, so IPv4 and IPv6 behave the same (netdev Sashiko) - mptcp.rst: 0 does not re-create the subflows (netdev Sashiko) - the re-created subflows closed with the msk get no SUB_CLOSED event, like the other subflows closed with it - if no other subflow can send data, the close timeout still applies: creating the new subflow no longer keeps the connection open for the whole SYN retries - doc and commit message: a single connection attempt, ended by an ICMP error or a local connect error - __mptcp_close_ssk() always cleans the msk backlog of the closed subflow: a re-created subflow closed with the msk can have been established in between, with data in the backlog (netdev Sashiko). It is still closed without MPTCP_CF_PUSH, which would re-send all the pending data - tcp_sock_set_syncnt(), without the ssk lock v8: - mptcp.rst gives the default, 127, about 4 hours - close the re-created subflows still connecting before __mptcp_wr_shutdown() disconnects them, and also the already disconnected ones, e.g. after shutdown(SHUT_WR) (sashiko.dev) - mptcp.rst: the initial subflow on the client side, the subflows limit, and only one attempt (netdev Sashiko) v7: - only after a path error, a retransmission or keepalive timeout: a reset from the peer or the path could make it loop (netdev Sashiko) - retry and no_retry are bools under the msk socket lock, in the padding after close_event_done, not bits next to the reset ones (netdev Sashiko) - mark all the subflows using a removed address, whatever their state (netdev Sashiko) - doc: tcp_retries2, and keepalive for a subflow that only receives data (Abdul Wasey's tests) - not retried after a reset by MPTCP itself, even with an old sk_err left by IP_RECVERR (netdev Sashiko) - close the re-created subflows still connecting without MPTCP_CF_PUSH: they never carried data (netdev Sashiko) v6 (netdev Sashiko): - only re-create subflows that were established: the PM still tries the next endpoints when one never gets established - only once TCP closed the subflow, to read its reset flags safely - removed addresses, also the remote ones and the local ID 0: the dead subflows are marked in mptcp_pm_rm_addr_or_subflow(), instead of looking up the endpoint v5 (Matthieu): - split in 4 patches - reverse xmas tree in mptcp_pm_kernel_retry_check() - "in-kernel" in the subject, like the other in-kernel PM patches - close the re-created subflows still connecting when the socket is closed (Matthieu: closing the socket during a retry) v4 (Matthieu): - netlink limit, on by default, instead of the sysctl - one new subflow per failure, its SYN retries set from the limit: no socket reuse, no attempts counter, no 60s check - no MIB counter - not retried if the endpoint was removed, instead of the pm.c change - called through pm.c - new bit in the subflow context, no new hole - commit message: MP_RST, not any RST (Sashiko) v3 (Sashiko): - no retry after MP_RST without the T flag, local abort or EBADMSG - no retry on a removed endpoint or address - use only request_bkup for the backup flag v2: - use the subflow sockets, no list or timer - code moved to pm_kernel.c Documentation/networking/mptcp.rst | 26 +++++ net/mptcp/pm.c | 50 +++++++++ net/mptcp/pm_kernel.c | 160 +++++++++++++++++++++++++++++ net/mptcp/protocol.c | 77 +++++++++++++- net/mptcp/protocol.h | 51 +++++++++ 5 files changed, 363 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/mptcp.rst b/Documentation/networking/= mptcp.rst index b6753ffb9c9a..da978ef92726 100644 --- a/Documentation/networking/mptcp.rst +++ b/Documentation/networking/mptcp.rst @@ -73,6 +73,32 @@ To be able to use multiple IP addresses on a host to cre= ate multiple *subflows* addresses can be used. This can be configured with ``ip mptcp endpoint`` f= or example. =20 +When TCP gives up on an established subflow created by this host, after a +retransmission or keepalive timeout, the in-kernel path manager creates it +again, with the ``subflow-retries`` limit as its number of SYN retries, li= ke +``TCP_SYNCNT``: 127 by default, about 4 hours, and 0 to not re-create it. = This +is done for the additional subflows, and for the initial one on the client= side: +the new subflow then takes one of the additional subflows allowed by the +``subflows`` limit. It keeps this place while it is being created again: an +endpoint not used yet is only tried if the ``subflows`` limit allows one m= ore +subflow, or if the new one cannot be created. The initial one is not creat= ed +again if the peer does not accept joins to its initial address. This is a = single +connection attempt: an ICMP error, or a local error when connecting, ends = it, +and it is not created again. This is only done while the connection is +established and another subflow can send data: otherwise the connection is +closed after ``close_timeout``, as before, and the subflows being re-creat= ed are +closed too, as they are once the connection is no longer established, e.g.= after +a DATA_FIN from the peer. A subflow reset by the peer, or by anything on t= he +path, is not created again either. TCP only gives up on a subflow after a +retransmission timeout set by ``tcp_retries2``, about 15 minutes by defaul= t: +shorter outages are handled by TCP itself. A subflow that only receives da= ta +does not notice that its path stopped working: the application should then +enable TCP keepalive on the MPTCP socket (``SO_KEEPALIVE``, ``TCP_KEEPIDLE= ``, +``TCP_KEEPINTVL`` and ``TCP_KEEPCNT``), which MPTCP applies to all its sub= flows. +The peer must also have room for the new subflow: if it only receives data= , it +can still count the old one, and if this reached its ``subflows`` limit, it +rejects the new one, which is not created again. + Packet Scheduler ---------------- =20 diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index d7c5b50b34cc..2772b97303bf 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -694,6 +694,37 @@ void mptcp_pm_subflow_check_next(struct mptcp_sock *ms= k, spin_unlock_bh(&pm->lock); } =20 +/* Called with the msk socket lock held, before closing a subflow: true if + * it has to be re-created once closed, see mptcp_pm_subflow_retry(). + */ +bool mptcp_pm_subflow_retry_check(struct mptcp_sock *msk, + const struct mptcp_subflow_context *subflow, + struct mptcp_pm_retry *retry) +{ + if (!mptcp_pm_is_kernel(msk)) + return false; + + return mptcp_pm_kernel_retry_check(msk, subflow, retry); +} + +void mptcp_pm_subflow_retry(struct mptcp_sock *msk, + const struct mptcp_pm_retry *retry) +{ + struct sock *sk =3D (struct sock *)msk; + + if (!mptcp_pm_is_kernel(msk) || mptcp_pm_kernel_retry(msk, retry)) + return; + + /* Not re-created, e.g. a local error when connecting: its slot is free + * again, try the next endpoints, like mptcp_pm_subflow_check_next(). + */ + spin_lock_bh(&msk->pm.lock); + if (mptcp_is_fully_established(sk) && + mptcp_pm_nl_check_work_pending(msk)) + mptcp_pm_schedule_work(msk, MPTCP_PM_SUBFLOW_ESTABLISHED); + spin_unlock_bh(&msk->pm.lock); +} + void mptcp_pm_add_addr_received(const struct sock *ssk, const struct mptcp_addr_info *addr) { @@ -762,6 +793,24 @@ static void mptcp_pm_add_addr_send_ack(struct mptcp_so= ck *msk) mptcp_pm_schedule_work(msk, MPTCP_PM_ADD_ADDR_SEND_ACK); } =20 +/* A subflow using a removed address must not be re-created, whatever its + * state, e.g. closed by TCP but not collected yet, see + * mptcp_pm_kernel_retry_check(). + */ +static void mptcp_pm_rm_no_retry(struct mptcp_subflow_context *subflow, + enum linux_mptcp_mib_field rm_type, u8 rm_id) +{ + u8 id; + + if (rm_type =3D=3D MPTCP_MIB_RMADDR) + id =3D READ_ONCE(subflow->remote_id); + else + id =3D subflow_get_local_id(subflow); + + if (id =3D=3D rm_id) + subflow->no_retry =3D true; +} + static void mptcp_pm_rm_addr_or_subflow(struct mptcp_sock *msk, const struct mptcp_rm_list *rm_list, enum linux_mptcp_mib_field rm_type) @@ -794,6 +843,7 @@ static void mptcp_pm_rm_addr_or_subflow(struct mptcp_so= ck *msk, int how =3D RCV_SHUTDOWN | SEND_SHUTDOWN; u8 id =3D subflow_get_local_id(subflow); =20 + mptcp_pm_rm_no_retry(subflow, rm_type, rm_id); if ((1 << inet_sk_state_load(ssk)) & (TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2 | TCPF_CLOSING | TCPF_CLOSE)) continue; diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c index 07ce4c7887e1..c7b266f13e62 100644 --- a/net/mptcp/pm_kernel.c +++ b/net/mptcp/pm_kernel.c @@ -1583,6 +1583,166 @@ bool mptcp_pm_nl_check_work_pending(struct mptcp_so= ck *msk) return true; } =20 +/* TCP gave up on the path: after a retransmission, keepalive or user + * timeout, sk_err is ETIMEDOUT, or the last soft error, e.g. an ICMP error + * from the path, see tcp_write_err(). The other errors on an established + * subflow are not path failures: a reset, from the peer or from anything = on + * the path (ECONNRESET, or EPIPE in CLOSE_WAIT, see tcp_reset()), a local + * abort (ECONNABORTED), or an MPTCP protocol error (EBADMSG). + */ +static bool mptcp_pm_retry_path_error(int err) +{ + switch (err) { + case ECONNRESET: + case EPIPE: + case ECONNABORTED: + case EBADMSG: + return false; + } + + return true; +} + +static bool mptcp_pm_retry_other_subflow(struct mptcp_sock *msk) +{ + struct mptcp_subflow_context *subflow; + + mptcp_for_each_subflow(msk, subflow) { + if (mptcp_subflow_can_send(subflow)) + return true; + } + + return false; +} + +/* Called with the msk socket lock held, before the subflow is closed */ +bool mptcp_pm_kernel_retry_check(struct mptcp_sock *msk, + const struct mptcp_subflow_context *subflow, + struct mptcp_pm_retry *retry) +{ + struct sock *ssk =3D mptcp_subflow_tcp_sock(subflow); + struct sock *sk =3D (struct sock *)msk; + int err; + + /* Only once TCP closed it: the acquire in inet_sk_state_load() makes + * sure sk_err and the resetting flag checked below are the ones of + * this close. + */ + if (inet_sk_state_load(ssk) !=3D TCP_CLOSE) + return false; + + err =3D READ_ONCE(ssk->sk_err); + retry->syn_retries =3D mptcp_pm_get_limit_subflow_retries(msk); + if (!retry->syn_retries || !err || sk->sk_state !=3D TCP_ESTABLISHED || + __mptcp_check_fallback(msk)) + return false; + + /* mptcp_close_ssk() will not handle it again, e.g. the initial + * subflow, kept in the list once closed + */ + if (subflow->close_event_done || READ_ONCE(subflow->local_id) < 0) + return false; + + /* Only a path that worked is tried again. If a subflow never got + * established, the PM moves on to the next endpoints instead, and a + * re-created subflow that cannot connect is not retried again: its SYN + * retransmissions already spaced out the tries. + */ + if (!READ_ONCE(subflow->fully_established)) + return false; + + /* its address has been removed, see mptcp_pm_rm_addr_or_subflow() */ + if (subflow->no_retry) + return false; + + /* Only while another subflow can send data: without one, the + * connection is closed after the close timeout, as before, see + * __mptcp_close_ssk(). + */ + if (!mptcp_pm_retry_other_subflow(msk)) + return false; + + /* Only when the path stopped working. A reset, from the peer or from + * anything on the path, means the path works: re-creating the subflow + * right away could loop. Not after a reset by MPTCP itself either, with + * mptcp_subflow_reset(), whatever sk_err says. + */ + if (!mptcp_pm_retry_path_error(err) || subflow->resetting) + return false; + + /* Only re-create subflows initiated by this side: MP_JOIN ones, or the + * initial one on the client side. + */ + if (!subflow->request_join && + (subflow->mp_join || READ_ONCE(msk->pm.server_side))) + return false; + + mptcp_remote_address((struct sock_common *)ssk, &retry->remote); + retry->remote.id =3D READ_ONCE(subflow->remote_id); + if (!retry->remote.id && READ_ONCE(msk->pm.remote_deny_join_id0)) + return false; + + mptcp_local_address((struct sock_common *)ssk, &retry->local.addr); + retry->local.addr.port =3D 0; + retry->local.addr.id =3D subflow_get_local_id(subflow); + retry->local.flags =3D subflow->request_bkup ? + MPTCP_PM_ADDR_FLAG_BACKUP : 0; + retry->local.ifindex =3D ssk->sk_bound_dev_if; + + /* The slot of an additional subflow is released when it is closed: keep + * it for the new one, a join from the peer could take it before. + */ + retry->keep_slot =3D subflow->request_join; + if (retry->keep_slot) { + spin_lock_bh(&msk->pm.lock); + msk->pm.extra_subflows++; + spin_unlock_bh(&msk->pm.lock); + } + + return true; +} + +/* Called with the msk socket lock held, after the subflow has been closed: + * true if a new subflow has been created + */ +bool mptcp_pm_kernel_retry(struct mptcp_sock *msk, + const struct mptcp_pm_retry *retry) +{ + struct mptcp_subflow_context *subflow; + struct sock *sk =3D (struct sock *)msk; + bool allowed; + u8 limit; + + spin_lock_bh(&msk->pm.lock); + limit =3D mptcp_pm_get_limit_extra_subflows(msk); + if (retry->keep_slot) { + /* the limit can have been lowered in the meantime */ + allowed =3D msk->pm.extra_subflows <=3D limit; + if (!allowed) + __mptcp_pm_close_subflow(msk); + } else { + /* the initial subflow takes one of the additional subflows */ + allowed =3D msk->pm.extra_subflows < limit; + if (allowed) + msk->pm.extra_subflows++; + } + spin_unlock_bh(&msk->pm.lock); + + /* on error, the slot is released, see __mptcp_subflow_connect() */ + if (!allowed || + __mptcp_subflow_connect(sk, &retry->local, &retry->remote)) + return false; + + /* the new subflow is the last one */ + subflow =3D list_last_entry(&msk->conn_list, struct mptcp_subflow_context, + node); + /* from 1 to MAX_TCP_SYNCNT, see mptcp_pm_kernel_retry_check() */ + tcp_sock_set_syncnt(mptcp_subflow_tcp_sock(subflow), + retry->syn_retries); + subflow->retry =3D true; + return true; +} + /* Called under PM lock */ void __mptcp_pm_kernel_worker(struct mptcp_sock *msk) { diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 02a4f479d9d8..cb1350ba1a41 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -736,6 +736,10 @@ static void mptcp_check_data_fin(struct sock *sk) if (!__mptcp_check_fallback(msk)) mptcp_send_ack(msk); mptcp_close_wake_up(sk); + + /* not always called from the worker, which closes them */ + if (mptcp_retry_subflows_stale(msk)) + mptcp_schedule_work(sk); } } =20 @@ -2637,6 +2641,7 @@ bool __mptcp_retransmit_pending_data(struct sock *sk) =20 /* flags for __mptcp_close_ssk() */ #define MPTCP_CF_PUSH BIT(1) +#define MPTCP_CF_RETRY BIT(2) =20 /* be sure to send a reset only if the caller asked for it, also * clean completely the subflow status when the subflow reaches @@ -2695,6 +2700,26 @@ static void __mptcp_close_ssk(struct sock *sk, struc= t sock *ssk, * subflow close. */ lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); + + /* a re-created subflow, see mptcp_close_retry_subflows(): the ssk lock + * defers the 4th ACK, so it cannot get fully established meanwhile + */ + if (flags & MPTCP_CF_RETRY) { + if (READ_ONCE(subflow->fully_established)) { + release_sock(ssk); + return; + } + + /* like mptcp_close_ssk(): it can have joined already */ + if (sk->sk_state =3D=3D TCP_ESTABLISHED && + !subflow->close_event_done) { + subflow->close_event_done =3D true; + mptcp_event(MPTCP_EVENT_SUB_CLOSED, msk, ssk, + GFP_KERNEL); + } + mptcp_pm_subflow_check_next(msk, subflow); + } + subflow->closing =3D 1; =20 if (flags & MPTCP_CF_PUSH) @@ -2788,6 +2813,34 @@ static void __mptcp_close_ssk(struct sock *sk, struc= t sock *ssk, } } =20 +/* Re-created subflows not established yet are useless once the msk is + * closed or shut down for writing, or no other subflow can send data, see + * mptcp_retry_subflows_stale(). Not called while iterating on the subflow= s. + */ +static void mptcp_close_retry_subflows(struct sock *sk, bool stale) +{ + struct mptcp_subflow_context *subflow, *tmp; + struct mptcp_sock *msk =3D mptcp_sk(sk); + + mptcp_for_each_subflow_safe(msk, subflow, tmp) { + struct sock *ssk =3D mptcp_subflow_tcp_sock(subflow); + + if (!subflow->retry || READ_ONCE(subflow->fully_established)) + continue; + + /* another one got established in the meantime */ + if (stale && !mptcp_retry_subflows_stale(msk)) + break; + + /* Checked again under the ssk lock: it is kept if it got fully + * established in between. Otherwise, it has no data in the msk + * backlog, and the MPTCP retransmissions cover what it sent. No + * need to push the pending data again. + */ + __mptcp_close_ssk(sk, ssk, subflow, MPTCP_CF_RETRY); + } +} + void mptcp_close_ssk(struct sock *sk, struct sock *ssk, struct mptcp_subflow_context *subflow) { @@ -2806,6 +2859,12 @@ void mptcp_close_ssk(struct sock *sk, struct sock *s= sk, mptcp_pm_subflow_check_next(mptcp_sk(sk), subflow); =20 __mptcp_close_ssk(sk, ssk, subflow, MPTCP_CF_PUSH); + + /* the caller can iterate on the subflows: the worker closes the + * re-created ones + */ + if (mptcp_retry_subflows_stale(mptcp_sk(sk))) + mptcp_schedule_work(sk); } =20 static unsigned int mptcp_sync_mss(struct sock *sk, u32 pmtu) @@ -2823,6 +2882,8 @@ static void __mptcp_close_subflow(struct sock *sk) mptcp_for_each_subflow_safe(msk, subflow, tmp) { struct sock *ssk =3D mptcp_subflow_tcp_sock(subflow); int ssk_state =3D inet_sk_state_load(ssk); + struct mptcp_pm_retry retry; + bool do_retry; =20 if (ssk_state !=3D TCP_CLOSE && (ssk_state !=3D TCP_CLOSE_WAIT || @@ -2834,7 +2895,10 @@ static void __mptcp_close_subflow(struct sock *sk) if (!skb_queue_empty_lockless(&ssk->sk_receive_queue)) continue; =20 + do_retry =3D mptcp_pm_subflow_retry_check(msk, subflow, &retry); mptcp_close_ssk(sk, ssk, subflow); + if (do_retry) + mptcp_pm_subflow_retry(msk, &retry); } =20 } @@ -3185,6 +3249,9 @@ static void mptcp_worker(struct work_struct *work) if (test_and_clear_bit(MPTCP_WORK_CLOSE_SUBFLOW, &msk->flags)) __mptcp_close_subflow(sk); =20 + if (mptcp_retry_subflows_stale(msk)) + mptcp_close_retry_subflows(sk, true); + if (mptcp_close_tout_expired(sk)) { struct mptcp_subflow_context *subflow, *tmp; =20 @@ -3516,6 +3583,9 @@ bool __mptcp_close(struct sock *sk, long timeout) goto cleanup; } =20 + /* before __mptcp_wr_shutdown() disconnects them */ + mptcp_close_retry_subflows(sk, false); + if (mptcp_data_avail(msk) || timeout < 0 || (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime)) { /* If the msk has read data, or the caller explicitly ask it, @@ -4140,7 +4210,12 @@ static void mptcp_shutdown(struct sock *sk, int how) { pr_debug("sk=3D%p, how=3D%d\n", sk, how); =20 - if ((how & SEND_SHUTDOWN) && mptcp_close_state(sk)) + if (!(how & SEND_SHUTDOWN)) + return; + + /* before the state change and __mptcp_wr_shutdown(), as on close */ + mptcp_close_retry_subflows(sk, false); + if (mptcp_close_state(sk)) __mptcp_wr_shutdown(sk); } =20 diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 4aa4aedb2ba7..0b2b9299a032 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -610,6 +610,8 @@ struct mptcp_subflow_context { bool close_event_done; /* has done the post-closed part, * protected by msk socket lock */ + bool retry; /* re-created after an error, msk lock */ + bool no_retry; /* uses a removed address, msk lock */ u32 lent_mem_frag; u32 remote_nonce; u32 local_nonce; @@ -919,6 +921,37 @@ static inline bool __mptcp_subflow_active(struct mptcp= _subflow_context *subflow) return __tcp_can_send(mptcp_subflow_tcp_sock(subflow)); } =20 +/* Can send data, and is not being closed, e.g. in CLOSE_WAIT */ +static inline bool mptcp_subflow_can_send(struct mptcp_subflow_context *su= bflow) +{ + struct sock *ssk =3D mptcp_subflow_tcp_sock(subflow); + + return __mptcp_subflow_active(subflow) && + inet_sk_state_load(ssk) =3D=3D TCP_ESTABLISHED; +} + +/* Re-created subflows not established yet, while no other subflow can send + * data, or once the msk is no longer established: they cannot join anymor= e, + * see mptcp_finish_join(), or must not keep the connection open. + */ +static inline bool mptcp_retry_subflows_stale(struct mptcp_sock *msk) +{ + struct mptcp_subflow_context *subflow; + struct sock *sk =3D (struct sock *)msk; + bool retry =3D false; + + mptcp_for_each_subflow(msk, subflow) { + if (sk->sk_state =3D=3D TCP_ESTABLISHED && + mptcp_subflow_can_send(subflow)) + return false; + + retry |=3D subflow->retry && + !READ_ONCE(subflow->fully_established); + } + + return retry; +} + void mptcp_subflow_set_active(struct mptcp_subflow_context *subflow); =20 bool mptcp_subflow_active(struct mptcp_subflow_context *subflow); @@ -1139,6 +1172,19 @@ void mptcp_pm_subflow_established(struct mptcp_sock = *msk); bool mptcp_pm_nl_check_work_pending(struct mptcp_sock *msk); void mptcp_pm_subflow_check_next(struct mptcp_sock *msk, const struct mptcp_subflow_context *subflow); + +struct mptcp_pm_retry { + struct mptcp_pm_local local; + struct mptcp_addr_info remote; + u8 syn_retries; + bool keep_slot; +}; + +bool mptcp_pm_subflow_retry_check(struct mptcp_sock *msk, + const struct mptcp_subflow_context *subflow, + struct mptcp_pm_retry *retry); +void mptcp_pm_subflow_retry(struct mptcp_sock *msk, + const struct mptcp_pm_retry *retry); void mptcp_pm_add_addr_received(const struct sock *ssk, const struct mptcp_addr_info *addr); void mptcp_pm_add_addr_echoed(struct mptcp_sock *msk, @@ -1260,6 +1306,11 @@ void __init mptcp_pm_userspace_register(void); void __init mptcp_pm_nl_init(void); void mptcp_pm_worker(struct mptcp_sock *msk); void __mptcp_pm_kernel_worker(struct mptcp_sock *msk); +bool mptcp_pm_kernel_retry_check(struct mptcp_sock *msk, + const struct mptcp_subflow_context *subflow, + struct mptcp_pm_retry *retry); +bool mptcp_pm_kernel_retry(struct mptcp_sock *msk, + const struct mptcp_pm_retry *retry); u8 mptcp_pm_get_endp_signal_max(const struct mptcp_sock *msk); u8 mptcp_pm_get_endp_subflow_max(const struct mptcp_sock *msk); u8 mptcp_pm_get_endp_laminar_max(const struct mptcp_sock *msk); --=20 2.25.1 From nobody Thu Nov 5 07:28:13 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.173 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793695982; b=mjtqpiODuin5Gp3tuYtMm+TOVwwmDQOCg8JwuXjKzm9Dj9/dwZTUQTEDl+QJXPnUC0Y3 JsA5wc6udtR33QkiQLyoW98o1yoJPAB+G4FpaQ10PoRUs9Af933lpoeJo58Kvp0wmJJDM m7skh3sCOyuPoT2SGftVb+0Cx6/u4ckC5/Y4lvYQNu4cJT6gWDBGqcsV+rRWFWhten0Z/ c2y4ozKK//FDmSiwXl/OnyaxY4zV24wESxTCTfzQvS/LcY8yAKfowRWrGT5y2WoEh0C4b nuY0nYI3Q0fbYP5VzCI11bTuHdc4QxeomI+mRhWIpZibbP0uDxcoeKZf++hWnwHfiuw== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793695982; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:From:To:Cc:Subject: Date:Message-Id:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=bk08wlQLOckMDa/tQlUY0Gs+7pq28CqTbcZ5/8r0npg=; b=B0h1DSgEpQyrOMGAT1i4z3pU5v9cInp1gflzpPDn1csU/A064EVVW5F6T233AHIcrbOx sO24ZE07fZ5APVYU0/Ojf5MxikIy4ontoWUqD3b9DI7F8BBF3fqK0rQ0srNE3lwDE19fm vo6K5n/xr/S9E5gOSRw5ZZd2Liw+dPPBGexdx67QD7ng/aZyt6QJnoyuNTsJcma3Zn691 GtSC9RJXtJnmF9KOM14SxAeEjdZnG4/YsbbsR3CX2Fez4pHnO3XjfKHaBTF9ioxcz961o KX0tFchVi5I4mk4S2SK/+9s7XXBHif/3mbE6tqCXJDIVN9XWhm45bcESK8hYsVg95/Q== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=ItuPpE7S; arc=none smtp.remote-ip=74.125.82.173 Received: from mail-dy1-f173.google.com (mail-dy1-f173.google.com [74.125.82.173]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A4766606A326 for ; Tue, 3 Nov 2026 08:53:01 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org A4766606A326 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=ItuPpE7S Received: by mail-dy1-f173.google.com with SMTP id 5a478bee46e88-3535bf8b3e0so5544270eec.0 for ; Tue, 03 Nov 2026 00:53:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793695981; x=1794300781; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=bk08wlQLOckMDa/tQlUY0Gs+7pq28CqTbcZ5/8r0npg=; b=ItuPpE7SKP2r6gKbOeKkDoxEdQLrNHwPCjTjQnYsXhVhhq8FwYurUcZu4dN/tAmaHA yxm76unfHHDBp+nB4ODxRtS2ohUanLloPht9T1VQteMQraNNq8XJ/z+7kVMYUZn5dOWF NCexhlUl/yxb+bt9BfRD7IVfdtJT8t3c+WtEhfy3xwujN+WneNxmWCN9/ztJv9MI9yBz WPZ11puZ0ugue54vVAitYMiCkY6XIr+5dljnw4s+dlnMnIjvEU5tUnBxVCaJB55+1yiF StVST+seoW58MGTHQv77Vus1beu/2ZSUVIjBRaIY3UJmt85R/pqUvHZAjl6bm9VJh4g/ UXWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793695981; x=1794300781; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=bk08wlQLOckMDa/tQlUY0Gs+7pq28CqTbcZ5/8r0npg=; b=Ngrd0ALs38U/k078bmYayBDGSLH7qbhPHAt7Mi4cE1FuGNn8kvfjstiZ/7pLCMf3EF 07bT3aUAqhwxURAUq601j+0zIbl1+pXwnIOb/9MPEYS4okJKquyvPq9lxsr99LXM0Ams NWORMV/WvFF2TVgru+vc5ZkWts6tLpRI03EFX2Z6H7FGVT6I+24Io9js9mYX0l4kHHxP XUyD+sZYF3mYp4Hkac4rHWdp9gxaXDwb4D4fWQ1sgwDUuQU0g8xPFzfuBfrC7Dmg4Qu3 cEERwYAoKz+EssY0re0a2fwc1YrKXHd90Ld8i1AP8ntJfHWdImzQcaokp+NfMriUX5ab OO7g== X-Gm-Message-State: APuU4Z8UgnaNGCVfkGYgYMLc/B7Hv+6tl8UZOn7JJz0Rkb/udSBWrEht b2P4g82kY3iomlaDDjsgxZiuuClJ7If8GWjp4PzNKnqnB/r8XvzFhOXZIifEeQ== X-Gm-Gg: AYBFou11lZifaWOdLivKd2hMub+3wgkyE+eHBo1zja2kSFwbVJbi3hCbmuCwdBXN9FQ bP9MM9zCvTIYhyxARGO3n10FqRR/amjKHuoc087zYfFEaygYs7+9X2ZPGvFrKepgb005aBf8QJP VuTh8nRe1DdDhOIpY2yHEADWczN8gh6F729S/PYbpIeJHuHJT+knNlha1rwDEFOwYFEvSz8Z2GT XnDDGyOxiUJI89cFRG43Gx3iYvbPk5JIlTvipaiQxf83Brkw790CMPZB/9WvfWSWWWbAcsXRrdi WT0tyF+shYmojCTi53VIui5tInHqIkNJPfgD66wKEWODhzY2Lh7++/D9NZPA1i59sqrMmSts6YH 6XbD3EXQgQAAzo2dKbMZo0TJaZrWExwQrnwkGg4PGsrrNXcEQGmjhuKUzuXsutBELviQpbZhfxY OyrMuOWGARcsMsGcTp5PpWrbJnQIhm0QtMm2c34uB+XQL29CZxvrv9xreV3BSul/HIPUlsG/B19 eC/d2u6+ZXYa4jIkGbDCcYO7jKry7cvytXGZsYIL+EeTV7oTJsVt1z4lXbpYEfZG0GLINrI2SSM kY5F+3rP4UtT6+J0QHEdQWr8rEYnmc7LUOmVNAwgDTBXnwbFhW0BU2HMDTUAmcCQyoQkJIXXkVQ wkw1Mr2OZdOp+cevTyekbcY0o/urIUIvKAAW9Yp09IbqwklHfCqpAQ36d1u/RuktqWJP6n9+LAy lW3dXMuo5s6nW50ZBI1Xmw X-Received: by 2002:a05:693c:881b:20b0:35d:eb9c:fce with SMTP id 5a478bee46e88-36a423ea9b4mr18762082eec.25.1793695980573; Tue, 03 Nov 2026 00:53:00 -0800 (PST) Received: from ramen-dev-security-2.asia-south1-c.c.ramen-dev-345017.internal (90.156.200.35.bc.googleusercontent.com. [35.200.156.90]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-36a42a6f7f9sm53065635eec.25.2026.11.03.00.52.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Nov 2026 00:53:00 -0800 (PST) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v15 6/7] selftests: mptcp: pm_nl_ctl: support the subflow-retries limit Date: Tue, 3 Nov 2026 08:52:43 +0000 Message-Id: <20261103085244.23064-7-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261103085244.23064-1-anilkaushikwireless@gmail.com> References: <20261103085244.23064-1-anilkaushikwireless@gmail.com> X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 'pm_nl_ctl limits' can now set the subflow-retries limit with a third value, and shows it when the kernel supports it. pm_netlink.sh checks its default value, that a value above the maximum is refused, that it can be set, and that it is kept when only the other limits are set. This is only done with pm_nl_ctl for the moment: iproute2 does not support this limit yet. Signed-off-by: Anil Kaushik --- Notes: v13: - get_limits_nb only reads the two limits set_limits restores v12: - check the limit is kept when only the other limits are set (netdev Sashiko) v9: - try the value above the maximum after setting 0, not the default value, which is also the maximum: a refused value is then told apart from a clamped one (netdev Sashiko) v8: - the default value is now 127 v7: - also check that the maximum value, 127, is accepted (netdev Sashiko) v5: - new (Matthieu) .../testing/selftests/net/mptcp/pm_netlink.sh | 55 ++++++++++++++++++- tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 34 +++++++++--- 2 files changed, 79 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testin= g/selftests/net/mptcp/pm_netlink.sh index 21bfe1311f11..80e100d105a2 100755 --- a/tools/testing/selftests/net/mptcp/pm_netlink.sh +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -46,15 +46,20 @@ trap cleanup EXIT =20 mptcp_lib_ns_init ns1 =20 +# $3: subflow retries, only shown by pm_nl_ctl, if supported format_limits() { local accept=3D"${1}" local subflows=3D"${2}" + local retries=3D"${3}" =20 if mptcp_lib_is_ip_mptcp; then # with a space at the end printf "add_addr_accepted %d subflows %d \n" "${accept}" "${subflows}" else printf "accept %d\nsubflows %d\n" "${accept}" "${subflows}" + if [ -n "${retries}" ]; then + printf "subflow_retries %d\n" "${retries}" + fi fi } =20 @@ -71,7 +76,23 @@ get_limits_nb() { ip -n "${ns1}" mptcp limits | awk '{ print $2" "$4 }' else ip netns exec "${ns1}" ./pm_nl_ctl limits | \ - awk '{ printf "%s ", $2 }' + awk 'NR <=3D 2 { printf "%s ", $2 }' + fi +} + +# The subflow retries limit is only set and shown with pm_nl_ctl for now. +# Prints its default value if it is supported. +get_default_retries() { + if mptcp_lib_is_ip_mptcp; then + return + fi + + if ip netns exec "${ns1}" ./pm_nl_ctl limits | + grep -q "^subflow_retries"; then + echo 127 + elif mptcp_lib_expect_all_features; then + # missing feature: the defaults limits check will fail + echo 127 fi } =20 @@ -154,8 +175,10 @@ mptcp_lib_subtests_last_ts_reset check "show_endpoints" "" "defaults addr list" =20 default_limits=3D"$(get_limits)" +default_retries=3D"$(get_default_retries)" if mptcp_lib_expect_all_features; then - check "get_limits" "$(format_limits 0 2)" "defaults limits" + check "get_limits" "$(format_limits 0 2 "${default_retries}")" \ + "defaults limits" fi =20 add_endpoint 10.0.1.1 @@ -224,7 +247,33 @@ check "get_limits" "${default_limits}" "subflows above= hard limit" =20 set_limits ${max_limits} ${max_limits} flush_endpoint ## to make sure it doesn't affect the limits -check "get_limits" "$(format_limits ${max_limits} ${max_limits})" "set lim= its" +max_limits_out=3D"$(format_limits ${max_limits} ${max_limits} \ + "${default_retries}")" +check "get_limits" "${max_limits_out}" "set limits" + +if [ -n "${default_retries}" ]; then + ip netns exec "${ns1}" ./pm_nl_ctl limits ${max_limits} ${max_limits} 0 + check "get_limits" "$(format_limits ${max_limits} ${max_limits} 0)" \ + "set subflow retries" + + # not given: unchanged + set_limits ${max_limits} ${max_limits} + check "get_limits" "$(format_limits ${max_limits} ${max_limits} 0)" \ + "subflow retries kept" + + # not from the default: a refused value differs from a clamped one + ip netns exec "${ns1}" ./pm_nl_ctl limits ${max_limits} ${max_limits} \ + 128 2>/dev/null + check "get_limits" "$(format_limits ${max_limits} ${max_limits} 0)" \ + "subflow retries above hard limit" + + ip netns exec "${ns1}" ./pm_nl_ctl limits ${max_limits} ${max_limits} \ + 127 + check "get_limits" "$(format_limits ${max_limits} ${max_limits} 127)" \ + "set max subflow retries" + ip netns exec "${ns1}" ./pm_nl_ctl limits ${max_limits} ${max_limits} \ + "${default_retries}" +fi =20 flush_endpoint add_endpoint 10.0.1.1 diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/= selftests/net/mptcp/pm_nl_ctl.c index 78180da1efcc..41467b997018 100644 --- a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c +++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c @@ -39,7 +39,7 @@ static void syntax(char *argv[]) fprintf(stderr, "\tset [] [id ] flags [no]backup|[no]fullmesh [po= rt ] [token ] [rip ] [rport ]\n"); fprintf(stderr, "\tflush\n"); fprintf(stderr, "\tdump\n"); - fprintf(stderr, "\tlimits [ ]\n"); + fprintf(stderr, "\tlimits [ []]\n"); fprintf(stderr, "\tevents\n"); fprintf(stderr, "\tlisten \n"); exit(0); @@ -1217,6 +1217,7 @@ int flush_addrs(int fd, int pm_family, int argc, char= *argv[]) static void print_limits(struct nlmsghdr *nh, int pm_family, int total_len) { struct rtattr *attrs; + const char *name; uint32_t max; =20 for (; NLMSG_OK(nh, total_len); nh =3D NLMSG_NEXT(nh, total_len)) { @@ -1235,13 +1236,22 @@ static void print_limits(struct nlmsghdr *nh, int p= m_family, int total_len) while (RTA_OK(attrs, len)) { int type =3D attrs->rta_type; =20 - if (type !=3D MPTCP_PM_ATTR_RCV_ADD_ADDRS && - type !=3D MPTCP_PM_ATTR_SUBFLOWS) + switch (type) { + case MPTCP_PM_ATTR_RCV_ADD_ADDRS: + name =3D "accept"; + break; + case MPTCP_PM_ATTR_SUBFLOWS: + name =3D "subflows"; + break; + case MPTCP_PM_ATTR_SUBFLOW_RETRIES: + name =3D "subflow_retries"; + break; + default: goto next; + } =20 memcpy(&max, RTA_DATA(attrs), 4); - printf("%s %u\n", type =3D=3D MPTCP_PM_ATTR_SUBFLOWS ? - "subflows" : "accept", max); + printf("%s %u\n", name, max); =20 next: attrs =3D RTA_NEXT(attrs, len); @@ -1254,15 +1264,17 @@ int get_set_limits(int fd, int pm_family, int argc,= char *argv[]) char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct genlmsghdr)) + 1024]; - uint32_t rcv_addr =3D 0, subflows =3D 0; + uint32_t rcv_addr =3D 0, subflows =3D 0, retries =3D 0; int cmd, len =3D sizeof(data); struct nlmsghdr *nh; int off =3D 0; =20 /* limit */ - if (argc =3D=3D 4) { + if (argc =3D=3D 4 || argc =3D=3D 5) { rcv_addr =3D atoi(argv[2]); subflows =3D atoi(argv[3]); + if (argc =3D=3D 5) + retries =3D atoi(argv[4]); cmd =3D MPTCP_PM_CMD_SET_LIMITS; } else { cmd =3D MPTCP_PM_CMD_GET_LIMITS; @@ -1287,6 +1299,14 @@ int get_set_limits(int fd, int pm_family, int argc, = char *argv[]) memcpy(RTA_DATA(rta), &subflows, 4); off +=3D NLMSG_ALIGN(rta->rta_len); =20 + if (argc =3D=3D 5) { + rta =3D (void *)(data + off); + rta->rta_type =3D MPTCP_PM_ATTR_SUBFLOW_RETRIES; + rta->rta_len =3D RTA_LENGTH(4); + memcpy(RTA_DATA(rta), &retries, 4); + off +=3D NLMSG_ALIGN(rta->rta_len); + } + /* do not expect a reply */ len =3D 0; } --=20 2.25.1 From nobody Thu Nov 5 07:28:13 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.177 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793695989; b=tICI95toq0GGsd4wjnXh1PGizjnvV20ZCkIe6qjSK8kb+dzfN6dSEi6fxqThuts/pymP 9uKxBEMdqjJHyJtaSBheDoxEYs5MnShwQ/0LNNpOTikwpPKFVkXc8U+kmH2JQbD9OhGMK MK8hRflnkjoPGlvfRej8BQzpsj0lOUEpnSx/6H+t9iqfz8JnBq5FtEalMRGasaElgLYyD 0Hof+vqH0duDV90lD8EG5AHg3J1QcvZ0X1YUB6AZ6vu2lzFRcGgADGWjqXgoDtyHPHtC8 cn/4Y+aruK9eYof4KZnV5nLZ8TRWyk+se4oS2h46YptOSgCI05o9e7G4yqi3001of1Q== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793695989; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:From:To:Cc:Subject: Date:Message-Id:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=jk7Y1AW17LZtNIUFq1ncquOOo12/Cf1+ZOKhEDQbK50=; b=emPg/JmJ5LK+K5bUY/0BhQIktZSe0+1lWY2IW9+E/JQxXR+s2P87ysj6paUi7DUH2Ro+ 7dcoG8uGsyLwN7c0w111S6fz3RYLY+tD2B3vyJKgjz05egQNxdA727vqz3sy5vhBPhtsJ PRN509hdZ4cXNjMlfTejdhA2CLtZjQG3+JRhNFcqpAEcqyJol0tfkhCODCy7rJrThnKjU 5P6lqYic/t5GTCvgsEyduMDix9BInj8xCkx+7+aovTwoGJDlZRO5HsGf3dvaabTkImL3v IRy29817An6GQ+pm8Gik6pP8pNczffasB13p2CAaTimVLZj2apGcl8YieYx5LnJIvUg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b="eFT4/a7w"; arc=none smtp.remote-ip=74.125.82.177 Received: from mail-dy1-f177.google.com (mail-dy1-f177.google.com [74.125.82.177]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 958B1606A324 for ; Tue, 3 Nov 2026 08:53:08 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 958B1606A324 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=eFT4/a7w Received: by mail-dy1-f177.google.com with SMTP id 5a478bee46e88-33fb4680717so6450937eec.1 for ; Tue, 03 Nov 2026 00:53:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793695988; x=1794300788; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=jk7Y1AW17LZtNIUFq1ncquOOo12/Cf1+ZOKhEDQbK50=; b=eFT4/a7w934PYn5rxP/jGvT8i6Se5GbUPS8xLe7DR9ICiFLkj0X19HIOgV2QQSMXxb x0HzMgXloIqbw21UzAkft5L86lfqF02xvs/+vw6soQNhhdeQ+KU7jK+LP/KJyWdlIUIQ npjlj0gK4a3zATm3EY9OSUSmFgkpn5nXLyVmoVaPfSqezrd8xS9yCEIP2yIlbigomZpb OjWtlG7FQszN2Pz5T227ms13alCfKuX8Pdw9EYelzOYLdn8Hi3+bAzmhsb5o6dpxl/kY ejPNdv6bzQeAPHw0cqB9CaPyR5WjnHo/7w2OMg6fVXW7VZbLJtnQB3Ast4Gifho5K+P2 pGvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793695988; x=1794300788; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=jk7Y1AW17LZtNIUFq1ncquOOo12/Cf1+ZOKhEDQbK50=; b=Ru2qXKbf6EvHKyslWGz66kh4w3P8Fi0MvcfCoD10NhR3MN9JNb/44yG+6zci8/s97I mIqummEmC4Nan18GtnMRPY6xg5ajd5yxyC6LpwoUkINFkfhxwKxm+AzJ9zfRaB5C78ly rQRbtvIU0gLbn5IRBopwcGAn5YGWyseFO/ckdEsLQNvRs0wV+jodRoBHHhxHDCFgdymU hLiZNlq2n8aNWz9MSPayE2jHHnyuscOQd4FkvtvncgJ0BlbHFh5SBAB6Dkk4nHgoZlA+ EuvoMU7GPGHBFN5gmRgSJUMitqOxlSOvWVp5VCnfsZgnfFoVQdJlrivA56+E7WiRV9nr d0wQ== X-Gm-Message-State: APuU4Z8ER0S5k3APo2j+ckwS8lKVd68PtmFXhhDZuJSdxPjjVD1xKOU9 kVlvQ9sk/oG5g3c0Nv8zg3JqxSLZFh7azUSJrsI+WSHqzevTiNGdNm3MgDnSrg== X-Gm-Gg: AYBFou14zH8v0dS060dv+I8sWlRLw0m8tpnJtZS2Mwwo/5pnO2GzUCBGNeSwDKEjS8B stLAftib9LH4NE4xoc+GkFvwsTrjoZp/VZHKsAUsSAnF3epSHWnFEltIlLj9BLzyhQYCuos0Qfd ifoDe3nB5VeRGfC/6A5t7ntslAZoTmyP2iZopqMiE+wm+cK7OpELntRM3S6WvWKQ4rSTLIaloRq wiy+EqHfJiP3K7cHDEJx2r0ABAcXFrRLpZmUhEbdk8JrSDfoP6WQC0Cu62bufA7QZtbKFOVU7V8 FqUPwA5ZdMe7R9RaZE1lGAMFcS7BruqY5ZrmGlwFV5iDDliaWflMBqp1atdxGsnScXAsGtsawDf DBnjyUOMbwgJ1bIjHrj9pxDZGcQ4odUKfWdV4gA6OSg3cgOM++DdjhtQp4ECa1pJB+ITK4InOlA tvSEG8TojElaFGmwBF52nWtqmBWSV9rADbJY+DlPptB4TdH3bAW3e1bBUHCMz9o7EXDxdtARNI9 uhljqEedpTWO09+nI0cK0Oau3iqe14n+EpDk75uzbT1UPV3SyiiiXjxUqhWR8B/wWkjs/qZInE5 3OXa7pOBfaXpBxInDyUZaztLSKrGw9a/96eVkJ7kebp3D3w4T0xZUco86xsw66+ui3S9b19jV59 XTf3I81u3Sy00ry/KT++ub8YftQuasZqmrthFdi/PBahfvUyv1RBos25vrtZtQl711Sn7vcGhzw == X-Received: by 2002:a05:7300:a8f:b0:368:4c93:544e with SMTP id 5a478bee46e88-36a422084d2mr19393341eec.1.1793695982738; Tue, 03 Nov 2026 00:53:02 -0800 (PST) Received: from ramen-dev-security-2.asia-south1-c.c.ramen-dev-345017.internal (90.156.200.35.bc.googleusercontent.com. [35.200.156.90]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-36a42a6f7f9sm53065635eec.25.2026.11.03.00.53.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Nov 2026 00:53:02 -0800 (PST) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v15 7/7] selftests: mptcp: join: validate subflow retry after link failure Date: Tue, 3 Nov 2026 08:52:44 +0000 Message-Id: <20261103085244.23064-8-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261103085244.23064-1-anilkaushikwireless@gmail.com> References: <20261103085244.23064-1-anilkaushikwireless@gmail.com> X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Test the subflow-retries limit, with its default value, and a long connection in the background: drop the packets the client sends from the additional subflow address with netfilter, without bringing the link down, until the subflow is closed and re-created. Let them through again once the SYN of the new subflow has been retransmitted more times than tcp_syn_retries allows, and check the subflow comes back from the same address. Then do the same with the initial subflow. A second test closes the connection while a subflow is being re-created, and checks the netlink events. Only the client sends data: the server reads it all first, in sendfile mode, and keeps its side open. The client gets a big MPTCP send buffer compared to its subflows, and both paths are blocked: the data left in it is never sent, nor the DATA_FIN, so only the close of the MPTCP socket can stop the subflow being re-created. Only the client is closed: it has nothing left to read, so the connection is not closed with a fastclose, which would reset all the subflows anyway. The check is skipped if a fastclose happens. The big files used for the long connection are emptied at the end. A third test checks the only subflow is not re-created: there is no other subflow to send data. A fourth test removes the address of the additional subflow while its packets are dropped: the subflow times out, the new one cannot be created, bind() fails, and the PM uses the next endpoint instead. The tests are skipped if pm_nl_ctl does not show the new limit, or if the filter chain cannot be created. Signed-off-by: Anil Kaushik --- Notes: v15: - the first test really starts with tcp_retries2=3D15: only lowered wh= ile a path is blocked, and restored on errors. It is per netns, so the other path has the short timeout during that window (netdev Sashiko) - the second test also only lowers tcp_retries2 just before the block - the skip condition in the commit message matches the code - the first and fourth tests wait for the last events before stopping = the listener, like the two others v14: - new test: the subflow cannot be re-created, the next endpoint is used instead - check the join counters before killing the transfer, which can send MP_RST, counted with -C (netdev Sashiko) - tcp_retries2 is lowered while a path is blocked in the first test v13: - wait for the SUB_CLOSED event before stopping the listener (netdev Sashiko on v12) - the first test stops if no additional subflow got established, the third one waits for the SUB_CLOSED event, and say why the tcp_wmem min value is above the max v12: - wait for the client side of a re-created subflow to be fully established before going on: the close would close it too otherwise (netdev Sashiko) - the third test only checks the subflow is not re-created, say only that (netdev Sashiko) v11: - the third test checks the only subflow is not re-created, and the path-back test is gone: a subflow is only re-created while another one can send data - the close test keeps the initial subflow while checking: without the fix, it would no longer pass when the initial subflow closes after a few retransmissions; a SUB_CLOSED event for the re-created subflow v10: - new test: the path of the only subflow comes back before the close timeout, and the connection stays open (sashiko.dev) The path comes back as soon as the subflow is re-created, with a 30s close timeout, longer than the SYN backoff, and its transfer has a longer poll timeout, with no data at all while the only path is blocked: both are needed on a slow debug kernel v9: - wait for absolute counter values, read before blocking the path, or 0 and 1 in the new netns: the subflow can be re-created before a later read (netdev Sashiko) - check that the connection is established before blocking it, in the close test and the new one (netdev Sashiko) - the close test blocks the other path too, with a big msk send buffer: the pending data and the DATA_FIN no longer close the subflow being re-created, only the close of the MPTCP socket can. Checked: both the close test and the new test fail without their fix - new test: the only subflow is re-created, and the connection is still closed after the close timeout - $sinfail as the server input, kill errors ignored, clearer failure message, $cout and $sout emptied too - the server of the second test reads all the data from the client first, in sendfile mode, instead of blocking on a FIFO in poll mode: the client always has data in flight (netdev Sashiko) v8: - comment in run_tests() matching the new condition (netdev Sashiko) - a separate test closes the connection while a subflow is re-created: the server sends nothing but keeps its side open, and only the client is closed, so there is no fastclose, which would reset all the subflows anyway; the check is skipped if one happens (netdev Sashiko) v7 (netdev Sashiko): - the test_linkfail=3D1/2 modes regenerate the shared files when they are empty, e.g. when running link_failure_tests() twice - read the MP_JOIN ACK counter before letting the packets through, and do not compare the ports, they can be reused v6 (Sashiko): - use the slowwait helpers from net/lib.sh - longer first wait, while the 128 MB files are created - skip if the filter rules cannot be set, check each drop rule (netdev Sashiko) - empty the big files at the end, cinsent too, keep the variables (netdev Sashiko) v5 (Matthieu): - skipped if pm_nl_ctl does not show the new limit - wait for the SYN retransmissions instead of a sleep, with tcp_syn_linear_timeouts=3D0 - helper for the check after the close - slower links with short queues, so the transfer lasts the whole test on any machine, longer timeout (killed at the end) v4: - default limit, no sysctl or MIB counter - more SYN retransmissions than tcp_syn_retries - close the connection while a subflow is re-created - check the netlink events - check the waits and the new subflow port, remove the big files at the end (Sashiko) v3: - check the subflow address, create the files before the background job v2: - one test, with netfilter .../testing/selftests/net/mptcp/mptcp_join.sh | 420 +++++++++++++++++- 1 file changed, 412 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testin= g/selftests/net/mptcp/mptcp_join.sh index c3ccce96b45b..82240addfec4 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -52,6 +52,7 @@ nr_blank=3D6 # These var are used only in some tests, make sure they are not already set unset FAILING_LINKS unset test_linkfail +unset server_in unset addr_nr_ns1 unset addr_nr_ns2 unset cestab_ns1 @@ -1038,7 +1039,12 @@ do_transfer() fi =20 extra_srv_args=3D"$extra_args $extra_srv_args" - if [ "$test_linkfail" -gt 1 ];then + if [ -n "${server_in}" ]; then + # the server reads all the data from the client before sending + # this file: it does not block on its input + extra_srv_args=3D"$extra_srv_args -m sendfile" + listener_in=3D"${server_in}" + elif [ "$test_linkfail" -gt 1 ]; then listener_in=3D"${sinfail}" fi ip netns exec ${listener_ns} \ @@ -1135,9 +1141,9 @@ run_tests() cinfail=3D$(mktemp) fi make_file "$cinfail" "client" $size - # create the input file for the failure test when - # the first failure test run - elif [ "$test_linkfail" -ne 0 ] && [ -z "$cinfail" ]; then + # create the input file for the failure test, when + # it is missing or has been emptied + elif [ "$test_linkfail" -ne 0 ] && [ ! -s "$cinfail" ]; then # the client file must be considerably larger # of the maximum expected cwin value, or the # link utilization will be not predicable @@ -1146,23 +1152,27 @@ run_tests() size=3D$((size*8192)) size=3D$((size + ( RANDOM % 8192) )) =20 - cinfail=3D$(mktemp) + if [ -z "$cinfail" ]; then + cinfail=3D$(mktemp) + fi make_file "$cinfail" "client" $size fi =20 - if [ "$test_linkfail" -gt 2 ]; then + if [ "$test_linkfail" -gt 2 ] && [ -z "${server_in}" ]; then size=3D$test_linkfail =20 if [ -z "$sinfail" ]; then sinfail=3D$(mktemp) fi make_file "$sinfail" "server" $size - elif [ "$test_linkfail" -eq 2 ] && [ -z "$sinfail" ]; then + elif [ "$test_linkfail" -eq 2 ] && [ ! -s "$sinfail" ]; then size=3D$((RANDOM%16)) size=3D$((size+1)) size=3D$((size*2048)) =20 - sinfail=3D$(mktemp) + if [ -z "$sinfail" ]; then + sinfail=3D$(mktemp) + fi make_file "$sinfail" "server" $size fi =20 @@ -2151,6 +2161,210 @@ wait_attempt_fail() return 1 } =20 +# $1: timeout in seconds ; $2: ns ; $3: counter ; $4: increment, 1 by defa= ult +wait_counter_inc() +{ + slowwait_for_counter "${1}" "${4:-1}" \ + mptcp_lib_get_counter "${2}" "${3}" >/dev/null +} + +# $1: timeout ; $2: ns ; $3: counter ; $4: value to reach +wait_counter_ge() +{ + slowwait "${1}" until_counter_is ">=3D ${4}" \ + mptcp_lib_get_counter "${2}" "${3}" >/dev/null +} + +# $1: client address ; prints the local address and port of its subflow +sf_from() +{ + ip netns exec $ns2 ss -tnH state established src "${1}" | + awk '{ print $3; exit }' +} + +# the client got the 4th ACK of the MP_JOIN +sf_fully_established() +{ + ip netns exec $ns2 ss -tniH state established src "${1}" | + grep -q "tcp-ulp-mptcp flags:[A-Za-z]*e" +} + +has_subflow_retries() +{ + ip netns exec $ns2 ./pm_nl_ctl limits | grep -q "^subflow_retries" +} + +# the client packets are dropped with rules in this chain +init_nft_filter() +{ + init_nftables + if ! ip netns exec $ns2 nft list chain ip filter OUTPUT \ + &>/dev/null; then + mark_as_skipped "unable to set the filter rules" + return 1 + fi +} + +# $1: client address +nft_drop_from() +{ + ip netns exec $ns2 nft add rule ip filter OUTPUT ip saddr "${1}" drop +} + +# $1: client address to block +# +# The link stays up, but the client packets from this address are dropped: +# the subflow using it is closed after a few retransmissions, and re-creat= ed. +# The packets go through again once the SYN has been retransmitted more ti= mes +# than tcp_syn_retries allows: the new subflow still connects thanks to the +# subflow-retries limit. +sf_retry_after_block() +{ + local before ack_rx syn_tx + + before=3D$(sf_from "${1}") + + print_check "subflow from ${1}" + if [ -z "${before}" ]; then + fail_test "no established subflow from ${1} before the block" + return + fi + + # read before the block: the subflow can be re-created right after it + syn_tx=3D$(mptcp_lib_get_counter $ns2 "MPTcpExtMPJoinSynTx") + # a short timeout only while the path is blocked: it is per netns, so + # keep this window short, the other path could time out on losses + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D3 + if ! nft_drop_from "${1}"; then + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D15 + fail_test "unable to drop the packets from ${1}" + return + fi + if ! wait_counter_ge "${timeout_poll}" $ns2 "MPTcpExtMPJoinSynTx" \ + $((syn_tx + 1)); then + ip netns exec $ns2 nft flush chain ip filter OUTPUT + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D15 + fail_test "subflow from ${1} not re-created" + return + fi + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D15 + # tcp_syn_retries=3D1: one more retransmission than allowed + if ! wait_counter_inc "${timeout_poll}" \ + $ns2 "TcpExtTCPSynRetrans" 2; then + ip netns exec $ns2 nft flush chain ip filter OUTPUT + fail_test "SYN from ${1} not retransmitted enough" + return + fi + # read before the flush: the next SYN can be sent right after it + ack_rx=3D$(mptcp_lib_get_counter $ns1 "MPTcpExtMPJoinAckRx") + ip netns exec $ns2 nft flush chain ip filter OUTPUT + + if ! wait_counter_ge "${timeout_poll}" $ns1 "MPTcpExtMPJoinAckRx" \ + $((ack_rx + 1)); then + fail_test "re-created subflow from ${1} not established" + return + fi + + # before a close, which would close it too if not fully established + if ! slowwait "${timeout_poll}" sf_fully_established "${1}"; then + fail_test "no fully established subflow from ${1}" + else + print_ok + fi +} + +# $1: ns ; prints the number of MP_FASTCLOSE sent and received +fastclose_nr() +{ + local tx rx + + tx=3D$(mptcp_lib_get_counter "${1}" "MPTcpExtMPFastcloseTx") + rx=3D$(mptcp_lib_get_counter "${1}" "MPTcpExtMPFastcloseRx") + echo $((${tx:-0} + ${rx:-0})) +} + +no_sf_connecting() +{ + ! ip netns exec $ns2 ss -tnH state syn-sent | grep -q . +} + +# The client has nothing left to read: it closes the connection without a +# fastclose, which would reset all the subflows anyway. +kill_client() +{ + local pid + + for pid in $(ip netns pids $ns2); do + if [ "$(cat "/proc/${pid}/comm" 2>/dev/null)" =3D mptcp_connect ] + then + kill "${pid}" 2>/dev/null + fi + done +} + +# $1: why the subflow is not being re-created before the close, if it is n= ot +# $2: number of MP_FASTCLOSE before the close +chk_no_sf_connecting() +{ + local closed + + print_check "no subflow connecting after close" + if [ -n "${1}" ]; then + fail_test "${1}" + return + fi + + # closed with the MPTCP socket + slowwait "${timeout_poll}" no_sf_connecting + closed=3D$? + if [ "$(fastclose_nr $ns2)" -gt "${2}" ]; then + print_skip "closed with a fastclose, resetting all the subflows" + elif [ "${closed}" -eq 0 ]; then + print_ok + else + fail_test "subflow still connecting after close" + fi +} + +# Slow links with short queues: the transfer lasts the whole test on any +# machine, and the blocked subflow always has data in flight. +sf_retry_init() +{ + local i + + # link 4: the next endpoint of the "creation error" test + for i in 1 3 4; do + tc -n $ns1 qdisc add dev ns1eth$i root netem \ + rate 4mbit limit 30 delay 1ms + tc -n $ns2 qdisc add dev ns2eth$i root netem \ + rate 4mbit limit 30 delay 1ms + done + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D3 + ip netns exec $ns2 sysctl -q net.ipv4.tcp_syn_retries=3D1 + ip netns exec $ns2 sysctl -q net.ipv4.tcp_syn_linear_timeouts=3D0 + pm_nl_set_limits $ns1 0 4 + pm_nl_set_limits $ns2 0 2 + pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow + # created here, not in the background job, for cleanup() + if [ -z "$cinfail" ]; then + cinfail=3D$(mktemp) + fi + if [ -z "$sinfail" ]; then + sinfail=3D$(mktemp) + fi +} + +sf_retry_cleanup() +{ + ip netns exec $ns2 nft flush chain ip filter OUTPUT + # big files: empty them, the next tests write their own content + : > "$cinfail" + : > "$sinfail" + : > "$cinsent" + : > "$cout" + : > "$sout" +} + set_userspace_pm() { local ns=3D$1 @@ -2585,6 +2799,196 @@ link_failure_tests() chk_stale_nr $ns2 1 -1 2 chk_link_usage $ns2 ns2eth3 $cinsent 50 fi + + # subflows closed after a silent link failure, then re-created: the + # additional one, then the initial one + if reset_with_events "subflow retry after link failure" && + continue_if has_subflow_retries && init_nft_filter; then + sf_retry_init + # lowered by sf_retry_after_block() while a path is blocked + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D15 + # killed at the end: the timeout only stops a stuck test + { timeout_test=3D600 test_linkfail=3D131072 \ + run_tests $ns1 $ns2 10.0.1.1 & } 2>/dev/null + local tests_pid=3D$! + + # the 128 MB files are created first, that can take a while + if wait_counter_ge $((timeout_poll * 4)) \ + $ns1 "MPTcpExtMPJoinAckRx" 1; then + sf_retry_after_block 10.0.3.2 + sf_retry_after_block 10.0.1.2 + else + print_check "additional subflow" + fail_test "no additional subflow before the blocks" + fi + # before the kill, which can send MP_RST (checked with -C) + join_syn_tx=3D3 chk_join_nr 3 3 3 + mptcp_lib_kill_group_wait $tests_pid + # the listener could be stopped before it got the last events + wait_event ns2 MPTCP_LIB_EVENT_SUB_ESTABLISHED 3 + wait_event ns2 MPTCP_LIB_EVENT_SUB_CLOSED 2 + kill_events_pids + + chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_ESTABLISHED 3 + chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_CLOSED 2 error 2 + sf_retry_cleanup + fi + + # close the connection while a subflow is being re-created + if reset_with_events "subflow retry, close while connecting" && + continue_if has_subflow_retries && init_nft_filter; then + sf_retry_init + # lowered just before the block: both paths are used until then + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D15 + # A big msk send buffer, compared to the subflow ones: when the + # client is closed, some data is not given to any subflow yet, + # so the DATA_FIN, which would also close the subflow being + # re-created, is not sent. The min value is above the max on + # purpose: __mptcp_sync_sndbuf() adds it to the subflow ones. + ip netns exec $ns2 sysctl -q \ + net.ipv4.tcp_wmem=3D"1048576 65536 262144" + # The server reads until the client closes, and sends nothing: + # the client has nothing to read, and the connection stays + # established. The transfer is stopped on purpose, its result + # is not checked. + : > "$sinfail" + { timeout_test=3D600 test_linkfail=3D131072 server_in=3D"$sinfail" \ + run_tests $ns1 $ns2 10.0.1.1 >/dev/null & } 2>/dev/null + local tests_pid=3D$! + + local not_ready=3D"" syn_tx + if ! wait_counter_ge $((timeout_poll * 4)) \ + $ns1 "MPTcpExtMPJoinAckRx" 1; then + not_ready=3D"no additional subflow before the block" + else + # read before the block: the subflow can be re-created + # right after it + syn_tx=3D$(mptcp_lib_get_counter $ns2 \ + "MPTcpExtMPJoinSynTx") + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D3 + if ! nft_drop_from 10.0.3.2; then + not_ready=3D"unable to drop the packets" + elif ! wait_counter_ge "${timeout_poll}" \ + $ns2 "MPTcpExtMPJoinSynTx" $((syn_tx + 1)); then + not_ready=3D"no subflow re-created before close" + fi + fi + # Block the other path too, so this data is never sent: only + # the close of the MPTCP socket can then stop the subflow being + # re-created. The initial subflow is no longer closed after a + # few retransmissions: that would close it too. + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D15 + if [ -z "${not_ready}" ] && ! nft_drop_from 10.0.1.2; then + not_ready=3D"unable to drop the packets" + fi + local fc_before + fc_before=3D$(fastclose_nr $ns2) + # before the kill, which can send MP_RST (checked with -C) + join_syn_tx=3D2 chk_join_nr 1 1 1 + kill_client + chk_no_sf_connecting "${not_ready}" "${fc_before}" + mptcp_lib_kill_group_wait $tests_pid + # the listener could be stopped before it got the last event + wait_event ns2 MPTCP_LIB_EVENT_SUB_CLOSED 2 + kill_events_pids + + chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_ESTABLISHED 1 + # the re-created subflow, closed with the MPTCP socket + chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_CLOSED 2 error 1 + sf_retry_cleanup + fi + + # the only subflow is not re-created + if reset_with_events "subflow retry, no other subflow" && + continue_if has_subflow_retries && init_nft_filter; then + sf_retry_init + pm_nl_flush_endpoint $ns2 + { timeout_test=3D600 test_linkfail=3D131072 \ + run_tests $ns1 $ns2 10.0.1.1 >/dev/null & } 2>/dev/null + local tests_pid=3D$! + + print_check "not re-created" + if ! wait_counter_ge $((timeout_poll * 4)) \ + $ns1 "MPTcpExtMPCapableACKRX" 1; then + fail_test "connection not established" + elif ! nft_drop_from 10.0.1.2; then + fail_test "unable to drop the packets" + elif ! wait_counter_ge "${timeout_poll}" \ + $ns2 "TcpExtTCPAbortOnTimeout" 1; then + fail_test "subflow not closed" + else + # the worker decides right after it reports the close + wait_event ns2 MPTCP_LIB_EVENT_SUB_CLOSED 1 + sleep 1 + local syn_tx + syn_tx=3D$(mptcp_lib_get_counter $ns2 \ + "MPTcpExtMPJoinSynTx") + if [ "${syn_tx}" !=3D 0 ]; then + fail_test "subflow re-created" + else + print_ok + fi + fi + # before the kill, which can send MP_RST (checked with -C) + join_syn_tx=3D0 chk_join_nr 0 0 0 + mptcp_lib_kill_group_wait $tests_pid + kill_events_pids + + chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_CLOSED 1 error 1 + sf_retry_cleanup + fi + + # the subflow cannot be re-created: the next endpoint takes its place + if reset_with_events "subflow retry, creation error" && + continue_if has_subflow_retries && init_nft_filter; then + sf_retry_init + pm_nl_set_limits $ns2 0 1 + pm_nl_add_endpoint $ns2 10.0.4.2 dev ns2eth4 flags subflow + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D15 + { timeout_test=3D600 test_linkfail=3D131072 \ + run_tests $ns1 $ns2 10.0.1.1 >/dev/null & } 2>/dev/null + local tests_pid=3D$! + + print_check "next endpoint used" + if ! wait_counter_ge $((timeout_poll * 4)) \ + $ns1 "MPTcpExtMPJoinAckRx" 1; then + fail_test "no additional subflow before the block" + elif [ -z "$(sf_from 10.0.3.2)" ]; then + fail_test "no subflow from 10.0.3.2 before the block" + else + # The subflow times out, then the new one cannot be + # created: its address is gone, bind() fails. + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D3 + if ! nft_drop_from 10.0.3.2; then + fail_test "unable to drop the packets" + elif ! ip -n $ns2 addr del 10.0.3.2/24 dev ns2eth3; then + fail_test "unable to remove 10.0.3.2" + elif ! wait_counter_ge "${timeout_poll}" $ns2 \ + "MPTcpExtMPJoinSynTxBindErr" 1; then + fail_test "subflow from 10.0.3.2 not re-created" + elif ! wait_counter_ge "${timeout_poll}" $ns1 \ + "MPTcpExtMPJoinAckRx" 2; then + fail_test "next endpoint not used" + elif ! slowwait "${timeout_poll}" \ + sf_fully_established 10.0.4.2; then + fail_test "10.0.4.2 not fully established" + else + print_ok + fi + ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3D15 + fi + # before the kill, which can send MP_RST (checked with -C) + join_syn_tx=3D2 join_bind_err=3D1 chk_join_nr 2 2 2 + mptcp_lib_kill_group_wait $tests_pid + # the listener could be stopped before it got the last events + wait_event ns2 MPTCP_LIB_EVENT_SUB_ESTABLISHED 2 + wait_event ns2 MPTCP_LIB_EVENT_SUB_CLOSED 1 + kill_events_pids + + chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_ESTABLISHED 2 + chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_CLOSED 1 error 1 + sf_retry_cleanup + fi } =20 add_addr_timeout_tests() --=20 2.25.1