From ac2f154dd516f9702763a22018a77073f5d752dd Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 18 Mar 2024 13:45:26 +0800 Subject: [PATCH] bpf: Add mptcp [gs]etsockopt per subflow Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/76 Signed-off-by: Geliang Tang --- .../testing/selftests/bpf/prog_tests/mptcp.c | 70 ++++++++++++++ .../bpf/progs/mptcp_set_sf_sockopt_kern.c | 96 +++++++++++++++++++ .../testing/selftests/bpf/progs/mptcp_sock.c | 14 +++ 3 files changed, 180 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/mptcp_set_sf_sockopt_kern.c diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c index c0a7cb22d8e5f..b982ad7fc21c2 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -10,6 +10,7 @@ #include "network_helpers.h" #include "mptcp_sock.skel.h" #include "mptcpify.skel.h" +#include "mptcp_set_sf_sockopt_kern.skel.h" #include "mptcp_bpf_first.skel.h" #include "mptcp_bpf_bkup.skel.h" #include "mptcp_bpf_rr.skel.h" @@ -329,6 +330,73 @@ static void test_mptcpify(void) close(cgroup_fd); } +static int getsetsockopt(int map_fd) +{ + int server_fd, client_fd, err = 0; + + server_fd = start_mptcp_server(AF_INET, NULL, 0, 0); + if (!ASSERT_GE(server_fd, 0, "start_mptcp_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); + SYS(out, "ip netns exec %s ss -Menita", NS_TEST); + + close(client_fd); +close_server: + close(server_fd); +out: + return err; +} + +static void run_test_sockopt(int cgroup_fd) +{ + int prog_fd, map_fd, err; + struct mptcp_set_sf_sockopt_kern *skel; + + skel = mptcp_set_sf_sockopt_kern__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_load")) + goto cleanup; + + prog_fd = bpf_program__fd(skel->progs.mark_mptcp_sf); + map_fd = bpf_map__fd(skel->maps.mptcp_sf); + err = bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_SOCK_OPS, 0); + if (!ASSERT_OK(err, "bpf_prog_attach")) + goto cleanup; + + ASSERT_OK(getsetsockopt(map_fd), "getsetsockopt"); + +cleanup: + mptcp_set_sf_sockopt_kern__destroy(skel); +} + +void test_sockopt_mptcp(void) +{ + struct nstoken *nstoken = NULL; + int cgroup_fd; + + cgroup_fd = test__join_cgroup("/sockopt_mptcp"); + if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup /sockopt_mptcp")) + return; + + nstoken = create_netns(); + if (!ASSERT_OK_PTR(nstoken, "create_netns")) + goto fail; + + run_test_sockopt(cgroup_fd); + +fail: + cleanup_netns(nstoken); + close(cgroup_fd); +} + static const unsigned int total_bytes = 10 * 1024 * 1024; static int stop, duration; @@ -693,6 +761,8 @@ void test_mptcp(void) test_base(); if (test__start_subtest("mptcpify")) test_mptcpify(); + if (test__start_subtest("sockopt")) + test_sockopt_mptcp(); if (test__start_subtest("default")) test_default(); if (test__start_subtest("first")) diff --git a/tools/testing/selftests/bpf/progs/mptcp_set_sf_sockopt_kern.c b/tools/testing/selftests/bpf/progs/mptcp_set_sf_sockopt_kern.c new file mode 100644 index 0000000000000..eb2b3bb034083 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/mptcp_set_sf_sockopt_kern.c @@ -0,0 +1,96 @@ +#include // SOL_SOCKET, SO_MARK, ... +#include // TCP_CONGESTION +#include +#include +#include "bpf_tcp_helpers.h" + +char _license[] SEC("license") = "GPL"; + +#ifndef SOL_TCP +#define SOL_TCP 6 +#endif + +#ifndef TCP_CA_NAME_MAX +#define TCP_CA_NAME_MAX 16 +#endif + +char cc[TCP_CA_NAME_MAX] = "reno"; + +/* Associate a subflow counter to each token */ +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); + __uint(max_entries, 100); +} mptcp_sf SEC(".maps"); + +#define DEBUG 1 + +#ifdef DEBUG +char fmt1[] = "Mark <%u> : return code <%i>\n"; +char fmt2[] = "Failed to get bpf_sock\n"; +char fmt3[] = "Failed to get bpf_mptcp_sock\n"; +char fmt4[] = "Failed to update sockopt cc err=%d\n"; +char fmt5[] = "Failed to update sockopt mark\n"; + +#define pr_debug(msg, ...) bpf_trace_printk(msg, sizeof(msg), ##__VA_ARGS__); + +#else + +#define pr_debug(msg, ...) + +#endif + +SEC("sockops") +int mark_mptcp_sf(struct bpf_sock_ops *skops) +{ + __u32 init = 1, key, mark, *cnt; + int err; + + if (skops->op != BPF_SOCK_OPS_TCP_CONNECT_CB) + goto out; + + struct bpf_sock *sk = skops->sk; + if (!sk) { + pr_debug(fmt2); + goto out; + } + + struct mptcp_sock *msk = bpf_skc_to_mptcp_sock(sk); + if (!msk) { + pr_debug(fmt3); + goto out; + } + + key = msk->token; + cnt = bpf_map_lookup_elem(&mptcp_sf, &key); + + if (cnt) { + /* A new subflow is added to an existing MPTCP connection */ + __sync_fetch_and_add(cnt, 1); + mark = *cnt; + } else { + /* A new MPTCP connection is just initiated and this is its primary + * subflow + */ + bpf_map_update_elem(&mptcp_sf, &key, &init, BPF_ANY); + mark = init; + } + + /* Set the mark of the subflow's socket to its apparition order */ + err = bpf_setsockopt(skops, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)); + pr_debug(fmt1, mark, err); + + if (err < 0) + pr_debug(fmt5); + + if (mark == 1) + err = err ?: bpf_setsockopt(skops, SOL_TCP, TCP_CONGESTION, cc, + TCP_CA_NAME_MAX); + + if (err < 0) + pr_debug(fmt4, err); + +out: + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/mptcp_sock.c b/tools/testing/selftests/bpf/progs/mptcp_sock.c index 91a0d7eff2ac7..bb9b075f9edd3 100644 --- a/tools/testing/selftests/bpf/progs/mptcp_sock.c +++ b/tools/testing/selftests/bpf/progs/mptcp_sock.c @@ -9,6 +9,8 @@ char _license[] SEC("license") = "GPL"; __u32 token = 0; +int page_size = 0; /* userspace should set it */ + struct mptcp_storage { __u32 invoked; __u32 is_mptcp; @@ -86,3 +88,15 @@ int BPF_PROG(trace_mptcp_pm_new_connection, struct mptcp_sock *msk, return 0; } + +SEC("cgroup/getsockopt") +int _getsockopt(struct bpf_sockopt *ctx) +{ + return 1; +} + +SEC("cgroup/setsockopt") +int _setsockopt(struct bpf_sockopt *ctx) +{ + return 1; +}