summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests/kfree_skb.c
blob: d651079199982f809de79b2a0126fb73465c3523 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include <network_helpers.h>

struct meta {
	int ifindex;
	__u32 cb32_0;
	__u8 cb8_0;
};

static union {
	__u32 cb32[5];
	__u8 cb8[20];
} cb = {
	.cb32[0] = 0x81828384,
};

static void on_sample(void *ctx, int cpu, void *data, __u32 size)
{
	struct meta *meta = (struct meta *)data;
	struct ipv6_packet *pkt_v6 = data + sizeof(*meta);
	int duration = 0;

	if (CHECK(size != 72 + sizeof(*meta), "check_size", "size %u != %zu\n",
		  size, 72 + sizeof(*meta)))
		return;
	if (CHECK(meta->ifindex != 1, "check_meta_ifindex",
		  "meta->ifindex = %d\n", meta->ifindex))
		/* spurious kfree_skb not on loopback device */
		return;
	if (CHECK(meta->cb8_0 != cb.cb8[0], "check_cb8_0", "cb8_0 %x != %x\n",
		  meta->cb8_0, cb.cb8[0]))
		return;
	if (CHECK(meta->cb32_0 != cb.cb32[0], "check_cb32_0",
		  "cb32_0 %x != %x\n",
		  meta->cb32_0, cb.cb32[0]))
		return;
	if (CHECK(pkt_v6->eth.h_proto != 0xdd86, "check_eth",
		  "h_proto %x\n", pkt_v6->eth.h_proto))
		return;
	if (CHECK(pkt_v6->iph.nexthdr != 6, "check_ip",
		  "iph.nexthdr %x\n", pkt_v6->iph.nexthdr))
		return;
	if (CHECK(pkt_v6->tcp.doff != 5, "check_tcp",
		  "tcp.doff %x\n", pkt_v6->tcp.doff))
		return;

	*(bool *)ctx = true;
}

void test_kfree_skb(void)
{
	struct __sk_buff skb = {};
	struct bpf_prog_test_run_attr tattr = {
		.data_in = &pkt_v6,
		.data_size_in = sizeof(pkt_v6),
		.ctx_in = &skb,
		.ctx_size_in = sizeof(skb),
	};
	struct bpf_prog_load_attr attr = {
		.file = "./kfree_skb.o",
	};

	struct bpf_link *link = NULL, *link_fentry = NULL, *link_fexit = NULL;
	struct bpf_map *perf_buf_map, *global_data;
	struct bpf_program *prog, *fentry, *fexit;
	struct bpf_object *obj, *obj2 = NULL;
	struct perf_buffer_opts pb_opts = {};
	struct perf_buffer *pb = NULL;
	int err, kfree_skb_fd;
	bool passed = false;
	__u32 duration = 0;
	const int zero = 0;
	bool test_ok[2];

	err = bpf_prog_load("./test_pkt_access.o", BPF_PROG_TYPE_SCHED_CLS,
			    &obj, &tattr.prog_fd);
	if (CHECK(err, "prog_load sched cls", "err %d errno %d\n", err, errno))
		return;

	err = bpf_prog_load_xattr(&attr, &obj2, &kfree_skb_fd);
	if (CHECK(err, "prog_load raw tp", "err %d errno %d\n", err, errno))
		goto close_prog;

	prog = bpf_object__find_program_by_title(obj2, "tp_btf/kfree_skb");
	if (CHECK(!prog, "find_prog", "prog kfree_skb not found\n"))
		goto close_prog;
	fentry = bpf_object__find_program_by_title(obj2, "fentry/eth_type_trans");
	if (CHECK(!fentry, "find_prog", "prog eth_type_trans not found\n"))
		goto close_prog;
	fexit = bpf_object__find_program_by_title(obj2, "fexit/eth_type_trans");
	if (CHECK(!fexit, "find_prog", "prog eth_type_trans not found\n"))
		goto close_prog;

	global_data = bpf_object__find_map_by_name(obj2, "kfree_sk.bss");
	if (CHECK(!global_data, "find global data", "not found\n"))
		goto close_prog;

	link = bpf_program__attach_raw_tracepoint(prog, NULL);
	if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", PTR_ERR(link)))
		goto close_prog;
	link_fentry = bpf_program__attach_trace(fentry);
	if (CHECK(IS_ERR(link_fentry), "attach fentry", "err %ld\n",
		  PTR_ERR(link_fentry)))
		goto close_prog;
	link_fexit = bpf_program__attach_trace(fexit);
	if (CHECK(IS_ERR(link_fexit), "attach fexit", "err %ld\n",
		  PTR_ERR(link_fexit)))
		goto close_prog;

	perf_buf_map = bpf_object__find_map_by_name(obj2, "perf_buf_map");
	if (CHECK(!perf_buf_map, "find_perf_buf_map", "not found\n"))
		goto close_prog;

	/* set up perf buffer */
	pb_opts.sample_cb = on_sample;
	pb_opts.ctx = &passed;
	pb = perf_buffer__new(bpf_map__fd(perf_buf_map), 1, &pb_opts);
	if (CHECK(IS_ERR(pb), "perf_buf__new", "err %ld\n", PTR_ERR(pb)))
		goto close_prog;

	memcpy(skb.cb, &cb, sizeof(cb));
	err = bpf_prog_test_run_xattr(&tattr);
	duration = tattr.duration;
	CHECK(err || tattr.retval, "ipv6",
	      "err %d errno %d retval %d duration %d\n",
	      err, errno, tattr.retval, duration);

	/* read perf buffer */
	err = perf_buffer__poll(pb, 100);
	if (CHECK(err < 0, "perf_buffer__poll", "err %d\n", err))
		goto close_prog;

	/* make sure kfree_skb program was triggered
	 * and it sent expected skb into ring buffer
	 */
	ASSERT_TRUE(passed, "passed");

	err = bpf_map_lookup_elem(bpf_map__fd(global_data), &zero, test_ok);
	if (CHECK(err, "get_result",
		  "failed to get output data: %d\n", err))
		goto close_prog;

	CHECK_FAIL(!test_ok[0] || !test_ok[1]);
close_prog:
	perf_buffer__free(pb);
	if (!IS_ERR_OR_NULL(link))
		bpf_link__destroy(link);
	if (!IS_ERR_OR_NULL(link_fentry))
		bpf_link__destroy(link_fentry);
	if (!IS_ERR_OR_NULL(link_fexit))
		bpf_link__destroy(link_fexit);
	bpf_object__close(obj);
	bpf_object__close(obj2);
}