Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add MPTCP test base

This patch adds a base for MPTCP specific tests.

It is currently limited to the is_mptcp field in case of plain TCP
connection because there is no easy way to get the subflow sk from a msk
in userspace. This implies that we cannot lookup the sk_storage attached
to the subflow sk in the sockops program.

v4:
- add copyright 2022 (Andrii)
- use ASSERT_* instead of CHECK_FAIL (Andrii)
- drop SEC("version") (Andrii)
- use is_mptcp in tcp_sock, instead of bpf_tcp_sock (Martin & Andrii)

v5:
- Drop connect_to_mptcp_fd (Martin)
- Use BPF test skeleton (Andrii)
- Use ASSERT_EQ (Andrii)
- Drop the 'msg' parameter of verify_sk

Co-developed-by: Geliang Tang <geliang.tang@suse.com>
Signed-off-by: Geliang Tang <geliang.tang@suse.com>
Signed-off-by: Nicolas Rybowski <nicolas.rybowski@tessares.net>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Link: https://lore.kernel.org/bpf/20220519233016.105670-4-mathew.j.martineau@linux.intel.com

authored by

Nicolas Rybowski and committed by
Andrii Nakryiko
8039d353 d3294cb1

+201 -9
+1
MAINTAINERS
··· 13780 13780 F: include/trace/events/mptcp.h 13781 13781 F: include/uapi/linux/mptcp.h 13782 13782 F: net/mptcp/ 13783 + F: tools/testing/selftests/bpf/*/*mptcp*.c 13783 13784 F: tools/testing/selftests/net/mptcp/ 13784 13785 13785 13786 NETWORKING [TCP]
+1
tools/testing/selftests/bpf/bpf_tcp_helpers.h
··· 81 81 __u32 lsndtime; 82 82 __u32 prior_cwnd; 83 83 __u64 tcp_mstamp; /* most recent packet received/sent */ 84 + bool is_mptcp; 84 85 } __attribute__((preserve_access_index)); 85 86 86 87 static __always_inline struct inet_connection_sock *inet_csk(const struct sock *sk)
+1
tools/testing/selftests/bpf/config
··· 56 56 CONFIG_FPROBE=y 57 57 CONFIG_IKCONFIG=y 58 58 CONFIG_IKCONFIG_PROC=y 59 + CONFIG_MPTCP=y
+31 -9
tools/testing/selftests/bpf/network_helpers.c
··· 21 21 #include "network_helpers.h" 22 22 #include "test_progs.h" 23 23 24 + #ifndef IPPROTO_MPTCP 25 + #define IPPROTO_MPTCP 262 26 + #endif 27 + 24 28 #define clean_errno() (errno == 0 ? "None" : strerror(errno)) 25 29 #define log_err(MSG, ...) ({ \ 26 30 int __save = errno; \ ··· 77 73 78 74 #define save_errno_close(fd) ({ int __save = errno; close(fd); errno = __save; }) 79 75 80 - static int __start_server(int type, const struct sockaddr *addr, 76 + static int __start_server(int type, int protocol, const struct sockaddr *addr, 81 77 socklen_t addrlen, int timeout_ms, bool reuseport) 82 78 { 83 79 int on = 1; 84 80 int fd; 85 81 86 - fd = socket(addr->sa_family, type, 0); 82 + fd = socket(addr->sa_family, type, protocol); 87 83 if (fd < 0) { 88 84 log_err("Failed to create server socket"); 89 85 return -1; ··· 117 113 return -1; 118 114 } 119 115 120 - int start_server(int family, int type, const char *addr_str, __u16 port, 121 - int timeout_ms) 116 + static int start_server_proto(int family, int type, int protocol, 117 + const char *addr_str, __u16 port, int timeout_ms) 122 118 { 123 119 struct sockaddr_storage addr; 124 120 socklen_t addrlen; ··· 126 122 if (make_sockaddr(family, addr_str, port, &addr, &addrlen)) 127 123 return -1; 128 124 129 - return __start_server(type, (struct sockaddr *)&addr, 125 + return __start_server(type, protocol, (struct sockaddr *)&addr, 130 126 addrlen, timeout_ms, false); 127 + } 128 + 129 + int start_server(int family, int type, const char *addr_str, __u16 port, 130 + int timeout_ms) 131 + { 132 + return start_server_proto(family, type, 0, addr_str, port, timeout_ms); 133 + } 134 + 135 + int start_mptcp_server(int family, const char *addr_str, __u16 port, 136 + int timeout_ms) 137 + { 138 + return start_server_proto(family, SOCK_STREAM, IPPROTO_MPTCP, addr_str, 139 + port, timeout_ms); 131 140 } 132 141 133 142 int *start_reuseport_server(int family, int type, const char *addr_str, ··· 161 144 if (!fds) 162 145 return NULL; 163 146 164 - fds[0] = __start_server(type, (struct sockaddr *)&addr, addrlen, 147 + fds[0] = __start_server(type, 0, (struct sockaddr *)&addr, addrlen, 165 148 timeout_ms, true); 166 149 if (fds[0] == -1) 167 150 goto close_fds; ··· 171 154 goto close_fds; 172 155 173 156 for (; nr_fds < nr_listens; nr_fds++) { 174 - fds[nr_fds] = __start_server(type, (struct sockaddr *)&addr, 157 + fds[nr_fds] = __start_server(type, 0, (struct sockaddr *)&addr, 175 158 addrlen, timeout_ms, true); 176 159 if (fds[nr_fds] == -1) 177 160 goto close_fds; ··· 264 247 struct sockaddr_storage addr; 265 248 struct sockaddr_in *addr_in; 266 249 socklen_t addrlen, optlen; 267 - int fd, type; 250 + int fd, type, protocol; 268 251 269 252 if (!opts) 270 253 opts = &default_opts; ··· 275 258 return -1; 276 259 } 277 260 261 + if (getsockopt(server_fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &optlen)) { 262 + log_err("getsockopt(SOL_PROTOCOL)"); 263 + return -1; 264 + } 265 + 278 266 addrlen = sizeof(addr); 279 267 if (getsockname(server_fd, (struct sockaddr *)&addr, &addrlen)) { 280 268 log_err("Failed to get server addr"); ··· 287 265 } 288 266 289 267 addr_in = (struct sockaddr_in *)&addr; 290 - fd = socket(addr_in->sin_family, type, 0); 268 + fd = socket(addr_in->sin_family, type, protocol); 291 269 if (fd < 0) { 292 270 log_err("Failed to create client socket"); 293 271 return -1;
+2
tools/testing/selftests/bpf/network_helpers.h
··· 42 42 int settimeo(int fd, int timeout_ms); 43 43 int start_server(int family, int type, const char *addr, __u16 port, 44 44 int timeout_ms); 45 + int start_mptcp_server(int family, const char *addr, __u16 port, 46 + int timeout_ms); 45 47 int *start_reuseport_server(int family, int type, const char *addr_str, 46 48 __u16 port, int timeout_ms, 47 49 unsigned int nr_listens);
+112
tools/testing/selftests/bpf/prog_tests/mptcp.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2020, Tessares SA. */ 3 + /* Copyright (c) 2022, SUSE. */ 4 + 5 + #include <test_progs.h> 6 + #include "cgroup_helpers.h" 7 + #include "network_helpers.h" 8 + #include "mptcp_sock.skel.h" 9 + 10 + struct mptcp_storage { 11 + __u32 invoked; 12 + __u32 is_mptcp; 13 + }; 14 + 15 + static int verify_sk(int map_fd, int client_fd, __u32 is_mptcp) 16 + { 17 + int err, cfd = client_fd; 18 + struct mptcp_storage val; 19 + 20 + if (is_mptcp == 1) 21 + return 0; 22 + 23 + err = bpf_map_lookup_elem(map_fd, &cfd, &val); 24 + if (!ASSERT_OK(err, "bpf_map_lookup_elem")) 25 + return err; 26 + 27 + if (!ASSERT_EQ(val.invoked, 1, "unexpected invoked count")) 28 + err++; 29 + 30 + if (!ASSERT_EQ(val.is_mptcp, 0, "unexpected is_mptcp")) 31 + err++; 32 + 33 + return err; 34 + } 35 + 36 + static int run_test(int cgroup_fd, int server_fd, bool is_mptcp) 37 + { 38 + int client_fd, prog_fd, map_fd, err; 39 + struct mptcp_sock *sock_skel; 40 + 41 + sock_skel = mptcp_sock__open_and_load(); 42 + if (!ASSERT_OK_PTR(sock_skel, "skel_open_load")) 43 + return -EIO; 44 + 45 + prog_fd = bpf_program__fd(sock_skel->progs._sockops); 46 + if (!ASSERT_GE(prog_fd, 0, "bpf_program__fd")) { 47 + err = -EIO; 48 + goto out; 49 + } 50 + 51 + map_fd = bpf_map__fd(sock_skel->maps.socket_storage_map); 52 + if (!ASSERT_GE(map_fd, 0, "bpf_map__fd")) { 53 + err = -EIO; 54 + goto out; 55 + } 56 + 57 + err = bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_SOCK_OPS, 0); 58 + if (!ASSERT_OK(err, "bpf_prog_attach")) 59 + goto out; 60 + 61 + client_fd = connect_to_fd(server_fd, 0); 62 + if (!ASSERT_GE(client_fd, 0, "connect to fd")) { 63 + err = -EIO; 64 + goto out; 65 + } 66 + 67 + err += is_mptcp ? verify_sk(map_fd, client_fd, 1) : 68 + verify_sk(map_fd, client_fd, 0); 69 + 70 + close(client_fd); 71 + 72 + out: 73 + mptcp_sock__destroy(sock_skel); 74 + return err; 75 + } 76 + 77 + static void test_base(void) 78 + { 79 + int server_fd, cgroup_fd; 80 + 81 + cgroup_fd = test__join_cgroup("/mptcp"); 82 + if (!ASSERT_GE(cgroup_fd, 0, "test__join_cgroup")) 83 + return; 84 + 85 + /* without MPTCP */ 86 + server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0); 87 + if (!ASSERT_GE(server_fd, 0, "start_server")) 88 + goto with_mptcp; 89 + 90 + ASSERT_OK(run_test(cgroup_fd, server_fd, false), "run_test tcp"); 91 + 92 + close(server_fd); 93 + 94 + with_mptcp: 95 + /* with MPTCP */ 96 + server_fd = start_mptcp_server(AF_INET, NULL, 0, 0); 97 + if (!ASSERT_GE(server_fd, 0, "start_mptcp_server")) 98 + goto close_cgroup_fd; 99 + 100 + ASSERT_OK(run_test(cgroup_fd, server_fd, true), "run_test mptcp"); 101 + 102 + close(server_fd); 103 + 104 + close_cgroup_fd: 105 + close(cgroup_fd); 106 + } 107 + 108 + void test_mptcp(void) 109 + { 110 + if (test__start_subtest("base")) 111 + test_base(); 112 + }
+53
tools/testing/selftests/bpf/progs/mptcp_sock.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2020, Tessares SA. */ 3 + /* Copyright (c) 2022, SUSE. */ 4 + 5 + #include <linux/bpf.h> 6 + #include <bpf/bpf_helpers.h> 7 + #include "bpf_tcp_helpers.h" 8 + 9 + char _license[] SEC("license") = "GPL"; 10 + 11 + struct mptcp_storage { 12 + __u32 invoked; 13 + __u32 is_mptcp; 14 + }; 15 + 16 + struct { 17 + __uint(type, BPF_MAP_TYPE_SK_STORAGE); 18 + __uint(map_flags, BPF_F_NO_PREALLOC); 19 + __type(key, int); 20 + __type(value, struct mptcp_storage); 21 + } socket_storage_map SEC(".maps"); 22 + 23 + SEC("sockops") 24 + int _sockops(struct bpf_sock_ops *ctx) 25 + { 26 + struct mptcp_storage *storage; 27 + int op = (int)ctx->op; 28 + struct tcp_sock *tsk; 29 + struct bpf_sock *sk; 30 + bool is_mptcp; 31 + 32 + if (op != BPF_SOCK_OPS_TCP_CONNECT_CB) 33 + return 1; 34 + 35 + sk = ctx->sk; 36 + if (!sk) 37 + return 1; 38 + 39 + tsk = bpf_skc_to_tcp_sock(sk); 40 + if (!tsk) 41 + return 1; 42 + 43 + is_mptcp = bpf_core_field_exists(tsk->is_mptcp) ? tsk->is_mptcp : 0; 44 + storage = bpf_sk_storage_get(&socket_storage_map, sk, 0, 45 + BPF_SK_STORAGE_GET_F_CREATE); 46 + if (!storage) 47 + return 1; 48 + 49 + storage->invoked++; 50 + storage->is_mptcp = is_mptcp; 51 + 52 + return 1; 53 + }