tcp: socket option to check for MPTCP fallback to TCP
authorMatthieu Baerts (NGI0) <matttbe@kernel.org>
Thu, 9 May 2024 18:10:10 +0000 (20:10 +0200)
committerJakub Kicinski <kuba@kernel.org>
Mon, 13 May 2024 21:48:04 +0000 (14:48 -0700)
A way for an application to know if an MPTCP connection fell back to TCP
is to use getsockopt(MPTCP_INFO) and look for errors. The issue with
this technique is that the same errors -- EOPNOTSUPP (IPv4) and
ENOPROTOOPT (IPv6) -- are returned if there was a fallback, *or* if the
kernel doesn't support this socket option. The userspace then has to
look at the kernel version to understand what the errors mean.

It is not clean, and it doesn't take into account older kernels where
the socket option has been backported. A cleaner way would be to expose
this info to the TCP socket level. In case of MPTCP socket where no
fallback happened, the socket options for the TCP level will be handled
in MPTCP code, in mptcp_getsockopt_sol_tcp(). If not, that will be in
TCP code, in do_tcp_getsockopt(). So MPTCP simply has to set the value
1, while TCP has to set 0.

If the socket option is not supported, one of these two errors will be
reported:
- EOPNOTSUPP (95 - Operation not supported) for MPTCP sockets
- ENOPROTOOPT (92 - Protocol not available) for TCP sockets, e.g. on the
  socket received after an 'accept()', when the client didn't request to
  use MPTCP: this socket will be a TCP one, even if the listen socket
  was an MPTCP one.

With this new option, the kernel can return a clear answer to both "Is
this kernel new enough to tell me the fallback status?" and "If it is
new enough, is it currently a TCP or MPTCP socket?" questions, while not
breaking the previous method.

Acked-by: Mat Martineau <martineau@kernel.org>
Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Link: https://lore.kernel.org/r/20240509-upstream-net-next-20240509-mptcp-tcp_is_mptcp-v1-1-f846df999202@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/uapi/linux/tcp.h
net/ipv4/tcp.c
net/mptcp/sockopt.c

index c07e9f90c0843caf8c5daaacac8296181f0c3860..dbf896f3146c5fb2a35d68f8633fda4e0b74a6fb 100644 (file)
@@ -135,6 +135,8 @@ enum {
 #define TCP_AO_GET_KEYS                41      /* List MKT(s) */
 #define TCP_AO_REPAIR          42      /* Get/Set SNEs and ISNs */
 
+#define TCP_IS_MPTCP           43      /* Is MPTCP being used? */
+
 #define TCP_REPAIR_ON          1
 #define TCP_REPAIR_OFF         0
 #define TCP_REPAIR_OFF_NO_WP   -1      /* Turn off without window probes */
index 06aab937d60a683b6062cde614fb8e1fb5aed99c..681b54e1f3a64387787738ab6495531b8abe1771 100644 (file)
@@ -4363,6 +4363,9 @@ zerocopy_rcv_out:
 
                return err;
        }
+       case TCP_IS_MPTCP:
+               val = 0;
+               break;
        default:
                return -ENOPROTOOPT;
        }
index 1fea43f5b6f3de5f8ad54df2b791ba3649c51502..eaa3b79651a42319f5241001eeaee4dc170fc313 100644 (file)
@@ -1348,6 +1348,8 @@ static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
                return mptcp_put_int_option(msk, optval, optlen, msk->nodelay);
        case TCP_NOTSENT_LOWAT:
                return mptcp_put_int_option(msk, optval, optlen, msk->notsent_lowat);
+       case TCP_IS_MPTCP:
+               return mptcp_put_int_option(msk, optval, optlen, 1);
        }
        return -EOPNOTSUPP;
 }