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
>
>

Reply via email to