As is described in the "How to use MPTCP?" section in MPTCP wiki [1]:
"Your app should create sockets with IPPROTO_MPTCP as the proto: ( socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP); ). Legacy apps can be forced to create and use MPTCP sockets instead of TCP ones via the mptcpize command bundled with the mptcpd daemon."
But the mptcpize (LD_PRELOAD technique) command has some limitations [2]:
- it doesn't work if the application is not using libc (e.g. GoLang apps) - in some envs, it might not be easy to set env vars / change the way apps are launched, e.g. on Android - mptcpize needs to be launched with all apps that want MPTCP: we could have more control from BPF to enable MPTCP only for some apps or all the ones of a netns or a cgroup, etc. - it is not in BPF, we cannot talk about it at netdev conf.
So this patchset attempts to use BPF to implement functions similer to mptcpize.
The main idea is to add a hook in sys_socket() to change the protocol id from IPPROTO_TCP (or 0) to IPPROTO_MPTCP.
[1] https://github.com/multipath-tcp/mptcp_net-next/wiki [2] https://github.com/multipath-tcp/mptcp_net-next/issues/79
v9: - update comment for 'update_socket_protocol'.
v8: - drop the additional checks on the 'protocol' value after the 'update_socket_protocol()' call.
v7: - add __weak and __diag_* for update_socket_protocol.
v6: - add update_socket_protocol.
v5: - add bpf_mptcpify helper.
v4: - use lsm_cgroup/socket_create
v3: - patch 8: char cmd[128]; -> char cmd[256];
v2: - Fix build selftests errors reported by CI
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/79
Geliang Tang (4): bpf: Add update_socket_protocol hook selftests/bpf: Use random netns name for mptcp selftests/bpf: Add two mptcp netns helpers selftests/bpf: Add mptcpify test
net/mptcp/bpf.c | 17 +++ net/socket.c | 24 ++++ .../testing/selftests/bpf/prog_tests/mptcp.c | 125 ++++++++++++++++-- tools/testing/selftests/bpf/progs/mptcpify.c | 25 ++++ 4 files changed, 182 insertions(+), 9 deletions(-) create mode 100644 tools/testing/selftests/bpf/progs/mptcpify.c
Add a hook named update_socket_protocol in __sys_socket(), for bpf progs to attach to and update socket protocol. One user case is to force legacy TCP apps to create and use MPTCP sockets instead of TCP ones.
Define a mod_ret set named bpf_mptcp_fmodret_ids, add the hook update_socket_protocol into this set, and register it in bpf_mptcp_kfunc_init().
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/79 Acked-by: Matthieu Baerts matthieu.baerts@tessares.net Signed-off-by: Geliang Tang geliang.tang@suse.com --- net/mptcp/bpf.c | 17 +++++++++++++++++ net/socket.c | 24 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+)
diff --git a/net/mptcp/bpf.c b/net/mptcp/bpf.c index 5a0a84ad94af..c43aee31014d 100644 --- a/net/mptcp/bpf.c +++ b/net/mptcp/bpf.c @@ -12,6 +12,23 @@ #include <linux/bpf.h> #include "protocol.h"
+#ifdef CONFIG_BPF_JIT +BTF_SET8_START(bpf_mptcp_fmodret_ids) +BTF_ID_FLAGS(func, update_socket_protocol) +BTF_SET8_END(bpf_mptcp_fmodret_ids) + +static const struct btf_kfunc_id_set bpf_mptcp_fmodret_set = { + .owner = THIS_MODULE, + .set = &bpf_mptcp_fmodret_ids, +}; + +static int __init bpf_mptcp_kfunc_init(void) +{ + return register_btf_fmodret_id_set(&bpf_mptcp_fmodret_set); +} +late_initcall(bpf_mptcp_kfunc_init); +#endif /* CONFIG_BPF_JIT */ + struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk) { if (sk && sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP && sk_is_mptcp(sk)) diff --git a/net/socket.c b/net/socket.c index 2b0e54b2405c..9f98ced88ac5 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1644,11 +1644,35 @@ struct file *__sys_socket_file(int family, int type, int protocol) return sock_alloc_file(sock, flags, NULL); }
+/* A hook for bpf progs to attach to and update socket protocol. + * + * A static noinline declaration here could cause the compiler to + * optimize away the function. A global noinline declaration will + * keep the definition, but may optimize away the callsite. + * Therefore, __weak is needed to ensure that the call is still + * emitted, by telling the compiler that we don't know what the + * function might eventually be. + * + * __diag_* below are needed to dismiss the missing prototype warning. + */ + +__diag_push(); +__diag_ignore_all("-Wmissing-prototypes", + "kfuncs which will be used in BPF programs"); + +__weak noinline int update_socket_protocol(int family, int type, int protocol) +{ + return protocol; +} + +__diag_pop(); + int __sys_socket(int family, int type, int protocol) { struct socket *sock; int flags;
+ protocol = update_socket_protocol(family, type, protocol); sock = __sys_socket_create(family, type, protocol); if (IS_ERR(sock)) return PTR_ERR(sock);
On 8/3/23 6:41 AM, Geliang Tang wrote:
Add a hook named update_socket_protocol in __sys_socket(), for bpf progs to attach to and update socket protocol. One user case is to force legacy TCP apps to create and use MPTCP sockets instead of TCP ones.
Define a mod_ret set named bpf_mptcp_fmodret_ids, add the hook update_socket_protocol into this set, and register it in bpf_mptcp_kfunc_init().
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/79 Acked-by: Matthieu Baerts matthieu.baerts@tessares.net Signed-off-by: Geliang Tang geliang.tang@suse.com
net/mptcp/bpf.c | 17 +++++++++++++++++ net/socket.c | 24 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+)
diff --git a/net/mptcp/bpf.c b/net/mptcp/bpf.c index 5a0a84ad94af..c43aee31014d 100644 --- a/net/mptcp/bpf.c +++ b/net/mptcp/bpf.c @@ -12,6 +12,23 @@ #include <linux/bpf.h> #include "protocol.h" +#ifdef CONFIG_BPF_JIT
Is this necessary? Most other register_btf_* functions do not have a config like this.
+BTF_SET8_START(bpf_mptcp_fmodret_ids) +BTF_ID_FLAGS(func, update_socket_protocol) +BTF_SET8_END(bpf_mptcp_fmodret_ids)
+static const struct btf_kfunc_id_set bpf_mptcp_fmodret_set = {
- .owner = THIS_MODULE,
- .set = &bpf_mptcp_fmodret_ids,
+};
+static int __init bpf_mptcp_kfunc_init(void) +{
- return register_btf_fmodret_id_set(&bpf_mptcp_fmodret_set);
+} +late_initcall(bpf_mptcp_kfunc_init); +#endif /* CONFIG_BPF_JIT */
- struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk) { if (sk && sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP && sk_is_mptcp(sk))
diff --git a/net/socket.c b/net/socket.c index 2b0e54b2405c..9f98ced88ac5 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1644,11 +1644,35 @@ struct file *__sys_socket_file(int family, int type, int protocol) return sock_alloc_file(sock, flags, NULL); } +/* A hook for bpf progs to attach to and update socket protocol.
- A static noinline declaration here could cause the compiler to
- optimize away the function. A global noinline declaration will
- keep the definition, but may optimize away the callsite.
- Therefore, __weak is needed to ensure that the call is still
- emitted, by telling the compiler that we don't know what the
- function might eventually be.
- __diag_* below are needed to dismiss the missing prototype warning.
- */
+__diag_push(); +__diag_ignore_all("-Wmissing-prototypes",
"kfuncs which will be used in BPF programs");
+__weak noinline int update_socket_protocol(int family, int type, int protocol) +{
- return protocol;
+}
+__diag_pop();
- int __sys_socket(int family, int type, int protocol) { struct socket *sock; int flags;
- protocol = update_socket_protocol(family, type, protocol); sock = __sys_socket_create(family, type, protocol); if (IS_ERR(sock)) return PTR_ERR(sock);
Use rand() to generate a random netns name instead of using the fixed name "mptcp_ns" for every test.
By doing that, we can re-launch the test even if there was an issue removing the previous netns or if by accident, a netns with this generic name already existed on the system.
Note that using a different name each will also help adding more subtests in future commits.
Signed-off-by: Geliang Tang geliang.tang@suse.com Reviewed-by: Matthieu Baerts matthieu.baerts@tessares.net --- tools/testing/selftests/bpf/prog_tests/mptcp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c index cd0c42fff7c0..4ccca3d39a8f 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -7,7 +7,7 @@ #include "network_helpers.h" #include "mptcp_sock.skel.h"
-#define NS_TEST "mptcp_ns" +char NS_TEST[32];
#ifndef TCP_CA_NAME_MAX #define TCP_CA_NAME_MAX 16 @@ -147,6 +147,8 @@ static void test_base(void) if (!ASSERT_GE(cgroup_fd, 0, "test__join_cgroup")) return;
+ srand(time(NULL)); + snprintf(NS_TEST, sizeof(NS_TEST), "mptcp_ns_%d", rand()); SYS(fail, "ip netns add %s", NS_TEST); SYS(fail, "ip -net %s link set dev lo up", NS_TEST);
On 8/3/23 6:41 AM, Geliang Tang wrote:
Use rand() to generate a random netns name instead of using the fixed name "mptcp_ns" for every test.
By doing that, we can re-launch the test even if there was an issue removing the previous netns or if by accident, a netns with this generic name already existed on the system.
Note that using a different name each will also help adding more subtests in future commits.
Signed-off-by: Geliang Tang geliang.tang@suse.com Reviewed-by: Matthieu Baerts matthieu.baerts@tessares.net
Acked-by: Yonghong Song yonghong.song@linux.dev
Add two netns helpers for mptcp tests: create_netns() and cleanup_netns(). Use them in test_base().
These new helpers will be re-used in the following commits introducing new tests.
Signed-off-by: Geliang Tang geliang.tang@suse.com Reviewed-by: Matthieu Baerts matthieu.baerts@tessares.net --- .../testing/selftests/bpf/prog_tests/mptcp.c | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c index 4ccca3d39a8f..4407bd5c9e9a 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -22,6 +22,26 @@ struct mptcp_storage { char ca_name[TCP_CA_NAME_MAX]; };
+static struct nstoken *create_netns(void) +{ + srand(time(NULL)); + snprintf(NS_TEST, sizeof(NS_TEST), "mptcp_ns_%d", rand()); + SYS(fail, "ip netns add %s", NS_TEST); + SYS(fail, "ip -net %s link set dev lo up", NS_TEST); + + return open_netns(NS_TEST); +fail: + return NULL; +} + +static void cleanup_netns(struct nstoken *nstoken) +{ + if (nstoken) + close_netns(nstoken); + + SYS_NOFAIL("ip netns del %s &> /dev/null", NS_TEST); +} + static int verify_tsk(int map_fd, int client_fd) { int err, cfd = client_fd; @@ -147,13 +167,8 @@ static void test_base(void) if (!ASSERT_GE(cgroup_fd, 0, "test__join_cgroup")) return;
- srand(time(NULL)); - snprintf(NS_TEST, sizeof(NS_TEST), "mptcp_ns_%d", rand()); - SYS(fail, "ip netns add %s", NS_TEST); - SYS(fail, "ip -net %s link set dev lo up", NS_TEST); - - nstoken = open_netns(NS_TEST); - if (!ASSERT_OK_PTR(nstoken, "open_netns")) + nstoken = create_netns(); + if (!ASSERT_OK_PTR(nstoken, "create_netns")) goto fail;
/* without MPTCP */ @@ -176,11 +191,7 @@ static void test_base(void) close(server_fd);
fail: - if (nstoken) - close_netns(nstoken); - - SYS_NOFAIL("ip netns del " NS_TEST " &> /dev/null"); - + cleanup_netns(nstoken); close(cgroup_fd); }
On 8/3/23 6:41 AM, Geliang Tang wrote:
Add two netns helpers for mptcp tests: create_netns() and cleanup_netns(). Use them in test_base().
These new helpers will be re-used in the following commits introducing new tests.
Signed-off-by: Geliang Tang geliang.tang@suse.com Reviewed-by: Matthieu Baerts matthieu.baerts@tessares.net
Acked-by: Yonghong Song yonghong.song@linux.dev
Implement a new test program mptcpify: if the family is AF_INET or AF_INET6, the type is SOCK_STREAM, and the protocol ID is 0 or IPPROTO_TCP, set it to IPPROTO_MPTCP. It will be hooked in update_socket_protocol().
Extend the MPTCP test base, add a selftest test_mptcpify() for the mptcpify case. Open and load the mptcpify test prog to mptcpify the TCP sockets dynamically, then use start_server() and connect_to_fd() to create a TCP socket, but actually what's created is an MPTCP socket, which can be verified through the outputs of 'ss' and 'nstat' commands.
Reviewed-by: Matthieu Baerts matthieu.baerts@tessares.net Signed-off-by: Geliang Tang geliang.tang@suse.com --- .../testing/selftests/bpf/prog_tests/mptcp.c | 94 +++++++++++++++++++ tools/testing/selftests/bpf/progs/mptcpify.c | 25 +++++ 2 files changed, 119 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/mptcpify.c
diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c index 4407bd5c9e9a..caab3aa6a162 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -6,6 +6,7 @@ #include "cgroup_helpers.h" #include "network_helpers.h" #include "mptcp_sock.skel.h" +#include "mptcpify.skel.h"
char NS_TEST[32];
@@ -195,8 +196,101 @@ static void test_base(void) close(cgroup_fd); }
+static void send_byte(int fd) +{ + char b = 0x55; + + ASSERT_EQ(write(fd, &b, sizeof(b)), 1, "send single byte"); +} + +static int verify_mptcpify(void) +{ + char cmd[256]; + int err = 0; + + snprintf(cmd, sizeof(cmd), + "ip netns exec %s ss -tOni | grep -q '%s'", + NS_TEST, "tcp-ulp-mptcp"); + if (!ASSERT_OK(system(cmd), "No tcp-ulp-mptcp found!")) + err++; + + snprintf(cmd, sizeof(cmd), + "ip netns exec %s nstat -asz %s | awk '%s' | grep -q '%s'", + NS_TEST, "MPTcpExtMPCapableSYNACKRX", + "NR==1 {next} {print $2}", "1"); + if (!ASSERT_OK(system(cmd), "No MPTcpExtMPCapableSYNACKRX found!")) + err++; + + return err; +} + +static int run_mptcpify(int cgroup_fd) +{ + int server_fd, client_fd, prog_fd, err = 0; + struct mptcpify *mptcpify_skel; + + mptcpify_skel = mptcpify__open_and_load(); + if (!ASSERT_OK_PTR(mptcpify_skel, "skel_open_load")) + return -EIO; + + err = mptcpify__attach(mptcpify_skel); + if (!ASSERT_OK(err, "skel_attach")) + goto out; + + prog_fd = bpf_program__fd(mptcpify_skel->progs.mptcpify); + if (!ASSERT_GE(prog_fd, 0, "bpf_program__fd")) { + err = -EIO; + goto out; + } + + /* without MPTCP */ + server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0); + if (!ASSERT_GE(server_fd, 0, "start_server")) { + err = -EIO; + goto out; + } + + client_fd = connect_to_fd(server_fd, 0); + if (!ASSERT_GE(client_fd, 0, "connect to fd")) { + err = -EIO; + goto close_server; + } + + send_byte(client_fd); + err += verify_mptcpify(); + + close(client_fd); +close_server: + close(server_fd); +out: + mptcpify__destroy(mptcpify_skel); + return err; +} + +static void test_mptcpify(void) +{ + struct nstoken *nstoken = NULL; + int cgroup_fd; + + cgroup_fd = test__join_cgroup("/mptcpify"); + if (!ASSERT_GE(cgroup_fd, 0, "test__join_cgroup")) + return; + + nstoken = create_netns(); + if (!ASSERT_OK_PTR(nstoken, "create_netns")) + goto fail; + + ASSERT_OK(run_mptcpify(cgroup_fd), "run_mptcpify"); + +fail: + cleanup_netns(nstoken); + close(cgroup_fd); +} + void test_mptcp(void) { if (test__start_subtest("base")) test_base(); + if (test__start_subtest("mptcpify")) + test_mptcpify(); } diff --git a/tools/testing/selftests/bpf/progs/mptcpify.c b/tools/testing/selftests/bpf/progs/mptcpify.c new file mode 100644 index 000000000000..9cf1febe982d --- /dev/null +++ b/tools/testing/selftests/bpf/progs/mptcpify.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023, SUSE. */ + +#include <linux/bpf.h> +#include <bpf/bpf_tracing.h> + +char _license[] SEC("license") = "GPL"; + +#define AF_INET 2 +#define AF_INET6 10 +#define SOCK_STREAM 1 +#define IPPROTO_TCP 6 +#define IPPROTO_MPTCP 262 + +SEC("fmod_ret/update_socket_protocol") +int BPF_PROG(mptcpify, int family, int type, int protocol) +{ + if ((family == AF_INET || family == AF_INET6) && + type == SOCK_STREAM && + (!protocol || protocol == IPPROTO_TCP)) { + return IPPROTO_MPTCP; + } + + return protocol; +}
On 8/3/23 6:41 AM, Geliang Tang wrote:
Implement a new test program mptcpify: if the family is AF_INET or AF_INET6, the type is SOCK_STREAM, and the protocol ID is 0 or IPPROTO_TCP, set it to IPPROTO_MPTCP. It will be hooked in update_socket_protocol().
Extend the MPTCP test base, add a selftest test_mptcpify() for the mptcpify case. Open and load the mptcpify test prog to mptcpify the TCP sockets dynamically, then use start_server() and connect_to_fd() to create a TCP socket, but actually what's created is an MPTCP socket, which can be verified through the outputs of 'ss' and 'nstat' commands.
Reviewed-by: Matthieu Baerts matthieu.baerts@tessares.net Signed-off-by: Geliang Tang geliang.tang@suse.com
.../testing/selftests/bpf/prog_tests/mptcp.c | 94 +++++++++++++++++++ tools/testing/selftests/bpf/progs/mptcpify.c | 25 +++++ 2 files changed, 119 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/mptcpify.c
diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c index 4407bd5c9e9a..caab3aa6a162 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -6,6 +6,7 @@ #include "cgroup_helpers.h" #include "network_helpers.h" #include "mptcp_sock.skel.h" +#include "mptcpify.skel.h" char NS_TEST[32]; @@ -195,8 +196,101 @@ static void test_base(void) close(cgroup_fd); } +static void send_byte(int fd) +{
- char b = 0x55;
- ASSERT_EQ(write(fd, &b, sizeof(b)), 1, "send single byte");
+}
+static int verify_mptcpify(void) +{
- char cmd[256];
- int err = 0;
- snprintf(cmd, sizeof(cmd),
"ip netns exec %s ss -tOni | grep -q '%s'",
NS_TEST, "tcp-ulp-mptcp");
- if (!ASSERT_OK(system(cmd), "No tcp-ulp-mptcp found!"))
err++;
- snprintf(cmd, sizeof(cmd),
"ip netns exec %s nstat -asz %s | awk '%s' | grep -q '%s'",
NS_TEST, "MPTcpExtMPCapableSYNACKRX",
"NR==1 {next} {print $2}", "1");
- if (!ASSERT_OK(system(cmd), "No MPTcpExtMPCapableSYNACKRX found!"))
err++;
- return err;
+}
+static int run_mptcpify(int cgroup_fd) +{
- int server_fd, client_fd, prog_fd, err = 0;
- struct mptcpify *mptcpify_skel;
- mptcpify_skel = mptcpify__open_and_load();
- if (!ASSERT_OK_PTR(mptcpify_skel, "skel_open_load"))
return -EIO;
- err = mptcpify__attach(mptcpify_skel);
- if (!ASSERT_OK(err, "skel_attach"))
goto out;
- prog_fd = bpf_program__fd(mptcpify_skel->progs.mptcpify);
- if (!ASSERT_GE(prog_fd, 0, "bpf_program__fd")) {
err = -EIO;
goto out;
- }
load success means prog_fd is always valid. So above ASSERT_GE not needed.
- /* without MPTCP */
- server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0);
- if (!ASSERT_GE(server_fd, 0, "start_server")) {
err = -EIO;
goto out;
- }
- client_fd = connect_to_fd(server_fd, 0);
- if (!ASSERT_GE(client_fd, 0, "connect to fd")) {
err = -EIO;
goto close_server;
- }
- send_byte(client_fd);
- err += verify_mptcpify();
- close(client_fd);
+close_server:
- close(server_fd);
+out:
- mptcpify__destroy(mptcpify_skel);
- return err;
+}
+static void test_mptcpify(void) +{
- struct nstoken *nstoken = NULL;
- int cgroup_fd;
- cgroup_fd = test__join_cgroup("/mptcpify");
- if (!ASSERT_GE(cgroup_fd, 0, "test__join_cgroup"))
return;
- nstoken = create_netns();
- if (!ASSERT_OK_PTR(nstoken, "create_netns"))
goto fail;
- ASSERT_OK(run_mptcpify(cgroup_fd), "run_mptcpify");
+fail:
- cleanup_netns(nstoken);
- close(cgroup_fd);
+}
- void test_mptcp(void) { if (test__start_subtest("base")) test_base();
- if (test__start_subtest("mptcpify"))
}test_mptcpify();
diff --git a/tools/testing/selftests/bpf/progs/mptcpify.c b/tools/testing/selftests/bpf/progs/mptcpify.c new file mode 100644 index 000000000000..9cf1febe982d --- /dev/null +++ b/tools/testing/selftests/bpf/progs/mptcpify.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023, SUSE. */
+#include <linux/bpf.h> +#include <bpf/bpf_tracing.h>
+char _license[] SEC("license") = "GPL";
+#define AF_INET 2 +#define AF_INET6 10 +#define SOCK_STREAM 1 +#define IPPROTO_TCP 6 +#define IPPROTO_MPTCP 262
To avoid the above macros, you can use vmlinux.h and bpf_tracing_net.h.
+SEC("fmod_ret/update_socket_protocol") +int BPF_PROG(mptcpify, int family, int type, int protocol) +{
- if ((family == AF_INET || family == AF_INET6) &&
type == SOCK_STREAM &&
(!protocol || protocol == IPPROTO_TCP)) {
return IPPROTO_MPTCP;
- }
- return protocol;
+}
On 8/3/23 6:41 AM, Geliang Tang wrote:
Implement a new test program mptcpify: if the family is AF_INET or AF_INET6, the type is SOCK_STREAM, and the protocol ID is 0 or IPPROTO_TCP, set it to IPPROTO_MPTCP. It will be hooked in update_socket_protocol().
Extend the MPTCP test base, add a selftest test_mptcpify() for the mptcpify case. Open and load the mptcpify test prog to mptcpify the TCP sockets dynamically, then use start_server() and connect_to_fd() to create a TCP socket, but actually what's created is an MPTCP socket, which can be verified through the outputs of 'ss' and 'nstat' commands.
Reviewed-by: Matthieu Baerts matthieu.baerts@tessares.net Signed-off-by: Geliang Tang geliang.tang@suse.com
.../testing/selftests/bpf/prog_tests/mptcp.c | 94 +++++++++++++++++++ tools/testing/selftests/bpf/progs/mptcpify.c | 25 +++++ 2 files changed, 119 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/mptcpify.c
diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c index 4407bd5c9e9a..caab3aa6a162 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -6,6 +6,7 @@ #include "cgroup_helpers.h" #include "network_helpers.h" #include "mptcp_sock.skel.h" +#include "mptcpify.skel.h" char NS_TEST[32]; @@ -195,8 +196,101 @@ static void test_base(void) close(cgroup_fd); } +static void send_byte(int fd) +{
- char b = 0x55;
- ASSERT_EQ(write(fd, &b, sizeof(b)), 1, "send single byte");
+}
+static int verify_mptcpify(void) +{
- char cmd[256];
- int err = 0;
- snprintf(cmd, sizeof(cmd),
"ip netns exec %s ss -tOni | grep -q '%s'",
NS_TEST, "tcp-ulp-mptcp");
Could you show what is the expected output from the above command line ip netns exec %s ss -tOni ? This way, users can easily reason about the ss states based on tests.
- if (!ASSERT_OK(system(cmd), "No tcp-ulp-mptcp found!"))
err++;
- snprintf(cmd, sizeof(cmd),
"ip netns exec %s nstat -asz %s | awk '%s' | grep -q '%s'",
NS_TEST, "MPTcpExtMPCapableSYNACKRX",
"NR==1 {next} {print $2}", "1");
The same thing here. Could you show the expected output with ip netns exec %s nstat -asz %s ?
- if (!ASSERT_OK(system(cmd), "No MPTcpExtMPCapableSYNACKRX found!"))
err++;
- return err;
+}
[...]
Hi Yonghong,
On Thu, Aug 03, 2023 at 06:23:57PM -0700, Yonghong Song wrote:
On 8/3/23 6:41 AM, Geliang Tang wrote:
Implement a new test program mptcpify: if the family is AF_INET or AF_INET6, the type is SOCK_STREAM, and the protocol ID is 0 or IPPROTO_TCP, set it to IPPROTO_MPTCP. It will be hooked in update_socket_protocol().
Extend the MPTCP test base, add a selftest test_mptcpify() for the mptcpify case. Open and load the mptcpify test prog to mptcpify the TCP sockets dynamically, then use start_server() and connect_to_fd() to create a TCP socket, but actually what's created is an MPTCP socket, which can be verified through the outputs of 'ss' and 'nstat' commands.
Reviewed-by: Matthieu Baerts matthieu.baerts@tessares.net Signed-off-by: Geliang Tang geliang.tang@suse.com
.../testing/selftests/bpf/prog_tests/mptcp.c | 94 +++++++++++++++++++ tools/testing/selftests/bpf/progs/mptcpify.c | 25 +++++ 2 files changed, 119 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/mptcpify.c
diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c index 4407bd5c9e9a..caab3aa6a162 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -6,6 +6,7 @@ #include "cgroup_helpers.h" #include "network_helpers.h" #include "mptcp_sock.skel.h" +#include "mptcpify.skel.h" char NS_TEST[32]; @@ -195,8 +196,101 @@ static void test_base(void) close(cgroup_fd); } +static void send_byte(int fd) +{
- char b = 0x55;
- ASSERT_EQ(write(fd, &b, sizeof(b)), 1, "send single byte");
+}
+static int verify_mptcpify(void) +{
- char cmd[256];
- int err = 0;
- snprintf(cmd, sizeof(cmd),
"ip netns exec %s ss -tOni | grep -q '%s'",
NS_TEST, "tcp-ulp-mptcp");
Could you show what is the expected output from the above command line ip netns exec %s ss -tOni ? This way, users can easily reason about the ss states based on tests.
There're too many items in the output of command 'ip netns exec %s ss -tOni':
''' State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ESTAB 0 0 127.0.0.1:42225 127.0.0.1:44180 cubic wscale:7,7 rto:201 rtt:0.034/0.017 ato:40 mss:16640 pmtu:65535 rcvmss:536 advmss:65483 cwnd:10 bytes_received:1 segs_out:1 segs_in:3 data_segs_in:1 send 39152941176bps lastsnd:7 lastrcv:7 lastack:7 pacing_rate 78305882352bps delivered:1 app_limited rcv_space:33280 rcv_ssthresh:33280 minrtt:0.034 snd_wnd:33280 tcp-ulp-mptcp flags:Mec token:0000(id:0)/3a1e0d3c(id:0) seq:c2802f11c5228db6 sfseq:1 ssnoff:49d3c135 maplen:1 ESTAB 0 0 127.0.0.1:44180 127.0.0.1:42225 cubic wscale:7,7 rto:201 rtt:0.036/0.02 mss:16640 pmtu:65535 rcvmss:536 advmss:65483 cwnd:10 bytes_sent:1 bytes_acked:2 segs_out:3 segs_in:2 data_segs_out:1 send 36977777778bps lastsnd:7 lastrcv:7 lastack:7 pacing_rate 72200677960bps delivery_rate 8874666664bps delivered:2 rcv_space:33280 rcv_ssthresh:33280 minrtt:0.015 snd_wnd:33280 tcp-ulp-mptcp flags:Mmec token:0000(id:0)/39429ce(id:0) seq:e3ed00de37c805c sfseq:1 ssnoff:d4e4d561 maplen:0 '''
We only care about this 'tcp-ulp-mptcp' item.
Show all output will confuse users. So we just pick and test the only item we care.
- if (!ASSERT_OK(system(cmd), "No tcp-ulp-mptcp found!"))
err++;
- snprintf(cmd, sizeof(cmd),
"ip netns exec %s nstat -asz %s | awk '%s' | grep -q '%s'",
NS_TEST, "MPTcpExtMPCapableSYNACKRX",
"NR==1 {next} {print $2}", "1");
The same thing here. Could you show the expected output with ip netns exec %s nstat -asz %s ?
The output of 'ip netns exec %s nstat -asz %s' is:
''' #kernel MPTcpExtMPCapableSYNACKRX 1 0.0 '''
The same, we only check if it contains an MPTcpExtMPCapableSYNACKRX, not show the output.
-Geliang
- if (!ASSERT_OK(system(cmd), "No MPTcpExtMPCapableSYNACKRX found!"))
err++;
- return err;
+}
[...]
On 8/3/23 7:24 PM, Geliang Tang wrote:
Hi Yonghong,
On Thu, Aug 03, 2023 at 06:23:57PM -0700, Yonghong Song wrote:
On 8/3/23 6:41 AM, Geliang Tang wrote:
Implement a new test program mptcpify: if the family is AF_INET or AF_INET6, the type is SOCK_STREAM, and the protocol ID is 0 or IPPROTO_TCP, set it to IPPROTO_MPTCP. It will be hooked in update_socket_protocol().
Extend the MPTCP test base, add a selftest test_mptcpify() for the mptcpify case. Open and load the mptcpify test prog to mptcpify the TCP sockets dynamically, then use start_server() and connect_to_fd() to create a TCP socket, but actually what's created is an MPTCP socket, which can be verified through the outputs of 'ss' and 'nstat' commands.
Reviewed-by: Matthieu Baerts matthieu.baerts@tessares.net Signed-off-by: Geliang Tang geliang.tang@suse.com
.../testing/selftests/bpf/prog_tests/mptcp.c | 94 +++++++++++++++++++ tools/testing/selftests/bpf/progs/mptcpify.c | 25 +++++ 2 files changed, 119 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/mptcpify.c
diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c index 4407bd5c9e9a..caab3aa6a162 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -6,6 +6,7 @@ #include "cgroup_helpers.h" #include "network_helpers.h" #include "mptcp_sock.skel.h" +#include "mptcpify.skel.h" char NS_TEST[32]; @@ -195,8 +196,101 @@ static void test_base(void) close(cgroup_fd); } +static void send_byte(int fd) +{
- char b = 0x55;
- ASSERT_EQ(write(fd, &b, sizeof(b)), 1, "send single byte");
+}
+static int verify_mptcpify(void) +{
- char cmd[256];
- int err = 0;
- snprintf(cmd, sizeof(cmd),
"ip netns exec %s ss -tOni | grep -q '%s'",
NS_TEST, "tcp-ulp-mptcp");
Could you show what is the expected output from the above command line ip netns exec %s ss -tOni ? This way, users can easily reason about the ss states based on tests.
There're too many items in the output of command 'ip netns exec %s ss -tOni':
''' State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ESTAB 0 0 127.0.0.1:42225 127.0.0.1:44180 cubic wscale:7,7 rto:201 rtt:0.034/0.017 ato:40 mss:16640 pmtu:65535 rcvmss:536 advmss:65483 cwnd:10 bytes_received:1 segs_out:1 segs_in:3 data_segs_in:1 send 39152941176bps lastsnd:7 lastrcv:7 lastack:7 pacing_rate 78305882352bps delivered:1 app_limited rcv_space:33280 rcv_ssthresh:33280 minrtt:0.034 snd_wnd:33280 tcp-ulp-mptcp flags:Mec token:0000(id:0)/3a1e0d3c(id:0) seq:c2802f11c5228db6 sfseq:1 ssnoff:49d3c135 maplen:1 ESTAB 0 0 127.0.0.1:44180 127.0.0.1:42225 cubic wscale:7,7 rto:201 rtt:0.036/0.02 mss:16640 pmtu:65535 rcvmss:536 advmss:65483 cwnd:10 bytes_sent:1 bytes_acked:2 segs_out:3 segs_in:2 data_segs_out:1 send 36977777778bps lastsnd:7 lastrcv:7 lastack:7 pacing_rate 72200677960bps delivery_rate 8874666664bps delivered:2 rcv_space:33280 rcv_ssthresh:33280 minrtt:0.015 snd_wnd:33280 tcp-ulp-mptcp flags:Mmec token:0000(id:0)/39429ce(id:0) seq:e3ed00de37c805c sfseq:1 ssnoff:d4e4d561 maplen:0 '''
We only care about this 'tcp-ulp-mptcp' item.
Show all output will confuse users. So we just pick and test the only item we care.
Thanks. Originally I thought at least we should put one line in the comment which has 'tcp-ulp-mptcp' like
ESTAB 0 0 127.0.0.1:44180 127.0.0.1:42225 cubic wscale:7,7 rto:201 rtt:0.036/0.02 mss:16640 pmtu:65535 rcvmss:536 advmss:65483 cwnd:10 bytes_sent:1 bytes_acked:2 segs_out:3 segs_in:2 data_segs_out:1 send 36977777778bps lastsnd:7 lastrcv:7 lastack:7 pacing_rate 72200677960bps delivery_rate 8874666664bps delivered:2 rcv_space:33280 rcv_ssthresh:33280 minrtt:0.015 snd_wnd:33280 tcp-ulp-mptcp flags:Mmec token:0000(id:0)/39429ce(id:0) seq:e3ed00de37c805c sfseq:1 ssnoff:d4e4d561 maplen:0
or simplified version
ESTAB 0 0 127.0.0.1:44180 127.0.0.1:42225 cubic ... tcp-ulp-mptcp flags:Mmec ...
But people familiar with 'ss' should be able to dump it and get the above (maybe without tcp-ulp-mptcp) easily. So I am okay with no additional comments.
- if (!ASSERT_OK(system(cmd), "No tcp-ulp-mptcp found!"))
err++;
- snprintf(cmd, sizeof(cmd),
"ip netns exec %s nstat -asz %s | awk '%s' | grep -q '%s'",
NS_TEST, "MPTcpExtMPCapableSYNACKRX",
"NR==1 {next} {print $2}", "1");
The same thing here. Could you show the expected output with ip netns exec %s nstat -asz %s ?
The output of 'ip netns exec %s nstat -asz %s' is:
''' #kernel MPTcpExtMPCapableSYNACKRX 1 0.0 '''
The same, we only check if it contains an MPTcpExtMPCapableSYNACKRX, not show the output.
-Geliang
- if (!ASSERT_OK(system(cmd), "No MPTcpExtMPCapableSYNACKRX found!"))
err++;
- return err;
+}
[...]
linux-kselftest-mirror@lists.linaro.org