--- /dev/null
+From stable+bounces-186341-greg=kroah.com@vger.kernel.org Fri Oct 17 16:42:16 2025
+From: "Matthieu Baerts (NGI0)" <matttbe@kernel.org>
+Date: Fri, 17 Oct 2025 16:39:51 +0200
+Subject: mptcp: pm: in-kernel: usable client side with C-flag
+To: mptcp@lists.linux.dev, stable@vger.kernel.org, gregkh@linuxfoundation.org
+Cc: "Matthieu Baerts (NGI0)" <matttbe@kernel.org>, sashal@kernel.org, Geliang Tang <geliang@kernel.org>, Jakub Kicinski <kuba@kernel.org>
+Message-ID: <20251017143949.2844546-5-matttbe@kernel.org>
+
+From: "Matthieu Baerts (NGI0)" <matttbe@kernel.org>
+
+commit 4b1ff850e0c1aacc23e923ed22989b827b9808f9 upstream.
+
+When servers set the C-flag in their MP_CAPABLE to tell clients not to
+create subflows to the initial address and port, clients will likely not
+use their other endpoints. That's because the in-kernel path-manager
+uses the 'subflow' endpoints to create subflows only to the initial
+address and port.
+
+If the limits have not been modified to accept ADD_ADDR, the client
+doesn't try to establish new subflows. If the limits accept ADD_ADDR,
+the routing routes will be used to select the source IP.
+
+The C-flag is typically set when the server is operating behind a legacy
+Layer 4 load balancer, or using anycast IP address. Clients having their
+different 'subflow' endpoints setup, don't end up creating multiple
+subflows as expected, and causing some deployment issues.
+
+A special case is then added here: when servers set the C-flag in the
+MPC and directly sends an ADD_ADDR, this single ADD_ADDR is accepted.
+The 'subflows' endpoints will then be used with this new remote IP and
+port. This exception is only allowed when the ADD_ADDR is sent
+immediately after the 3WHS, and makes the client switching to the 'fully
+established' mode. After that, 'select_local_address()' will not be able
+to find any subflows, because 'id_avail_bitmap' will be filled in
+mptcp_pm_create_subflow_or_signal_addr(), when switching to 'fully
+established' mode.
+
+Fixes: df377be38725 ("mptcp: add deny_join_id0 in mptcp_options_received")
+Cc: stable@vger.kernel.org
+Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/536
+Reviewed-by: Geliang Tang <geliang@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20250925-net-next-mptcp-c-flag-laminar-v1-1-ad126cc47c6b@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ Conflict in pm.c, because commit 498d7d8b75f1 ("mptcp: pm: remove
+ '_nl' from mptcp_pm_nl_is_init_remote_addr") renamed an helper in the
+ context, and it is not in this version. The same new code can be
+ applied at the same place.
+ Another conflict in pm.c, because commit 4d25247d3ae4 ("mptcp: bypass
+ in-kernel PM restrictions for non-kernel PMs") switched the modified
+ 'if' statement to an 'else if', and is not in this version. The same
+ modification can still be applied.
+ Conflict in pm_kernel.c, because the modified code has been moved from
+ pm_netlink.c to pm_kernel.c in commit 8617e85e04bd ("mptcp: pm: split
+ in-kernel PM specific code"), which is not in this version. The
+ resolution is easy: simply by applying the patch where 'pm_kernel.c'
+ has been replaced 'pm_netlink.c'.
+ Conflict in pm_netlink.c, because commit b83fbca1b4c9 ("mptcp: pm:
+ reduce entries iterations on connect") is not in this version. Instead
+ of using the 'locals' variable (struct mptcp_pm_local *) from the new
+ version and embedding a "struct mptcp_addr_info", we can simply
+ continue to use the 'addrs' variable (struct mptcp_addr_info *).
+ Because commit b9d69db87fb7 ("mptcp: let the in-kernel PM use mixed
+ IPv4 and IPv6 addresses") is not in this version, it is also required
+ to pass an extra parameter to fill_local_addresses_vec(): struct
+ mptcp_addr_info *remote, which is available from the caller side.
+ Same with commit 4638de5aefe5 ("mptcp: handle local addrs announced by
+ userspace PMs") adding the 'mptcp_' prefix to addresses_equal().
+ Conflict in protocol.h, because commit af3dc0ad3167 ("mptcp: Remove
+ unused declaration mptcp_sockopt_sync()") is not in this version and
+ it removed one line in the context. The resolution is easy because the
+ new function can still be added at the same place. A similar conflict
+ has been resolved due to commit 95d686517884 ("mptcp: fix subflow
+ accounting on close"). ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/mptcp/pm.c | 7 +++++--
+ net/mptcp/pm_netlink.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
+ net/mptcp/protocol.h | 8 ++++++++
+ 3 files changed, 61 insertions(+), 3 deletions(-)
+
+--- a/net/mptcp/pm.c
++++ b/net/mptcp/pm.c
+@@ -189,9 +189,12 @@ void mptcp_pm_add_addr_received(struct m
+
+ spin_lock_bh(&pm->lock);
+
+- /* id0 should not have a different address */
++ /* - id0 should not have a different address
++ * - special case for C-flag: linked to fill_local_addresses_vec()
++ */
+ if ((addr->id == 0 && !mptcp_pm_nl_is_init_remote_addr(msk, addr)) ||
+- (addr->id > 0 && !READ_ONCE(pm->accept_addr))) {
++ (addr->id > 0 && !READ_ONCE(pm->accept_addr) &&
++ !mptcp_pm_add_addr_c_flag_case(msk))) {
+ mptcp_pm_announce_addr(msk, addr, true);
+ mptcp_pm_add_addr_send_ack(msk);
+ } else if (mptcp_pm_schedule_work(msk, MPTCP_PM_ADD_ADDR_RECEIVED)) {
+--- a/net/mptcp/pm_netlink.c
++++ b/net/mptcp/pm_netlink.c
+@@ -571,6 +571,7 @@ static void mptcp_pm_nl_subflow_establis
+ * and return the array size.
+ */
+ static unsigned int fill_local_addresses_vec(struct mptcp_sock *msk,
++ struct mptcp_addr_info *remote,
+ struct mptcp_addr_info *addrs)
+ {
+ struct sock *sk = (struct sock *)msk;
+@@ -578,10 +579,12 @@ static unsigned int fill_local_addresses
+ struct mptcp_addr_info mpc_addr;
+ struct pm_nl_pernet *pernet;
+ unsigned int subflows_max;
++ bool c_flag_case;
+ int i = 0;
+
+ pernet = net_generic(sock_net(sk), pm_nl_pernet_id);
+ subflows_max = mptcp_pm_get_subflows_max(msk);
++ c_flag_case = remote->id && mptcp_pm_add_addr_c_flag_case(msk);
+
+ mptcp_local_address((struct sock_common *)msk, &mpc_addr);
+
+@@ -605,6 +608,10 @@ static unsigned int fill_local_addresses
+ msk->pm.subflows++;
+ addrs[i] = entry->addr;
+
++ if (c_flag_case &&
++ (entry->flags & MPTCP_PM_ADDR_FLAG_SUBFLOW))
++ msk->pm.local_addr_used++;
++
+ /* Special case for ID0: set the correct ID */
+ if (addresses_equal(&entry->addr, &mpc_addr, entry->addr.port))
+ addrs[i].id = 0;
+@@ -614,6 +621,46 @@ static unsigned int fill_local_addresses
+ }
+ rcu_read_unlock();
+
++ /* Special case: peer sets the C flag, accept one ADD_ADDR if default
++ * limits are used -- accepting no ADD_ADDR -- and use subflow endpoints
++ */
++ if (!i && c_flag_case) {
++ unsigned int local_addr_max = mptcp_pm_get_local_addr_max(msk);
++
++ rcu_read_lock();
++ __mptcp_flush_join_list(msk);
++ list_for_each_entry_rcu(entry, &pernet->local_addr_list, list) {
++ if (!(entry->flags & MPTCP_PM_ADDR_FLAG_SUBFLOW))
++ continue;
++
++ if (entry->addr.family != sk->sk_family) {
++#if IS_ENABLED(CONFIG_MPTCP_IPV6)
++ if ((entry->addr.family == AF_INET &&
++ !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) ||
++ (sk->sk_family == AF_INET &&
++ !ipv6_addr_v4mapped(&entry->addr.addr6)))
++#endif
++ continue;
++ }
++
++ /* avoid any address already in use by subflows and
++ * pending join
++ */
++ if (!lookup_subflow_by_saddr(&msk->conn_list, &entry->addr) &&
++ msk->pm.local_addr_used < local_addr_max &&
++ msk->pm.subflows < subflows_max) {
++ addrs[i] = entry->addr;
++
++ msk->pm.local_addr_used++;
++ msk->pm.subflows++;
++ i++;
++ }
++ }
++ rcu_read_unlock();
++
++ return i;
++ }
++
+ /* If the array is empty, fill in the single
+ * 'IPADDRANY' local address
+ */
+@@ -661,7 +708,7 @@ static void mptcp_pm_nl_add_addr_receive
+ /* connect to the specified remote address, using whatever
+ * local address the routing configuration will pick.
+ */
+- nr = fill_local_addresses_vec(msk, addrs);
++ nr = fill_local_addresses_vec(msk, &remote, addrs);
+
+ spin_unlock_bh(&msk->pm.lock);
+ for (i = 0; i < nr; i++)
+--- a/net/mptcp/protocol.h
++++ b/net/mptcp/protocol.h
+@@ -846,6 +846,14 @@ unsigned int mptcp_pm_get_add_addr_accep
+ unsigned int mptcp_pm_get_subflows_max(const struct mptcp_sock *msk);
+ unsigned int mptcp_pm_get_local_addr_max(const struct mptcp_sock *msk);
+
++static inline bool mptcp_pm_add_addr_c_flag_case(struct mptcp_sock *msk)
++{
++ return READ_ONCE(msk->pm.remote_deny_join_id0) &&
++ msk->pm.local_addr_used == 0 &&
++ mptcp_pm_get_add_addr_accept_max(msk) == 0 &&
++ msk->pm.subflows < mptcp_pm_get_subflows_max(msk);
++}
++
+ void mptcp_sockopt_sync(struct mptcp_sock *msk, struct sock *ssk);
+ void mptcp_sockopt_sync_all(struct mptcp_sock *msk);
+
--- /dev/null
+From stable+bounces-186342-greg=kroah.com@vger.kernel.org Fri Oct 17 16:43:01 2025
+From: "Matthieu Baerts (NGI0)" <matttbe@kernel.org>
+Date: Fri, 17 Oct 2025 16:39:52 +0200
+Subject: selftests: mptcp: join: validate C-flag + def limit
+To: mptcp@lists.linux.dev, stable@vger.kernel.org, gregkh@linuxfoundation.org
+Cc: "Matthieu Baerts (NGI0)" <matttbe@kernel.org>, sashal@kernel.org, Geliang Tang <geliang@kernel.org>, Jakub Kicinski <kuba@kernel.org>
+Message-ID: <20251017143949.2844546-6-matttbe@kernel.org>
+
+From: "Matthieu Baerts (NGI0)" <matttbe@kernel.org>
+
+commit 008385efd05e04d8dff299382df2e8be0f91d8a0 upstream.
+
+The previous commit adds an exception for the C-flag case. The
+'mptcp_join.sh' selftest is extended to validate this case.
+
+In this subtest, there is a typical CDN deployment with a client where
+MPTCP endpoints have been 'automatically' configured:
+
+- the server set net.mptcp.allow_join_initial_addr_port=0
+
+- the client has multiple 'subflow' endpoints, and the default limits:
+ not accepting ADD_ADDRs.
+
+Without the parent patch, the client is not able to establish new
+subflows using its 'subflow' endpoints. The parent commit fixes that.
+
+The 'Fixes' tag here below is the same as the one from the previous
+commit: this patch here is not fixing anything wrong in the selftests,
+but it validates the previous fix for an issue introduced by this commit
+ID.
+
+Fixes: df377be38725 ("mptcp: add deny_join_id0 in mptcp_options_received")
+Cc: stable@vger.kernel.org
+Reviewed-by: Geliang Tang <geliang@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20250925-net-next-mptcp-c-flag-laminar-v1-2-ad126cc47c6b@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ Conflicts in mptcp_join.sh, because many different helpers have been
+ modified in newer kernel versions, e.g. in commit 03668c65d153
+ ("selftests: mptcp: join: rework detailed report"), or commit
+ 985de45923e2 ("selftests: mptcp: centralize stats dumping"), etc.
+ Adaptations have been made to use the old way, similar to what is done
+ just above. ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ tools/testing/selftests/net/mptcp/mptcp_join.sh | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/tools/testing/selftests/net/mptcp/mptcp_join.sh
++++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh
+@@ -1826,6 +1826,16 @@ deny_join_id0_tests()
+ ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
+ run_tests $ns1 $ns2 10.0.1.1
+ chk_join_nr "subflow and address allow join id0 2" 1 1 1
++
++ # default limits, server deny join id 0 + signal
++ reset_with_allow_join_id0 0 1
++ ip netns exec $ns1 ./pm_nl_ctl limits 0 2
++ ip netns exec $ns2 ./pm_nl_ctl limits 0 2
++ ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal
++ ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
++ ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 flags subflow
++ run_tests $ns1 $ns2 10.0.1.1
++ chk_join_nr "default limits, server deny join id 0" 2 2 2
+ }
+
+ fullmesh_tests()