linux/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c

155 lines
3.7 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
selftests/bpf: Remove the bpf_tcp_helpers.h usages from other non tcp-cc tests The patch removes the remaining bpf_tcp_helpers.h usages in the non tcp-cc networking tests. It either replaces it with bpf_tracing_net.h or just removed it because the test is not actually using any kernel sockets. For the later, the missing macro (mainly SOL_TCP) is defined locally. An exception is the test_sock_fields which is testing the "struct bpf_sock" type instead of the kernel sock type. Whenever "vmlinux.h" is used instead, it hits a verifier error on doing arithmetic on the sock_common pointer: ; return !a6[0] && !a6[1] && !a6[2] && a6[3] == bpf_htonl(1); @ test_sock_fields.c:54 21: (61) r2 = *(u32 *)(r1 +28) ; R1_w=sock_common() R2_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff)) 22: (56) if w2 != 0x0 goto pc-6 ; R2_w=0 23: (b7) r3 = 28 ; R3_w=28 24: (bf) r2 = r1 ; R1_w=sock_common() R2_w=sock_common() 25: (0f) r2 += r3 R2 pointer arithmetic on sock_common prohibited Hence, instead of including bpf_tracing_net.h, the test_sock_fields test defines a tcp_sock with one lsndtime field in it. Another highlight is, in sockopt_qos_to_cc.c, the tcp_cc_eq() is replaced by bpf_strncmp(). tcp_cc_eq() was a workaround in bpf_tcp_helpers.h before bpf_strncmp had been added. The SOL_IPV6 addition to bpf_tracing_net.h is needed by the test_tcpbpf_kern test. Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org> Link: https://lore.kernel.org/r/20240509175026.3423614-10-martin.lau@linux.dev Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2024-05-09 10:50:25 -07:00
#include "bpf_tracing_net.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include "test_tcpbpf.h"
struct tcpbpf_globals global = {};
/**
* SOL_TCP is defined in <netinet/tcp.h> while
* TCP_SAVED_SYN is defined in already included <linux/tcp.h>
*/
#ifndef SOL_TCP
#define SOL_TCP 6
#endif
static __always_inline int get_tp_window_clamp(struct bpf_sock_ops *skops)
{
struct bpf_sock *sk;
struct tcp_sock *tp;
sk = skops->sk;
if (!sk)
return -1;
tp = bpf_skc_to_tcp_sock(sk);
if (!tp)
return -1;
return tp->window_clamp;
}
SEC("sockops")
int bpf_testcb(struct bpf_sock_ops *skops)
{
char header[sizeof(struct ipv6hdr) + sizeof(struct tcphdr)];
struct bpf_sock_ops *reuse = skops;
struct tcphdr *thdr;
int window_clamp = 9216;
int save_syn = 1;
int rv = -1;
int v = 0;
int op;
/* Test reading fields in bpf_sock_ops using single register */
asm volatile (
"%[reuse] = *(u32 *)(%[reuse] +96)"
: [reuse] "+r"(reuse)
:);
asm volatile (
"%[op] = *(u32 *)(%[skops] +96)"
: [op] "=r"(op)
: [skops] "r"(skops)
:);
asm volatile (
"r9 = %[skops];\n"
"r8 = *(u32 *)(r9 +164);\n"
"*(u32 *)(r9 +164) = r8;\n"
:: [skops] "r"(skops)
: "r9", "r8");
bpf, selftests: Add tests to sock_ops for loading sk Add tests to directly accesse sock_ops sk field. Then use it to ensure a bad pointer access will fault if something goes wrong. We do three tests: The first test ensures when we read sock_ops sk pointer into the same register that we don't fault as described earlier. Here r9 is chosen as the temp register. The xlated code is, 36: (7b) *(u64 *)(r1 +32) = r9 37: (61) r9 = *(u32 *)(r1 +28) 38: (15) if r9 == 0x0 goto pc+3 39: (79) r9 = *(u64 *)(r1 +32) 40: (79) r1 = *(u64 *)(r1 +0) 41: (05) goto pc+1 42: (79) r9 = *(u64 *)(r1 +32) The second test ensures the temp register selection does not collide with in-use register r9. Shown here r8 is chosen because r9 is the sock_ops pointer. The xlated code is as follows, 46: (7b) *(u64 *)(r9 +32) = r8 47: (61) r8 = *(u32 *)(r9 +28) 48: (15) if r8 == 0x0 goto pc+3 49: (79) r8 = *(u64 *)(r9 +32) 50: (79) r9 = *(u64 *)(r9 +0) 51: (05) goto pc+1 52: (79) r8 = *(u64 *)(r9 +32) And finally, ensure we didn't break the base case where dst_reg does not equal the source register, 56: (61) r2 = *(u32 *)(r1 +28) 57: (15) if r2 == 0x0 goto pc+1 58: (79) r2 = *(u64 *)(r1 +0) Notice it takes us an extra four instructions when src reg is the same as dst reg. One to save the reg, two to restore depending on the branch taken and a goto to jump over the second restore. Signed-off-by: John Fastabend <john.fastabend@gmail.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Song Liu <songliubraving@fb.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Link: https://lore.kernel.org/bpf/159718355325.4728.4163036953345999636.stgit@john-Precision-5820-Tower
2020-08-11 15:05:53 -07:00
asm volatile (
"r1 = %[skops];\n"
"r1 = *(u64 *)(r1 +184);\n"
"if r1 == 0 goto +1;\n"
"r1 = *(u32 *)(r1 +4);\n"
:: [skops] "r"(skops):"r1");
asm volatile (
"r9 = %[skops];\n"
"r9 = *(u64 *)(r9 +184);\n"
"if r9 == 0 goto +1;\n"
"r9 = *(u32 *)(r9 +4);\n"
:: [skops] "r"(skops):"r9");
asm volatile (
"r1 = %[skops];\n"
"r2 = *(u64 *)(r1 +184);\n"
"if r2 == 0 goto +1;\n"
"r2 = *(u32 *)(r2 +4);\n"
:: [skops] "r"(skops):"r1", "r2");
op = (int) skops->op;
global.event_map |= (1 << op);
switch (op) {
case BPF_SOCK_OPS_TCP_CONNECT_CB:
rv = bpf_setsockopt(skops, SOL_TCP, TCP_WINDOW_CLAMP,
&window_clamp, sizeof(window_clamp));
global.window_clamp_client = get_tp_window_clamp(skops);
break;
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
/* Test failure to set largest cb flag (assumes not defined) */
global.bad_cb_test_rv = bpf_sock_ops_cb_flags_set(skops, 0x80);
/* Set callback */
global.good_cb_test_rv = bpf_sock_ops_cb_flags_set(skops,
BPF_SOCK_OPS_STATE_CB_FLAG);
break;
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
skops->sk_txhash = 0x12345f;
v = 0xff;
rv = bpf_setsockopt(skops, SOL_IPV6, IPV6_TCLASS, &v,
sizeof(v));
if (skops->family == AF_INET6) {
v = bpf_getsockopt(skops, IPPROTO_TCP, TCP_SAVED_SYN,
header, (sizeof(struct ipv6hdr) +
sizeof(struct tcphdr)));
if (!v) {
int offset = sizeof(struct ipv6hdr);
thdr = (struct tcphdr *)(header + offset);
v = thdr->syn;
global.tcp_saved_syn = v;
}
}
rv = bpf_setsockopt(skops, SOL_TCP, TCP_WINDOW_CLAMP,
&window_clamp, sizeof(window_clamp));
global.window_clamp_server = get_tp_window_clamp(skops);
break;
case BPF_SOCK_OPS_RTO_CB:
break;
case BPF_SOCK_OPS_RETRANS_CB:
break;
case BPF_SOCK_OPS_STATE_CB:
if (skops->args[1] == BPF_TCP_CLOSE) {
if (skops->args[0] == BPF_TCP_LISTEN) {
global.num_listen++;
} else {
global.total_retrans = skops->total_retrans;
global.data_segs_in = skops->data_segs_in;
global.data_segs_out = skops->data_segs_out;
global.bytes_received = skops->bytes_received;
global.bytes_acked = skops->bytes_acked;
}
global.num_close_events++;
}
break;
case BPF_SOCK_OPS_TCP_LISTEN_CB:
bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG);
v = bpf_setsockopt(skops, IPPROTO_TCP, TCP_SAVE_SYN,
&save_syn, sizeof(save_syn));
/* Update global map w/ result of setsock opt */
global.tcp_save_syn = v;
break;
default:
rv = -1;
}
skops->reply = rv;
return 1;
}
char _license[] SEC("license") = "GPL";