[PATCH mptcp-next RFC] mptcp: socket option to check for fallback

Matthieu Baerts (NGI0) posted 1 patch 1 week, 6 days ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/multipath-tcp/mptcp_net-next tags/patchew/20240506-getsockopt-tcp._5Fis._5Fmptcp-v1-1-4f044660f7c3@kernel.org
include/uapi/linux/tcp.h | 2 ++
net/ipv4/tcp.c           | 3 +++
net/mptcp/sockopt.c      | 2 ++
3 files changed, 7 insertions(+)
[PATCH mptcp-next RFC] mptcp: socket option to check for fallback
Posted by Matthieu Baerts (NGI0) 1 week, 6 days ago
A way for an application to know if the 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 know what to expect.

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 one simply has to return 1, while
the other can return 0.

If the socket option is not supported, an error will be reported:
EOPNOTSUPP (95 - Operation not supported) for MPTCP sockets, ENOPROTOOPT
(92 - Protocol not available) for TCP sockets.

Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
---
Notes:
  - This is linked to a discussion we had at the last meeting: having a
    cleaner way, not having to check the kernel version. But maybe it is
    fine to look at the kernel version? I mean: 5.16 is quite old now,
    maybe too late for that kind of patch?
  - During these discussions, I thought we had to also look at the
    mptcpi_flags field from the 'struct mptcp_info', and check if
    MPTCP_INFO_FLAG_FALLBACK was set: that's not needed, and this
    simplify a bit what needs to be done to check for a fallback:
    https://github.com/multipath-tcp/mptcp.dev/pull/6
  - Maybe it is enough to optimise the getsockopt(MPTCP_INFO) like this?
    https://lore.kernel.org/mptcp/20240418-mptcp-getsockopt-info-opti-v1-1-7138a96eb50c@kernel.org/

    Note: typically, it is not possible to do a getsockopt() with an
    optlen of size 0 from higher level lib (Python, Go, Rust, etc.).
    Maybe we would need a similar optimisation for optlen <= 4?
---
 include/uapi/linux/tcp.h | 2 ++
 net/ipv4/tcp.c           | 3 +++
 net/mptcp/sockopt.c      | 2 ++
 3 files changed, 7 insertions(+)

diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index c07e9f90c084..dbf896f3146c 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -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 */
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 95ca73937a1c..39ee5a107544 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -4363,6 +4363,9 @@ int do_tcp_getsockopt(struct sock *sk, int level,
 
 		return err;
 	}
+	case TCP_IS_MPTCP:
+		val = 0;
+		break;
 	default:
 		return -ENOPROTOOPT;
 	}
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index 2edaf1a16005..e50262b8942e 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -1354,6 +1354,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;
 }

---
base-commit: f16f6f211e692c812d4d5b24e8eff95188ecb62c
change-id: 20240506-getsockopt-tcp_is_mptcp-735fec263981

Best regards,
-- 
Matthieu Baerts (NGI0) <matttbe@kernel.org>
Re: [PATCH mptcp-next RFC] mptcp: socket option to check for fallback
Posted by Mat Martineau 1 week, 5 days ago
On Mon, 6 May 2024, Matthieu Baerts (NGI0) wrote:

> A way for an application to know if the 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 know what to expect.
>
> 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 one simply has to return 1, while
> the other can return 0.
>
> If the socket option is not supported, an error will be reported:
> EOPNOTSUPP (95 - Operation not supported) for MPTCP sockets, ENOPROTOOPT
> (92 - Protocol not available) for TCP sockets.
>

Hi Matthieu -

I like this approach as it's much simpler to check than MPTCP_INFO, and 
gives 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?".

- Mat

> Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
> ---
> Notes:
>  - This is linked to a discussion we had at the last meeting: having a
>    cleaner way, not having to check the kernel version. But maybe it is
>    fine to look at the kernel version? I mean: 5.16 is quite old now,
>    maybe too late for that kind of patch?
>  - During these discussions, I thought we had to also look at the
>    mptcpi_flags field from the 'struct mptcp_info', and check if
>    MPTCP_INFO_FLAG_FALLBACK was set: that's not needed, and this
>    simplify a bit what needs to be done to check for a fallback:
>    https://github.com/multipath-tcp/mptcp.dev/pull/6
>  - Maybe it is enough to optimise the getsockopt(MPTCP_INFO) like this?
>    https://lore.kernel.org/mptcp/20240418-mptcp-getsockopt-info-opti-v1-1-7138a96eb50c@kernel.org/
>
>    Note: typically, it is not possible to do a getsockopt() with an
>    optlen of size 0 from higher level lib (Python, Go, Rust, etc.).
>    Maybe we would need a similar optimisation for optlen <= 4?
> ---
> include/uapi/linux/tcp.h | 2 ++
> net/ipv4/tcp.c           | 3 +++
> net/mptcp/sockopt.c      | 2 ++
> 3 files changed, 7 insertions(+)
>
> diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
> index c07e9f90c084..dbf896f3146c 100644
> --- a/include/uapi/linux/tcp.h
> +++ b/include/uapi/linux/tcp.h
> @@ -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 */
> diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> index 95ca73937a1c..39ee5a107544 100644
> --- a/net/ipv4/tcp.c
> +++ b/net/ipv4/tcp.c
> @@ -4363,6 +4363,9 @@ int do_tcp_getsockopt(struct sock *sk, int level,
>
> 		return err;
> 	}
> +	case TCP_IS_MPTCP:
> +		val = 0;
> +		break;
> 	default:
> 		return -ENOPROTOOPT;
> 	}
> diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
> index 2edaf1a16005..e50262b8942e 100644
> --- a/net/mptcp/sockopt.c
> +++ b/net/mptcp/sockopt.c
> @@ -1354,6 +1354,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;
> }
>
> ---
> base-commit: f16f6f211e692c812d4d5b24e8eff95188ecb62c
> change-id: 20240506-getsockopt-tcp_is_mptcp-735fec263981
>
> Best regards,
> -- 
> Matthieu Baerts (NGI0) <matttbe@kernel.org>
>
>
>
Re: [PATCH mptcp-next RFC] mptcp: socket option to check for fallback
Posted by Matthieu Baerts 1 week, 3 days ago
Hi Mat,

On 07/05/2024 03:09, Mat Martineau wrote:
> On Mon, 6 May 2024, Matthieu Baerts (NGI0) wrote:
> 
>> A way for an application to know if the 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 know what to expect.
>>
>> 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 one simply has to return 1, while
>> the other can return 0.
>>
>> If the socket option is not supported, an error will be reported:
>> EOPNOTSUPP (95 - Operation not supported) for MPTCP sockets, ENOPROTOOPT
>> (92 - Protocol not available) for TCP sockets.
>>
> 
> Hi Matthieu -
> 
> I like this approach as it's much simpler to check than MPTCP_INFO, and
> gives 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?".

Thank you for your feedback!

As discussed yesterday, I just applied this patch in our tree, and I
will send it alone to netdev for review.

New patches for t/upstream:
- d035042ea2b4: mptcp: socket option to check for fallback
- Results: 5bc872cb274e..6d5ca2a78a70 (export)

Tests are now in progress:

- export:
https://github.com/multipath-tcp/mptcp_net-next/commit/689430a018a9111229371ebb6167604e8dd6a7ae/checks

Cheers,
Matt
-- 
Sponsored by the NGI0 Core fund.