From nobody Thu Nov 5 07:28:36 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=1792936156; b=hc0PWsxGOi0lGQrIcJkqYyCPg9QQgBd1zpG777O6KgoP3dWK3YO29+T/R0CctqUYIdmm JKCeIspbRKy0mEViucMfcgmDmpUtM/E+ZtQgj+BQhbvTsxdzMRWbiFYAl667w/l6a3Yge cVwb/2KOHm+Ij83m8mC83nILu5lF2U8oB0GvmFJp1aV+Gs5tzf0r94yCz052zfwaC9Jse +lPMAV75lL1LCarUR9t8iT3SnayXDvnwvtmjf29nfFJRug0/rk9K6uowNkDKGYDQC+tLC U0U4Xx6HjAKAq2ReDlGd3LGShsQmI9pQnHeA9/OljFxw2XAta0hdGtbryY9cpM2lq7w== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792936156; 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=KvEq34+Yf4dDq/T/EyGx6kouG+iVHJ1jZQO8J1NJeBI=; b=ceoEDy3e22JiwNBVE5+V/WlNASxxghCF4d5J4Ux/Ks8doxZXBaxYXVTVzgBisEodyJE7 ccMxsWsH8OCJCuXPAiINjDiLRoPXwVCvL6wUFzoYTdcFd1z+zDlcOEY0TybHKBX+iTJay Xwi7C8Wj3pWicC6k1I4TtrW2nPkdABfSY+wffnSP+IYxgJ40HvcCk2qqlfWr/0vb/5bJ9 TWVVA3C4dyL7HFfm+77/vP4Z/RB0CZlwCWXMGWta/RdK8nwn6Ssvjnt9OjyWCndAv9rZc Kdg0RXP1lOR6kjSZGxz4Tj4VlA7y/wGisPASt+qKiAMBX/3lugPLtBtXNOKtuIhnQCQ== 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=20251104 header.b=dh7bl+iR; 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 5DF74606A31B for ; Sun, 25 Oct 2026 13:49:16 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 5DF74606A31B 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=20251104 header.b=dh7bl+iR Received: by mail-dy1-f174.google.com with SMTP id 5a478bee46e88-369f33df08eso1288536eec.0 for ; Sun, 25 Oct 2026 06:49:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792936155; x=1793540955; 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=KvEq34+Yf4dDq/T/EyGx6kouG+iVHJ1jZQO8J1NJeBI=; b=dh7bl+iR9P2rdzphVp7vWnjz/3eMWIziizvJf0wTHON+QtNpQ8+1YAZcTPOjM/5nYo TEdHZ3dQjc4Gp4z4hXNbXb70Jj8wcVmqksbjga6wwbHxo9VXHqq4ZjNBCO2m87KWWprY 5ng4PV6zHBZfgEiTCEw8Zj5RLF9xbEML1BRstw/qsn963zbOPXsXtJdbE08fwrr0ziD8 KtI4Z7HnLBI/heqf+CX09QUSjjHe2kerwUeOE3qzHA6J4Xn3JIyuOXvenwduNAuFS8oI bcok9fiXbG3Atav+ZJdH7/Ir9T+8+Iu6G8fw7cmgU8J2UUwRlxwhkoekEnpGEwfSqEiV eZWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792936155; x=1793540955; 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=KvEq34+Yf4dDq/T/EyGx6kouG+iVHJ1jZQO8J1NJeBI=; b=kTO8eevQnyA8oYn5boYy8yv9rjy/sB+RnVlOomCeqwtKI1Utgdakq1PhRnYLUmHPHA 2AYvJ9S5gmFsgG+CAYGSzoQaVcRIvgveZO5nQtjdnRkDZdoCuNClDoAF7tw3bReBhDeO 49RNgCavxPt/USS5/jnSkD60Yj4IhawwThxVPxxqQ2TynG6jo8HSIQCTybOlGAAsLHkU HzyI8SMlGMcZl8naz++D4nVWnIlFUHr1Y1SqT49x4hSNPfflgPc9MQn6YOToQLWpMPOZ BFaZW3VB/hf2ArPZtpCAwSRae3FwyNSt143KQ63XWi+dTigrqYIdrjA4wdBwYLykwRok jDRg== X-Gm-Message-State: AFq9FYI9rCim5xTl0U3dH91KfJMNjfYraZSwmyfaDU1N3tbuGhzXPoEf icss/Tht6rzlU1TA7vCjz96eYHid8eS7GhwhnmRw6xmcfu3BhB53E3QXJqpYhg== X-Gm-Gg: AYBFou3m/HRUs1fn233y0+TVtsnPTsc101OZDjjdi1J/QhUYSBCJi91rHf68AgTBunF GxjlnJpWcH+Y4OYpMNFtgFYPcEuLfiwvfmjlRaJq3GlsGmbIdE5ul1Drzzuch4XCkKD4jdFMXmh CK7MEiysqeDHgHvFN+ElGAqRylqjUrikmXun00MqPDPOYpAs2ou520MWubcINNH6n/P/EynwLbj 6nGLZnN8BYBaU53VS9B3J4iG1uWjba71v1UFyqfpBV0wIG9wo9LA6ZgngiNHdH8f2clQZVWvZTn Kj6AxmB/ShiW9Vtw7PwkOL/YjCLyKasi5alarrmmIxouZtMkNJdwdxE+lDdvjmAziSpGJCTz3J5 OdizclQ0y5dRZo90j703o90q1N1yKG/M2LDrwDYu7EhtP7Id5B7YqNM5wz43K+HiRM9U0QdO9QL I9nErdawV4dGDS/Sbi6GSEr7AgG3wl+Wh6YOv7JfTheopDSrGcCFLkoKFA1b9slAbkw3uDdUJ3+ 1DRz/LNaLuOWKgFV5h/ks4N2NRko83C0qaWmP4K8nS5lInwmRrU5dCJFLFQJQmzo6sSRb+vZ1qN /Zw8bk4HWu6RgGnHl0co8feh0NO9olBld9vyVCVpuEcUCAF6n8Hm4ZPaQqnxhX+8HG5INrCMTeZ ye2EOEEnVw9o6fGodmLI3l027 X-Received: by 2002:a05:7300:4706:b0:369:f05d:6c67 with SMTP id 5a478bee46e88-369f05d6ea9mr4395763eec.12.1792936155200; Sun, 25 Oct 2026 06:49:15 -0700 (PDT) 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-36a002a486csm1188159eec.22.2026.10.25.06.49.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 25 Oct 2026 06:49:14 -0700 (PDT) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v9 1/6] selftests: mptcp: connect: retry write() on EAGAIN Date: Sun, 25 Oct 2026 13:49:04 +0000 Message-Id: <20261025134909.2794368-2-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261025134909.2794368-1-anilkaushikwireless@gmail.com> References: <20261025134909.2794368-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 retries, 1ms apart: poll() does not check every cause of EAGAIN. Fixes: 048d19d444be ("mptcp: add basic kselftest for mptcp") Assisted-by: LLM Signed-off-by: Anil Kaushik --- Notes: 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:36 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=1792936158; b=balwgSjCHlG2E9Pwm3biaxqlEVJ+V0VFVaZnG2rBt7Vd324Cx2i2eELkRgrinN5injdP gWD+YQJ6OlPmFRtTNF7YEGAPSejrdaYpNlGUdjoEZvDNCZ8CmsYQ50887jZFHuq+mb6d1 ItlmVChwJKC4kKuDYLV/BzG1UFRVfwPO/nIBPHhhT6NpM5rZQBai5nbU/thkTq6/FOUIz bGXB8/rERtrGLCfUGJp/pNiB6+LcoHGUJBVGpqYZvUqNgxDsCazmhGvF+0iZcVdMn7wwb W8KAe7yRjJXFUTxxp3A1TlILyxLoK93i1TxN2mbFWhlcbpU8mzJQo4cBtM/RLyUejKw== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792936158; 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=FoHLCgKqo6W+cZA4LHdCievgvvTPpTAKrPT7nHC+Dpk=; b=0MJk6bQiS3xRM3KcK83noB2heql+1YQDOFXK5LP+BZikgO76swlDx2dhJwLHKnrPwo6V 5WxnObuxE/uKH1AmccnUMgtoaaF4+H1Ccg8xB2q0kTMZWp6k8daULKmezSFvzvV1M+BE6 U2z7S6c2aguqn3USGbBuHRg0iteo/U0vIfXyBHAa/NCl8sM9dVZ7Iy+auvSgTEK5FQO20 nHl2hKM/5QhMFttwGCI2QUWY75OosFvjaw1eUP5599d3+yK7Em2mLDbJsT6l1gbJHB6kH 9rjhhPXnQl6eyjRI7q1iaHKwSH4A2b11m2A1uTrEk+1FGgxzOXHURZG/ZHZnWL7sE3w== 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=20251104 header.b=MVLxTF9g; 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 7F874606A31B for ; Sun, 25 Oct 2026 13:49:18 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 7F874606A31B 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=20251104 header.b=MVLxTF9g Received: by mail-dy1-f172.google.com with SMTP id 5a478bee46e88-35943f55c06so3018215eec.0 for ; Sun, 25 Oct 2026 06:49:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792936158; x=1793540958; 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=FoHLCgKqo6W+cZA4LHdCievgvvTPpTAKrPT7nHC+Dpk=; b=MVLxTF9gXJnLwZUCi0llIRSCTUJvpEvknR/PCfAAnHYb+OeeBYoe5bnKDbP8wpCsmz NNjEUINdX8CzclsREoDp94eR54cyWmH/N/lYub5liC2Gy0g2O3ohkyaHyMW9RtSqdBOu J8Op0RU9b3BpAaJW8slKDBEsDyNDsNahdxzXv9/E39F//NQBFztl9++mQqyrQuAZPvSq 323ezL0xrc4+kjc0iVpr0x59YVzDAaKI+SSjefuq9uhTkz/v9ASpMgWgQSlOZKtUM4vd v+K7cCBpfc+sQ6ilQlGA37s/iqF0vzLeU7grifgMJo0epXkxGC8LRDy9TlNOvk5sY+9J OKHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792936158; x=1793540958; 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=FoHLCgKqo6W+cZA4LHdCievgvvTPpTAKrPT7nHC+Dpk=; b=GSSRHzmFIGm2DKH8OS/rb+2GucTs8RQov1FpH/sz39rwDUbSLcJUV9J5/Id7URcxRM QxY35+icdY6lJBuiBw0tW/P7yNzCj2nLCjHgCzSA0AA0jOOQxqJcrnCyUoUIdfw7Kv0P EUfXSYr+wvMkw1/ENEve+bkSXH56ef5vmLT6C8Rck1ULo1i0An10hMMF8NHZzVnqq7o2 lhmd1uQD2h1T36WwkPCKjfOkDXS2MfU08WywSUx1DMfB3PTWXKqQmTHFVXRx7PvMqZ1L jojdYvONcEvhkZDDEzPFNhfCf6TOjp0bw9u8Vp8CGY3U38k2/DbPwlIm2SnKqevfhtq3 FTuQ== X-Gm-Message-State: AFq9FYJcD4ZaiM/hG5B51SocDQNQJfZPYMrUMK07uAN93ymWv9NddIN7 TbXV9KDZpqrReiqt2qux50KoHNGxAZwylhJlOhxOV9oKRNvSNwewo7feOA+6Vg== X-Gm-Gg: AYBFou2u3lrKBgnelPl4Owl0x92t/3QFzz5GkXu6R/Z2jfa5zKU/c2w/NI0xOlwZ7wY Qt3aWsJzZHxOtqvKMtXjcJxq6yvnE1ps61X5FLMQ5Rj+K2eh4d4hhwLy/i1LhpCdNhy6UthUhn0 I6t+e+y55jsRL0YpMBuFMVaxTQnFn53TXBMoj1OEojP225gn+CwoTT/SJKSVmR7B5L/xyqekwFU 7Zp68hxwnPFQDqyTtwzrylp/UPGiD8ACUPUzvQYCtZO2eF4cqiVWW+VPEn/5lkBFbcIf+W0lz1K 9CIHItD2OV2JmImQeJNixMMzHZ8PVUIoWAE70oZ4AoU+h0mzya2G5oQ1ogzzV83m/RcsNPfMxxE OxvMC5fMb6yRscd6NrPT7qNqdmgayW0wfMsYn2EfhCR0CbOMxIXzbNsnKzBv55QLqXdD6i8Q77y ZZWwEPay3vjQ4CQnb3goXjAdu1TnBLbQ/THsHS1hNhE/z2/68NNoU7hYnxtoggpR55C6W8nKKnx G9WhbPJitErMHfwzh581fyo5xJIQuD2n7kbIv4g8QPrmM09y/TmM3m4Fl597bdVaUvi12u9TR4z JT2z9cPfYd4LCWa/qOcMrMAHHgAQ+fsL1+OVOY667uA8Dz8SZa611LziiXVbZOVrXRlvXkuR9D5 7exHOJW22maZs0XQnC5Zy9Vn6 X-Received: by 2002:a05:7300:559f:b0:341:c383:9afb with SMTP id 5a478bee46e88-369e84fa1e9mr6554230eec.20.1792936157271; Sun, 25 Oct 2026 06:49:17 -0700 (PDT) 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-36a002a486csm1188159eec.22.2026.10.25.06.49.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 25 Oct 2026 06:49:16 -0700 (PDT) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v9 2/6] netlink: specs: mptcp: add the subflow-retries attribute Date: Sun, 25 Oct 2026 13:49:05 +0000 Message-Id: <20261025134909.2794368-3-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261025134909.2794368-1-anilkaushikwireless@gmail.com> References: <20261025134909.2794368-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. Assisted-by: LLM 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 39f3facc38e5..690d3c5dfdd2 100644 --- a/Documentation/netlink/specs/mptcp_pm.yaml +++ b/Documentation/netlink/specs/mptcp_pm.yaml @@ -196,6 +196,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 @@ -327,6 +337,7 @@ operations: attributes: - rcv-add-addrs - subflows + - subflow-retries - name: get-limits doc: Get protocol limits @@ -337,10 +348,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 c180930a8e0e..e2d4af1bc1d5 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 { .type =3D NLA_U32, }, [MPTCP_PM_ATTR_SUBFLOWS] =3D { .type =3D NLA_U32, }, + [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 { .type =3D NLA_U32, }, [MPTCP_PM_ATTR_SUBFLOWS] =3D { .type =3D NLA_U32, }, + [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:36 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=1792936161; b=zFqoFUYZ/P6V5AoDhkHpdHM8H7IM5Gh0oPh3uC3M+d12UF2jVnC7OWkBgEB8oCHBRzu3 ACIrT3Ppdq4wVQR9POUU9t2zPoiJKlTDNSsZEQT/W5neyJSY2jaJyPoCTvSY8cy48B44h SBW8ww+oCzQiUVRRsW/dHuKiuRnKGeRRngLvNOzDosjaNDisVrkXguY+TpuP+ACpCLjkU q6dg5Fyie0keOSqsYMlc0k0clcSAVa/ru28E4Rl19mE6OPjMsIUoQYecND0tOHu5zjQaf 6Exk7/V3kfvbPQ1ep8NxX3uSgdfq+1wDeDfUBIHL24o9eQdXo8UVk9lDBG6aP++ZX0A== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792936161; 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=QYrJBSfkXW+dXRK+TgyPz0qRu/ngvO9Nzw6QrGK4VQY=; b=ExB+rqJ62K3+EZ9UvTFfqoslHwXsXP68p6yxgUV9Z085ENz+sl0fynCB0jQT3CYw1Kw+ 0TD8zQNwz514OIzUpbL7QxhG14sE6qt6QHxKqmcUJsUXevQ2icONTc7MFzw9KXhjvRzNK AqJVFvGkgmH+wkAMmNejCNSRwIUxAlmJyGCZcacOyCSLBWbBGfj3rQkkHS51APqJ4JkN3 CtR6toDP4Szm0kI9/Sbd4lc1H680uXkb42U1/xm/zvk6VIfZ/sEZ4VZAwdTrPvFDOmszY xT3D0+6UJM0WaL8gdWgXE8QMzd+5jp0xvfAfb+aVX7J9eG1uOKn3WvgGpdTGOBO7azg== 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=20251104 header.b="JpE+/0Oa"; 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 E9922606A31D for ; Sun, 25 Oct 2026 13:49:20 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org E9922606A31D 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=20251104 header.b=JpE+/0Oa Received: by mail-dy1-f169.google.com with SMTP id 5a478bee46e88-35cd4e8bdbdso3877988eec.1 for ; Sun, 25 Oct 2026 06:49:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792936160; x=1793540960; 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=QYrJBSfkXW+dXRK+TgyPz0qRu/ngvO9Nzw6QrGK4VQY=; b=JpE+/0Oa7VNZZeFTF86XOOXv5HyAS0FcgJK7sSIsfoUWGL9bZ9Jdr/VSM8Svo4pYA4 TnePfLnoqYNT9fnxA/sn2iSWVWrrdR/tfG+uVUlC7EB3xcJVqr+UAj7Qu4lnV1zSGUnG 8ULxpfnDSPohCUxnQxn/Bt47vr3K3AQTRrZyYlBmajmNlTsK8VSJup9BTmc2Ytb11Mgf jCPjQ3X9YFLB9VT9re+KTiEw2NXdD850ysvmYdZZkxOPqIDIIrsDPunMFA3s+BH63I7H a5JqEx4QzKLpGoVH4ZR/JVup83tCr6ungpeeqSQQLZ5ssAmNWK8Xki+M8WzPOtTX+5l/ zhxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792936160; x=1793540960; 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=QYrJBSfkXW+dXRK+TgyPz0qRu/ngvO9Nzw6QrGK4VQY=; b=CRHRG+GR/eOoH6vKoOdJm6gaIe2CR9Z58yMuItdUU+AtgIv4Jt7p789UWjB6z4yZE8 RbCM16/9QoKMTGTEuOEm5p4YvQbMj4npUbiy0Tr9xwbXGLAatSkpjb2iDqP5ZIYRnR/B v6sl5byx64skKVGbDwYTky5nSX4cSAzVCW2Wj823hqJ47VrpaxmV34VidTsjHBCqcwCa /a/VnYfqvcfseIZLw94J/rf75gOkqM8SEjB2rwA5LAH7YxHkxzKlnF3KzkV8sj+WpLWA 2eAZ8luuEWVc5d4JlXd6ZUN54dIvVmUxHWGrzwCzdra0tedkb9/POwQVASZa6r26hwD/ /lXA== X-Gm-Message-State: AFq9FYJikacSoST0XUzzIk+Y3tr9uTw1jPhb1S6KY94jWL/dCocKXBy+ nvwTY5tgLvW8kcZHssWJbIp1g4K/5ZTjKmmHKjJLy5FWkDsapc22s1l8Ux1zYg== X-Gm-Gg: AYBFou2PCPXeEMyZQ5/A5l/olIvzesB0adySqMzCN0cXCTN92IxFSxdSwVTjEDJP/fk 0vO7qQmVJtuAN7BSl+8cMHcgqGFW+g6NKXzQNYu4iwXWjUkyLmU30HaWNdZn1n7lLw9gNQl/74F u2HXVVwE+R9tVaG6/yUb0J4qLJo1jf+CHQbKlPS044OwKhPj/rkpQQWrd5898gqizIpKqEZogXQ W+2RFV7dJHV0Er26ZAcO5T0zGyv991cCJ2jJRFaEo3Gr3m0So+p2sxqZxqPwuJE2/teWZe5U0lS pq0bKSAY7ui5HhH6YmG9XmXcW12NQ9yFmXp9061iOZzRxdw4JrZGVYPOzYHt/PhB6qzpl4aqZaF Fza8/UEki5gOodU8DslKXFU/oOXawYWX3MRXL3Hdu1yzWZszY5+YfIzaFEa52+oWX6ic8IpuDpg mujjK8C8kmTurmt/hYsFHO3YvB1LDGlZVFHAiARh8Qdm2VcOaQItXhPT7LrOmnPt9jkG1ayy82A fSziXdi3mbZ5Foy/7mD7rV7HcfYgtd2q4W4eauqghCgM3br7QClhyygqtzNmr88nsJyOBuceHRK IVND1tux8L6s5I0Y9M5+dfyRalLPmVuF3he5P4cT16wVivC5zfOBX3x36LN8+qx6VuSuKF+W059 O5kL0z0yQ2VaFQKt/YfWQssUR X-Received: by 2002:a05:7300:5724:b0:369:fbc5:ce29 with SMTP id 5a478bee46e88-369fbc5cfd4mr2489017eec.38.1792936159268; Sun, 25 Oct 2026 06:49:19 -0700 (PDT) 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-36a002a486csm1188159eec.22.2026.10.25.06.49.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 25 Oct 2026 06:49:18 -0700 (PDT) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v9 3/6] mptcp: pm: in-kernel: add the subflow-retries limit Date: Sun, 25 Oct 2026 13:49:06 +0000 Message-Id: <20261025134909.2794368-4-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261025134909.2794368-1-anilkaushikwireless@gmail.com> References: <20261025134909.2794368-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. Assisted-by: LLM Signed-off-by: Anil Kaushik --- Notes: 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 | 23 ++++++++++++++++++++++- net/mptcp/protocol.h | 1 + net/mptcp/sockopt.c | 2 ++ 4 files changed, 26 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 d3014bf57bf3..7fb4c7874340 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); }; @@ -96,6 +97,14 @@ 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); +} +EXPORT_SYMBOL_GPL(mptcp_pm_get_limit_subflow_retries); + static bool has_subflow_daddr(const struct mptcp_sock *msk, const struct mptcp_addr_info *daddr) { @@ -1419,7 +1428,8 @@ static int parse_limit(struct genl_info *info, int id= , unsigned int *limit) 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; + unsigned int rcv_addrs, subflows, retries; + struct nlattr *attr; int ret; =20 spin_lock_bh(&pernet->lock); @@ -1433,8 +1443,13 @@ int mptcp_pm_nl_set_limits_doit(struct sk_buff *skb,= struct genl_info *info) if (ret) goto unlock; =20 + /* the maximum, MAX_TCP_SYNCNT, is checked by the policy */ + attr =3D info->attrs[MPTCP_PM_ATTR_SUBFLOW_RETRIES]; + retries =3D nla_get_u32_default(attr, pernet->limit_subflow_retries); + WRITE_ONCE(pernet->limit_add_addr_accepted, rcv_addrs); WRITE_ONCE(pernet->limit_extra_subflows, subflows); + WRITE_ONCE(pernet->limit_subflow_retries, retries); =20 unlock: spin_unlock_bh(&pernet->lock); @@ -1464,6 +1479,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 @@ -1618,6 +1637,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:36 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.170 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1792936164; b=zV7iUiDT86sQJ0YCgxeiR+jQ0TPdT1woxHdGDDJVlXS/LjQM1SqjkzwqKfBGSChXAYcb if5LXhblFp+S2SopuzaqOl9wMwcKvMkaUn6gnEGaAvtl2ndeC8p9Z0Rm4P/D2Av81xiSV 1DmIDVEXNiqUKKKMCUTG7x0aQjHno+LwK1G8lElCDQ28tH42ZLOhFG8CT8QKcq9BgVh2A wbIPNgeuyJSMkTZw9S8C12zP9m0W+Drno2QjlHLoHMjYG/Jjsnzaxs/gec8/sKUdXDfSf e+s8t44b2BcDuKzS1dxuT/+gagDXzeS8hFM2vop6Rpuu+HP0CJwD9Vco4+boduqlmOg== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792936164; 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=1BvY0DXxR9CJgs5pHYqiq9XbQfPxHYyDZlUcLyPbzM8=; b=vJ1L7Qi9+C5s/Nv7rh+5wNF1xApR7tHKHkguL72yuvJV9Kn+/tDX34DTnUT1s+hYWmZR GN/punYVTocS9V+MvMZRK+wuNcNS1UguMk8RFZ0DaGvxrjQ8CxjMQ63YZwLDLgfonhxBf 2Bs8qTeWIuzljb2P549hFQpThIcirCWqNCHrxxKugWHXUAvaxlFBpbRDU1QNz7gVwJqL4 AJgEDi20BVBxQ5h/bnNwp5axuDGeIvflEUa0iyzl0aTBIotcsKTPVdR0O7oKWwUASDXVV 92YfZxJ5AcZTVHkuP289kLJOrL/pRaZ/dS7RhPBOJeNt59RkUVS1BhHRcOyBWnc0/Sg== 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=20251104 header.b="PU0/RIHA"; arc=none smtp.remote-ip=74.125.82.170 Received: from mail-dy1-f170.google.com (mail-dy1-f170.google.com [74.125.82.170]) (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 51082606A31B for ; Sun, 25 Oct 2026 13:49:23 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 51082606A31B 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=20251104 header.b=PU0/RIHA Received: by mail-dy1-f170.google.com with SMTP id 5a478bee46e88-36661f953e4so4246387eec.1 for ; Sun, 25 Oct 2026 06:49:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792936162; x=1793540962; 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=1BvY0DXxR9CJgs5pHYqiq9XbQfPxHYyDZlUcLyPbzM8=; b=PU0/RIHA0ctI7tt9Bpg93YUSZ+qreyIVwQsa5nbC+8gjjZWWpesJg3phQn87mXlyAk RDO/tX3B8b/h8X1gVbxwiFOHPnuRGOztd8mTSvebA+fnZ/pf9zN26MgY5Hlkp87JjxxW 58QtW56L+hLHlW8Wt7VCeWKTuc5t+MQMBNS9/bCtfHdGr/qqOBgvk9B4Ulgyi+Zt92GP WcsATGTQofrLSxLrOUlMBQwihcd8cUGBRlP5iCv+n6VJpe8UgvI4tbO18JxSS3ZB931Q NSkSTmf7k2NdrLi89l+k0MvjpDiUCs29RbtLwBntfHnt1a/f9Df13zmhBO9+PiRqTUBK SeHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792936162; x=1793540962; 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=1BvY0DXxR9CJgs5pHYqiq9XbQfPxHYyDZlUcLyPbzM8=; b=muOGlMWX5ReBGpSLlkYYQLs3pCK5zgTSYeLifChu3sScj4OtQejiBWtmY1y9fFfcQW NsnU8VSN5U0e8yA16Zw1idGJ6duh9GYNtWOipe2awwgMh6Ns2yWv0VUV5+ZoSz2tGf/p +Pjh1mqvxlegVYwKj3uuKo56sfrfZCs4bRoIiay9vi8ydIaLxCAdkyzgnOJCqeEK9Bkw S5IyacncayVAhp6vF8at8I7lW7lYZLva5urnvyJ9bgCbw/nY0sMLNGIaiSE6cO3sKg7K EuTaXIhNwwI4JGhBMtowuCUcNPZy+JiPCLqnNljm8bu83X9smrBv3rV2j+U9w3zjzSP0 2Kjw== X-Gm-Message-State: AFq9FYLO5HGByVwPr3wtYjKNyhyoSa5PpiEyGUIEpf/6jic8EWFKYuJn NASljJZZ3lkUtXxhyyqs2X2wSNdecmtKEsaTWHyYPaCBnI9lhpc7EdvSke8kZw== X-Gm-Gg: AYBFou2S6LM9CDYZ9KkaetvjYKw3nRdIXxOra4nLcykdpLjBw4penZZwNE5i+iTwm3F l9P2hO+dhUv/TJM8ZokVfrO29vyPSELDWkMT4emOjzNs8oge2VO7wxMUrHs0xHtrBjG+Zf1TE0I A8SOqVnHeKFHzVPYcsLYsVEc6uGTKm6IVR5c99JVNsgiQqTRLtuG6Acgw77f1bM+qPKRkPh3+iy TkFoN4ioMLVsK4ExbFn5nrZy7FPZqh+aK6fFTDvflG13i73KKhMzOB2mPbzBCFhNueK2aFKmR/h Sj8VGn6ZsjAMEfRKF389GtQULpxu2xPwWgBiDAyNZIfLEVyWpUut5VISsUigrTjxC1XVvNlUqri D6qEb5OOrZZtj8SnJmd4rt/m9BHPgaZAbyPoMNYCjDibEDv4oCwGPvRC6aCso8e7z1x9Rbe2EG0 HK0zTIYrygkt67BTcRej0OEPTy4xTW/1vak0+DFkzz4iyDICcOAO6qMLUOZ2odMXEleiMOzJ2c6 RKRZtLmGNT/rfWZHXIsPHpzWTEPWhlKIeJwujVqcJElPeY2MbZ+0pTl7Lfc9Ni3PR2iz6pH7B5u eGe9TtimwM7AM2vhYacl6/SgMPrRRDSQimskxO0DKoS0nYMwumIW2Ct63Wh5tms+aRbe9yYZNqp noYzOb0IFC0q4liYNIkzqXk0S X-Received: by 2002:a05:693c:66d4:10b0:35f:227b:f8bc with SMTP id 5a478bee46e88-369e84936cfmr6715653eec.11.1792936161925; Sun, 25 Oct 2026 06:49:21 -0700 (PDT) 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-36a002a486csm1188159eec.22.2026.10.25.06.49.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 25 Oct 2026 06:49:21 -0700 (PDT) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v9 4/6] mptcp: pm: in-kernel: retry subflows closed after an error Date: Sun, 25 Oct 2026 13:49:07 +0000 Message-Id: <20261025134909.2794368-5-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261025134909.2794368-1-anilkaushikwireless@gmail.com> References: <20261025134909.2794368-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. If no other subflow can send data, the connection is still closed after the close timeout, as when all the subflows are closed, unless the new subflow connects before. 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. __mptcp_close_ssk() now always cleans the references to the closed subflow in the MPTCP backlog, not only with MPTCP_CF_PUSH: a re-created subflow closed with the MPTCP socket can get established in between. The other paths without MPTCP_CF_PUSH have already purged the backlog. 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. Subflows aborted locally, also by MPTCP, or using an address that has been removed are not retried. TCP only closes a subflow after tcp_retries2 retransmissions, 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. Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/440 Assisted-by: LLM Signed-off-by: Anil Kaushik --- Notes: 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 | 18 +++++ net/mptcp/pm.c | 39 ++++++++++ net/mptcp/pm_kernel.c | 118 +++++++++++++++++++++++++++++ net/mptcp/protocol.c | 77 ++++++++++++++++++- net/mptcp/protocol.h | 19 +++++ 5 files changed, 269 insertions(+), 2 deletions(-) diff --git a/Documentation/networking/mptcp.rst b/Documentation/networking/= mptcp.rst index b6753ffb9c9a..8a2c9b9d2a4f 100644 --- a/Documentation/networking/mptcp.rst +++ b/Documentation/networking/mptcp.rst @@ -73,6 +73,24 @@ 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. This is a single connection attempt: an ICMP error, or= a +local error when connecting, ends it, and it is not created again. If no o= ther +subflow can send data in the meantime, the connection is closed after +``close_timeout``, as when all the subflows are closed, unless the new sub= flow +connects before. A subflow reset by the peer, or by anything on the path, = is not +created again either. TCP only gives up on a subflow after ``tcp_retries2`` +retransmissions, about 15 minutes by default: shorter outages are handled = by TCP +itself. A subflow that only receives data does not notice that its path st= opped +working: the application should then enable TCP keepalive on the MPTCP soc= ket +(``SO_KEEPALIVE``, ``TCP_KEEPIDLE``, ``TCP_KEEPINTVL`` and ``TCP_KEEPCNT``= ), +which MPTCP applies to all its subflows. + Packet Scheduler ---------------- =20 diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index d7c5b50b34cc..2bb08be95cd1 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -694,6 +694,26 @@ 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) +{ + if (mptcp_pm_is_kernel(msk)) + mptcp_pm_kernel_retry(msk, retry); +} + void mptcp_pm_add_addr_received(const struct sock *ssk, const struct mptcp_addr_info *addr) { @@ -762,6 +782,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 +832,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 7fb4c7874340..de84aecd6a62 100644 --- a/net/mptcp/pm_kernel.c +++ b/net/mptcp/pm_kernel.c @@ -1610,6 +1610,124 @@ 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 ICMP error received 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; +} + +/* 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 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; + + return true; +} + +/* Called with the msk socket lock held, after the subflow has been closed= */ +void 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; + + spin_lock_bh(&msk->pm.lock); + allowed =3D msk->pm.extra_subflows < + mptcp_pm_get_limit_extra_subflows(msk); + if (allowed) + msk->pm.extra_subflows++; + spin_unlock_bh(&msk->pm.lock); + + if (!allowed || + __mptcp_subflow_connect(sk, &retry->local, &retry->remote)) + return; + + /* 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; +} + /* 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..25fd0628686c 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2697,8 +2697,11 @@ static void __mptcp_close_ssk(struct sock *sk, struc= t sock *ssk, lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); subflow->closing =3D 1; =20 - if (flags & MPTCP_CF_PUSH) - mptcp_cleanup_ssk_backlog(sk, ssk); + /* also without MPTCP_CF_PUSH: a re-created subflow closed with the msk + * can have been established in between, see + * mptcp_close_retry_subflows() + */ + mptcp_cleanup_ssk_backlog(sk, ssk); =20 /* Borrow the fwd allocated page left-over; fwd memory for the subflow * could be negative at this point, but will be reach zero soon - when @@ -2788,6 +2791,32 @@ static void __mptcp_close_ssk(struct sock *sk, struc= t sock *ssk, } } =20 +/* With no subflow left to send data, a re-created subflow still connecting + * must not keep the connection open longer than the close timeout, as when + * all the subflows are closed, see __mptcp_close_ssk(). A join stops it. + */ +static void mptcp_retry_check_tout(struct sock *sk) +{ + struct mptcp_subflow_context *subflow; + struct mptcp_sock *msk =3D mptcp_sk(sk); + bool connecting =3D false; + + if (sk->sk_state !=3D TCP_ESTABLISHED || + inet_csk(sk)->icsk_mtup.probe_timestamp) + return; + + mptcp_for_each_subflow(msk, subflow) { + if (__mptcp_subflow_active(subflow)) + return; + + connecting |=3D subflow->retry && + !READ_ONCE(subflow->fully_established); + } + + if (connecting) + mptcp_start_tout_timer(sk); +} + void mptcp_close_ssk(struct sock *sk, struct sock *ssk, struct mptcp_subflow_context *subflow) { @@ -2806,6 +2835,7 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ss= k, mptcp_pm_subflow_check_next(mptcp_sk(sk), subflow); =20 __mptcp_close_ssk(sk, ssk, subflow, MPTCP_CF_PUSH); + mptcp_retry_check_tout(sk); } =20 static unsigned int mptcp_sync_mss(struct sock *sk, u32 pmtu) @@ -2823,6 +2853,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 +2866,13 @@ 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); + /* creating a subflow stopped the close timeout */ + mptcp_retry_check_tout(sk); + } } =20 } @@ -3501,6 +3539,38 @@ static void mptcp_check_listen_stop(struct sock *sk) release_sock(ssk); } =20 +/* Re-created subflows still connecting are useless once the msk is closed: + * they would keep it around until the close timeout. + */ +static void mptcp_close_retry_subflows(struct sock *sk) +{ + 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); + int state; + + if (!subflow->retry || READ_ONCE(subflow->fully_established)) + continue; + + /* still connecting, or already disconnected by + * mptcp_subflow_shutdown(), e.g. after shutdown(SHUT_WR) + */ + state =3D inet_sk_state_load(ssk); + if (state !=3D TCP_SYN_SENT && state !=3D TCP_CLOSE) + continue; + + /* Checked without the ssk lock: it can have been established + * in between. __mptcp_close_ssk() cleans its data in the msk + * backlog, and the MPTCP retransmissions cover what it sent. + * No need to push the pending data again. + */ + mptcp_pm_subflow_check_next(msk, subflow); + __mptcp_close_ssk(sk, ssk, subflow, 0); + } +} + bool __mptcp_close(struct sock *sk, long timeout) { struct mptcp_subflow_context *subflow; @@ -3516,6 +3586,9 @@ bool __mptcp_close(struct sock *sk, long timeout) goto cleanup; } =20 + /* before __mptcp_wr_shutdown() disconnects them */ + mptcp_close_retry_subflows(sk); + 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, diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 4aa4aedb2ba7..d47430fc7dc8 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; @@ -1139,6 +1141,18 @@ 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 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 +1274,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); +void 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:36 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.181 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1792936165; b=pf3t6DapOZ0jt0bsiZo22zv9/q7OfT1q7Dm5Iv8XLalsZYdtH1lxhvKo4AlbWi4LqYly rnM2ahkZe8d7dp0glMcGhT3TaRPnmQ4nV5F1q4LHSdvT3qO6PrRCnYbQ/pbtEe0FTsloo jJi2ghyuq0yoh0Ebqe1difqS5ytzpO3xyT+HewY4pXvqYCkbUxoABE1tnmnupV1DEcdGu z8aUvu0ek2+GaleGzODv2j4+2lh/wF7JiOi/owpQp6sXzdTy9Q0nY5vB+7InlT7jwSzd5 SAn7pMNFseuk7F/VY+/TGO47wnIL2ynx4qnHi0hicOJcVQ5AJUrvm1TUL8BFd8uBw4A== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792936165; 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=KhC19Luu2JMGOYfja02bJsBK5t5EOeLtyB/kLQhB3HA=; b=LcKsri5hvXqpF9ZFhkLOspYXoqQ0ekfS4HcVwrC3PtgHVlpk6p1/TEqTWJqMnBodceZm 7dmZQAWCmfIt8nh3CFGyUzP35LSE8H22QQ8oxR3o1NSvvCX0QP6N8m3YuHu+m3/wwYIZQ V7lVL91JUZ1nsYpj5oCpgSop0YOXyrVKnrFd3dOGEt0XnZ0jWpr1Xd8syWBirEQCqUe8T atVsJZ4xD4no8tuFTtTtk3XK/nW5XrgSzZAuGFvLb9QV4XtEMk00d1+sfqUOAcHMO/mUh 05vfsyGVjSbrIIEQ34KlBGbl8Kqgn4MSjUw6RUWtGtDj8myWYoqBwQpGF8+05hW8FdQ== 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=20251104 header.b="fr/1PCn7"; arc=none smtp.remote-ip=74.125.82.181 Received: from mail-dy1-f181.google.com (mail-dy1-f181.google.com [74.125.82.181]) (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 0AF29606A31B for ; Sun, 25 Oct 2026 13:49:24 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 0AF29606A31B 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=20251104 header.b=fr/1PCn7 Received: by mail-dy1-f181.google.com with SMTP id 5a478bee46e88-36661f953e4so4246413eec.1 for ; Sun, 25 Oct 2026 06:49:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792936164; x=1793540964; 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=KhC19Luu2JMGOYfja02bJsBK5t5EOeLtyB/kLQhB3HA=; b=fr/1PCn7f/Fi7YB1hrG+fh8/a4+M68qBd/1sZtwxbIevmPQv+Mg6pDi6JbHGArq9PO X38BNJ1dYP1rbi+7SeYHyhoF5lrHpdc456GCYaQhQU5uPDfxE3BD2n2kJtxOLGG09dnM qfXi2wgfjCwNncC7gcWRKoGYqvUcXO578YSd8TDcTaaGaXU3EOdO4QqpQmgI+YYzwqLr tSOjy9P/TMYuPJuepzM+93Xjpl7iqokIFtS3oiFwUu0VtB0YIugsA8jzdW6iNAYccLkF E7Wn7jpIFPGfKbbnc2NjuTzZoxNRqpRktdrRpveIui76xPWLTkoDyioM9covuT2x0YLq dKDA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792936164; x=1793540964; 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=KhC19Luu2JMGOYfja02bJsBK5t5EOeLtyB/kLQhB3HA=; b=fx2ItuZgVDWNBKz0k6yRhIzMf8lb7+YmEJRvkgOFK24GfCPL/znzNyI4pvwjwVVRt7 fvHuz31ZorhuVOxdjEEfLBFYYRWhtw3yy+tL+DnPDwLGxNIRugT0mkT58scxt9kUyGIQ oJzkrKKvp0A5+XSHEyt1w7lEu57yJUwDgVtHVpRB4dPZs6jCug9jjUELFg4yWRDM8d6x KEddmREIwgBc54lejWdw3BwOQWGvKCTsCIYUl/VVPMCYuLvHlK8dxZN4TwPPNslJwHh6 g1tUqD5VmzHZAAKkfjw+K4smPBLEgBiPZqUcv5krY5rNhuXH7Qg9VLP4jIbPisef+ss2 RblQ== X-Gm-Message-State: AFq9FYL9E4uqjS5MYUzJdOB1ZcFFcdEsIZflMpdxbE8Pei8a2APupyKG qDthg4XMPnXxGNYkyJQN9aiihPeBfLz2Vw79nvrC45+6sItn4EpG79i6Wph6eQ== X-Gm-Gg: AYBFou3EK+U8Um/hnH6a/QW2ogR/qQ6uC1yyiLnjDqx7SWaCnONu2Uzct8jzjsmS5yb zwewoqhP0ktyGPIgQc+tNM9/S9q0hqIL3xtfZyWNE6yc7ODuyyWIg3uhXNItX+TngbuWjHIAuO1 abrnciX9GZT6tULPbkexGLMvrNZf5v6kWLJlGKWw137aSM1fNut2Z3sWDcOum2vvpK01nQiQdes yKvaWgzJ/3naIjR17N+dT6zC0yWafG63qQUpLTQzJCz4J+M8Ras68zP2w891B0MnWRGt4tQZA/5 4nsuy5LRUZyVTuC4rpnoHkmjxsEixAgJInF4DQQEARAahMDIYZKyhVOyP1mou+b+WCW587UfP7G 4EKevpn7Gh/iqpbJPKTZsYpZXZdJSxdj2SXyw/5qRTlPQLPhlludCWHvGei1IX6TJgbNTkcJGEp TCeXUrKvKRo42Q56+ardGM3U8aU2Lm9zbw0UhOhIIYD0oSexhploRaPq5n58JyJDHpPvV+Lb1dG kcR+U+qB9PzfWoaaz5Z4sCysXeybQpH3paca0lQTnRi71o59UXEVgzCPrZasXU70/ILZxIxzQnK I73qXhKyqDuvPwYl8msu+oRG08kyYloJsVQSqEYwyXZb7IlamPtBp59hAHlxj4OYf7DgF5c9+r7 FP2e2gJL/STMxcxynnoxnJPLJ X-Received: by 2002:a05:693c:68d2:b0:369:f3c4:e1fa with SMTP id 5a478bee46e88-369f3c4e87bmr3303481eec.24.1792936163871; Sun, 25 Oct 2026 06:49:23 -0700 (PDT) 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-36a002a486csm1188159eec.22.2026.10.25.06.49.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 25 Oct 2026 06:49:23 -0700 (PDT) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v9 5/6] selftests: mptcp: pm_nl_ctl: support the subflow-retries limit Date: Sun, 25 Oct 2026 13:49:08 +0000 Message-Id: <20261025134909.2794368-6-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261025134909.2794368-1-anilkaushikwireless@gmail.com> References: <20261025134909.2794368-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, and that it can be set. This is only done with pm_nl_ctl for the moment: iproute2 does not support this limit yet. Assisted-by: LLM Signed-off-by: Anil Kaushik --- Notes: 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 | 48 ++++++++++++++++++- tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 34 ++++++++++--- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testin= g/selftests/net/mptcp/pm_netlink.sh index 21bfe1311f11..9a6209181962 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 @@ -75,6 +80,22 @@ get_limits_nb() { fi } =20 +# 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 +} + format_endpoints() { mptcp_lib_pm_nl_format_endpoints "${@}" } @@ -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,28 @@ 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 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:36 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.82.170 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1792936167; b=w9OxrEJhWPaVy/Cqjw+B4M1zqVITCQPi41Y+SDFWZL7qC9JKRkFxgPgYD/tILj5KOyCR c20ocNDQTJnoEukE44n2rPZSpE4Kup9DxdZ0lPGNWlR4LMA0t5byZS0Nv0nuzrCBm3BEB /fjrQXAnGiKHCtPH7OhnMhDjBGnQVq/EQHuK9WLAFRzVNtdabEC7eS7XfEX0J/4Ot8q4c /xbV/SjkI+0fuMXxflkJG0eT/V6cNcJDYqlwMBh/VZDzcCnA5UNDMnvE4TqRV9ZWbLOva N6oHZ3Eztg24828r4X6uMAcMw6QJda1LuUG1fsJOUc+yzWfaOnGreACwPgp8y5IuMbA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792936167; 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=lZMmo915rHQkiU5gDqazvH2dOqZ5POWrufW/z8uQQec=; b=d/vBTHpFip7os1ZGylJX1t+SeL1psFTz7UyZrVXqNHy9OmpjhnIouDeeOOtgnqeZWn24 dUJZ0QJP3KfXmDqIviworRVNH11HxJOmpZLxDz2WnqneTIhDzSIahpVjTpU10o0QjeaeR IwuYxVVo2Cum8yk8izV7PuGG9FgOvhgtQOuGevcKjkhfgBouQO3xj2B3P2AyYqBxyYS3c b6/lOxhdhBb3RJKCZtC5aSgvOOzy+uLlUNixG9qYCd6aWpFylumZ895K00j2FX9xPk8jX bF+pra4XCoHwo7HiPiTIFdZAfYHXEJ/OnIC2N4inKKKvhVGwUEIHOsAbtkJL6DYQhnw== 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=20251104 header.b=F+Y34rzh; arc=none smtp.remote-ip=74.125.82.170 Received: from mail-dy1-f170.google.com (mail-dy1-f170.google.com [74.125.82.170]) (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 084DD606A31A for ; Sun, 25 Oct 2026 13:49:26 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 084DD606A31A 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=20251104 header.b=F+Y34rzh Received: by mail-dy1-f170.google.com with SMTP id 5a478bee46e88-354ba08cfd1so4061271eec.1 for ; Sun, 25 Oct 2026 06:49:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792936166; x=1793540966; 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=lZMmo915rHQkiU5gDqazvH2dOqZ5POWrufW/z8uQQec=; b=F+Y34rzhTUeOGRwP/EZqGfuJDqAcm7c+cb4JS89kohEf03F/0/lgZ2aCXnfML7kc0M 2V6o+zcGl9F5oaGMBX2v8TLwZT66yAHYKRyKXWrpGJikIY/yKIndiM0O459sAtAWYxSy 5mxziVpK26F+Q8UNOguT4rWJXqrBGwvxA/CR0tvoDbQWOUYg8ysNZ+xFfnZCF1t7gY00 wp94+HEbx8WEzqpWOBay4hQOuCFCEXNuA6W3D0ehkIWadBJjc4Ot3W7D7klxUQJO3oGe l160kic2wDXgRHu1W1tOgg3UMhO5KWoy/oKLMBQcd6aIFBZIaZ9IUCzifzZU2YgPj8iW WmHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792936166; x=1793540966; 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=lZMmo915rHQkiU5gDqazvH2dOqZ5POWrufW/z8uQQec=; b=zeRQzAmkCDIymjcpgphQeGQpjjthZx5ZvRZLe+zyC0JvAb0d3yFItP9k8mwpe7ZR/N /yIU+F92XtR9+HZvsnFqSYxVptJSvaZl/R9hf6T3KFbkeVelISsE4Una2NwZ1IQts2nx IZNarNgm4qw+iV1W4bLVyNoMI/9SQhMOOEKZdhLfzW4Nv1WasedsuUk2Uw2XC3WRCe8D SxPpDLIihcavjOAiXZbjzIo+lUdZr6XTN8r2HJjthNhnFB07W2hAtXwTVJo+9M1UUOzq VbBC/Wh0G+OL0P4b4BS/dOW3sYTCj/cRrHDf9gl6RopwgvOQKWcoZfpkzEpZXFtl2i5I 2Gig== X-Gm-Message-State: AFq9FYJD20oVpu4+F9DySDa1CHnn8+nJCynlDlyZ7bxWvt5FTDT0c18A 1/R4M6Gv+CHoeEzDvlnUD3vEDAWPw1waDWE7dxNQOUpRAWwQsm2M6nXHTFiujA== X-Gm-Gg: AYBFou0hcHN2Edjw35DcvfYUS1ipnHPXoxhaCnNICGbvxeBOvBnDPzynXJV+YArH1RP qbxC7dmluiOBzoZwZr48V354RlVuh4Qv5U/11uZ9/qg8wk6XyfYPGLWfugjHML5iS1/PofNgT26 iGU7AsAHpf5/EuB9PorjxfZZ7ZoxjWDqctVyfBa95C/Yk9hq0c0add9il7r81SHKSId0Z9ckJCe vlk1sTxUq3NYxZy6ACDPJOk5jVOA2F0G0IL5S5ayqmwSlplPlmobf40gX/16xGzyvp2uwzTkcns p13O3SaAMRlRjwZjogMDd7V+JMFZDkcMFX2JPNENklQV5BndEQJNVzDpr3caVx2J8pqLg1pYtuy jIS7pDx7WuLJDgsbbXI+kLhEgW9vakdKcFTBwERJZ/eVT9SCvxpyeJYYE+8QAn90Tf57ZfQczzc gVjVYmEjTlXRmgBMqAqmKl0YTKl/sDcI33ZTFWeATEVNeGuPgXsvNAGNIN2QqbBY1grt+NA8FLx 19AC8KY1zH6afFEFLMyEr7nYaUMYJXMJzpgu0ZfcEcxy6b2ulL07iZXd3uVPXPyWJ3RdzjuUCLi idrpzo02yW2Hp0k0P66is6AOACBTKdEIE7tKTT8xlceZm3BllAQaA73fYTUTEHQ0WqjJjE7UnFf 0RBL8mNrOjNpx9fdNCjtLjhM0 X-Received: by 2002:a05:7300:cf94:b0:35c:99e1:c755 with SMTP id 5a478bee46e88-369e86128c1mr6129918eec.38.1792936165821; Sun, 25 Oct 2026 06:49:25 -0700 (PDT) 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-36a002a486csm1188159eec.22.2026.10.25.06.49.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 25 Oct 2026 06:49:25 -0700 (PDT) From: Anil Kaushik To: mptcp@lists.linux.dev Cc: Anil Kaushik Subject: [PATCH mptcp-next v9 6/6] selftests: mptcp: join: validate subflow retry after link failure Date: Sun, 25 Oct 2026 13:49:09 +0000 Message-Id: <20261025134909.2794368-7-anilkaushikwireless@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20261025134909.2794368-1-anilkaushikwireless@gmail.com> References: <20261025134909.2794368-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 re-creates the only subflow, and checks the connection is still closed after the close timeout, the new subflow being unable to connect. The tests are skipped if pm_nl_ctl does not show the new limit, or if the filter rules cannot be set. Assisted-by: LLM Signed-off-by: Anil Kaushik --- Notes: 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 | 328 +++++++++++++++++- 1 file changed, 320 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..e80f3f5f9997 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,195 @@ 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 }' +} + +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") + if ! nft_drop_from "${1}"; then + 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 + fail_test "subflow from ${1} not re-created" + return + fi + # 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 + + if [ -z "$(sf_from "${1}")" ]; then + fail_test "no 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 + + for i in 1 3; 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 +2784,119 @@ 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 + # 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 + wait_counter_ge $((timeout_poll * 4)) \ + $ns1 "MPTcpExtMPJoinAckRx" 1 + sf_retry_after_block 10.0.3.2 + sf_retry_after_block 10.0.1.2 + mptcp_lib_kill_group_wait $tests_pid + kill_events_pids + + join_syn_tx=3D3 chk_join_nr 3 3 3 + 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 + # 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. + 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") + 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. + 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) + kill_client + chk_no_sf_connecting "${not_ready}" "${fc_before}" + mptcp_lib_kill_group_wait $tests_pid + kill_events_pids + + join_syn_tx=3D2 chk_join_nr 1 1 1 + chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_ESTABLISHED 1 + chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_CLOSED 1 error 1 + sf_retry_cleanup + fi + + # the only subflow is re-created: the connection is still closed after + # the close timeout, the new subflow cannot connect + 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 + ip netns exec $ns2 sysctl -q net.mptcp.close_timeout=3D5 + { 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 "closed after the close timeout" + 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" + # no other subflow: the first MP_JOIN SYN is the re-created one + elif ! wait_counter_ge "${timeout_poll}" \ + $ns2 "MPTcpExtMPJoinSynTx" 1; then + fail_test "no subflow re-created" + # closed with the connection, after 5 seconds + elif slowwait "${timeout_poll}" no_sf_connecting; then + print_ok + else + fail_test "subflow still connecting" + fi + mptcp_lib_kill_group_wait $tests_pid + kill_events_pids + + join_syn_tx=3D1 chk_join_nr 0 0 0 + 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