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

selftests/bpf: Add tests for {set|get} socket option from setsockopt BPF

Adding selftests for the newly added functionality to call bpf_setsockopt()
and bpf_getsockopt() from setsockopt BPF programs.

Test Details:

1. BPF Program

Checks for changes in IPV6_TCLASS(SOL_IPV6) via setsockopt
If the cca for the socket is not cubic do nothing
If the newly set value for IPV6_TCLASS is 45 (0x2d) (as per our use-case)
then change the cc from cubic to reno

2. User Space Program

Creates an AF_INET6 socket and set the cca for that to be "cubic"
Attach the program and set the IPV6_TCLASS to 0x2d using setsockopt
Verify the cca for the socket changed to reno

Signed-off-by: Prankur Gupta <prankgup@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Song Liu <songliubraving@fb.com>
Link: https://lore.kernel.org/bpf/20210817224221.3257826-3-prankgup@fb.com

authored by

Prankur Gupta and committed by
Daniel Borkmann
f2a6ee92 2c531639

+127
+18
tools/testing/selftests/bpf/bpf_tcp_helpers.h
··· 12 12 SEC("struct_ops/"#name) \ 13 13 BPF_PROG(name, args) 14 14 15 + #ifndef SOL_TCP 16 + #define SOL_TCP 6 17 + #endif 18 + 15 19 #define tcp_jiffies32 ((__u32)bpf_jiffies64()) 16 20 17 21 struct sock_common { ··· 205 201 return tp->snd_cwnd < 2 * tp->max_packets_out; 206 202 207 203 return !!BPF_CORE_READ_BITFIELD(tp, is_cwnd_limited); 204 + } 205 + 206 + static __always_inline bool tcp_cc_eq(const char *a, const char *b) 207 + { 208 + int i; 209 + 210 + for (i = 0; i < TCP_CA_NAME_MAX; i++) { 211 + if (a[i] != b[i]) 212 + return false; 213 + if (!a[i]) 214 + break; 215 + } 216 + 217 + return true; 208 218 } 209 219 210 220 extern __u32 tcp_slow_start(struct tcp_sock *tp, __u32 acked) __ksym;
+70
tools/testing/selftests/bpf/prog_tests/sockopt_qos_to_cc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2021 Facebook */ 3 + #include <test_progs.h> 4 + #include <netinet/tcp.h> 5 + #include "sockopt_qos_to_cc.skel.h" 6 + 7 + static void run_setsockopt_test(int cg_fd, int sock_fd) 8 + { 9 + socklen_t optlen; 10 + char cc[16]; /* TCP_CA_NAME_MAX */ 11 + int buf; 12 + int err = -1; 13 + 14 + buf = 0x2D; 15 + err = setsockopt(sock_fd, SOL_IPV6, IPV6_TCLASS, &buf, sizeof(buf)); 16 + if (!ASSERT_OK(err, "setsockopt(sock_fd, IPV6_TCLASS)")) 17 + return; 18 + 19 + /* Verify the setsockopt cc change */ 20 + optlen = sizeof(cc); 21 + err = getsockopt(sock_fd, SOL_TCP, TCP_CONGESTION, cc, &optlen); 22 + if (!ASSERT_OK(err, "getsockopt(sock_fd, TCP_CONGESTION)")) 23 + return; 24 + 25 + if (!ASSERT_STREQ(cc, "reno", "getsockopt(sock_fd, TCP_CONGESTION)")) 26 + return; 27 + } 28 + 29 + void test_sockopt_qos_to_cc(void) 30 + { 31 + struct sockopt_qos_to_cc *skel; 32 + char cc_cubic[16] = "cubic"; /* TCP_CA_NAME_MAX */ 33 + int cg_fd = -1; 34 + int sock_fd = -1; 35 + int err; 36 + 37 + cg_fd = test__join_cgroup("/sockopt_qos_to_cc"); 38 + if (!ASSERT_GE(cg_fd, 0, "cg-join(sockopt_qos_to_cc)")) 39 + return; 40 + 41 + skel = sockopt_qos_to_cc__open_and_load(); 42 + if (!ASSERT_OK_PTR(skel, "skel")) 43 + goto done; 44 + 45 + sock_fd = socket(AF_INET6, SOCK_STREAM, 0); 46 + if (!ASSERT_GE(sock_fd, 0, "v6 socket open")) 47 + goto done; 48 + 49 + err = setsockopt(sock_fd, SOL_TCP, TCP_CONGESTION, &cc_cubic, 50 + sizeof(cc_cubic)); 51 + if (!ASSERT_OK(err, "setsockopt(sock_fd, TCP_CONGESTION)")) 52 + goto done; 53 + 54 + skel->links.sockopt_qos_to_cc = 55 + bpf_program__attach_cgroup(skel->progs.sockopt_qos_to_cc, 56 + cg_fd); 57 + if (!ASSERT_OK_PTR(skel->links.sockopt_qos_to_cc, 58 + "prog_attach(sockopt_qos_to_cc)")) 59 + goto done; 60 + 61 + run_setsockopt_test(cg_fd, sock_fd); 62 + 63 + done: 64 + if (sock_fd != -1) 65 + close(sock_fd); 66 + if (cg_fd != -1) 67 + close(cg_fd); 68 + /* destroy can take null and error pointer */ 69 + sockopt_qos_to_cc__destroy(skel); 70 + }
+39
tools/testing/selftests/bpf/progs/sockopt_qos_to_cc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2021 Facebook */ 3 + #include <string.h> 4 + #include <linux/tcp.h> 5 + #include <netinet/in.h> 6 + #include <linux/bpf.h> 7 + #include <bpf/bpf_helpers.h> 8 + #include "bpf_tcp_helpers.h" 9 + 10 + char _license[] SEC("license") = "GPL"; 11 + 12 + SEC("cgroup/setsockopt") 13 + int sockopt_qos_to_cc(struct bpf_sockopt *ctx) 14 + { 15 + void *optval_end = ctx->optval_end; 16 + int *optval = ctx->optval; 17 + char buf[TCP_CA_NAME_MAX]; 18 + char cc_reno[TCP_CA_NAME_MAX] = "reno"; 19 + char cc_cubic[TCP_CA_NAME_MAX] = "cubic"; 20 + 21 + if (ctx->level != SOL_IPV6 || ctx->optname != IPV6_TCLASS) 22 + return 1; 23 + 24 + if (optval + 1 > optval_end) 25 + return 0; /* EPERM, bounds check */ 26 + 27 + if (bpf_getsockopt(ctx->sk, SOL_TCP, TCP_CONGESTION, &buf, sizeof(buf))) 28 + return 0; 29 + 30 + if (!tcp_cc_eq(buf, cc_cubic)) 31 + return 0; 32 + 33 + if (*optval == 0x2d) { 34 + if (bpf_setsockopt(ctx->sk, SOL_TCP, TCP_CONGESTION, &cc_reno, 35 + sizeof(cc_reno))) 36 + return 0; 37 + } 38 + return 1; 39 + }