]> www.infradead.org Git - users/hch/misc.git/commitdiff
mptcp: pm: in-kernel: usable client side with C-flag
authorMatthieu Baerts (NGI0) <matttbe@kernel.org>
Thu, 25 Sep 2025 10:32:36 +0000 (12:32 +0200)
committerJakub Kicinski <kuba@kernel.org>
Sat, 27 Sep 2025 00:44:03 +0000 (17:44 -0700)
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>
net/mptcp/pm.c
net/mptcp/pm_kernel.c
net/mptcp/protocol.h

index 204e1f61212e2be77a8476f024b59be67d04b80a..584cab90aa6eff4c01cdf4ca4d3dce8894829920 100644 (file)
@@ -637,9 +637,12 @@ void mptcp_pm_add_addr_received(const struct sock *ssk,
                } else {
                        __MPTCP_INC_STATS(sock_net((struct sock *)msk), MPTCP_MIB_ADDADDRDROP);
                }
-       /* 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()
+        */
        } else if ((addr->id == 0 && !mptcp_pm_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)) {
index 667803d72b643a0bb98365003b136c53f2a9a975..8c46493a0835b0e2d5e70950662ae6e845393777 100644 (file)
@@ -389,10 +389,12 @@ static unsigned int fill_local_addresses_vec(struct mptcp_sock *msk,
        struct mptcp_addr_info mpc_addr;
        struct pm_nl_pernet *pernet;
        unsigned int subflows_max;
+       bool c_flag_case;
        int i = 0;
 
        pernet = pm_nl_get_pernet_from_msk(msk);
        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);
 
@@ -405,12 +407,27 @@ static unsigned int fill_local_addresses_vec(struct mptcp_sock *msk,
                        continue;
 
                if (msk->pm.subflows < subflows_max) {
+                       bool is_id0;
+
                        locals[i].addr = entry->addr;
                        locals[i].flags = entry->flags;
                        locals[i].ifindex = entry->ifindex;
 
+                       is_id0 = mptcp_addresses_equal(&locals[i].addr,
+                                                      &mpc_addr,
+                                                      locals[i].addr.port);
+
+                       if (c_flag_case &&
+                           (entry->flags & MPTCP_PM_ADDR_FLAG_SUBFLOW)) {
+                               __clear_bit(locals[i].addr.id,
+                                           msk->pm.id_avail_bitmap);
+
+                               if (!is_id0)
+                                       msk->pm.local_addr_used++;
+                       }
+
                        /* Special case for ID0: set the correct ID */
-                       if (mptcp_addresses_equal(&locals[i].addr, &mpc_addr, locals[i].addr.port))
+                       if (is_id0)
                                locals[i].addr.id = 0;
 
                        msk->pm.subflows++;
@@ -419,6 +436,37 @@ static unsigned int fill_local_addresses_vec(struct mptcp_sock *msk,
        }
        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);
+
+               while (msk->pm.local_addr_used < local_addr_max &&
+                      msk->pm.subflows < subflows_max) {
+                       struct mptcp_pm_local *local = &locals[i];
+
+                       if (!select_local_address(pernet, msk, local))
+                               break;
+
+                       __clear_bit(local->addr.id, msk->pm.id_avail_bitmap);
+
+                       if (!mptcp_pm_addr_families_match(sk, &local->addr,
+                                                         remote))
+                               continue;
+
+                       if (mptcp_addresses_equal(&local->addr, &mpc_addr,
+                                                 local->addr.port))
+                               continue;
+
+                       msk->pm.local_addr_used++;
+                       msk->pm.subflows++;
+                       i++;
+               }
+
+               return i;
+       }
+
        /* If the array is empty, fill in the single
         * 'IPADDRANY' local address
         */
index a1787a1344ac1bbeefdb4548740d6aef980b79e7..cbe54331e5c745989af50409d9cb79c6d90a8201 100644 (file)
@@ -1199,6 +1199,14 @@ static inline void mptcp_pm_close_subflow(struct mptcp_sock *msk)
        spin_unlock_bh(&msk->pm.lock);
 }
 
+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_locked(struct mptcp_sock *msk, struct sock *ssk);
 
 static inline struct mptcp_ext *mptcp_get_ext(const struct sk_buff *skb)