On Mon, Jun 29, 2026 at 6:19 AM Guillaume @layus Maudoux
<[email protected]> wrote:
>
> The mptcpify BPF prog hooks update_socket_protocol() to rewrite
> eligible TCP socket() calls to IPPROTO_MPTCP. It only does so when the
> socket type is exactly SOCK_STREAM:
>
> type == SOCK_STREAM
>
> The problem is that update_socket_protocol() in __sys_socket() is
> called on the raw type argument as passed from userspace, before
> __sys_socket_create() strips the flag bits with
> "type &= SOCK_TYPE_MASK". The type argument may therefore carry
> SOCK_CLOEXEC and/or SOCK_NONBLOCK in its upper bits, and the equality
> check above then fails.
>
> As a result, any socket created with e.g.
> socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0) -- which is what common
> libraries do by default -- is silently left as plain TCP instead of
> being upgraded to MPTCP. This was observed in practice with curl,
> whose connections were not upgraded to MPTCP despite the prog being
> attached.
>
> The impact reaches beyond the test, because mptcpify.c is referenced
> as example code for users who want to transparently enable MPTCP. The
> same mistake is therefore likely to be copied into real deployments,
> where it fails the same way and is hard to diagnose.
>
> The fix is to mask off the flag bits before comparing, mirroring what
> the socket core does:
>
> (type & SOCK_TYPE_MASK) == SOCK_STREAM
>
> Since SOCK_TYPE_MASK is not exposed through vmlinux.h, define it in
> bpf_tracing_net.h.
>
> To exercise the regression directly, extend the mptcpify test to also
> create the server socket with SOCK_CLOEXEC and SOCK_NONBLOCK set.
> Routing a flagged type through start_server() then revealed a second
> instance of the same pattern: start_server_addr() compared the type
> against SOCK_STREAM for equality to decide whether to set SO_REUSEADDR
> and call listen(), and so would skip listening for a flagged type.
> Mask the type there as well. As SOCK_TYPE_MASK is not exposed by
> glibc's <sys/socket.h> either, define it in network_helpers.h,
> mirroring prog_tests/socket_helpers.h.
>
> Fixes: ddba122428a7 ("selftests/bpf: Add mptcpify test")
> Signed-off-by: Guillaume @layus Maudoux <[email protected]>
Hello,
You will need to use your real name in the SOB. At least "@" caused
some problems when I downloaded the patch.
Please also include "bpf-next" in your subject prefix so that the CI
can properly test it.
> ---
> tools/testing/selftests/bpf/network_helpers.c | 4 ++--
> tools/testing/selftests/bpf/network_helpers.h | 5 +++++
> .../testing/selftests/bpf/prog_tests/mptcp.c | 20 +++++++++++++++----
> .../selftests/bpf/progs/bpf_tracing_net.h | 3 +++
> tools/testing/selftests/bpf/progs/mptcpify.c | 2 +-
> 5 files changed, 27 insertions(+), 7 deletions(-)
>
> diff --git a/tools/testing/selftests/bpf/network_helpers.c
> b/tools/testing/selftests/bpf/network_helpers.c
> index b82f572641b7..db935a9d9fc1 100644
> --- a/tools/testing/selftests/bpf/network_helpers.c
> +++ b/tools/testing/selftests/bpf/network_helpers.c
> @@ -111,7 +111,7 @@ int start_server_addr(int type, const struct
> sockaddr_storage *addr, socklen_t a
> if (settimeo(fd, opts->timeout_ms))
> goto error_close;
>
> - if (type == SOCK_STREAM &&
> + if ((type & SOCK_TYPE_MASK) == SOCK_STREAM &&
> setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
> log_err("Failed to enable SO_REUSEADDR");
> goto error_close;
> @@ -128,7 +128,7 @@ int start_server_addr(int type, const struct
> sockaddr_storage *addr, socklen_t a
> goto error_close;
> }
>
> - if (type == SOCK_STREAM) {
> + if ((type & SOCK_TYPE_MASK) == SOCK_STREAM) {
> if (listen(fd, opts->backlog ? MAX(opts->backlog, 0) : 1) <
> 0) {
> log_err("Failed to listed on socket");
> goto error_close;
> diff --git a/tools/testing/selftests/bpf/network_helpers.h
> b/tools/testing/selftests/bpf/network_helpers.h
> index 79a010c88e11..75133119c04a 100644
> --- a/tools/testing/selftests/bpf/network_helpers.h
> +++ b/tools/testing/selftests/bpf/network_helpers.h
> @@ -25,6 +25,11 @@ typedef __u16 __sum16;
> #define VIP_NUM 5
> #define MAGIC_BYTES 123
>
> +/* include/linux/net.h */
> +#ifndef SOCK_TYPE_MASK
> +#define SOCK_TYPE_MASK 0xf
> +#endif
> +
> struct network_helper_opts {
> int timeout_ms;
> int proto;
> diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c
> b/tools/testing/selftests/bpf/prog_tests/mptcp.c
> index 8fade8bdc451..faa001ea84ab 100644
> --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c
> +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c
> @@ -264,7 +264,7 @@ static int verify_mptcpify(int server_fd, int client_fd)
> return err;
> }
>
> -static int run_mptcpify(int cgroup_fd)
> +static int run_mptcpify(int cgroup_fd, int type)
> {
> int server_fd, client_fd, err = 0;
> struct mptcpify *mptcpify_skel;
> @@ -280,7 +280,7 @@ static int run_mptcpify(int cgroup_fd)
> goto out;
>
> /* without MPTCP */
> - server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0);
> + server_fd = start_server(AF_INET, type, NULL, 0, 0);
> if (!ASSERT_GE(server_fd, 0, "start_server")) {
> err = -EIO;
> goto out;
> @@ -307,7 +307,18 @@ static int run_mptcpify(int cgroup_fd)
> static void test_mptcpify(void)
> {
> struct netns_obj *netns = NULL;
> - int cgroup_fd;
> + int cgroup_fd, i;
> + int types[] = {
> + SOCK_STREAM,
> + /* userspace sets these flags together with the type, and the
> + * BPF prog must still upgrade the socket to MPTCP. See
> + * update_socket_protocol() in net/socket.c, which runs before
> + * the type is masked with SOCK_TYPE_MASK.
> + */
> + SOCK_STREAM | SOCK_CLOEXEC,
> + SOCK_STREAM | SOCK_NONBLOCK,
> + SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
> + };
>
> cgroup_fd = test__join_cgroup("/mptcpify");
> if (!ASSERT_GE(cgroup_fd, 0, "test__join_cgroup"))
> @@ -317,7 +328,8 @@ static void test_mptcpify(void)
> if (!ASSERT_OK_PTR(netns, "netns_new"))
> goto fail;
>
> - ASSERT_OK(run_mptcpify(cgroup_fd), "run_mptcpify");
> + for (i = 0; i < ARRAY_SIZE(types); i++)
> + ASSERT_OK(run_mptcpify(cgroup_fd, types[i]), "run_mptcpify");
nit: testing all four type combinations adds little value here. I'd
drop array+loop and keep two. For example:
ASSERT_OK(run_mptcpify(cgroup_fd, SOCK_STREAM), "run_mptcpify");
ASSERT_OK(run_mptcpify(cgroup_fd, SOCK_STREAM | SOCK_CLOEXEC),
"run_mptcpify_cloexec");
>
> fail:
> netns_free(netns);
> diff --git a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h
> b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h
> index d8dacef37c16..c4b438854565 100644
> --- a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h
> +++ b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h
> @@ -8,6 +8,9 @@
> #define AF_INET 2
> #define AF_INET6 10
>
> +/* include/linux/net.h */
> +#define SOCK_TYPE_MASK 0xf
> +
> #define SOL_SOCKET 1
> #define SO_REUSEADDR 2
> #define SO_SNDBUF 7
> diff --git a/tools/testing/selftests/bpf/progs/mptcpify.c
> b/tools/testing/selftests/bpf/progs/mptcpify.c
> index cbdc730c3a47..e3f8cb54dbe9 100644
> --- a/tools/testing/selftests/bpf/progs/mptcpify.c
> +++ b/tools/testing/selftests/bpf/progs/mptcpify.c
> @@ -15,7 +15,7 @@ int BPF_PROG(mptcpify, int family, int type, int protocol)
> return protocol;
>
> if ((family == AF_INET || family == AF_INET6) &&
> - type == SOCK_STREAM &&
> + (type & SOCK_TYPE_MASK) == SOCK_STREAM &&
> (!protocol || protocol == IPPROTO_TCP)) {
> return IPPROTO_MPTCP;
> }
> --
> 2.54.0
>
>