1cd13c91dSDaniel Borkmann // SPDX-License-Identifier: GPL-2.0
2cd13c91dSDaniel Borkmann /* Copyright (c) 2023 Isovalent */
3cd13c91dSDaniel Borkmann #include <uapi/linux/if_link.h>
4cd13c91dSDaniel Borkmann #include <net/if.h>
5cd13c91dSDaniel Borkmann #include <test_progs.h>
6cd13c91dSDaniel Borkmann 
7cd13c91dSDaniel Borkmann #define loopback 1
8cd13c91dSDaniel Borkmann #define ping_cmd "ping -q -c1 -w1 127.0.0.1 > /dev/null"
9cd13c91dSDaniel Borkmann 
10cd13c91dSDaniel Borkmann #include "test_tc_link.skel.h"
11cd13c91dSDaniel Borkmann #include "tc_helpers.h"
12cd13c91dSDaniel Borkmann 
13cd13c91dSDaniel Borkmann void serial_test_tc_opts_basic(void)
14cd13c91dSDaniel Borkmann {
15cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
16cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
17cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
18cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, id1, id2;
19cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
20cd13c91dSDaniel Borkmann 	__u32 prog_ids[2];
21cd13c91dSDaniel Borkmann 	int err;
22cd13c91dSDaniel Borkmann 
23cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
24cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
25cd13c91dSDaniel Borkmann 		goto cleanup;
26cd13c91dSDaniel Borkmann 
27cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
28cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
29cd13c91dSDaniel Borkmann 
30cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
31cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
32cd13c91dSDaniel Borkmann 
33cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
34cd13c91dSDaniel Borkmann 
35cd13c91dSDaniel Borkmann 	assert_mprog_count(BPF_TCX_INGRESS, 0);
36cd13c91dSDaniel Borkmann 	assert_mprog_count(BPF_TCX_EGRESS, 0);
37cd13c91dSDaniel Borkmann 
38cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, false, "seen_tc1");
39cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
40cd13c91dSDaniel Borkmann 
41cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, BPF_TCX_INGRESS, &opta);
42cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
43cd13c91dSDaniel Borkmann 		goto cleanup;
44cd13c91dSDaniel Borkmann 
45cd13c91dSDaniel Borkmann 	assert_mprog_count(BPF_TCX_INGRESS, 1);
46cd13c91dSDaniel Borkmann 	assert_mprog_count(BPF_TCX_EGRESS, 0);
47cd13c91dSDaniel Borkmann 
48cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
49cd13c91dSDaniel Borkmann 
50cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
51cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
52cd13c91dSDaniel Borkmann 
53cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, BPF_TCX_INGRESS, &optq);
54cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
55cd13c91dSDaniel Borkmann 		goto cleanup_in;
56cd13c91dSDaniel Borkmann 
57cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 1, "count");
58cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 2, "revision");
59cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
60cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
61cd13c91dSDaniel Borkmann 
62cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
63cd13c91dSDaniel Borkmann 
64cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
65cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
66cd13c91dSDaniel Borkmann 
67cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, BPF_TCX_EGRESS, &opta);
68cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
69cd13c91dSDaniel Borkmann 		goto cleanup_in;
70cd13c91dSDaniel Borkmann 
71cd13c91dSDaniel Borkmann 	assert_mprog_count(BPF_TCX_INGRESS, 1);
72cd13c91dSDaniel Borkmann 	assert_mprog_count(BPF_TCX_EGRESS, 1);
73cd13c91dSDaniel Borkmann 
74cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
75cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
76cd13c91dSDaniel Borkmann 
77cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, BPF_TCX_EGRESS, &optq);
78cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
79cd13c91dSDaniel Borkmann 		goto cleanup_eg;
80cd13c91dSDaniel Borkmann 
81cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 1, "count");
82cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 2, "revision");
83cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
84cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
85cd13c91dSDaniel Borkmann 
86cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
87cd13c91dSDaniel Borkmann 
88cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
89cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
90cd13c91dSDaniel Borkmann 
91cd13c91dSDaniel Borkmann cleanup_eg:
92cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, BPF_TCX_EGRESS, &optd);
93cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach_eg");
94cd13c91dSDaniel Borkmann 
95cd13c91dSDaniel Borkmann 	assert_mprog_count(BPF_TCX_INGRESS, 1);
96cd13c91dSDaniel Borkmann 	assert_mprog_count(BPF_TCX_EGRESS, 0);
97cd13c91dSDaniel Borkmann 
98cd13c91dSDaniel Borkmann cleanup_in:
99cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, BPF_TCX_INGRESS, &optd);
100cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach_in");
101cd13c91dSDaniel Borkmann 
102cd13c91dSDaniel Borkmann 	assert_mprog_count(BPF_TCX_INGRESS, 0);
103cd13c91dSDaniel Borkmann 	assert_mprog_count(BPF_TCX_EGRESS, 0);
104cd13c91dSDaniel Borkmann 
105cd13c91dSDaniel Borkmann cleanup:
106cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
107cd13c91dSDaniel Borkmann }
108cd13c91dSDaniel Borkmann 
109cd13c91dSDaniel Borkmann static void test_tc_opts_before_target(int target)
110cd13c91dSDaniel Borkmann {
111cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
112cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
113cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
114cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
115cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
116cd13c91dSDaniel Borkmann 	__u32 prog_ids[5];
117cd13c91dSDaniel Borkmann 	int err;
118cd13c91dSDaniel Borkmann 
119cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
120cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
121cd13c91dSDaniel Borkmann 		goto cleanup;
122cd13c91dSDaniel Borkmann 
123cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
124cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
125cd13c91dSDaniel Borkmann 	fd3 = bpf_program__fd(skel->progs.tc3);
126cd13c91dSDaniel Borkmann 	fd4 = bpf_program__fd(skel->progs.tc4);
127cd13c91dSDaniel Borkmann 
128cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
129cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
130cd13c91dSDaniel Borkmann 	id3 = id_from_prog_fd(fd3);
131cd13c91dSDaniel Borkmann 	id4 = id_from_prog_fd(fd4);
132cd13c91dSDaniel Borkmann 
133cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
134cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
135cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
136cd13c91dSDaniel Borkmann 
137cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
138cd13c91dSDaniel Borkmann 
139cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
140cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
141cd13c91dSDaniel Borkmann 		goto cleanup;
142cd13c91dSDaniel Borkmann 
143cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
144cd13c91dSDaniel Borkmann 
145cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
146cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
147cd13c91dSDaniel Borkmann 		goto cleanup_target;
148cd13c91dSDaniel Borkmann 
149cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
150cd13c91dSDaniel Borkmann 
151cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
152cd13c91dSDaniel Borkmann 
153cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
154cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
155cd13c91dSDaniel Borkmann 
156cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
157cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
158cd13c91dSDaniel Borkmann 		goto cleanup_target2;
159cd13c91dSDaniel Borkmann 
160cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
161cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 3, "revision");
162cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
163cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
164cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
165cd13c91dSDaniel Borkmann 
166cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
167cd13c91dSDaniel Borkmann 
168cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
169cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
170cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
171cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
172cd13c91dSDaniel Borkmann 
173cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
174cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
175cd13c91dSDaniel Borkmann 		.relative_fd = fd2,
176cd13c91dSDaniel Borkmann 	);
177cd13c91dSDaniel Borkmann 
178cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
179cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
180cd13c91dSDaniel Borkmann 		goto cleanup_target2;
181cd13c91dSDaniel Borkmann 
182cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
183cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
184cd13c91dSDaniel Borkmann 
185cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
186cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
187cd13c91dSDaniel Borkmann 		goto cleanup_target3;
188cd13c91dSDaniel Borkmann 
189cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 3, "count");
190cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 4, "revision");
191cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
192cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
193cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id2, "prog_ids[2]");
194cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
195cd13c91dSDaniel Borkmann 
196cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
197cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
198cd13c91dSDaniel Borkmann 		.relative_id = id1,
199cd13c91dSDaniel Borkmann 	);
200cd13c91dSDaniel Borkmann 
201cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
202cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
203cd13c91dSDaniel Borkmann 		goto cleanup_target3;
204cd13c91dSDaniel Borkmann 
205cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 4);
206cd13c91dSDaniel Borkmann 
207cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
208cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
209cd13c91dSDaniel Borkmann 
210cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
211cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
212cd13c91dSDaniel Borkmann 		goto cleanup_target4;
213cd13c91dSDaniel Borkmann 
214cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 4, "count");
215cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 5, "revision");
216cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id4, "prog_ids[0]");
217cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]");
218cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
219cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], id2, "prog_ids[3]");
220cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
221cd13c91dSDaniel Borkmann 
222cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
223cd13c91dSDaniel Borkmann 
224cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
225cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
226cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
227cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
228cd13c91dSDaniel Borkmann 
229cd13c91dSDaniel Borkmann cleanup_target4:
230cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
231cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
232cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
233cd13c91dSDaniel Borkmann 
234cd13c91dSDaniel Borkmann cleanup_target3:
235cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
236cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
237cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
238cd13c91dSDaniel Borkmann 
239cd13c91dSDaniel Borkmann cleanup_target2:
240cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
241cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
242cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
243cd13c91dSDaniel Borkmann 
244cd13c91dSDaniel Borkmann cleanup_target:
245cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
246cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
247cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
248cd13c91dSDaniel Borkmann 
249cd13c91dSDaniel Borkmann cleanup:
250cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
251cd13c91dSDaniel Borkmann }
252cd13c91dSDaniel Borkmann 
253cd13c91dSDaniel Borkmann void serial_test_tc_opts_before(void)
254cd13c91dSDaniel Borkmann {
255cd13c91dSDaniel Borkmann 	test_tc_opts_before_target(BPF_TCX_INGRESS);
256cd13c91dSDaniel Borkmann 	test_tc_opts_before_target(BPF_TCX_EGRESS);
257cd13c91dSDaniel Borkmann }
258cd13c91dSDaniel Borkmann 
259cd13c91dSDaniel Borkmann static void test_tc_opts_after_target(int target)
260cd13c91dSDaniel Borkmann {
261cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
262cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
263cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
264cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
265cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
266cd13c91dSDaniel Borkmann 	__u32 prog_ids[5];
267cd13c91dSDaniel Borkmann 	int err;
268cd13c91dSDaniel Borkmann 
269cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
270cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
271cd13c91dSDaniel Borkmann 		goto cleanup;
272cd13c91dSDaniel Borkmann 
273cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
274cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
275cd13c91dSDaniel Borkmann 	fd3 = bpf_program__fd(skel->progs.tc3);
276cd13c91dSDaniel Borkmann 	fd4 = bpf_program__fd(skel->progs.tc4);
277cd13c91dSDaniel Borkmann 
278cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
279cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
280cd13c91dSDaniel Borkmann 	id3 = id_from_prog_fd(fd3);
281cd13c91dSDaniel Borkmann 	id4 = id_from_prog_fd(fd4);
282cd13c91dSDaniel Borkmann 
283cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
284cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
285cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
286cd13c91dSDaniel Borkmann 
287cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
288cd13c91dSDaniel Borkmann 
289cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
290cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
291cd13c91dSDaniel Borkmann 		goto cleanup;
292cd13c91dSDaniel Borkmann 
293cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
294cd13c91dSDaniel Borkmann 
295cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
296cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
297cd13c91dSDaniel Borkmann 		goto cleanup_target;
298cd13c91dSDaniel Borkmann 
299cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
300cd13c91dSDaniel Borkmann 
301cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
302cd13c91dSDaniel Borkmann 
303cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
304cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
305cd13c91dSDaniel Borkmann 
306cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
307cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
308cd13c91dSDaniel Borkmann 		goto cleanup_target2;
309cd13c91dSDaniel Borkmann 
310cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
311cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 3, "revision");
312cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
313cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
314cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
315cd13c91dSDaniel Borkmann 
316cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
317cd13c91dSDaniel Borkmann 
318cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
319cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
320cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
321cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
322cd13c91dSDaniel Borkmann 
323cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
324cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
325cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
326cd13c91dSDaniel Borkmann 	);
327cd13c91dSDaniel Borkmann 
328cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
329cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
330cd13c91dSDaniel Borkmann 		goto cleanup_target2;
331cd13c91dSDaniel Borkmann 
332cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
333cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
334cd13c91dSDaniel Borkmann 
335cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
336cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
337cd13c91dSDaniel Borkmann 		goto cleanup_target3;
338cd13c91dSDaniel Borkmann 
339cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 3, "count");
340cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 4, "revision");
341cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
342cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
343cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id2, "prog_ids[2]");
344cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
345cd13c91dSDaniel Borkmann 
346cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
347cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
348cd13c91dSDaniel Borkmann 		.relative_id = id2,
349cd13c91dSDaniel Borkmann 	);
350cd13c91dSDaniel Borkmann 
351cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
352cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
353cd13c91dSDaniel Borkmann 		goto cleanup_target3;
354cd13c91dSDaniel Borkmann 
355cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 4);
356cd13c91dSDaniel Borkmann 
357cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
358cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
359cd13c91dSDaniel Borkmann 
360cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
361cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
362cd13c91dSDaniel Borkmann 		goto cleanup_target4;
363cd13c91dSDaniel Borkmann 
364cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 4, "count");
365cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 5, "revision");
366cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
367cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
368cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id2, "prog_ids[2]");
369cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
370cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
371cd13c91dSDaniel Borkmann 
372cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
373cd13c91dSDaniel Borkmann 
374cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
375cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
376cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
377cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
378cd13c91dSDaniel Borkmann 
379cd13c91dSDaniel Borkmann cleanup_target4:
380cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
381cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
382cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
383cd13c91dSDaniel Borkmann 
384cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
385cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
386cd13c91dSDaniel Borkmann 
387cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
388cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
389cd13c91dSDaniel Borkmann 		goto cleanup_target3;
390cd13c91dSDaniel Borkmann 
391cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 3, "count");
392cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 6, "revision");
393cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
394cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
395cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id2, "prog_ids[2]");
396cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
397cd13c91dSDaniel Borkmann 
398cd13c91dSDaniel Borkmann cleanup_target3:
399cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
400cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
401cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
402cd13c91dSDaniel Borkmann 
403cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
404cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
405cd13c91dSDaniel Borkmann 
406cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
407cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
408cd13c91dSDaniel Borkmann 		goto cleanup_target2;
409cd13c91dSDaniel Borkmann 
410cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
411cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 7, "revision");
412cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
413cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
414cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
415cd13c91dSDaniel Borkmann 
416cd13c91dSDaniel Borkmann cleanup_target2:
417cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
418cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
419cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
420cd13c91dSDaniel Borkmann 
421cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
422cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
423cd13c91dSDaniel Borkmann 
424cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
425cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
426cd13c91dSDaniel Borkmann 		goto cleanup_target;
427cd13c91dSDaniel Borkmann 
428cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 1, "count");
429cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 8, "revision");
430cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
431cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
432cd13c91dSDaniel Borkmann 
433cd13c91dSDaniel Borkmann cleanup_target:
434cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
435cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
436cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
437cd13c91dSDaniel Borkmann 
438cd13c91dSDaniel Borkmann cleanup:
439cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
440cd13c91dSDaniel Borkmann }
441cd13c91dSDaniel Borkmann 
442cd13c91dSDaniel Borkmann void serial_test_tc_opts_after(void)
443cd13c91dSDaniel Borkmann {
444cd13c91dSDaniel Borkmann 	test_tc_opts_after_target(BPF_TCX_INGRESS);
445cd13c91dSDaniel Borkmann 	test_tc_opts_after_target(BPF_TCX_EGRESS);
446cd13c91dSDaniel Borkmann }
447cd13c91dSDaniel Borkmann 
448cd13c91dSDaniel Borkmann static void test_tc_opts_revision_target(int target)
449cd13c91dSDaniel Borkmann {
450cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
451cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
452cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
453cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, id1, id2;
454cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
455cd13c91dSDaniel Borkmann 	__u32 prog_ids[3];
456cd13c91dSDaniel Borkmann 	int err;
457cd13c91dSDaniel Borkmann 
458cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
459cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
460cd13c91dSDaniel Borkmann 		goto cleanup;
461cd13c91dSDaniel Borkmann 
462cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
463cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
464cd13c91dSDaniel Borkmann 
465cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
466cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
467cd13c91dSDaniel Borkmann 
468cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
469cd13c91dSDaniel Borkmann 
470cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
471cd13c91dSDaniel Borkmann 
472cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
473cd13c91dSDaniel Borkmann 		.expected_revision = 1,
474cd13c91dSDaniel Borkmann 	);
475cd13c91dSDaniel Borkmann 
476cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
477cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
478cd13c91dSDaniel Borkmann 		goto cleanup;
479cd13c91dSDaniel Borkmann 
480cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
481cd13c91dSDaniel Borkmann 
482cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
483cd13c91dSDaniel Borkmann 		.expected_revision = 1,
484cd13c91dSDaniel Borkmann 	);
485cd13c91dSDaniel Borkmann 
486cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
487cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, -ESTALE, "prog_attach"))
488cd13c91dSDaniel Borkmann 		goto cleanup_target;
489cd13c91dSDaniel Borkmann 
490cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
491cd13c91dSDaniel Borkmann 
492cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
493cd13c91dSDaniel Borkmann 		.expected_revision = 2,
494cd13c91dSDaniel Borkmann 	);
495cd13c91dSDaniel Borkmann 
496cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
497cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
498cd13c91dSDaniel Borkmann 		goto cleanup_target;
499cd13c91dSDaniel Borkmann 
500cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
501cd13c91dSDaniel Borkmann 
502cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
503cd13c91dSDaniel Borkmann 
504cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
505cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
506cd13c91dSDaniel Borkmann 
507cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
508cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
509cd13c91dSDaniel Borkmann 		goto cleanup_target2;
510cd13c91dSDaniel Borkmann 
511cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
512cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 3, "revision");
513cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
514cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
515cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
516cd13c91dSDaniel Borkmann 
517cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
518cd13c91dSDaniel Borkmann 
519cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
520cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
521cd13c91dSDaniel Borkmann 
522cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
523cd13c91dSDaniel Borkmann 		.expected_revision = 2,
524cd13c91dSDaniel Borkmann 	);
525cd13c91dSDaniel Borkmann 
526cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
527cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ESTALE, "prog_detach");
528cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
529cd13c91dSDaniel Borkmann 
530cd13c91dSDaniel Borkmann cleanup_target2:
531cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
532cd13c91dSDaniel Borkmann 		.expected_revision = 3,
533cd13c91dSDaniel Borkmann 	);
534cd13c91dSDaniel Borkmann 
535cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
536cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
537cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
538cd13c91dSDaniel Borkmann 
539cd13c91dSDaniel Borkmann cleanup_target:
540cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd);
541cd13c91dSDaniel Borkmann 
542cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
543cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
544cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
545cd13c91dSDaniel Borkmann 
546cd13c91dSDaniel Borkmann cleanup:
547cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
548cd13c91dSDaniel Borkmann }
549cd13c91dSDaniel Borkmann 
550cd13c91dSDaniel Borkmann void serial_test_tc_opts_revision(void)
551cd13c91dSDaniel Borkmann {
552cd13c91dSDaniel Borkmann 	test_tc_opts_revision_target(BPF_TCX_INGRESS);
553cd13c91dSDaniel Borkmann 	test_tc_opts_revision_target(BPF_TCX_EGRESS);
554cd13c91dSDaniel Borkmann }
555cd13c91dSDaniel Borkmann 
556cd13c91dSDaniel Borkmann static void test_tc_chain_classic(int target, bool chain_tc_old)
557cd13c91dSDaniel Borkmann {
558cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
559cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback);
560cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
561cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
562cd13c91dSDaniel Borkmann 	bool hook_created = false, tc_attached = false;
563cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, fd3, id1, id2, id3;
564cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
565cd13c91dSDaniel Borkmann 	int err;
566cd13c91dSDaniel Borkmann 
567cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
568cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
569cd13c91dSDaniel Borkmann 		goto cleanup;
570cd13c91dSDaniel Borkmann 
571cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
572cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
573cd13c91dSDaniel Borkmann 	fd3 = bpf_program__fd(skel->progs.tc3);
574cd13c91dSDaniel Borkmann 
575cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
576cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
577cd13c91dSDaniel Borkmann 	id3 = id_from_prog_fd(fd3);
578cd13c91dSDaniel Borkmann 
579cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
580cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
581cd13c91dSDaniel Borkmann 
582cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
583cd13c91dSDaniel Borkmann 
584cd13c91dSDaniel Borkmann 	if (chain_tc_old) {
585cd13c91dSDaniel Borkmann 		tc_hook.attach_point = target == BPF_TCX_INGRESS ?
586cd13c91dSDaniel Borkmann 				       BPF_TC_INGRESS : BPF_TC_EGRESS;
587cd13c91dSDaniel Borkmann 		err = bpf_tc_hook_create(&tc_hook);
588cd13c91dSDaniel Borkmann 		if (err == 0)
589cd13c91dSDaniel Borkmann 			hook_created = true;
590cd13c91dSDaniel Borkmann 		err = err == -EEXIST ? 0 : err;
591cd13c91dSDaniel Borkmann 		if (!ASSERT_OK(err, "bpf_tc_hook_create"))
592cd13c91dSDaniel Borkmann 			goto cleanup;
593cd13c91dSDaniel Borkmann 
594cd13c91dSDaniel Borkmann 		tc_opts.prog_fd = fd3;
595cd13c91dSDaniel Borkmann 		err = bpf_tc_attach(&tc_hook, &tc_opts);
596cd13c91dSDaniel Borkmann 		if (!ASSERT_OK(err, "bpf_tc_attach"))
597cd13c91dSDaniel Borkmann 			goto cleanup;
598cd13c91dSDaniel Borkmann 		tc_attached = true;
599cd13c91dSDaniel Borkmann 	}
600cd13c91dSDaniel Borkmann 
601cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
602cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
603cd13c91dSDaniel Borkmann 		goto cleanup;
604cd13c91dSDaniel Borkmann 
605cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
606cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
607cd13c91dSDaniel Borkmann 		goto cleanup_detach;
608cd13c91dSDaniel Borkmann 
609cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
610cd13c91dSDaniel Borkmann 
611cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
612cd13c91dSDaniel Borkmann 
613cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
614cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
615cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3");
616cd13c91dSDaniel Borkmann 
617cd13c91dSDaniel Borkmann 	skel->bss->seen_tc1 = false;
618cd13c91dSDaniel Borkmann 	skel->bss->seen_tc2 = false;
619cd13c91dSDaniel Borkmann 	skel->bss->seen_tc3 = false;
620cd13c91dSDaniel Borkmann 
621cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
622cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_detach"))
623cd13c91dSDaniel Borkmann 		goto cleanup_detach;
624cd13c91dSDaniel Borkmann 
625cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
626cd13c91dSDaniel Borkmann 
627cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
628cd13c91dSDaniel Borkmann 
629cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
630cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
631cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3");
632cd13c91dSDaniel Borkmann 
633cd13c91dSDaniel Borkmann cleanup_detach:
634cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
635cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_detach"))
636cd13c91dSDaniel Borkmann 		goto cleanup;
637cd13c91dSDaniel Borkmann 
638cd13c91dSDaniel Borkmann 	__assert_mprog_count(target, 0, chain_tc_old, loopback);
639cd13c91dSDaniel Borkmann cleanup:
640cd13c91dSDaniel Borkmann 	if (tc_attached) {
641cd13c91dSDaniel Borkmann 		tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0;
642cd13c91dSDaniel Borkmann 		err = bpf_tc_detach(&tc_hook, &tc_opts);
643cd13c91dSDaniel Borkmann 		ASSERT_OK(err, "bpf_tc_detach");
644cd13c91dSDaniel Borkmann 	}
645cd13c91dSDaniel Borkmann 	if (hook_created) {
646cd13c91dSDaniel Borkmann 		tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
647cd13c91dSDaniel Borkmann 		bpf_tc_hook_destroy(&tc_hook);
648cd13c91dSDaniel Borkmann 	}
649cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
650cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
651cd13c91dSDaniel Borkmann }
652cd13c91dSDaniel Borkmann 
653cd13c91dSDaniel Borkmann void serial_test_tc_opts_chain_classic(void)
654cd13c91dSDaniel Borkmann {
655cd13c91dSDaniel Borkmann 	test_tc_chain_classic(BPF_TCX_INGRESS, false);
656cd13c91dSDaniel Borkmann 	test_tc_chain_classic(BPF_TCX_EGRESS, false);
657cd13c91dSDaniel Borkmann 	test_tc_chain_classic(BPF_TCX_INGRESS, true);
658cd13c91dSDaniel Borkmann 	test_tc_chain_classic(BPF_TCX_EGRESS, true);
659cd13c91dSDaniel Borkmann }
660cd13c91dSDaniel Borkmann 
661cd13c91dSDaniel Borkmann static void test_tc_opts_replace_target(int target)
662cd13c91dSDaniel Borkmann {
663cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
664cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
665cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
666cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, fd3, id1, id2, id3, detach_fd;
667cd13c91dSDaniel Borkmann 	__u32 prog_ids[4], prog_flags[4];
668cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
669cd13c91dSDaniel Borkmann 	int err;
670cd13c91dSDaniel Borkmann 
671cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
672cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
673cd13c91dSDaniel Borkmann 		goto cleanup;
674cd13c91dSDaniel Borkmann 
675cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
676cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
677cd13c91dSDaniel Borkmann 	fd3 = bpf_program__fd(skel->progs.tc3);
678cd13c91dSDaniel Borkmann 
679cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
680cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
681cd13c91dSDaniel Borkmann 	id3 = id_from_prog_fd(fd3);
682cd13c91dSDaniel Borkmann 
683cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
684cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
685cd13c91dSDaniel Borkmann 
686cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
687cd13c91dSDaniel Borkmann 
688cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
689cd13c91dSDaniel Borkmann 		.expected_revision = 1,
690cd13c91dSDaniel Borkmann 	);
691cd13c91dSDaniel Borkmann 
692cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
693cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
694cd13c91dSDaniel Borkmann 		goto cleanup;
695cd13c91dSDaniel Borkmann 
696cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
697cd13c91dSDaniel Borkmann 
698cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
699cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
700cd13c91dSDaniel Borkmann 		.relative_id = id1,
701cd13c91dSDaniel Borkmann 		.expected_revision = 2,
702cd13c91dSDaniel Borkmann 	);
703cd13c91dSDaniel Borkmann 
704cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
705cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
706cd13c91dSDaniel Borkmann 		goto cleanup_target;
707cd13c91dSDaniel Borkmann 
708cd13c91dSDaniel Borkmann 	detach_fd = fd2;
709cd13c91dSDaniel Borkmann 
710cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
711cd13c91dSDaniel Borkmann 
712cd13c91dSDaniel Borkmann 	optq.prog_attach_flags = prog_flags;
713cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
714cd13c91dSDaniel Borkmann 
715cd13c91dSDaniel Borkmann 	memset(prog_flags, 0, sizeof(prog_flags));
716cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
717cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
718cd13c91dSDaniel Borkmann 
719cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
720cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
721cd13c91dSDaniel Borkmann 		goto cleanup_target2;
722cd13c91dSDaniel Borkmann 
723cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
724cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 3, "revision");
725cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
726cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]");
727cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
728cd13c91dSDaniel Borkmann 
729cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_attach_flags[0], 0, "prog_flags[0]");
730cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_attach_flags[1], 0, "prog_flags[1]");
731cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_attach_flags[2], 0, "prog_flags[2]");
732cd13c91dSDaniel Borkmann 
733cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
734cd13c91dSDaniel Borkmann 
735cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
736cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
737cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
738cd13c91dSDaniel Borkmann 
739cd13c91dSDaniel Borkmann 	skel->bss->seen_tc1 = false;
740cd13c91dSDaniel Borkmann 	skel->bss->seen_tc2 = false;
741cd13c91dSDaniel Borkmann 	skel->bss->seen_tc3 = false;
742cd13c91dSDaniel Borkmann 
743cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
744cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE,
745cd13c91dSDaniel Borkmann 		.replace_prog_fd = fd2,
746cd13c91dSDaniel Borkmann 		.expected_revision = 3,
747cd13c91dSDaniel Borkmann 	);
748cd13c91dSDaniel Borkmann 
749cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
750cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
751cd13c91dSDaniel Borkmann 		goto cleanup_target2;
752cd13c91dSDaniel Borkmann 
753cd13c91dSDaniel Borkmann 	detach_fd = fd3;
754cd13c91dSDaniel Borkmann 
755cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
756cd13c91dSDaniel Borkmann 
757cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
758cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
759cd13c91dSDaniel Borkmann 
760cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
761cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
762cd13c91dSDaniel Borkmann 		goto cleanup_target2;
763cd13c91dSDaniel Borkmann 
764cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
765cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 4, "revision");
766cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id3, "prog_ids[0]");
767cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]");
768cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
769cd13c91dSDaniel Borkmann 
770cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
771cd13c91dSDaniel Borkmann 
772cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
773cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
774cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
775cd13c91dSDaniel Borkmann 
776cd13c91dSDaniel Borkmann 	skel->bss->seen_tc1 = false;
777cd13c91dSDaniel Borkmann 	skel->bss->seen_tc2 = false;
778cd13c91dSDaniel Borkmann 	skel->bss->seen_tc3 = false;
779cd13c91dSDaniel Borkmann 
780cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
781cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE | BPF_F_BEFORE,
782cd13c91dSDaniel Borkmann 		.replace_prog_fd = fd3,
783cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
784cd13c91dSDaniel Borkmann 		.expected_revision = 4,
785cd13c91dSDaniel Borkmann 	);
786cd13c91dSDaniel Borkmann 
787cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
788cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
789cd13c91dSDaniel Borkmann 		goto cleanup_target2;
790cd13c91dSDaniel Borkmann 
791cd13c91dSDaniel Borkmann 	detach_fd = fd2;
792cd13c91dSDaniel Borkmann 
793cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
794cd13c91dSDaniel Borkmann 
795cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
796cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
797cd13c91dSDaniel Borkmann 
798cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
799cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
800cd13c91dSDaniel Borkmann 		goto cleanup_target2;
801cd13c91dSDaniel Borkmann 
802cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
803cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 5, "revision");
804cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
805cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]");
806cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
807cd13c91dSDaniel Borkmann 
808cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
809cd13c91dSDaniel Borkmann 
810cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
811cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
812cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
813cd13c91dSDaniel Borkmann 
814cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
815cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE,
816cd13c91dSDaniel Borkmann 		.replace_prog_fd = fd2,
817cd13c91dSDaniel Borkmann 	);
818cd13c91dSDaniel Borkmann 
819cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
820cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EEXIST, "prog_attach");
821cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
822cd13c91dSDaniel Borkmann 
823cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
824cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE | BPF_F_AFTER,
825cd13c91dSDaniel Borkmann 		.replace_prog_fd = fd2,
826cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
827cd13c91dSDaniel Borkmann 		.expected_revision = 5,
828cd13c91dSDaniel Borkmann 	);
829cd13c91dSDaniel Borkmann 
830cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
831cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ERANGE, "prog_attach");
832cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
833cd13c91dSDaniel Borkmann 
834cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
835cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE | BPF_F_AFTER | BPF_F_REPLACE,
836cd13c91dSDaniel Borkmann 		.replace_prog_fd = fd2,
837cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
838cd13c91dSDaniel Borkmann 		.expected_revision = 5,
839cd13c91dSDaniel Borkmann 	);
840cd13c91dSDaniel Borkmann 
841cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
842cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ERANGE, "prog_attach");
843cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
844cd13c91dSDaniel Borkmann 
845cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
846cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
847cd13c91dSDaniel Borkmann 		.relative_id = id1,
848cd13c91dSDaniel Borkmann 		.expected_revision = 5,
849cd13c91dSDaniel Borkmann 	);
850cd13c91dSDaniel Borkmann 
851cd13c91dSDaniel Borkmann cleanup_target2:
852cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(detach_fd, loopback, target, &optd);
853cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
854cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
855cd13c91dSDaniel Borkmann 
856cd13c91dSDaniel Borkmann cleanup_target:
857cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd);
858cd13c91dSDaniel Borkmann 
859cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
860cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
861cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
862cd13c91dSDaniel Borkmann 
863cd13c91dSDaniel Borkmann cleanup:
864cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
865cd13c91dSDaniel Borkmann }
866cd13c91dSDaniel Borkmann 
867cd13c91dSDaniel Borkmann void serial_test_tc_opts_replace(void)
868cd13c91dSDaniel Borkmann {
869cd13c91dSDaniel Borkmann 	test_tc_opts_replace_target(BPF_TCX_INGRESS);
870cd13c91dSDaniel Borkmann 	test_tc_opts_replace_target(BPF_TCX_EGRESS);
871cd13c91dSDaniel Borkmann }
872cd13c91dSDaniel Borkmann 
873cd13c91dSDaniel Borkmann static void test_tc_opts_invalid_target(int target)
874cd13c91dSDaniel Borkmann {
875cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
876cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
877cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, id1, id2;
878cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
879cd13c91dSDaniel Borkmann 	int err;
880cd13c91dSDaniel Borkmann 
881cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
882cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
883cd13c91dSDaniel Borkmann 		goto cleanup;
884cd13c91dSDaniel Borkmann 
885cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
886cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
887cd13c91dSDaniel Borkmann 
888cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
889cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
890cd13c91dSDaniel Borkmann 
891cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
892cd13c91dSDaniel Borkmann 
893cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
894cd13c91dSDaniel Borkmann 
895cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
896cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE | BPF_F_AFTER,
897cd13c91dSDaniel Borkmann 	);
898cd13c91dSDaniel Borkmann 
899cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
900cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ERANGE, "prog_attach");
901cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
902cd13c91dSDaniel Borkmann 
903cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
904cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE | BPF_F_ID,
905cd13c91dSDaniel Borkmann 	);
906cd13c91dSDaniel Borkmann 
907cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
908cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_attach");
909cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
910cd13c91dSDaniel Borkmann 
911cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
912cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER | BPF_F_ID,
913cd13c91dSDaniel Borkmann 	);
914cd13c91dSDaniel Borkmann 
915cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
916cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_attach");
917cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
918cd13c91dSDaniel Borkmann 
919cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
920cd13c91dSDaniel Borkmann 		.relative_fd = fd2,
921cd13c91dSDaniel Borkmann 	);
922cd13c91dSDaniel Borkmann 
923cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
924cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EINVAL, "prog_attach");
925cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
926cd13c91dSDaniel Borkmann 
927cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
928cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE | BPF_F_AFTER,
929cd13c91dSDaniel Borkmann 		.relative_fd = fd2,
930cd13c91dSDaniel Borkmann 	);
931cd13c91dSDaniel Borkmann 
932cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
933cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_attach");
934cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
935cd13c91dSDaniel Borkmann 
936cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
937cd13c91dSDaniel Borkmann 		.flags = BPF_F_ID,
938cd13c91dSDaniel Borkmann 		.relative_id = id2,
939cd13c91dSDaniel Borkmann 	);
940cd13c91dSDaniel Borkmann 
941cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
942cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EINVAL, "prog_attach");
943cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
944cd13c91dSDaniel Borkmann 
945cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
946cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
947cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
948cd13c91dSDaniel Borkmann 	);
949cd13c91dSDaniel Borkmann 
950cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
951cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_attach");
952cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
953cd13c91dSDaniel Borkmann 
954cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
955cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
956cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
957cd13c91dSDaniel Borkmann 	);
958cd13c91dSDaniel Borkmann 
959cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
960cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_attach");
961cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
962cd13c91dSDaniel Borkmann 
963cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta);
964cd13c91dSDaniel Borkmann 
965cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
966cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
967cd13c91dSDaniel Borkmann 		goto cleanup;
968cd13c91dSDaniel Borkmann 
969cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
970cd13c91dSDaniel Borkmann 
971cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta);
972cd13c91dSDaniel Borkmann 
973cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
974cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EEXIST, "prog_attach");
975cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
976cd13c91dSDaniel Borkmann 
977cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
978cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
979cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
980cd13c91dSDaniel Borkmann 	);
981cd13c91dSDaniel Borkmann 
982cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
983cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EEXIST, "prog_attach");
984cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
985cd13c91dSDaniel Borkmann 
986cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
987cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
988cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
989cd13c91dSDaniel Borkmann 	);
990cd13c91dSDaniel Borkmann 
991cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
992cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EEXIST, "prog_attach");
993cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
994cd13c91dSDaniel Borkmann 
995cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
996cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE,
997cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
998cd13c91dSDaniel Borkmann 	);
999cd13c91dSDaniel Borkmann 
1000cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1001cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EINVAL, "prog_attach_x1");
1002cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1003cd13c91dSDaniel Borkmann 
1004cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1005cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE,
1006cd13c91dSDaniel Borkmann 		.replace_prog_fd = fd1,
1007cd13c91dSDaniel Borkmann 	);
1008cd13c91dSDaniel Borkmann 
1009cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1010cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1011cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1012cd13c91dSDaniel Borkmann 
1013cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1014cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1015cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1016cd13c91dSDaniel Borkmann cleanup:
1017cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
1018cd13c91dSDaniel Borkmann }
1019cd13c91dSDaniel Borkmann 
1020cd13c91dSDaniel Borkmann void serial_test_tc_opts_invalid(void)
1021cd13c91dSDaniel Borkmann {
1022cd13c91dSDaniel Borkmann 	test_tc_opts_invalid_target(BPF_TCX_INGRESS);
1023cd13c91dSDaniel Borkmann 	test_tc_opts_invalid_target(BPF_TCX_EGRESS);
1024cd13c91dSDaniel Borkmann }
1025cd13c91dSDaniel Borkmann 
1026cd13c91dSDaniel Borkmann static void test_tc_opts_prepend_target(int target)
1027cd13c91dSDaniel Borkmann {
1028cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1029cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1030cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1031cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1032cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
1033cd13c91dSDaniel Borkmann 	__u32 prog_ids[5];
1034cd13c91dSDaniel Borkmann 	int err;
1035cd13c91dSDaniel Borkmann 
1036cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
1037cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1038cd13c91dSDaniel Borkmann 		goto cleanup;
1039cd13c91dSDaniel Borkmann 
1040cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
1041cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
1042cd13c91dSDaniel Borkmann 	fd3 = bpf_program__fd(skel->progs.tc3);
1043cd13c91dSDaniel Borkmann 	fd4 = bpf_program__fd(skel->progs.tc4);
1044cd13c91dSDaniel Borkmann 
1045cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
1046cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
1047cd13c91dSDaniel Borkmann 	id3 = id_from_prog_fd(fd3);
1048cd13c91dSDaniel Borkmann 	id4 = id_from_prog_fd(fd4);
1049cd13c91dSDaniel Borkmann 
1050cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1051cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1052cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1053cd13c91dSDaniel Borkmann 
1054cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1055cd13c91dSDaniel Borkmann 
1056cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1057cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1058cd13c91dSDaniel Borkmann 		goto cleanup;
1059cd13c91dSDaniel Borkmann 
1060cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1061cd13c91dSDaniel Borkmann 
1062cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1063cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1064cd13c91dSDaniel Borkmann 	);
1065cd13c91dSDaniel Borkmann 
1066cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
1067cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1068cd13c91dSDaniel Borkmann 		goto cleanup_target;
1069cd13c91dSDaniel Borkmann 
1070cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1071cd13c91dSDaniel Borkmann 
1072cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
1073cd13c91dSDaniel Borkmann 
1074cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1075cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1076cd13c91dSDaniel Borkmann 
1077cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1078cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1079cd13c91dSDaniel Borkmann 		goto cleanup_target2;
1080cd13c91dSDaniel Borkmann 
1081cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
1082cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 3, "revision");
1083cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1084cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]");
1085cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1086cd13c91dSDaniel Borkmann 
1087cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
1088cd13c91dSDaniel Borkmann 
1089cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1090cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1091cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
1092cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
1093cd13c91dSDaniel Borkmann 
1094cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1095cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1096cd13c91dSDaniel Borkmann 	);
1097cd13c91dSDaniel Borkmann 
1098cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
1099cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1100cd13c91dSDaniel Borkmann 		goto cleanup_target2;
1101cd13c91dSDaniel Borkmann 
1102cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1103cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1104cd13c91dSDaniel Borkmann 	);
1105cd13c91dSDaniel Borkmann 
1106cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
1107cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1108cd13c91dSDaniel Borkmann 		goto cleanup_target3;
1109cd13c91dSDaniel Borkmann 
1110cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 4);
1111cd13c91dSDaniel Borkmann 
1112cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1113cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1114cd13c91dSDaniel Borkmann 
1115cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1116cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1117cd13c91dSDaniel Borkmann 		goto cleanup_target4;
1118cd13c91dSDaniel Borkmann 
1119cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 4, "count");
1120cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 5, "revision");
1121cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id4, "prog_ids[0]");
1122cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1123cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id2, "prog_ids[2]");
1124cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], id1, "prog_ids[3]");
1125cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1126cd13c91dSDaniel Borkmann 
1127cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
1128cd13c91dSDaniel Borkmann 
1129cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1130cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1131cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
1132cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
1133cd13c91dSDaniel Borkmann 
1134cd13c91dSDaniel Borkmann cleanup_target4:
1135cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1136cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1137cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1138cd13c91dSDaniel Borkmann 
1139cd13c91dSDaniel Borkmann cleanup_target3:
1140cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1141cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1142cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1143cd13c91dSDaniel Borkmann 
1144cd13c91dSDaniel Borkmann cleanup_target2:
1145cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1146cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1147cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1148cd13c91dSDaniel Borkmann 
1149cd13c91dSDaniel Borkmann cleanup_target:
1150cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1151cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1152cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1153cd13c91dSDaniel Borkmann 
1154cd13c91dSDaniel Borkmann cleanup:
1155cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
1156cd13c91dSDaniel Borkmann }
1157cd13c91dSDaniel Borkmann 
1158cd13c91dSDaniel Borkmann void serial_test_tc_opts_prepend(void)
1159cd13c91dSDaniel Borkmann {
1160cd13c91dSDaniel Borkmann 	test_tc_opts_prepend_target(BPF_TCX_INGRESS);
1161cd13c91dSDaniel Borkmann 	test_tc_opts_prepend_target(BPF_TCX_EGRESS);
1162cd13c91dSDaniel Borkmann }
1163cd13c91dSDaniel Borkmann 
1164cd13c91dSDaniel Borkmann static void test_tc_opts_append_target(int target)
1165cd13c91dSDaniel Borkmann {
1166cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1167cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1168cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1169cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1170cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
1171cd13c91dSDaniel Borkmann 	__u32 prog_ids[5];
1172cd13c91dSDaniel Borkmann 	int err;
1173cd13c91dSDaniel Borkmann 
1174cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
1175cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1176cd13c91dSDaniel Borkmann 		goto cleanup;
1177cd13c91dSDaniel Borkmann 
1178cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
1179cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
1180cd13c91dSDaniel Borkmann 	fd3 = bpf_program__fd(skel->progs.tc3);
1181cd13c91dSDaniel Borkmann 	fd4 = bpf_program__fd(skel->progs.tc4);
1182cd13c91dSDaniel Borkmann 
1183cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
1184cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
1185cd13c91dSDaniel Borkmann 	id3 = id_from_prog_fd(fd3);
1186cd13c91dSDaniel Borkmann 	id4 = id_from_prog_fd(fd4);
1187cd13c91dSDaniel Borkmann 
1188cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1189cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1190cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1191cd13c91dSDaniel Borkmann 
1192cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1193cd13c91dSDaniel Borkmann 
1194cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1195cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1196cd13c91dSDaniel Borkmann 		goto cleanup;
1197cd13c91dSDaniel Borkmann 
1198cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1199cd13c91dSDaniel Borkmann 
1200cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1201cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
1202cd13c91dSDaniel Borkmann 	);
1203cd13c91dSDaniel Borkmann 
1204cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
1205cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1206cd13c91dSDaniel Borkmann 		goto cleanup_target;
1207cd13c91dSDaniel Borkmann 
1208cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1209cd13c91dSDaniel Borkmann 
1210cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
1211cd13c91dSDaniel Borkmann 
1212cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1213cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1214cd13c91dSDaniel Borkmann 
1215cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1216cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1217cd13c91dSDaniel Borkmann 		goto cleanup_target2;
1218cd13c91dSDaniel Borkmann 
1219cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
1220cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 3, "revision");
1221cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
1222cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
1223cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1224cd13c91dSDaniel Borkmann 
1225cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
1226cd13c91dSDaniel Borkmann 
1227cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1228cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1229cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
1230cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
1231cd13c91dSDaniel Borkmann 
1232cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1233cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
1234cd13c91dSDaniel Borkmann 	);
1235cd13c91dSDaniel Borkmann 
1236cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
1237cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1238cd13c91dSDaniel Borkmann 		goto cleanup_target2;
1239cd13c91dSDaniel Borkmann 
1240cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1241cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
1242cd13c91dSDaniel Borkmann 	);
1243cd13c91dSDaniel Borkmann 
1244cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
1245cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1246cd13c91dSDaniel Borkmann 		goto cleanup_target3;
1247cd13c91dSDaniel Borkmann 
1248cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 4);
1249cd13c91dSDaniel Borkmann 
1250cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1251cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1252cd13c91dSDaniel Borkmann 
1253cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1254cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1255cd13c91dSDaniel Borkmann 		goto cleanup_target4;
1256cd13c91dSDaniel Borkmann 
1257cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 4, "count");
1258cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 5, "revision");
1259cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
1260cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
1261cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
1262cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
1263cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1264cd13c91dSDaniel Borkmann 
1265cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
1266cd13c91dSDaniel Borkmann 
1267cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1268cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1269cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
1270cd13c91dSDaniel Borkmann 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
1271cd13c91dSDaniel Borkmann 
1272cd13c91dSDaniel Borkmann cleanup_target4:
1273cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1274cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1275cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1276cd13c91dSDaniel Borkmann 
1277cd13c91dSDaniel Borkmann cleanup_target3:
1278cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1279cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1280cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1281cd13c91dSDaniel Borkmann 
1282cd13c91dSDaniel Borkmann cleanup_target2:
1283cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1284cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1285cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1286cd13c91dSDaniel Borkmann 
1287cd13c91dSDaniel Borkmann cleanup_target:
1288cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1289cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1290cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1291cd13c91dSDaniel Borkmann 
1292cd13c91dSDaniel Borkmann cleanup:
1293cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
1294cd13c91dSDaniel Borkmann }
1295cd13c91dSDaniel Borkmann 
1296cd13c91dSDaniel Borkmann void serial_test_tc_opts_append(void)
1297cd13c91dSDaniel Borkmann {
1298cd13c91dSDaniel Borkmann 	test_tc_opts_append_target(BPF_TCX_INGRESS);
1299cd13c91dSDaniel Borkmann 	test_tc_opts_append_target(BPF_TCX_EGRESS);
1300cd13c91dSDaniel Borkmann }
1301cd13c91dSDaniel Borkmann 
1302cd13c91dSDaniel Borkmann static void test_tc_opts_dev_cleanup_target(int target)
1303cd13c91dSDaniel Borkmann {
1304cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1305cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1306cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1307cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1308cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
1309cd13c91dSDaniel Borkmann 	int err, ifindex;
1310cd13c91dSDaniel Borkmann 
1311cd13c91dSDaniel Borkmann 	ASSERT_OK(system("ip link add dev tcx_opts1 type veth peer name tcx_opts2"), "add veth");
1312cd13c91dSDaniel Borkmann 	ifindex = if_nametoindex("tcx_opts1");
1313cd13c91dSDaniel Borkmann 	ASSERT_NEQ(ifindex, 0, "non_zero_ifindex");
1314cd13c91dSDaniel Borkmann 
1315cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
1316cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1317cd13c91dSDaniel Borkmann 		goto cleanup;
1318cd13c91dSDaniel Borkmann 
1319cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
1320cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
1321cd13c91dSDaniel Borkmann 	fd3 = bpf_program__fd(skel->progs.tc3);
1322cd13c91dSDaniel Borkmann 	fd4 = bpf_program__fd(skel->progs.tc4);
1323cd13c91dSDaniel Borkmann 
1324cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
1325cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
1326cd13c91dSDaniel Borkmann 	id3 = id_from_prog_fd(fd3);
1327cd13c91dSDaniel Borkmann 	id4 = id_from_prog_fd(fd4);
1328cd13c91dSDaniel Borkmann 
1329cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1330cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1331cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1332cd13c91dSDaniel Borkmann 
1333cd13c91dSDaniel Borkmann 	assert_mprog_count_ifindex(ifindex, target, 0);
1334cd13c91dSDaniel Borkmann 
1335cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, ifindex, target, &opta);
1336cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1337cd13c91dSDaniel Borkmann 		goto cleanup;
1338cd13c91dSDaniel Borkmann 
1339cd13c91dSDaniel Borkmann 	assert_mprog_count_ifindex(ifindex, target, 1);
1340cd13c91dSDaniel Borkmann 
1341cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, ifindex, target, &opta);
1342cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1343cd13c91dSDaniel Borkmann 		goto cleanup1;
1344cd13c91dSDaniel Borkmann 
1345cd13c91dSDaniel Borkmann 	assert_mprog_count_ifindex(ifindex, target, 2);
1346cd13c91dSDaniel Borkmann 
1347cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, ifindex, target, &opta);
1348cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1349cd13c91dSDaniel Borkmann 		goto cleanup2;
1350cd13c91dSDaniel Borkmann 
1351cd13c91dSDaniel Borkmann 	assert_mprog_count_ifindex(ifindex, target, 3);
1352cd13c91dSDaniel Borkmann 
1353cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd4, ifindex, target, &opta);
1354cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1355cd13c91dSDaniel Borkmann 		goto cleanup3;
1356cd13c91dSDaniel Borkmann 
1357cd13c91dSDaniel Borkmann 	assert_mprog_count_ifindex(ifindex, target, 4);
1358cd13c91dSDaniel Borkmann 
1359cd13c91dSDaniel Borkmann 	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1360cd13c91dSDaniel Borkmann 	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1361cd13c91dSDaniel Borkmann 	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1362cd13c91dSDaniel Borkmann 	return;
1363cd13c91dSDaniel Borkmann cleanup3:
1364cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1365cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1366cd13c91dSDaniel Borkmann 
1367cd13c91dSDaniel Borkmann 	assert_mprog_count_ifindex(ifindex, target, 2);
1368cd13c91dSDaniel Borkmann cleanup2:
1369cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1370cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1371cd13c91dSDaniel Borkmann 
1372cd13c91dSDaniel Borkmann 	assert_mprog_count_ifindex(ifindex, target, 1);
1373cd13c91dSDaniel Borkmann cleanup1:
1374cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1375cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1376cd13c91dSDaniel Borkmann 
1377cd13c91dSDaniel Borkmann 	assert_mprog_count_ifindex(ifindex, target, 0);
1378cd13c91dSDaniel Borkmann cleanup:
1379cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
1380cd13c91dSDaniel Borkmann 
1381cd13c91dSDaniel Borkmann 	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1382cd13c91dSDaniel Borkmann 	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1383cd13c91dSDaniel Borkmann 	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1384cd13c91dSDaniel Borkmann }
1385cd13c91dSDaniel Borkmann 
1386cd13c91dSDaniel Borkmann void serial_test_tc_opts_dev_cleanup(void)
1387cd13c91dSDaniel Borkmann {
1388cd13c91dSDaniel Borkmann 	test_tc_opts_dev_cleanup_target(BPF_TCX_INGRESS);
1389cd13c91dSDaniel Borkmann 	test_tc_opts_dev_cleanup_target(BPF_TCX_EGRESS);
1390cd13c91dSDaniel Borkmann }
1391cd13c91dSDaniel Borkmann 
1392cd13c91dSDaniel Borkmann static void test_tc_opts_mixed_target(int target)
1393cd13c91dSDaniel Borkmann {
1394cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1395cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1396cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1397cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_tcx_opts, optl);
1398cd13c91dSDaniel Borkmann 	__u32 pid1, pid2, pid3, pid4, lid2, lid4;
1399cd13c91dSDaniel Borkmann 	__u32 prog_flags[4], link_flags[4];
1400cd13c91dSDaniel Borkmann 	__u32 prog_ids[4], link_ids[4];
1401cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
1402cd13c91dSDaniel Borkmann 	struct bpf_link *link;
1403cd13c91dSDaniel Borkmann 	int err, detach_fd;
1404cd13c91dSDaniel Borkmann 
1405cd13c91dSDaniel Borkmann 	skel = test_tc_link__open();
1406cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_open"))
1407cd13c91dSDaniel Borkmann 		goto cleanup;
1408cd13c91dSDaniel Borkmann 
1409cd13c91dSDaniel Borkmann 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1410cd13c91dSDaniel Borkmann 		  0, "tc1_attach_type");
1411cd13c91dSDaniel Borkmann 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1412cd13c91dSDaniel Borkmann 		  0, "tc2_attach_type");
1413cd13c91dSDaniel Borkmann 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
1414cd13c91dSDaniel Borkmann 		  0, "tc3_attach_type");
1415cd13c91dSDaniel Borkmann 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1416cd13c91dSDaniel Borkmann 		  0, "tc4_attach_type");
1417cd13c91dSDaniel Borkmann 
1418cd13c91dSDaniel Borkmann 	err = test_tc_link__load(skel);
1419cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "skel_load"))
1420cd13c91dSDaniel Borkmann 		goto cleanup;
1421cd13c91dSDaniel Borkmann 
1422cd13c91dSDaniel Borkmann 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1423cd13c91dSDaniel Borkmann 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1424cd13c91dSDaniel Borkmann 	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1425cd13c91dSDaniel Borkmann 	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1426cd13c91dSDaniel Borkmann 
1427cd13c91dSDaniel Borkmann 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1428cd13c91dSDaniel Borkmann 	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
1429cd13c91dSDaniel Borkmann 	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1430cd13c91dSDaniel Borkmann 
1431cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1432cd13c91dSDaniel Borkmann 
1433cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc1),
1434cd13c91dSDaniel Borkmann 				   loopback, target, &opta);
1435cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1436cd13c91dSDaniel Borkmann 		goto cleanup;
1437cd13c91dSDaniel Borkmann 
1438cd13c91dSDaniel Borkmann 	detach_fd = bpf_program__fd(skel->progs.tc1);
1439cd13c91dSDaniel Borkmann 
1440cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1441cd13c91dSDaniel Borkmann 
1442cd13c91dSDaniel Borkmann 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1443cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(link, "link_attach"))
1444cd13c91dSDaniel Borkmann 		goto cleanup1;
1445cd13c91dSDaniel Borkmann 	skel->links.tc2 = link;
1446cd13c91dSDaniel Borkmann 
1447cd13c91dSDaniel Borkmann 	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
1448cd13c91dSDaniel Borkmann 
1449cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1450cd13c91dSDaniel Borkmann 
1451cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1452cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE,
1453cd13c91dSDaniel Borkmann 		.replace_prog_fd = bpf_program__fd(skel->progs.tc1),
1454cd13c91dSDaniel Borkmann 	);
1455cd13c91dSDaniel Borkmann 
1456cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc2),
1457cd13c91dSDaniel Borkmann 				   loopback, target, &opta);
1458cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1459cd13c91dSDaniel Borkmann 
1460cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1461cd13c91dSDaniel Borkmann 
1462cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1463cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE,
1464cd13c91dSDaniel Borkmann 		.replace_prog_fd = bpf_program__fd(skel->progs.tc2),
1465cd13c91dSDaniel Borkmann 	);
1466cd13c91dSDaniel Borkmann 
1467cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc1),
1468cd13c91dSDaniel Borkmann 				   loopback, target, &opta);
1469cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1470cd13c91dSDaniel Borkmann 
1471cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1472cd13c91dSDaniel Borkmann 
1473cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1474cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE,
1475cd13c91dSDaniel Borkmann 		.replace_prog_fd = bpf_program__fd(skel->progs.tc2),
1476cd13c91dSDaniel Borkmann 	);
1477cd13c91dSDaniel Borkmann 
1478cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc3),
1479cd13c91dSDaniel Borkmann 				   loopback, target, &opta);
1480cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EBUSY, "prog_attach");
1481cd13c91dSDaniel Borkmann 
1482cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1483cd13c91dSDaniel Borkmann 
1484cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1485cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE,
1486cd13c91dSDaniel Borkmann 		.replace_prog_fd = bpf_program__fd(skel->progs.tc1),
1487cd13c91dSDaniel Borkmann 	);
1488cd13c91dSDaniel Borkmann 
1489cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc3),
1490cd13c91dSDaniel Borkmann 				   loopback, target, &opta);
1491cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1492cd13c91dSDaniel Borkmann 		goto cleanup1;
1493cd13c91dSDaniel Borkmann 
1494cd13c91dSDaniel Borkmann 	detach_fd = bpf_program__fd(skel->progs.tc3);
1495cd13c91dSDaniel Borkmann 
1496cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1497cd13c91dSDaniel Borkmann 
1498cd13c91dSDaniel Borkmann 	link = bpf_program__attach_tcx(skel->progs.tc4, loopback, &optl);
1499cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(link, "link_attach"))
1500cd13c91dSDaniel Borkmann 		goto cleanup1;
1501cd13c91dSDaniel Borkmann 	skel->links.tc4 = link;
1502cd13c91dSDaniel Borkmann 
1503cd13c91dSDaniel Borkmann 	lid4 = id_from_link_fd(bpf_link__fd(skel->links.tc4));
1504cd13c91dSDaniel Borkmann 
1505cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1506cd13c91dSDaniel Borkmann 
1507cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(opta,
1508cd13c91dSDaniel Borkmann 		.flags = BPF_F_REPLACE,
1509cd13c91dSDaniel Borkmann 		.replace_prog_fd = bpf_program__fd(skel->progs.tc4),
1510cd13c91dSDaniel Borkmann 	);
1511cd13c91dSDaniel Borkmann 
1512cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc2),
1513cd13c91dSDaniel Borkmann 				   loopback, target, &opta);
1514cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1515cd13c91dSDaniel Borkmann 
1516cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
1517cd13c91dSDaniel Borkmann 	optq.prog_attach_flags = prog_flags;
1518cd13c91dSDaniel Borkmann 	optq.link_ids = link_ids;
1519cd13c91dSDaniel Borkmann 	optq.link_attach_flags = link_flags;
1520cd13c91dSDaniel Borkmann 
1521cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1522cd13c91dSDaniel Borkmann 	memset(prog_flags, 0, sizeof(prog_flags));
1523cd13c91dSDaniel Borkmann 	memset(link_ids, 0, sizeof(link_ids));
1524cd13c91dSDaniel Borkmann 	memset(link_flags, 0, sizeof(link_flags));
1525cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1526cd13c91dSDaniel Borkmann 
1527cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1528cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1529cd13c91dSDaniel Borkmann 		goto cleanup1;
1530cd13c91dSDaniel Borkmann 
1531cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 3, "count");
1532cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 5, "revision");
1533cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], pid3, "prog_ids[0]");
1534cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_attach_flags[0], 0, "prog_flags[0]");
1535cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.link_ids[0], 0, "link_ids[0]");
1536cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.link_attach_flags[0], 0, "link_flags[0]");
1537cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
1538cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_attach_flags[1], 0, "prog_flags[1]");
1539cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
1540cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.link_attach_flags[1], 0, "link_flags[1]");
1541cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], pid4, "prog_ids[2]");
1542cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_attach_flags[2], 0, "prog_flags[2]");
1543cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.link_ids[2], lid4, "link_ids[2]");
1544cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.link_attach_flags[2], 0, "link_flags[2]");
1545cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
1546cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_attach_flags[3], 0, "prog_flags[3]");
1547cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.link_ids[3], 0, "link_ids[3]");
1548cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.link_attach_flags[3], 0, "link_flags[3]");
1549cd13c91dSDaniel Borkmann 
1550cd13c91dSDaniel Borkmann 	ASSERT_OK(system(ping_cmd), ping_cmd);
1551cd13c91dSDaniel Borkmann 
1552cd13c91dSDaniel Borkmann cleanup1:
1553cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(detach_fd, loopback, target, &optd);
1554cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1555cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1556cd13c91dSDaniel Borkmann 
1557cd13c91dSDaniel Borkmann cleanup:
1558cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
1559cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1560cd13c91dSDaniel Borkmann }
1561cd13c91dSDaniel Borkmann 
1562cd13c91dSDaniel Borkmann void serial_test_tc_opts_mixed(void)
1563cd13c91dSDaniel Borkmann {
1564cd13c91dSDaniel Borkmann 	test_tc_opts_mixed_target(BPF_TCX_INGRESS);
1565cd13c91dSDaniel Borkmann 	test_tc_opts_mixed_target(BPF_TCX_EGRESS);
1566cd13c91dSDaniel Borkmann }
1567cd13c91dSDaniel Borkmann 
1568cd13c91dSDaniel Borkmann static void test_tc_opts_demixed_target(int target)
1569cd13c91dSDaniel Borkmann {
1570cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1571cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1572cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_tcx_opts, optl);
1573cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
1574cd13c91dSDaniel Borkmann 	struct bpf_link *link;
1575cd13c91dSDaniel Borkmann 	__u32 pid1, pid2;
1576cd13c91dSDaniel Borkmann 	int err;
1577cd13c91dSDaniel Borkmann 
1578cd13c91dSDaniel Borkmann 	skel = test_tc_link__open();
1579cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_open"))
1580cd13c91dSDaniel Borkmann 		goto cleanup;
1581cd13c91dSDaniel Borkmann 
1582cd13c91dSDaniel Borkmann 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1583cd13c91dSDaniel Borkmann 		  0, "tc1_attach_type");
1584cd13c91dSDaniel Borkmann 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1585cd13c91dSDaniel Borkmann 		  0, "tc2_attach_type");
1586cd13c91dSDaniel Borkmann 
1587cd13c91dSDaniel Borkmann 	err = test_tc_link__load(skel);
1588cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "skel_load"))
1589cd13c91dSDaniel Borkmann 		goto cleanup;
1590cd13c91dSDaniel Borkmann 
1591cd13c91dSDaniel Borkmann 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1592cd13c91dSDaniel Borkmann 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1593cd13c91dSDaniel Borkmann 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1594cd13c91dSDaniel Borkmann 
1595cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1596cd13c91dSDaniel Borkmann 
1597cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc1),
1598cd13c91dSDaniel Borkmann 				   loopback, target, &opta);
1599cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1600cd13c91dSDaniel Borkmann 		goto cleanup;
1601cd13c91dSDaniel Borkmann 
1602cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1603cd13c91dSDaniel Borkmann 
1604cd13c91dSDaniel Borkmann 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1605cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(link, "link_attach"))
1606cd13c91dSDaniel Borkmann 		goto cleanup1;
1607cd13c91dSDaniel Borkmann 	skel->links.tc2 = link;
1608cd13c91dSDaniel Borkmann 
1609cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1610cd13c91dSDaniel Borkmann 
1611cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1612cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
1613cd13c91dSDaniel Borkmann 	);
1614cd13c91dSDaniel Borkmann 
1615cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1616cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -EBUSY, "prog_detach");
1617cd13c91dSDaniel Borkmann 
1618cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1619cd13c91dSDaniel Borkmann 
1620cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1621cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1622cd13c91dSDaniel Borkmann 	);
1623cd13c91dSDaniel Borkmann 
1624cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1625cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1626cd13c91dSDaniel Borkmann 
1627cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1628cd13c91dSDaniel Borkmann 	goto cleanup;
1629cd13c91dSDaniel Borkmann 
1630cd13c91dSDaniel Borkmann cleanup1:
1631cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(bpf_program__fd(skel->progs.tc1),
1632cd13c91dSDaniel Borkmann 				   loopback, target, &optd);
1633cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1634cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1635cd13c91dSDaniel Borkmann 
1636cd13c91dSDaniel Borkmann cleanup:
1637cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
1638cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1639cd13c91dSDaniel Borkmann }
1640cd13c91dSDaniel Borkmann 
1641cd13c91dSDaniel Borkmann void serial_test_tc_opts_demixed(void)
1642cd13c91dSDaniel Borkmann {
1643cd13c91dSDaniel Borkmann 	test_tc_opts_demixed_target(BPF_TCX_INGRESS);
1644cd13c91dSDaniel Borkmann 	test_tc_opts_demixed_target(BPF_TCX_EGRESS);
1645cd13c91dSDaniel Borkmann }
1646cd13c91dSDaniel Borkmann 
1647cd13c91dSDaniel Borkmann static void test_tc_opts_detach_target(int target)
1648cd13c91dSDaniel Borkmann {
1649cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1650cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1651cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1652cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1653cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
1654cd13c91dSDaniel Borkmann 	__u32 prog_ids[5];
1655cd13c91dSDaniel Borkmann 	int err;
1656cd13c91dSDaniel Borkmann 
1657cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
1658cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1659cd13c91dSDaniel Borkmann 		goto cleanup;
1660cd13c91dSDaniel Borkmann 
1661cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
1662cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
1663cd13c91dSDaniel Borkmann 	fd3 = bpf_program__fd(skel->progs.tc3);
1664cd13c91dSDaniel Borkmann 	fd4 = bpf_program__fd(skel->progs.tc4);
1665cd13c91dSDaniel Borkmann 
1666cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
1667cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
1668cd13c91dSDaniel Borkmann 	id3 = id_from_prog_fd(fd3);
1669cd13c91dSDaniel Borkmann 	id4 = id_from_prog_fd(fd4);
1670cd13c91dSDaniel Borkmann 
1671cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1672cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1673cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1674cd13c91dSDaniel Borkmann 
1675cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1676cd13c91dSDaniel Borkmann 
1677cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1678cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1679cd13c91dSDaniel Borkmann 		goto cleanup;
1680cd13c91dSDaniel Borkmann 
1681cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1682cd13c91dSDaniel Borkmann 
1683cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
1684cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1685cd13c91dSDaniel Borkmann 		goto cleanup1;
1686cd13c91dSDaniel Borkmann 
1687cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1688cd13c91dSDaniel Borkmann 
1689cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
1690cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1691cd13c91dSDaniel Borkmann 		goto cleanup2;
1692cd13c91dSDaniel Borkmann 
1693cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1694cd13c91dSDaniel Borkmann 
1695cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
1696cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1697cd13c91dSDaniel Borkmann 		goto cleanup3;
1698cd13c91dSDaniel Borkmann 
1699cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 4);
1700cd13c91dSDaniel Borkmann 
1701cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
1702cd13c91dSDaniel Borkmann 
1703cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1704cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1705cd13c91dSDaniel Borkmann 
1706cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1707cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1708cd13c91dSDaniel Borkmann 		goto cleanup4;
1709cd13c91dSDaniel Borkmann 
1710cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 4, "count");
1711cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 5, "revision");
1712cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
1713cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
1714cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
1715cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
1716cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1717cd13c91dSDaniel Borkmann 
1718cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1719cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1720cd13c91dSDaniel Borkmann 	);
1721cd13c91dSDaniel Borkmann 
1722cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1723cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1724cd13c91dSDaniel Borkmann 
1725cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1726cd13c91dSDaniel Borkmann 
1727cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1728cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1729cd13c91dSDaniel Borkmann 
1730cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1731cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1732cd13c91dSDaniel Borkmann 		goto cleanup4;
1733cd13c91dSDaniel Borkmann 
1734cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 3, "count");
1735cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 6, "revision");
1736cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1737cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1738cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id4, "prog_ids[2]");
1739cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
1740cd13c91dSDaniel Borkmann 
1741cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1742cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
1743cd13c91dSDaniel Borkmann 	);
1744cd13c91dSDaniel Borkmann 
1745cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1746cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1747cd13c91dSDaniel Borkmann 
1748cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1749cd13c91dSDaniel Borkmann 
1750cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1751cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1752cd13c91dSDaniel Borkmann 
1753cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1754cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1755cd13c91dSDaniel Borkmann 		goto cleanup4;
1756cd13c91dSDaniel Borkmann 
1757cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
1758cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 7, "revision");
1759cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1760cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1761cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1762cd13c91dSDaniel Borkmann 
1763cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd);
1764cd13c91dSDaniel Borkmann 
1765cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1766cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1767cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1768cd13c91dSDaniel Borkmann 
1769cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1770cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1771cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1772cd13c91dSDaniel Borkmann 
1773cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1774cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1775cd13c91dSDaniel Borkmann 	);
1776cd13c91dSDaniel Borkmann 
1777cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1778cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1779cd13c91dSDaniel Borkmann 
1780cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1781cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
1782cd13c91dSDaniel Borkmann 	);
1783cd13c91dSDaniel Borkmann 
1784cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1785cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1786cd13c91dSDaniel Borkmann 	goto cleanup;
1787cd13c91dSDaniel Borkmann 
1788cd13c91dSDaniel Borkmann cleanup4:
1789cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1790cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1791cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1792cd13c91dSDaniel Borkmann 
1793cd13c91dSDaniel Borkmann cleanup3:
1794cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1795cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1796cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1797cd13c91dSDaniel Borkmann 
1798cd13c91dSDaniel Borkmann cleanup2:
1799cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1800cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1801cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1802cd13c91dSDaniel Borkmann 
1803cd13c91dSDaniel Borkmann cleanup1:
1804cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1805cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1806cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1807cd13c91dSDaniel Borkmann 
1808cd13c91dSDaniel Borkmann cleanup:
1809cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
1810cd13c91dSDaniel Borkmann }
1811cd13c91dSDaniel Borkmann 
1812cd13c91dSDaniel Borkmann void serial_test_tc_opts_detach(void)
1813cd13c91dSDaniel Borkmann {
1814cd13c91dSDaniel Borkmann 	test_tc_opts_detach_target(BPF_TCX_INGRESS);
1815cd13c91dSDaniel Borkmann 	test_tc_opts_detach_target(BPF_TCX_EGRESS);
1816cd13c91dSDaniel Borkmann }
1817cd13c91dSDaniel Borkmann 
1818cd13c91dSDaniel Borkmann static void test_tc_opts_detach_before_target(int target)
1819cd13c91dSDaniel Borkmann {
1820cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1821cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1822cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1823cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1824cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
1825cd13c91dSDaniel Borkmann 	__u32 prog_ids[5];
1826cd13c91dSDaniel Borkmann 	int err;
1827cd13c91dSDaniel Borkmann 
1828cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
1829cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1830cd13c91dSDaniel Borkmann 		goto cleanup;
1831cd13c91dSDaniel Borkmann 
1832cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
1833cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
1834cd13c91dSDaniel Borkmann 	fd3 = bpf_program__fd(skel->progs.tc3);
1835cd13c91dSDaniel Borkmann 	fd4 = bpf_program__fd(skel->progs.tc4);
1836cd13c91dSDaniel Borkmann 
1837cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
1838cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
1839cd13c91dSDaniel Borkmann 	id3 = id_from_prog_fd(fd3);
1840cd13c91dSDaniel Borkmann 	id4 = id_from_prog_fd(fd4);
1841cd13c91dSDaniel Borkmann 
1842cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1843cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1844cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1845cd13c91dSDaniel Borkmann 
1846cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1847cd13c91dSDaniel Borkmann 
1848cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1849cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1850cd13c91dSDaniel Borkmann 		goto cleanup;
1851cd13c91dSDaniel Borkmann 
1852cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1853cd13c91dSDaniel Borkmann 
1854cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
1855cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1856cd13c91dSDaniel Borkmann 		goto cleanup1;
1857cd13c91dSDaniel Borkmann 
1858cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1859cd13c91dSDaniel Borkmann 
1860cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
1861cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1862cd13c91dSDaniel Borkmann 		goto cleanup2;
1863cd13c91dSDaniel Borkmann 
1864cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1865cd13c91dSDaniel Borkmann 
1866cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
1867cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1868cd13c91dSDaniel Borkmann 		goto cleanup3;
1869cd13c91dSDaniel Borkmann 
1870cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 4);
1871cd13c91dSDaniel Borkmann 
1872cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
1873cd13c91dSDaniel Borkmann 
1874cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1875cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1876cd13c91dSDaniel Borkmann 
1877cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1878cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1879cd13c91dSDaniel Borkmann 		goto cleanup4;
1880cd13c91dSDaniel Borkmann 
1881cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 4, "count");
1882cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 5, "revision");
1883cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
1884cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
1885cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
1886cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
1887cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1888cd13c91dSDaniel Borkmann 
1889cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1890cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1891cd13c91dSDaniel Borkmann 		.relative_fd = fd2,
1892cd13c91dSDaniel Borkmann 	);
1893cd13c91dSDaniel Borkmann 
1894cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1895cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1896cd13c91dSDaniel Borkmann 
1897cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1898cd13c91dSDaniel Borkmann 
1899cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1900cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1901cd13c91dSDaniel Borkmann 
1902cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1903cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1904cd13c91dSDaniel Borkmann 		goto cleanup4;
1905cd13c91dSDaniel Borkmann 
1906cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 3, "count");
1907cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 6, "revision");
1908cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1909cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1910cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id4, "prog_ids[2]");
1911cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
1912cd13c91dSDaniel Borkmann 
1913cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1914cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1915cd13c91dSDaniel Borkmann 		.relative_fd = fd2,
1916cd13c91dSDaniel Borkmann 	);
1917cd13c91dSDaniel Borkmann 
1918cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1919cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1920cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1921cd13c91dSDaniel Borkmann 
1922cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1923cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1924cd13c91dSDaniel Borkmann 		.relative_fd = fd4,
1925cd13c91dSDaniel Borkmann 	);
1926cd13c91dSDaniel Borkmann 
1927cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1928cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ERANGE, "prog_detach");
1929cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1930cd13c91dSDaniel Borkmann 
1931cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1932cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1933cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
1934cd13c91dSDaniel Borkmann 	);
1935cd13c91dSDaniel Borkmann 
1936cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1937cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1938cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1939cd13c91dSDaniel Borkmann 
1940cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1941cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1942cd13c91dSDaniel Borkmann 		.relative_fd = fd3,
1943cd13c91dSDaniel Borkmann 	);
1944cd13c91dSDaniel Borkmann 
1945cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1946cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1947cd13c91dSDaniel Borkmann 
1948cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
1949cd13c91dSDaniel Borkmann 
1950cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1951cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1952cd13c91dSDaniel Borkmann 
1953cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1954cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1955cd13c91dSDaniel Borkmann 		goto cleanup4;
1956cd13c91dSDaniel Borkmann 
1957cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
1958cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 7, "revision");
1959cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id3, "prog_ids[0]");
1960cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id4, "prog_ids[1]");
1961cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1962cd13c91dSDaniel Borkmann 
1963cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1964cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1965cd13c91dSDaniel Borkmann 		.relative_fd = fd4,
1966cd13c91dSDaniel Borkmann 	);
1967cd13c91dSDaniel Borkmann 
1968cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1969cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1970cd13c91dSDaniel Borkmann 
1971cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
1972cd13c91dSDaniel Borkmann 
1973cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
1974cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
1975cd13c91dSDaniel Borkmann 
1976cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
1977cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
1978cd13c91dSDaniel Borkmann 		goto cleanup4;
1979cd13c91dSDaniel Borkmann 
1980cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 1, "count");
1981cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 8, "revision");
1982cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id4, "prog_ids[0]");
1983cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
1984cd13c91dSDaniel Borkmann 
1985cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
1986cd13c91dSDaniel Borkmann 		.flags = BPF_F_BEFORE,
1987cd13c91dSDaniel Borkmann 	);
1988cd13c91dSDaniel Borkmann 
1989cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1990cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1991cd13c91dSDaniel Borkmann 
1992cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
1993cd13c91dSDaniel Borkmann 	goto cleanup;
1994cd13c91dSDaniel Borkmann 
1995cd13c91dSDaniel Borkmann cleanup4:
1996cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1997cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
1998cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
1999cd13c91dSDaniel Borkmann 
2000cd13c91dSDaniel Borkmann cleanup3:
2001cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2002cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2003cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
2004cd13c91dSDaniel Borkmann 
2005cd13c91dSDaniel Borkmann cleanup2:
2006cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2007cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2008cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
2009cd13c91dSDaniel Borkmann 
2010cd13c91dSDaniel Borkmann cleanup1:
2011cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2012cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2013cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
2014cd13c91dSDaniel Borkmann 
2015cd13c91dSDaniel Borkmann cleanup:
2016cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
2017cd13c91dSDaniel Borkmann }
2018cd13c91dSDaniel Borkmann 
2019cd13c91dSDaniel Borkmann void serial_test_tc_opts_detach_before(void)
2020cd13c91dSDaniel Borkmann {
2021cd13c91dSDaniel Borkmann 	test_tc_opts_detach_before_target(BPF_TCX_INGRESS);
2022cd13c91dSDaniel Borkmann 	test_tc_opts_detach_before_target(BPF_TCX_EGRESS);
2023cd13c91dSDaniel Borkmann }
2024cd13c91dSDaniel Borkmann 
2025cd13c91dSDaniel Borkmann static void test_tc_opts_detach_after_target(int target)
2026cd13c91dSDaniel Borkmann {
2027cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2028cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2029cd13c91dSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
2030cd13c91dSDaniel Borkmann 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
2031cd13c91dSDaniel Borkmann 	struct test_tc_link *skel;
2032cd13c91dSDaniel Borkmann 	__u32 prog_ids[5];
2033cd13c91dSDaniel Borkmann 	int err;
2034cd13c91dSDaniel Borkmann 
2035cd13c91dSDaniel Borkmann 	skel = test_tc_link__open_and_load();
2036cd13c91dSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_load"))
2037cd13c91dSDaniel Borkmann 		goto cleanup;
2038cd13c91dSDaniel Borkmann 
2039cd13c91dSDaniel Borkmann 	fd1 = bpf_program__fd(skel->progs.tc1);
2040cd13c91dSDaniel Borkmann 	fd2 = bpf_program__fd(skel->progs.tc2);
2041cd13c91dSDaniel Borkmann 	fd3 = bpf_program__fd(skel->progs.tc3);
2042cd13c91dSDaniel Borkmann 	fd4 = bpf_program__fd(skel->progs.tc4);
2043cd13c91dSDaniel Borkmann 
2044cd13c91dSDaniel Borkmann 	id1 = id_from_prog_fd(fd1);
2045cd13c91dSDaniel Borkmann 	id2 = id_from_prog_fd(fd2);
2046cd13c91dSDaniel Borkmann 	id3 = id_from_prog_fd(fd3);
2047cd13c91dSDaniel Borkmann 	id4 = id_from_prog_fd(fd4);
2048cd13c91dSDaniel Borkmann 
2049cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
2050cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
2051cd13c91dSDaniel Borkmann 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
2052cd13c91dSDaniel Borkmann 
2053cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
2054cd13c91dSDaniel Borkmann 
2055cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
2056cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2057cd13c91dSDaniel Borkmann 		goto cleanup;
2058cd13c91dSDaniel Borkmann 
2059cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
2060cd13c91dSDaniel Borkmann 
2061cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
2062cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2063cd13c91dSDaniel Borkmann 		goto cleanup1;
2064cd13c91dSDaniel Borkmann 
2065cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
2066cd13c91dSDaniel Borkmann 
2067cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
2068cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2069cd13c91dSDaniel Borkmann 		goto cleanup2;
2070cd13c91dSDaniel Borkmann 
2071cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
2072cd13c91dSDaniel Borkmann 
2073cd13c91dSDaniel Borkmann 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
2074cd13c91dSDaniel Borkmann 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2075cd13c91dSDaniel Borkmann 		goto cleanup3;
2076cd13c91dSDaniel Borkmann 
2077cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 4);
2078cd13c91dSDaniel Borkmann 
2079cd13c91dSDaniel Borkmann 	optq.prog_ids = prog_ids;
2080cd13c91dSDaniel Borkmann 
2081cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
2082cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
2083cd13c91dSDaniel Borkmann 
2084cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
2085cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
2086cd13c91dSDaniel Borkmann 		goto cleanup4;
2087cd13c91dSDaniel Borkmann 
2088cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 4, "count");
2089cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 5, "revision");
2090cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2091cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
2092cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
2093cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
2094cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
2095cd13c91dSDaniel Borkmann 
2096cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
2097cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
2098cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
2099cd13c91dSDaniel Borkmann 	);
2100cd13c91dSDaniel Borkmann 
2101cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2102cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2103cd13c91dSDaniel Borkmann 
2104cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
2105cd13c91dSDaniel Borkmann 
2106cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
2107cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
2108cd13c91dSDaniel Borkmann 
2109cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
2110cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
2111cd13c91dSDaniel Borkmann 		goto cleanup4;
2112cd13c91dSDaniel Borkmann 
2113cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 3, "count");
2114cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 6, "revision");
2115cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2116cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
2117cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], id4, "prog_ids[2]");
2118cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
2119cd13c91dSDaniel Borkmann 
2120cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
2121cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
2122cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
2123cd13c91dSDaniel Borkmann 	);
2124cd13c91dSDaniel Borkmann 
2125cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2126cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_detach");
2127cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
2128cd13c91dSDaniel Borkmann 
2129cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
2130cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
2131cd13c91dSDaniel Borkmann 		.relative_fd = fd4,
2132cd13c91dSDaniel Borkmann 	);
2133cd13c91dSDaniel Borkmann 
2134cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2135cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ERANGE, "prog_detach");
2136cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
2137cd13c91dSDaniel Borkmann 
2138cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
2139cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
2140cd13c91dSDaniel Borkmann 		.relative_fd = fd3,
2141cd13c91dSDaniel Borkmann 	);
2142cd13c91dSDaniel Borkmann 
2143cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2144cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ERANGE, "prog_detach");
2145cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
2146cd13c91dSDaniel Borkmann 
2147cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
2148cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
2149cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
2150cd13c91dSDaniel Borkmann 	);
2151cd13c91dSDaniel Borkmann 
2152cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2153cd13c91dSDaniel Borkmann 	ASSERT_EQ(err, -ERANGE, "prog_detach");
2154cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
2155cd13c91dSDaniel Borkmann 
2156cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
2157cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
2158cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
2159cd13c91dSDaniel Borkmann 	);
2160cd13c91dSDaniel Borkmann 
2161cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2162cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2163cd13c91dSDaniel Borkmann 
2164cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
2165cd13c91dSDaniel Borkmann 
2166cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
2167cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
2168cd13c91dSDaniel Borkmann 
2169cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
2170cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
2171cd13c91dSDaniel Borkmann 		goto cleanup4;
2172cd13c91dSDaniel Borkmann 
2173cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 2, "count");
2174cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 7, "revision");
2175cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2176cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], id4, "prog_ids[1]");
2177cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
2178cd13c91dSDaniel Borkmann 
2179cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
2180cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
2181cd13c91dSDaniel Borkmann 		.relative_fd = fd1,
2182cd13c91dSDaniel Borkmann 	);
2183cd13c91dSDaniel Borkmann 
2184cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
2185cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2186cd13c91dSDaniel Borkmann 
2187cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
2188cd13c91dSDaniel Borkmann 
2189cd13c91dSDaniel Borkmann 	memset(prog_ids, 0, sizeof(prog_ids));
2190cd13c91dSDaniel Borkmann 	optq.count = ARRAY_SIZE(prog_ids);
2191cd13c91dSDaniel Borkmann 
2192cd13c91dSDaniel Borkmann 	err = bpf_prog_query_opts(loopback, target, &optq);
2193cd13c91dSDaniel Borkmann 	if (!ASSERT_OK(err, "prog_query"))
2194cd13c91dSDaniel Borkmann 		goto cleanup4;
2195cd13c91dSDaniel Borkmann 
2196cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.count, 1, "count");
2197cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.revision, 8, "revision");
2198cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2199cd13c91dSDaniel Borkmann 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
2200cd13c91dSDaniel Borkmann 
2201cd13c91dSDaniel Borkmann 	LIBBPF_OPTS_RESET(optd,
2202cd13c91dSDaniel Borkmann 		.flags = BPF_F_AFTER,
2203cd13c91dSDaniel Borkmann 	);
2204cd13c91dSDaniel Borkmann 
2205cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
2206cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2207cd13c91dSDaniel Borkmann 
2208cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
2209cd13c91dSDaniel Borkmann 	goto cleanup;
2210cd13c91dSDaniel Borkmann 
2211cd13c91dSDaniel Borkmann cleanup4:
2212cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
2213cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2214cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 3);
2215cd13c91dSDaniel Borkmann 
2216cd13c91dSDaniel Borkmann cleanup3:
2217cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2218cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2219cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 2);
2220cd13c91dSDaniel Borkmann 
2221cd13c91dSDaniel Borkmann cleanup2:
2222cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2223cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2224cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 1);
2225cd13c91dSDaniel Borkmann 
2226cd13c91dSDaniel Borkmann cleanup1:
2227cd13c91dSDaniel Borkmann 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2228cd13c91dSDaniel Borkmann 	ASSERT_OK(err, "prog_detach");
2229cd13c91dSDaniel Borkmann 	assert_mprog_count(target, 0);
2230cd13c91dSDaniel Borkmann 
2231cd13c91dSDaniel Borkmann cleanup:
2232cd13c91dSDaniel Borkmann 	test_tc_link__destroy(skel);
2233cd13c91dSDaniel Borkmann }
2234cd13c91dSDaniel Borkmann 
2235cd13c91dSDaniel Borkmann void serial_test_tc_opts_detach_after(void)
2236cd13c91dSDaniel Borkmann {
2237cd13c91dSDaniel Borkmann 	test_tc_opts_detach_after_target(BPF_TCX_INGRESS);
2238cd13c91dSDaniel Borkmann 	test_tc_opts_detach_after_target(BPF_TCX_EGRESS);
2239cd13c91dSDaniel Borkmann }
2240*21ce6abeSDaniel Borkmann 
2241*21ce6abeSDaniel Borkmann static void test_tc_opts_delete_empty(int target, bool chain_tc_old)
2242*21ce6abeSDaniel Borkmann {
2243*21ce6abeSDaniel Borkmann 	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback);
2244*21ce6abeSDaniel Borkmann 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2245*21ce6abeSDaniel Borkmann 	int err;
2246*21ce6abeSDaniel Borkmann 
2247*21ce6abeSDaniel Borkmann 	assert_mprog_count(target, 0);
2248*21ce6abeSDaniel Borkmann 	if (chain_tc_old) {
2249*21ce6abeSDaniel Borkmann 		tc_hook.attach_point = target == BPF_TCX_INGRESS ?
2250*21ce6abeSDaniel Borkmann 				       BPF_TC_INGRESS : BPF_TC_EGRESS;
2251*21ce6abeSDaniel Borkmann 		err = bpf_tc_hook_create(&tc_hook);
2252*21ce6abeSDaniel Borkmann 		ASSERT_OK(err, "bpf_tc_hook_create");
2253*21ce6abeSDaniel Borkmann 		__assert_mprog_count(target, 0, true, loopback);
2254*21ce6abeSDaniel Borkmann 	}
2255*21ce6abeSDaniel Borkmann 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
2256*21ce6abeSDaniel Borkmann 	ASSERT_EQ(err, -ENOENT, "prog_detach");
2257*21ce6abeSDaniel Borkmann 	if (chain_tc_old) {
2258*21ce6abeSDaniel Borkmann 		tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
2259*21ce6abeSDaniel Borkmann 		bpf_tc_hook_destroy(&tc_hook);
2260*21ce6abeSDaniel Borkmann 	}
2261*21ce6abeSDaniel Borkmann 	assert_mprog_count(target, 0);
2262*21ce6abeSDaniel Borkmann }
2263*21ce6abeSDaniel Borkmann 
2264*21ce6abeSDaniel Borkmann void serial_test_tc_opts_delete_empty(void)
2265*21ce6abeSDaniel Borkmann {
2266*21ce6abeSDaniel Borkmann 	test_tc_opts_delete_empty(BPF_TCX_INGRESS, false);
2267*21ce6abeSDaniel Borkmann 	test_tc_opts_delete_empty(BPF_TCX_EGRESS, false);
2268*21ce6abeSDaniel Borkmann 	test_tc_opts_delete_empty(BPF_TCX_INGRESS, true);
2269*21ce6abeSDaniel Borkmann 	test_tc_opts_delete_empty(BPF_TCX_EGRESS, true);
2270*21ce6abeSDaniel Borkmann }
2271