xref: /linux/tools/testing/selftests/bpf/prog_tests/tc_opts.c (revision ea518afc992032f7570c0a89ac9240b387dc0faf)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2023 Isovalent */
3 #include <uapi/linux/if_link.h>
4 #include <net/if.h>
5 #include <test_progs.h>
6 
7 #define loopback 1
8 #define ping_cmd "ping -q -c1 -w1 127.0.0.1 > /dev/null"
9 
10 #include "test_tc_link.skel.h"
11 #include "tc_helpers.h"
12 
13 void serial_test_tc_opts_basic(void)
14 {
15 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
16 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
17 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
18 	__u32 fd1, fd2, id1, id2;
19 	struct test_tc_link *skel;
20 	__u32 prog_ids[2];
21 	int err;
22 
23 	skel = test_tc_link__open_and_load();
24 	if (!ASSERT_OK_PTR(skel, "skel_load"))
25 		goto cleanup;
26 
27 	fd1 = bpf_program__fd(skel->progs.tc1);
28 	fd2 = bpf_program__fd(skel->progs.tc2);
29 
30 	id1 = id_from_prog_fd(fd1);
31 	id2 = id_from_prog_fd(fd2);
32 
33 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
34 
35 	assert_mprog_count(BPF_TCX_INGRESS, 0);
36 	assert_mprog_count(BPF_TCX_EGRESS, 0);
37 
38 	ASSERT_EQ(skel->bss->seen_tc1, false, "seen_tc1");
39 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
40 
41 	err = bpf_prog_attach_opts(fd1, loopback, BPF_TCX_INGRESS, &opta);
42 	if (!ASSERT_EQ(err, 0, "prog_attach"))
43 		goto cleanup;
44 
45 	assert_mprog_count(BPF_TCX_INGRESS, 1);
46 	assert_mprog_count(BPF_TCX_EGRESS, 0);
47 
48 	optq.prog_ids = prog_ids;
49 
50 	memset(prog_ids, 0, sizeof(prog_ids));
51 	optq.count = ARRAY_SIZE(prog_ids);
52 
53 	err = bpf_prog_query_opts(loopback, BPF_TCX_INGRESS, &optq);
54 	if (!ASSERT_OK(err, "prog_query"))
55 		goto cleanup_in;
56 
57 	ASSERT_EQ(optq.count, 1, "count");
58 	ASSERT_EQ(optq.revision, 2, "revision");
59 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
60 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
61 
62 	tc_skel_reset_all_seen(skel);
63 	ASSERT_OK(system(ping_cmd), ping_cmd);
64 
65 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
66 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
67 
68 	err = bpf_prog_attach_opts(fd2, loopback, BPF_TCX_EGRESS, &opta);
69 	if (!ASSERT_EQ(err, 0, "prog_attach"))
70 		goto cleanup_in;
71 
72 	assert_mprog_count(BPF_TCX_INGRESS, 1);
73 	assert_mprog_count(BPF_TCX_EGRESS, 1);
74 
75 	memset(prog_ids, 0, sizeof(prog_ids));
76 	optq.count = ARRAY_SIZE(prog_ids);
77 
78 	err = bpf_prog_query_opts(loopback, BPF_TCX_EGRESS, &optq);
79 	if (!ASSERT_OK(err, "prog_query"))
80 		goto cleanup_eg;
81 
82 	ASSERT_EQ(optq.count, 1, "count");
83 	ASSERT_EQ(optq.revision, 2, "revision");
84 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
85 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
86 
87 	tc_skel_reset_all_seen(skel);
88 	ASSERT_OK(system(ping_cmd), ping_cmd);
89 
90 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
91 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
92 
93 cleanup_eg:
94 	err = bpf_prog_detach_opts(fd2, loopback, BPF_TCX_EGRESS, &optd);
95 	ASSERT_OK(err, "prog_detach_eg");
96 
97 	assert_mprog_count(BPF_TCX_INGRESS, 1);
98 	assert_mprog_count(BPF_TCX_EGRESS, 0);
99 
100 cleanup_in:
101 	err = bpf_prog_detach_opts(fd1, loopback, BPF_TCX_INGRESS, &optd);
102 	ASSERT_OK(err, "prog_detach_in");
103 
104 	assert_mprog_count(BPF_TCX_INGRESS, 0);
105 	assert_mprog_count(BPF_TCX_EGRESS, 0);
106 
107 cleanup:
108 	test_tc_link__destroy(skel);
109 }
110 
111 static void test_tc_opts_before_target(int target)
112 {
113 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
114 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
115 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
116 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
117 	struct test_tc_link *skel;
118 	__u32 prog_ids[5];
119 	int err;
120 
121 	skel = test_tc_link__open_and_load();
122 	if (!ASSERT_OK_PTR(skel, "skel_load"))
123 		goto cleanup;
124 
125 	fd1 = bpf_program__fd(skel->progs.tc1);
126 	fd2 = bpf_program__fd(skel->progs.tc2);
127 	fd3 = bpf_program__fd(skel->progs.tc3);
128 	fd4 = bpf_program__fd(skel->progs.tc4);
129 
130 	id1 = id_from_prog_fd(fd1);
131 	id2 = id_from_prog_fd(fd2);
132 	id3 = id_from_prog_fd(fd3);
133 	id4 = id_from_prog_fd(fd4);
134 
135 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
136 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
137 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
138 
139 	assert_mprog_count(target, 0);
140 
141 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
142 	if (!ASSERT_EQ(err, 0, "prog_attach"))
143 		goto cleanup;
144 
145 	assert_mprog_count(target, 1);
146 
147 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
148 	if (!ASSERT_EQ(err, 0, "prog_attach"))
149 		goto cleanup_target;
150 
151 	assert_mprog_count(target, 2);
152 
153 	optq.prog_ids = prog_ids;
154 
155 	memset(prog_ids, 0, sizeof(prog_ids));
156 	optq.count = ARRAY_SIZE(prog_ids);
157 
158 	err = bpf_prog_query_opts(loopback, target, &optq);
159 	if (!ASSERT_OK(err, "prog_query"))
160 		goto cleanup_target2;
161 
162 	ASSERT_EQ(optq.count, 2, "count");
163 	ASSERT_EQ(optq.revision, 3, "revision");
164 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
165 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
166 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
167 
168 	tc_skel_reset_all_seen(skel);
169 	ASSERT_OK(system(ping_cmd), ping_cmd);
170 
171 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
172 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
173 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
174 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
175 
176 	LIBBPF_OPTS_RESET(opta,
177 		.flags = BPF_F_BEFORE,
178 		.relative_fd = fd2,
179 	);
180 
181 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
182 	if (!ASSERT_EQ(err, 0, "prog_attach"))
183 		goto cleanup_target2;
184 
185 	memset(prog_ids, 0, sizeof(prog_ids));
186 	optq.count = ARRAY_SIZE(prog_ids);
187 
188 	err = bpf_prog_query_opts(loopback, target, &optq);
189 	if (!ASSERT_OK(err, "prog_query"))
190 		goto cleanup_target3;
191 
192 	ASSERT_EQ(optq.count, 3, "count");
193 	ASSERT_EQ(optq.revision, 4, "revision");
194 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
195 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
196 	ASSERT_EQ(optq.prog_ids[2], id2, "prog_ids[2]");
197 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
198 
199 	LIBBPF_OPTS_RESET(opta,
200 		.flags = BPF_F_BEFORE,
201 		.relative_id = id1,
202 	);
203 
204 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
205 	if (!ASSERT_EQ(err, 0, "prog_attach"))
206 		goto cleanup_target3;
207 
208 	assert_mprog_count(target, 4);
209 
210 	memset(prog_ids, 0, sizeof(prog_ids));
211 	optq.count = ARRAY_SIZE(prog_ids);
212 
213 	err = bpf_prog_query_opts(loopback, target, &optq);
214 	if (!ASSERT_OK(err, "prog_query"))
215 		goto cleanup_target4;
216 
217 	ASSERT_EQ(optq.count, 4, "count");
218 	ASSERT_EQ(optq.revision, 5, "revision");
219 	ASSERT_EQ(optq.prog_ids[0], id4, "prog_ids[0]");
220 	ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]");
221 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
222 	ASSERT_EQ(optq.prog_ids[3], id2, "prog_ids[3]");
223 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
224 
225 	tc_skel_reset_all_seen(skel);
226 	ASSERT_OK(system(ping_cmd), ping_cmd);
227 
228 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
229 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
230 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
231 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
232 
233 cleanup_target4:
234 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
235 	ASSERT_OK(err, "prog_detach");
236 	assert_mprog_count(target, 3);
237 
238 cleanup_target3:
239 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
240 	ASSERT_OK(err, "prog_detach");
241 	assert_mprog_count(target, 2);
242 
243 cleanup_target2:
244 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
245 	ASSERT_OK(err, "prog_detach");
246 	assert_mprog_count(target, 1);
247 
248 cleanup_target:
249 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
250 	ASSERT_OK(err, "prog_detach");
251 	assert_mprog_count(target, 0);
252 
253 cleanup:
254 	test_tc_link__destroy(skel);
255 }
256 
257 void serial_test_tc_opts_before(void)
258 {
259 	test_tc_opts_before_target(BPF_TCX_INGRESS);
260 	test_tc_opts_before_target(BPF_TCX_EGRESS);
261 }
262 
263 static void test_tc_opts_after_target(int target)
264 {
265 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
266 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
267 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
268 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
269 	struct test_tc_link *skel;
270 	__u32 prog_ids[5];
271 	int err;
272 
273 	skel = test_tc_link__open_and_load();
274 	if (!ASSERT_OK_PTR(skel, "skel_load"))
275 		goto cleanup;
276 
277 	fd1 = bpf_program__fd(skel->progs.tc1);
278 	fd2 = bpf_program__fd(skel->progs.tc2);
279 	fd3 = bpf_program__fd(skel->progs.tc3);
280 	fd4 = bpf_program__fd(skel->progs.tc4);
281 
282 	id1 = id_from_prog_fd(fd1);
283 	id2 = id_from_prog_fd(fd2);
284 	id3 = id_from_prog_fd(fd3);
285 	id4 = id_from_prog_fd(fd4);
286 
287 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
288 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
289 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
290 
291 	assert_mprog_count(target, 0);
292 
293 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
294 	if (!ASSERT_EQ(err, 0, "prog_attach"))
295 		goto cleanup;
296 
297 	assert_mprog_count(target, 1);
298 
299 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
300 	if (!ASSERT_EQ(err, 0, "prog_attach"))
301 		goto cleanup_target;
302 
303 	assert_mprog_count(target, 2);
304 
305 	optq.prog_ids = prog_ids;
306 
307 	memset(prog_ids, 0, sizeof(prog_ids));
308 	optq.count = ARRAY_SIZE(prog_ids);
309 
310 	err = bpf_prog_query_opts(loopback, target, &optq);
311 	if (!ASSERT_OK(err, "prog_query"))
312 		goto cleanup_target2;
313 
314 	ASSERT_EQ(optq.count, 2, "count");
315 	ASSERT_EQ(optq.revision, 3, "revision");
316 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
317 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
318 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
319 
320 	tc_skel_reset_all_seen(skel);
321 	ASSERT_OK(system(ping_cmd), ping_cmd);
322 
323 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
324 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
325 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
326 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
327 
328 	LIBBPF_OPTS_RESET(opta,
329 		.flags = BPF_F_AFTER,
330 		.relative_fd = fd1,
331 	);
332 
333 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
334 	if (!ASSERT_EQ(err, 0, "prog_attach"))
335 		goto cleanup_target2;
336 
337 	memset(prog_ids, 0, sizeof(prog_ids));
338 	optq.count = ARRAY_SIZE(prog_ids);
339 
340 	err = bpf_prog_query_opts(loopback, target, &optq);
341 	if (!ASSERT_OK(err, "prog_query"))
342 		goto cleanup_target3;
343 
344 	ASSERT_EQ(optq.count, 3, "count");
345 	ASSERT_EQ(optq.revision, 4, "revision");
346 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
347 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
348 	ASSERT_EQ(optq.prog_ids[2], id2, "prog_ids[2]");
349 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
350 
351 	LIBBPF_OPTS_RESET(opta,
352 		.flags = BPF_F_AFTER,
353 		.relative_id = id2,
354 	);
355 
356 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
357 	if (!ASSERT_EQ(err, 0, "prog_attach"))
358 		goto cleanup_target3;
359 
360 	assert_mprog_count(target, 4);
361 
362 	memset(prog_ids, 0, sizeof(prog_ids));
363 	optq.count = ARRAY_SIZE(prog_ids);
364 
365 	err = bpf_prog_query_opts(loopback, target, &optq);
366 	if (!ASSERT_OK(err, "prog_query"))
367 		goto cleanup_target4;
368 
369 	ASSERT_EQ(optq.count, 4, "count");
370 	ASSERT_EQ(optq.revision, 5, "revision");
371 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
372 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
373 	ASSERT_EQ(optq.prog_ids[2], id2, "prog_ids[2]");
374 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
375 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
376 
377 	tc_skel_reset_all_seen(skel);
378 	ASSERT_OK(system(ping_cmd), ping_cmd);
379 
380 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
381 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
382 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
383 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
384 
385 cleanup_target4:
386 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
387 	ASSERT_OK(err, "prog_detach");
388 	assert_mprog_count(target, 3);
389 
390 	memset(prog_ids, 0, sizeof(prog_ids));
391 	optq.count = ARRAY_SIZE(prog_ids);
392 
393 	err = bpf_prog_query_opts(loopback, target, &optq);
394 	if (!ASSERT_OK(err, "prog_query"))
395 		goto cleanup_target3;
396 
397 	ASSERT_EQ(optq.count, 3, "count");
398 	ASSERT_EQ(optq.revision, 6, "revision");
399 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
400 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
401 	ASSERT_EQ(optq.prog_ids[2], id2, "prog_ids[2]");
402 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
403 
404 cleanup_target3:
405 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
406 	ASSERT_OK(err, "prog_detach");
407 	assert_mprog_count(target, 2);
408 
409 	memset(prog_ids, 0, sizeof(prog_ids));
410 	optq.count = ARRAY_SIZE(prog_ids);
411 
412 	err = bpf_prog_query_opts(loopback, target, &optq);
413 	if (!ASSERT_OK(err, "prog_query"))
414 		goto cleanup_target2;
415 
416 	ASSERT_EQ(optq.count, 2, "count");
417 	ASSERT_EQ(optq.revision, 7, "revision");
418 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
419 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
420 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
421 
422 cleanup_target2:
423 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
424 	ASSERT_OK(err, "prog_detach");
425 	assert_mprog_count(target, 1);
426 
427 	memset(prog_ids, 0, sizeof(prog_ids));
428 	optq.count = ARRAY_SIZE(prog_ids);
429 
430 	err = bpf_prog_query_opts(loopback, target, &optq);
431 	if (!ASSERT_OK(err, "prog_query"))
432 		goto cleanup_target;
433 
434 	ASSERT_EQ(optq.count, 1, "count");
435 	ASSERT_EQ(optq.revision, 8, "revision");
436 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
437 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
438 
439 cleanup_target:
440 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
441 	ASSERT_OK(err, "prog_detach");
442 	assert_mprog_count(target, 0);
443 
444 cleanup:
445 	test_tc_link__destroy(skel);
446 }
447 
448 void serial_test_tc_opts_after(void)
449 {
450 	test_tc_opts_after_target(BPF_TCX_INGRESS);
451 	test_tc_opts_after_target(BPF_TCX_EGRESS);
452 }
453 
454 static void test_tc_opts_revision_target(int target)
455 {
456 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
457 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
458 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
459 	__u32 fd1, fd2, id1, id2;
460 	struct test_tc_link *skel;
461 	__u32 prog_ids[3];
462 	int err;
463 
464 	skel = test_tc_link__open_and_load();
465 	if (!ASSERT_OK_PTR(skel, "skel_load"))
466 		goto cleanup;
467 
468 	fd1 = bpf_program__fd(skel->progs.tc1);
469 	fd2 = bpf_program__fd(skel->progs.tc2);
470 
471 	id1 = id_from_prog_fd(fd1);
472 	id2 = id_from_prog_fd(fd2);
473 
474 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
475 
476 	assert_mprog_count(target, 0);
477 
478 	LIBBPF_OPTS_RESET(opta,
479 		.expected_revision = 1,
480 	);
481 
482 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
483 	if (!ASSERT_EQ(err, 0, "prog_attach"))
484 		goto cleanup;
485 
486 	assert_mprog_count(target, 1);
487 
488 	LIBBPF_OPTS_RESET(opta,
489 		.expected_revision = 1,
490 	);
491 
492 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
493 	if (!ASSERT_EQ(err, -ESTALE, "prog_attach"))
494 		goto cleanup_target;
495 
496 	assert_mprog_count(target, 1);
497 
498 	LIBBPF_OPTS_RESET(opta,
499 		.expected_revision = 2,
500 	);
501 
502 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
503 	if (!ASSERT_EQ(err, 0, "prog_attach"))
504 		goto cleanup_target;
505 
506 	assert_mprog_count(target, 2);
507 
508 	optq.prog_ids = prog_ids;
509 
510 	memset(prog_ids, 0, sizeof(prog_ids));
511 	optq.count = ARRAY_SIZE(prog_ids);
512 
513 	err = bpf_prog_query_opts(loopback, target, &optq);
514 	if (!ASSERT_OK(err, "prog_query"))
515 		goto cleanup_target2;
516 
517 	ASSERT_EQ(optq.count, 2, "count");
518 	ASSERT_EQ(optq.revision, 3, "revision");
519 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
520 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
521 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
522 
523 	tc_skel_reset_all_seen(skel);
524 	ASSERT_OK(system(ping_cmd), ping_cmd);
525 
526 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
527 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
528 
529 	LIBBPF_OPTS_RESET(optd,
530 		.expected_revision = 2,
531 	);
532 
533 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
534 	ASSERT_EQ(err, -ESTALE, "prog_detach");
535 	assert_mprog_count(target, 2);
536 
537 cleanup_target2:
538 	LIBBPF_OPTS_RESET(optd,
539 		.expected_revision = 3,
540 	);
541 
542 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
543 	ASSERT_OK(err, "prog_detach");
544 	assert_mprog_count(target, 1);
545 
546 cleanup_target:
547 	LIBBPF_OPTS_RESET(optd);
548 
549 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
550 	ASSERT_OK(err, "prog_detach");
551 	assert_mprog_count(target, 0);
552 
553 cleanup:
554 	test_tc_link__destroy(skel);
555 }
556 
557 void serial_test_tc_opts_revision(void)
558 {
559 	test_tc_opts_revision_target(BPF_TCX_INGRESS);
560 	test_tc_opts_revision_target(BPF_TCX_EGRESS);
561 }
562 
563 static void test_tc_chain_classic(int target, bool chain_tc_old)
564 {
565 	LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
566 	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback);
567 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
568 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
569 	bool hook_created = false, tc_attached = false;
570 	__u32 fd1, fd2, fd3, id1, id2, id3;
571 	struct test_tc_link *skel;
572 	int err;
573 
574 	skel = test_tc_link__open_and_load();
575 	if (!ASSERT_OK_PTR(skel, "skel_load"))
576 		goto cleanup;
577 
578 	fd1 = bpf_program__fd(skel->progs.tc1);
579 	fd2 = bpf_program__fd(skel->progs.tc2);
580 	fd3 = bpf_program__fd(skel->progs.tc3);
581 
582 	id1 = id_from_prog_fd(fd1);
583 	id2 = id_from_prog_fd(fd2);
584 	id3 = id_from_prog_fd(fd3);
585 
586 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
587 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
588 
589 	assert_mprog_count(target, 0);
590 
591 	if (chain_tc_old) {
592 		tc_hook.attach_point = target == BPF_TCX_INGRESS ?
593 				       BPF_TC_INGRESS : BPF_TC_EGRESS;
594 		err = bpf_tc_hook_create(&tc_hook);
595 		if (err == 0)
596 			hook_created = true;
597 		err = err == -EEXIST ? 0 : err;
598 		if (!ASSERT_OK(err, "bpf_tc_hook_create"))
599 			goto cleanup;
600 
601 		tc_opts.prog_fd = fd3;
602 		err = bpf_tc_attach(&tc_hook, &tc_opts);
603 		if (!ASSERT_OK(err, "bpf_tc_attach"))
604 			goto cleanup;
605 		tc_attached = true;
606 	}
607 
608 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
609 	if (!ASSERT_EQ(err, 0, "prog_attach"))
610 		goto cleanup;
611 
612 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
613 	if (!ASSERT_EQ(err, 0, "prog_attach"))
614 		goto cleanup_detach;
615 
616 	assert_mprog_count(target, 2);
617 
618 	tc_skel_reset_all_seen(skel);
619 	ASSERT_OK(system(ping_cmd), ping_cmd);
620 
621 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
622 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
623 	ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3");
624 
625 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
626 	if (!ASSERT_OK(err, "prog_detach"))
627 		goto cleanup_detach;
628 
629 	assert_mprog_count(target, 1);
630 
631 	tc_skel_reset_all_seen(skel);
632 	ASSERT_OK(system(ping_cmd), ping_cmd);
633 
634 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
635 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
636 	ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3");
637 
638 cleanup_detach:
639 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
640 	if (!ASSERT_OK(err, "prog_detach"))
641 		goto cleanup;
642 
643 	assert_mprog_count(target, 0);
644 cleanup:
645 	if (tc_attached) {
646 		tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0;
647 		err = bpf_tc_detach(&tc_hook, &tc_opts);
648 		ASSERT_OK(err, "bpf_tc_detach");
649 	}
650 	if (hook_created) {
651 		tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
652 		bpf_tc_hook_destroy(&tc_hook);
653 	}
654 	test_tc_link__destroy(skel);
655 	assert_mprog_count(target, 0);
656 }
657 
658 void serial_test_tc_opts_chain_classic(void)
659 {
660 	test_tc_chain_classic(BPF_TCX_INGRESS, false);
661 	test_tc_chain_classic(BPF_TCX_EGRESS, false);
662 	test_tc_chain_classic(BPF_TCX_INGRESS, true);
663 	test_tc_chain_classic(BPF_TCX_EGRESS, true);
664 }
665 
666 static void test_tc_opts_replace_target(int target)
667 {
668 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
669 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
670 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
671 	__u32 fd1, fd2, fd3, id1, id2, id3, detach_fd;
672 	__u32 prog_ids[4], prog_flags[4];
673 	struct test_tc_link *skel;
674 	int err;
675 
676 	skel = test_tc_link__open_and_load();
677 	if (!ASSERT_OK_PTR(skel, "skel_load"))
678 		goto cleanup;
679 
680 	fd1 = bpf_program__fd(skel->progs.tc1);
681 	fd2 = bpf_program__fd(skel->progs.tc2);
682 	fd3 = bpf_program__fd(skel->progs.tc3);
683 
684 	id1 = id_from_prog_fd(fd1);
685 	id2 = id_from_prog_fd(fd2);
686 	id3 = id_from_prog_fd(fd3);
687 
688 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
689 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
690 
691 	assert_mprog_count(target, 0);
692 
693 	LIBBPF_OPTS_RESET(opta,
694 		.expected_revision = 1,
695 	);
696 
697 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
698 	if (!ASSERT_EQ(err, 0, "prog_attach"))
699 		goto cleanup;
700 
701 	assert_mprog_count(target, 1);
702 
703 	LIBBPF_OPTS_RESET(opta,
704 		.flags = BPF_F_BEFORE,
705 		.relative_id = id1,
706 		.expected_revision = 2,
707 	);
708 
709 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
710 	if (!ASSERT_EQ(err, 0, "prog_attach"))
711 		goto cleanup_target;
712 
713 	detach_fd = fd2;
714 
715 	assert_mprog_count(target, 2);
716 
717 	optq.prog_attach_flags = prog_flags;
718 	optq.prog_ids = prog_ids;
719 
720 	memset(prog_flags, 0, sizeof(prog_flags));
721 	memset(prog_ids, 0, sizeof(prog_ids));
722 	optq.count = ARRAY_SIZE(prog_ids);
723 
724 	err = bpf_prog_query_opts(loopback, target, &optq);
725 	if (!ASSERT_OK(err, "prog_query"))
726 		goto cleanup_target2;
727 
728 	ASSERT_EQ(optq.count, 2, "count");
729 	ASSERT_EQ(optq.revision, 3, "revision");
730 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
731 	ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]");
732 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
733 
734 	ASSERT_EQ(optq.prog_attach_flags[0], 0, "prog_flags[0]");
735 	ASSERT_EQ(optq.prog_attach_flags[1], 0, "prog_flags[1]");
736 	ASSERT_EQ(optq.prog_attach_flags[2], 0, "prog_flags[2]");
737 
738 	tc_skel_reset_all_seen(skel);
739 	ASSERT_OK(system(ping_cmd), ping_cmd);
740 
741 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
742 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
743 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
744 
745 	LIBBPF_OPTS_RESET(opta,
746 		.flags = BPF_F_REPLACE,
747 		.replace_prog_fd = fd2,
748 		.expected_revision = 3,
749 	);
750 
751 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
752 	if (!ASSERT_EQ(err, 0, "prog_attach"))
753 		goto cleanup_target2;
754 
755 	detach_fd = fd3;
756 
757 	assert_mprog_count(target, 2);
758 
759 	memset(prog_ids, 0, sizeof(prog_ids));
760 	optq.count = ARRAY_SIZE(prog_ids);
761 
762 	err = bpf_prog_query_opts(loopback, target, &optq);
763 	if (!ASSERT_OK(err, "prog_query"))
764 		goto cleanup_target2;
765 
766 	ASSERT_EQ(optq.count, 2, "count");
767 	ASSERT_EQ(optq.revision, 4, "revision");
768 	ASSERT_EQ(optq.prog_ids[0], id3, "prog_ids[0]");
769 	ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]");
770 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
771 
772 	tc_skel_reset_all_seen(skel);
773 	ASSERT_OK(system(ping_cmd), ping_cmd);
774 
775 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
776 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
777 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
778 
779 	LIBBPF_OPTS_RESET(opta,
780 		.flags = BPF_F_REPLACE | BPF_F_BEFORE,
781 		.replace_prog_fd = fd3,
782 		.relative_fd = fd1,
783 		.expected_revision = 4,
784 	);
785 
786 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
787 	if (!ASSERT_EQ(err, 0, "prog_attach"))
788 		goto cleanup_target2;
789 
790 	detach_fd = fd2;
791 
792 	assert_mprog_count(target, 2);
793 
794 	memset(prog_ids, 0, sizeof(prog_ids));
795 	optq.count = ARRAY_SIZE(prog_ids);
796 
797 	err = bpf_prog_query_opts(loopback, target, &optq);
798 	if (!ASSERT_OK(err, "prog_query"))
799 		goto cleanup_target2;
800 
801 	ASSERT_EQ(optq.count, 2, "count");
802 	ASSERT_EQ(optq.revision, 5, "revision");
803 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
804 	ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]");
805 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
806 
807 	tc_skel_reset_all_seen(skel);
808 	ASSERT_OK(system(ping_cmd), ping_cmd);
809 
810 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
811 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
812 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
813 
814 	LIBBPF_OPTS_RESET(opta,
815 		.flags = BPF_F_REPLACE,
816 		.replace_prog_fd = fd2,
817 	);
818 
819 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
820 	ASSERT_EQ(err, -EEXIST, "prog_attach");
821 	assert_mprog_count(target, 2);
822 
823 	LIBBPF_OPTS_RESET(opta,
824 		.flags = BPF_F_REPLACE | BPF_F_AFTER,
825 		.replace_prog_fd = fd2,
826 		.relative_fd = fd1,
827 		.expected_revision = 5,
828 	);
829 
830 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
831 	ASSERT_EQ(err, -ERANGE, "prog_attach");
832 	assert_mprog_count(target, 2);
833 
834 	LIBBPF_OPTS_RESET(opta,
835 		.flags = BPF_F_BEFORE | BPF_F_AFTER | BPF_F_REPLACE,
836 		.replace_prog_fd = fd2,
837 		.relative_fd = fd1,
838 		.expected_revision = 5,
839 	);
840 
841 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
842 	ASSERT_EQ(err, -ERANGE, "prog_attach");
843 	assert_mprog_count(target, 2);
844 
845 	LIBBPF_OPTS_RESET(optd,
846 		.flags = BPF_F_BEFORE,
847 		.relative_id = id1,
848 		.expected_revision = 5,
849 	);
850 
851 cleanup_target2:
852 	err = bpf_prog_detach_opts(detach_fd, loopback, target, &optd);
853 	ASSERT_OK(err, "prog_detach");
854 	assert_mprog_count(target, 1);
855 
856 cleanup_target:
857 	LIBBPF_OPTS_RESET(optd);
858 
859 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
860 	ASSERT_OK(err, "prog_detach");
861 	assert_mprog_count(target, 0);
862 
863 cleanup:
864 	test_tc_link__destroy(skel);
865 }
866 
867 void serial_test_tc_opts_replace(void)
868 {
869 	test_tc_opts_replace_target(BPF_TCX_INGRESS);
870 	test_tc_opts_replace_target(BPF_TCX_EGRESS);
871 }
872 
873 static void test_tc_opts_invalid_target(int target)
874 {
875 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
876 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
877 	__u32 fd1, fd2, id1, id2;
878 	struct test_tc_link *skel;
879 	int err;
880 
881 	skel = test_tc_link__open_and_load();
882 	if (!ASSERT_OK_PTR(skel, "skel_load"))
883 		goto cleanup;
884 
885 	fd1 = bpf_program__fd(skel->progs.tc1);
886 	fd2 = bpf_program__fd(skel->progs.tc2);
887 
888 	id1 = id_from_prog_fd(fd1);
889 	id2 = id_from_prog_fd(fd2);
890 
891 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
892 
893 	assert_mprog_count(target, 0);
894 
895 	LIBBPF_OPTS_RESET(opta,
896 		.flags = BPF_F_BEFORE | BPF_F_AFTER,
897 	);
898 
899 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
900 	ASSERT_EQ(err, -ERANGE, "prog_attach");
901 	assert_mprog_count(target, 0);
902 
903 	LIBBPF_OPTS_RESET(opta,
904 		.flags = BPF_F_BEFORE | BPF_F_ID,
905 	);
906 
907 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
908 	ASSERT_EQ(err, -ENOENT, "prog_attach");
909 	assert_mprog_count(target, 0);
910 
911 	LIBBPF_OPTS_RESET(opta,
912 		.flags = BPF_F_AFTER | BPF_F_ID,
913 	);
914 
915 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
916 	ASSERT_EQ(err, -ENOENT, "prog_attach");
917 	assert_mprog_count(target, 0);
918 
919 	LIBBPF_OPTS_RESET(opta,
920 		.relative_fd = fd2,
921 	);
922 
923 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
924 	ASSERT_EQ(err, -EINVAL, "prog_attach");
925 	assert_mprog_count(target, 0);
926 
927 	LIBBPF_OPTS_RESET(opta,
928 		.flags = BPF_F_BEFORE | BPF_F_AFTER,
929 		.relative_fd = fd2,
930 	);
931 
932 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
933 	ASSERT_EQ(err, -ENOENT, "prog_attach");
934 	assert_mprog_count(target, 0);
935 
936 	LIBBPF_OPTS_RESET(opta,
937 		.flags = BPF_F_ID,
938 		.relative_id = id2,
939 	);
940 
941 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
942 	ASSERT_EQ(err, -EINVAL, "prog_attach");
943 	assert_mprog_count(target, 0);
944 
945 	LIBBPF_OPTS_RESET(opta,
946 		.flags = BPF_F_BEFORE,
947 		.relative_fd = fd1,
948 	);
949 
950 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
951 	ASSERT_EQ(err, -ENOENT, "prog_attach");
952 	assert_mprog_count(target, 0);
953 
954 	LIBBPF_OPTS_RESET(opta,
955 		.flags = BPF_F_AFTER,
956 		.relative_fd = fd1,
957 	);
958 
959 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
960 	ASSERT_EQ(err, -ENOENT, "prog_attach");
961 	assert_mprog_count(target, 0);
962 
963 	LIBBPF_OPTS_RESET(opta);
964 
965 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
966 	if (!ASSERT_EQ(err, 0, "prog_attach"))
967 		goto cleanup;
968 
969 	assert_mprog_count(target, 1);
970 
971 	LIBBPF_OPTS_RESET(opta);
972 
973 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
974 	ASSERT_EQ(err, -EEXIST, "prog_attach");
975 	assert_mprog_count(target, 1);
976 
977 	LIBBPF_OPTS_RESET(opta,
978 		.flags = BPF_F_BEFORE,
979 		.relative_fd = fd1,
980 	);
981 
982 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
983 	ASSERT_EQ(err, -EEXIST, "prog_attach");
984 	assert_mprog_count(target, 1);
985 
986 	LIBBPF_OPTS_RESET(opta,
987 		.flags = BPF_F_AFTER,
988 		.relative_fd = fd1,
989 	);
990 
991 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
992 	ASSERT_EQ(err, -EEXIST, "prog_attach");
993 	assert_mprog_count(target, 1);
994 
995 	LIBBPF_OPTS_RESET(opta,
996 		.flags = BPF_F_REPLACE,
997 		.relative_fd = fd1,
998 	);
999 
1000 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1001 	ASSERT_EQ(err, -EINVAL, "prog_attach_x1");
1002 	assert_mprog_count(target, 1);
1003 
1004 	LIBBPF_OPTS_RESET(opta,
1005 		.flags = BPF_F_REPLACE,
1006 		.replace_prog_fd = fd1,
1007 	);
1008 
1009 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1010 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1011 	assert_mprog_count(target, 1);
1012 
1013 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1014 	ASSERT_OK(err, "prog_detach");
1015 	assert_mprog_count(target, 0);
1016 cleanup:
1017 	test_tc_link__destroy(skel);
1018 }
1019 
1020 void serial_test_tc_opts_invalid(void)
1021 {
1022 	test_tc_opts_invalid_target(BPF_TCX_INGRESS);
1023 	test_tc_opts_invalid_target(BPF_TCX_EGRESS);
1024 }
1025 
1026 static void test_tc_opts_prepend_target(int target)
1027 {
1028 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1029 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1030 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1031 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1032 	struct test_tc_link *skel;
1033 	__u32 prog_ids[5];
1034 	int err;
1035 
1036 	skel = test_tc_link__open_and_load();
1037 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1038 		goto cleanup;
1039 
1040 	fd1 = bpf_program__fd(skel->progs.tc1);
1041 	fd2 = bpf_program__fd(skel->progs.tc2);
1042 	fd3 = bpf_program__fd(skel->progs.tc3);
1043 	fd4 = bpf_program__fd(skel->progs.tc4);
1044 
1045 	id1 = id_from_prog_fd(fd1);
1046 	id2 = id_from_prog_fd(fd2);
1047 	id3 = id_from_prog_fd(fd3);
1048 	id4 = id_from_prog_fd(fd4);
1049 
1050 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1051 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1052 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1053 
1054 	assert_mprog_count(target, 0);
1055 
1056 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1057 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1058 		goto cleanup;
1059 
1060 	assert_mprog_count(target, 1);
1061 
1062 	LIBBPF_OPTS_RESET(opta,
1063 		.flags = BPF_F_BEFORE,
1064 	);
1065 
1066 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
1067 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1068 		goto cleanup_target;
1069 
1070 	assert_mprog_count(target, 2);
1071 
1072 	optq.prog_ids = prog_ids;
1073 
1074 	memset(prog_ids, 0, sizeof(prog_ids));
1075 	optq.count = ARRAY_SIZE(prog_ids);
1076 
1077 	err = bpf_prog_query_opts(loopback, target, &optq);
1078 	if (!ASSERT_OK(err, "prog_query"))
1079 		goto cleanup_target2;
1080 
1081 	ASSERT_EQ(optq.count, 2, "count");
1082 	ASSERT_EQ(optq.revision, 3, "revision");
1083 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1084 	ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]");
1085 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1086 
1087 	tc_skel_reset_all_seen(skel);
1088 	ASSERT_OK(system(ping_cmd), ping_cmd);
1089 
1090 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1091 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1092 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
1093 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
1094 
1095 	LIBBPF_OPTS_RESET(opta,
1096 		.flags = BPF_F_BEFORE,
1097 	);
1098 
1099 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
1100 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1101 		goto cleanup_target2;
1102 
1103 	LIBBPF_OPTS_RESET(opta,
1104 		.flags = BPF_F_BEFORE,
1105 	);
1106 
1107 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
1108 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1109 		goto cleanup_target3;
1110 
1111 	assert_mprog_count(target, 4);
1112 
1113 	memset(prog_ids, 0, sizeof(prog_ids));
1114 	optq.count = ARRAY_SIZE(prog_ids);
1115 
1116 	err = bpf_prog_query_opts(loopback, target, &optq);
1117 	if (!ASSERT_OK(err, "prog_query"))
1118 		goto cleanup_target4;
1119 
1120 	ASSERT_EQ(optq.count, 4, "count");
1121 	ASSERT_EQ(optq.revision, 5, "revision");
1122 	ASSERT_EQ(optq.prog_ids[0], id4, "prog_ids[0]");
1123 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1124 	ASSERT_EQ(optq.prog_ids[2], id2, "prog_ids[2]");
1125 	ASSERT_EQ(optq.prog_ids[3], id1, "prog_ids[3]");
1126 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1127 
1128 	tc_skel_reset_all_seen(skel);
1129 	ASSERT_OK(system(ping_cmd), ping_cmd);
1130 
1131 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1132 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1133 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
1134 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
1135 
1136 cleanup_target4:
1137 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1138 	ASSERT_OK(err, "prog_detach");
1139 	assert_mprog_count(target, 3);
1140 
1141 cleanup_target3:
1142 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1143 	ASSERT_OK(err, "prog_detach");
1144 	assert_mprog_count(target, 2);
1145 
1146 cleanup_target2:
1147 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1148 	ASSERT_OK(err, "prog_detach");
1149 	assert_mprog_count(target, 1);
1150 
1151 cleanup_target:
1152 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1153 	ASSERT_OK(err, "prog_detach");
1154 	assert_mprog_count(target, 0);
1155 
1156 cleanup:
1157 	test_tc_link__destroy(skel);
1158 }
1159 
1160 void serial_test_tc_opts_prepend(void)
1161 {
1162 	test_tc_opts_prepend_target(BPF_TCX_INGRESS);
1163 	test_tc_opts_prepend_target(BPF_TCX_EGRESS);
1164 }
1165 
1166 static void test_tc_opts_append_target(int target)
1167 {
1168 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1169 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1170 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1171 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1172 	struct test_tc_link *skel;
1173 	__u32 prog_ids[5];
1174 	int err;
1175 
1176 	skel = test_tc_link__open_and_load();
1177 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1178 		goto cleanup;
1179 
1180 	fd1 = bpf_program__fd(skel->progs.tc1);
1181 	fd2 = bpf_program__fd(skel->progs.tc2);
1182 	fd3 = bpf_program__fd(skel->progs.tc3);
1183 	fd4 = bpf_program__fd(skel->progs.tc4);
1184 
1185 	id1 = id_from_prog_fd(fd1);
1186 	id2 = id_from_prog_fd(fd2);
1187 	id3 = id_from_prog_fd(fd3);
1188 	id4 = id_from_prog_fd(fd4);
1189 
1190 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1191 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1192 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1193 
1194 	assert_mprog_count(target, 0);
1195 
1196 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1197 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1198 		goto cleanup;
1199 
1200 	assert_mprog_count(target, 1);
1201 
1202 	LIBBPF_OPTS_RESET(opta,
1203 		.flags = BPF_F_AFTER,
1204 	);
1205 
1206 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
1207 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1208 		goto cleanup_target;
1209 
1210 	assert_mprog_count(target, 2);
1211 
1212 	optq.prog_ids = prog_ids;
1213 
1214 	memset(prog_ids, 0, sizeof(prog_ids));
1215 	optq.count = ARRAY_SIZE(prog_ids);
1216 
1217 	err = bpf_prog_query_opts(loopback, target, &optq);
1218 	if (!ASSERT_OK(err, "prog_query"))
1219 		goto cleanup_target2;
1220 
1221 	ASSERT_EQ(optq.count, 2, "count");
1222 	ASSERT_EQ(optq.revision, 3, "revision");
1223 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
1224 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
1225 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1226 
1227 	tc_skel_reset_all_seen(skel);
1228 	ASSERT_OK(system(ping_cmd), ping_cmd);
1229 
1230 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1231 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1232 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
1233 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
1234 
1235 	LIBBPF_OPTS_RESET(opta,
1236 		.flags = BPF_F_AFTER,
1237 	);
1238 
1239 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
1240 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1241 		goto cleanup_target2;
1242 
1243 	LIBBPF_OPTS_RESET(opta,
1244 		.flags = BPF_F_AFTER,
1245 	);
1246 
1247 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
1248 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1249 		goto cleanup_target3;
1250 
1251 	assert_mprog_count(target, 4);
1252 
1253 	memset(prog_ids, 0, sizeof(prog_ids));
1254 	optq.count = ARRAY_SIZE(prog_ids);
1255 
1256 	err = bpf_prog_query_opts(loopback, target, &optq);
1257 	if (!ASSERT_OK(err, "prog_query"))
1258 		goto cleanup_target4;
1259 
1260 	ASSERT_EQ(optq.count, 4, "count");
1261 	ASSERT_EQ(optq.revision, 5, "revision");
1262 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
1263 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
1264 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
1265 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
1266 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1267 
1268 	tc_skel_reset_all_seen(skel);
1269 	ASSERT_OK(system(ping_cmd), ping_cmd);
1270 
1271 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1272 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1273 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
1274 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
1275 
1276 cleanup_target4:
1277 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1278 	ASSERT_OK(err, "prog_detach");
1279 	assert_mprog_count(target, 3);
1280 
1281 cleanup_target3:
1282 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1283 	ASSERT_OK(err, "prog_detach");
1284 	assert_mprog_count(target, 2);
1285 
1286 cleanup_target2:
1287 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1288 	ASSERT_OK(err, "prog_detach");
1289 	assert_mprog_count(target, 1);
1290 
1291 cleanup_target:
1292 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1293 	ASSERT_OK(err, "prog_detach");
1294 	assert_mprog_count(target, 0);
1295 
1296 cleanup:
1297 	test_tc_link__destroy(skel);
1298 }
1299 
1300 void serial_test_tc_opts_append(void)
1301 {
1302 	test_tc_opts_append_target(BPF_TCX_INGRESS);
1303 	test_tc_opts_append_target(BPF_TCX_EGRESS);
1304 }
1305 
1306 static void test_tc_opts_dev_cleanup_target(int target)
1307 {
1308 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1309 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1310 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1311 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1312 	struct test_tc_link *skel;
1313 	int err, ifindex;
1314 
1315 	ASSERT_OK(system("ip link add dev tcx_opts1 type veth peer name tcx_opts2"), "add veth");
1316 	ifindex = if_nametoindex("tcx_opts1");
1317 	ASSERT_NEQ(ifindex, 0, "non_zero_ifindex");
1318 
1319 	skel = test_tc_link__open_and_load();
1320 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1321 		goto cleanup;
1322 
1323 	fd1 = bpf_program__fd(skel->progs.tc1);
1324 	fd2 = bpf_program__fd(skel->progs.tc2);
1325 	fd3 = bpf_program__fd(skel->progs.tc3);
1326 	fd4 = bpf_program__fd(skel->progs.tc4);
1327 
1328 	id1 = id_from_prog_fd(fd1);
1329 	id2 = id_from_prog_fd(fd2);
1330 	id3 = id_from_prog_fd(fd3);
1331 	id4 = id_from_prog_fd(fd4);
1332 
1333 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1334 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1335 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1336 
1337 	assert_mprog_count_ifindex(ifindex, target, 0);
1338 
1339 	err = bpf_prog_attach_opts(fd1, ifindex, target, &opta);
1340 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1341 		goto cleanup;
1342 
1343 	assert_mprog_count_ifindex(ifindex, target, 1);
1344 
1345 	err = bpf_prog_attach_opts(fd2, ifindex, target, &opta);
1346 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1347 		goto cleanup1;
1348 
1349 	assert_mprog_count_ifindex(ifindex, target, 2);
1350 
1351 	err = bpf_prog_attach_opts(fd3, ifindex, target, &opta);
1352 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1353 		goto cleanup2;
1354 
1355 	assert_mprog_count_ifindex(ifindex, target, 3);
1356 
1357 	err = bpf_prog_attach_opts(fd4, ifindex, target, &opta);
1358 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1359 		goto cleanup3;
1360 
1361 	assert_mprog_count_ifindex(ifindex, target, 4);
1362 
1363 	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1364 	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1365 	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1366 	return;
1367 cleanup3:
1368 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1369 	ASSERT_OK(err, "prog_detach");
1370 
1371 	assert_mprog_count_ifindex(ifindex, target, 2);
1372 cleanup2:
1373 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1374 	ASSERT_OK(err, "prog_detach");
1375 
1376 	assert_mprog_count_ifindex(ifindex, target, 1);
1377 cleanup1:
1378 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1379 	ASSERT_OK(err, "prog_detach");
1380 
1381 	assert_mprog_count_ifindex(ifindex, target, 0);
1382 cleanup:
1383 	test_tc_link__destroy(skel);
1384 
1385 	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1386 	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1387 	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1388 }
1389 
1390 void serial_test_tc_opts_dev_cleanup(void)
1391 {
1392 	test_tc_opts_dev_cleanup_target(BPF_TCX_INGRESS);
1393 	test_tc_opts_dev_cleanup_target(BPF_TCX_EGRESS);
1394 }
1395 
1396 static void test_tc_opts_mixed_target(int target)
1397 {
1398 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1399 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1400 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1401 	LIBBPF_OPTS(bpf_tcx_opts, optl);
1402 	__u32 pid1, pid2, pid3, pid4, lid2, lid4;
1403 	__u32 prog_flags[4], link_flags[4];
1404 	__u32 prog_ids[4], link_ids[4];
1405 	struct test_tc_link *skel;
1406 	struct bpf_link *link;
1407 	int err, detach_fd;
1408 
1409 	skel = test_tc_link__open();
1410 	if (!ASSERT_OK_PTR(skel, "skel_open"))
1411 		goto cleanup;
1412 
1413 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1414 		  0, "tc1_attach_type");
1415 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1416 		  0, "tc2_attach_type");
1417 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
1418 		  0, "tc3_attach_type");
1419 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1420 		  0, "tc4_attach_type");
1421 
1422 	err = test_tc_link__load(skel);
1423 	if (!ASSERT_OK(err, "skel_load"))
1424 		goto cleanup;
1425 
1426 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1427 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1428 	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1429 	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1430 
1431 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1432 	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
1433 	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1434 
1435 	assert_mprog_count(target, 0);
1436 
1437 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc1),
1438 				   loopback, target, &opta);
1439 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1440 		goto cleanup;
1441 
1442 	detach_fd = bpf_program__fd(skel->progs.tc1);
1443 
1444 	assert_mprog_count(target, 1);
1445 
1446 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1447 	if (!ASSERT_OK_PTR(link, "link_attach"))
1448 		goto cleanup1;
1449 	skel->links.tc2 = link;
1450 
1451 	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
1452 
1453 	assert_mprog_count(target, 2);
1454 
1455 	LIBBPF_OPTS_RESET(opta,
1456 		.flags = BPF_F_REPLACE,
1457 		.replace_prog_fd = bpf_program__fd(skel->progs.tc1),
1458 	);
1459 
1460 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc2),
1461 				   loopback, target, &opta);
1462 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1463 
1464 	assert_mprog_count(target, 2);
1465 
1466 	LIBBPF_OPTS_RESET(opta,
1467 		.flags = BPF_F_REPLACE,
1468 		.replace_prog_fd = bpf_program__fd(skel->progs.tc2),
1469 	);
1470 
1471 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc1),
1472 				   loopback, target, &opta);
1473 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1474 
1475 	assert_mprog_count(target, 2);
1476 
1477 	LIBBPF_OPTS_RESET(opta,
1478 		.flags = BPF_F_REPLACE,
1479 		.replace_prog_fd = bpf_program__fd(skel->progs.tc2),
1480 	);
1481 
1482 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc3),
1483 				   loopback, target, &opta);
1484 	ASSERT_EQ(err, -EBUSY, "prog_attach");
1485 
1486 	assert_mprog_count(target, 2);
1487 
1488 	LIBBPF_OPTS_RESET(opta,
1489 		.flags = BPF_F_REPLACE,
1490 		.replace_prog_fd = bpf_program__fd(skel->progs.tc1),
1491 	);
1492 
1493 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc3),
1494 				   loopback, target, &opta);
1495 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1496 		goto cleanup1;
1497 
1498 	detach_fd = bpf_program__fd(skel->progs.tc3);
1499 
1500 	assert_mprog_count(target, 2);
1501 
1502 	link = bpf_program__attach_tcx(skel->progs.tc4, loopback, &optl);
1503 	if (!ASSERT_OK_PTR(link, "link_attach"))
1504 		goto cleanup1;
1505 	skel->links.tc4 = link;
1506 
1507 	lid4 = id_from_link_fd(bpf_link__fd(skel->links.tc4));
1508 
1509 	assert_mprog_count(target, 3);
1510 
1511 	LIBBPF_OPTS_RESET(opta,
1512 		.flags = BPF_F_REPLACE,
1513 		.replace_prog_fd = bpf_program__fd(skel->progs.tc4),
1514 	);
1515 
1516 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc2),
1517 				   loopback, target, &opta);
1518 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1519 
1520 	optq.prog_ids = prog_ids;
1521 	optq.prog_attach_flags = prog_flags;
1522 	optq.link_ids = link_ids;
1523 	optq.link_attach_flags = link_flags;
1524 
1525 	memset(prog_ids, 0, sizeof(prog_ids));
1526 	memset(prog_flags, 0, sizeof(prog_flags));
1527 	memset(link_ids, 0, sizeof(link_ids));
1528 	memset(link_flags, 0, sizeof(link_flags));
1529 	optq.count = ARRAY_SIZE(prog_ids);
1530 
1531 	err = bpf_prog_query_opts(loopback, target, &optq);
1532 	if (!ASSERT_OK(err, "prog_query"))
1533 		goto cleanup1;
1534 
1535 	ASSERT_EQ(optq.count, 3, "count");
1536 	ASSERT_EQ(optq.revision, 5, "revision");
1537 	ASSERT_EQ(optq.prog_ids[0], pid3, "prog_ids[0]");
1538 	ASSERT_EQ(optq.prog_attach_flags[0], 0, "prog_flags[0]");
1539 	ASSERT_EQ(optq.link_ids[0], 0, "link_ids[0]");
1540 	ASSERT_EQ(optq.link_attach_flags[0], 0, "link_flags[0]");
1541 	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
1542 	ASSERT_EQ(optq.prog_attach_flags[1], 0, "prog_flags[1]");
1543 	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
1544 	ASSERT_EQ(optq.link_attach_flags[1], 0, "link_flags[1]");
1545 	ASSERT_EQ(optq.prog_ids[2], pid4, "prog_ids[2]");
1546 	ASSERT_EQ(optq.prog_attach_flags[2], 0, "prog_flags[2]");
1547 	ASSERT_EQ(optq.link_ids[2], lid4, "link_ids[2]");
1548 	ASSERT_EQ(optq.link_attach_flags[2], 0, "link_flags[2]");
1549 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
1550 	ASSERT_EQ(optq.prog_attach_flags[3], 0, "prog_flags[3]");
1551 	ASSERT_EQ(optq.link_ids[3], 0, "link_ids[3]");
1552 	ASSERT_EQ(optq.link_attach_flags[3], 0, "link_flags[3]");
1553 
1554 	ASSERT_OK(system(ping_cmd), ping_cmd);
1555 
1556 cleanup1:
1557 	err = bpf_prog_detach_opts(detach_fd, loopback, target, &optd);
1558 	ASSERT_OK(err, "prog_detach");
1559 	assert_mprog_count(target, 2);
1560 
1561 cleanup:
1562 	test_tc_link__destroy(skel);
1563 	assert_mprog_count(target, 0);
1564 }
1565 
1566 void serial_test_tc_opts_mixed(void)
1567 {
1568 	test_tc_opts_mixed_target(BPF_TCX_INGRESS);
1569 	test_tc_opts_mixed_target(BPF_TCX_EGRESS);
1570 }
1571 
1572 static void test_tc_opts_demixed_target(int target)
1573 {
1574 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1575 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1576 	LIBBPF_OPTS(bpf_tcx_opts, optl);
1577 	struct test_tc_link *skel;
1578 	struct bpf_link *link;
1579 	__u32 pid1, pid2;
1580 	int err;
1581 
1582 	skel = test_tc_link__open();
1583 	if (!ASSERT_OK_PTR(skel, "skel_open"))
1584 		goto cleanup;
1585 
1586 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1587 		  0, "tc1_attach_type");
1588 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1589 		  0, "tc2_attach_type");
1590 
1591 	err = test_tc_link__load(skel);
1592 	if (!ASSERT_OK(err, "skel_load"))
1593 		goto cleanup;
1594 
1595 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1596 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1597 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1598 
1599 	assert_mprog_count(target, 0);
1600 
1601 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc1),
1602 				   loopback, target, &opta);
1603 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1604 		goto cleanup;
1605 
1606 	assert_mprog_count(target, 1);
1607 
1608 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1609 	if (!ASSERT_OK_PTR(link, "link_attach"))
1610 		goto cleanup1;
1611 	skel->links.tc2 = link;
1612 
1613 	assert_mprog_count(target, 2);
1614 
1615 	LIBBPF_OPTS_RESET(optd,
1616 		.flags = BPF_F_AFTER,
1617 	);
1618 
1619 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1620 	ASSERT_EQ(err, -EBUSY, "prog_detach");
1621 
1622 	assert_mprog_count(target, 2);
1623 
1624 	LIBBPF_OPTS_RESET(optd,
1625 		.flags = BPF_F_BEFORE,
1626 	);
1627 
1628 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1629 	ASSERT_OK(err, "prog_detach");
1630 
1631 	assert_mprog_count(target, 1);
1632 	goto cleanup;
1633 
1634 cleanup1:
1635 	err = bpf_prog_detach_opts(bpf_program__fd(skel->progs.tc1),
1636 				   loopback, target, &optd);
1637 	ASSERT_OK(err, "prog_detach");
1638 	assert_mprog_count(target, 2);
1639 
1640 cleanup:
1641 	test_tc_link__destroy(skel);
1642 	assert_mprog_count(target, 0);
1643 }
1644 
1645 void serial_test_tc_opts_demixed(void)
1646 {
1647 	test_tc_opts_demixed_target(BPF_TCX_INGRESS);
1648 	test_tc_opts_demixed_target(BPF_TCX_EGRESS);
1649 }
1650 
1651 static void test_tc_opts_detach_target(int target)
1652 {
1653 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1654 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1655 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1656 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1657 	struct test_tc_link *skel;
1658 	__u32 prog_ids[5];
1659 	int err;
1660 
1661 	skel = test_tc_link__open_and_load();
1662 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1663 		goto cleanup;
1664 
1665 	fd1 = bpf_program__fd(skel->progs.tc1);
1666 	fd2 = bpf_program__fd(skel->progs.tc2);
1667 	fd3 = bpf_program__fd(skel->progs.tc3);
1668 	fd4 = bpf_program__fd(skel->progs.tc4);
1669 
1670 	id1 = id_from_prog_fd(fd1);
1671 	id2 = id_from_prog_fd(fd2);
1672 	id3 = id_from_prog_fd(fd3);
1673 	id4 = id_from_prog_fd(fd4);
1674 
1675 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1676 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1677 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1678 
1679 	assert_mprog_count(target, 0);
1680 
1681 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1682 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1683 		goto cleanup;
1684 
1685 	assert_mprog_count(target, 1);
1686 
1687 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
1688 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1689 		goto cleanup1;
1690 
1691 	assert_mprog_count(target, 2);
1692 
1693 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
1694 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1695 		goto cleanup2;
1696 
1697 	assert_mprog_count(target, 3);
1698 
1699 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
1700 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1701 		goto cleanup3;
1702 
1703 	assert_mprog_count(target, 4);
1704 
1705 	optq.prog_ids = prog_ids;
1706 
1707 	memset(prog_ids, 0, sizeof(prog_ids));
1708 	optq.count = ARRAY_SIZE(prog_ids);
1709 
1710 	err = bpf_prog_query_opts(loopback, target, &optq);
1711 	if (!ASSERT_OK(err, "prog_query"))
1712 		goto cleanup4;
1713 
1714 	ASSERT_EQ(optq.count, 4, "count");
1715 	ASSERT_EQ(optq.revision, 5, "revision");
1716 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
1717 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
1718 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
1719 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
1720 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1721 
1722 	LIBBPF_OPTS_RESET(optd,
1723 		.flags = BPF_F_BEFORE,
1724 	);
1725 
1726 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1727 	ASSERT_OK(err, "prog_detach");
1728 
1729 	assert_mprog_count(target, 3);
1730 
1731 	memset(prog_ids, 0, sizeof(prog_ids));
1732 	optq.count = ARRAY_SIZE(prog_ids);
1733 
1734 	err = bpf_prog_query_opts(loopback, target, &optq);
1735 	if (!ASSERT_OK(err, "prog_query"))
1736 		goto cleanup4;
1737 
1738 	ASSERT_EQ(optq.count, 3, "count");
1739 	ASSERT_EQ(optq.revision, 6, "revision");
1740 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1741 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1742 	ASSERT_EQ(optq.prog_ids[2], id4, "prog_ids[2]");
1743 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
1744 
1745 	LIBBPF_OPTS_RESET(optd,
1746 		.flags = BPF_F_AFTER,
1747 	);
1748 
1749 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1750 	ASSERT_OK(err, "prog_detach");
1751 
1752 	assert_mprog_count(target, 2);
1753 
1754 	memset(prog_ids, 0, sizeof(prog_ids));
1755 	optq.count = ARRAY_SIZE(prog_ids);
1756 
1757 	err = bpf_prog_query_opts(loopback, target, &optq);
1758 	if (!ASSERT_OK(err, "prog_query"))
1759 		goto cleanup4;
1760 
1761 	ASSERT_EQ(optq.count, 2, "count");
1762 	ASSERT_EQ(optq.revision, 7, "revision");
1763 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1764 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1765 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1766 
1767 	LIBBPF_OPTS_RESET(optd);
1768 
1769 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1770 	ASSERT_OK(err, "prog_detach");
1771 	assert_mprog_count(target, 1);
1772 
1773 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1774 	ASSERT_OK(err, "prog_detach");
1775 	assert_mprog_count(target, 0);
1776 
1777 	LIBBPF_OPTS_RESET(optd,
1778 		.flags = BPF_F_BEFORE,
1779 	);
1780 
1781 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1782 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1783 
1784 	LIBBPF_OPTS_RESET(optd,
1785 		.flags = BPF_F_AFTER,
1786 	);
1787 
1788 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1789 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1790 	goto cleanup;
1791 
1792 cleanup4:
1793 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1794 	ASSERT_OK(err, "prog_detach");
1795 	assert_mprog_count(target, 3);
1796 
1797 cleanup3:
1798 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1799 	ASSERT_OK(err, "prog_detach");
1800 	assert_mprog_count(target, 2);
1801 
1802 cleanup2:
1803 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1804 	ASSERT_OK(err, "prog_detach");
1805 	assert_mprog_count(target, 1);
1806 
1807 cleanup1:
1808 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1809 	ASSERT_OK(err, "prog_detach");
1810 	assert_mprog_count(target, 0);
1811 
1812 cleanup:
1813 	test_tc_link__destroy(skel);
1814 }
1815 
1816 void serial_test_tc_opts_detach(void)
1817 {
1818 	test_tc_opts_detach_target(BPF_TCX_INGRESS);
1819 	test_tc_opts_detach_target(BPF_TCX_EGRESS);
1820 }
1821 
1822 static void test_tc_opts_detach_before_target(int target)
1823 {
1824 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1825 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1826 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1827 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1828 	struct test_tc_link *skel;
1829 	__u32 prog_ids[5];
1830 	int err;
1831 
1832 	skel = test_tc_link__open_and_load();
1833 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1834 		goto cleanup;
1835 
1836 	fd1 = bpf_program__fd(skel->progs.tc1);
1837 	fd2 = bpf_program__fd(skel->progs.tc2);
1838 	fd3 = bpf_program__fd(skel->progs.tc3);
1839 	fd4 = bpf_program__fd(skel->progs.tc4);
1840 
1841 	id1 = id_from_prog_fd(fd1);
1842 	id2 = id_from_prog_fd(fd2);
1843 	id3 = id_from_prog_fd(fd3);
1844 	id4 = id_from_prog_fd(fd4);
1845 
1846 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1847 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1848 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1849 
1850 	assert_mprog_count(target, 0);
1851 
1852 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1853 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1854 		goto cleanup;
1855 
1856 	assert_mprog_count(target, 1);
1857 
1858 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
1859 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1860 		goto cleanup1;
1861 
1862 	assert_mprog_count(target, 2);
1863 
1864 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
1865 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1866 		goto cleanup2;
1867 
1868 	assert_mprog_count(target, 3);
1869 
1870 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
1871 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1872 		goto cleanup3;
1873 
1874 	assert_mprog_count(target, 4);
1875 
1876 	optq.prog_ids = prog_ids;
1877 
1878 	memset(prog_ids, 0, sizeof(prog_ids));
1879 	optq.count = ARRAY_SIZE(prog_ids);
1880 
1881 	err = bpf_prog_query_opts(loopback, target, &optq);
1882 	if (!ASSERT_OK(err, "prog_query"))
1883 		goto cleanup4;
1884 
1885 	ASSERT_EQ(optq.count, 4, "count");
1886 	ASSERT_EQ(optq.revision, 5, "revision");
1887 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
1888 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
1889 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
1890 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
1891 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1892 
1893 	LIBBPF_OPTS_RESET(optd,
1894 		.flags = BPF_F_BEFORE,
1895 		.relative_fd = fd2,
1896 	);
1897 
1898 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1899 	ASSERT_OK(err, "prog_detach");
1900 
1901 	assert_mprog_count(target, 3);
1902 
1903 	memset(prog_ids, 0, sizeof(prog_ids));
1904 	optq.count = ARRAY_SIZE(prog_ids);
1905 
1906 	err = bpf_prog_query_opts(loopback, target, &optq);
1907 	if (!ASSERT_OK(err, "prog_query"))
1908 		goto cleanup4;
1909 
1910 	ASSERT_EQ(optq.count, 3, "count");
1911 	ASSERT_EQ(optq.revision, 6, "revision");
1912 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1913 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1914 	ASSERT_EQ(optq.prog_ids[2], id4, "prog_ids[2]");
1915 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
1916 
1917 	LIBBPF_OPTS_RESET(optd,
1918 		.flags = BPF_F_BEFORE,
1919 		.relative_fd = fd2,
1920 	);
1921 
1922 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1923 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1924 	assert_mprog_count(target, 3);
1925 
1926 	LIBBPF_OPTS_RESET(optd,
1927 		.flags = BPF_F_BEFORE,
1928 		.relative_fd = fd4,
1929 	);
1930 
1931 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1932 	ASSERT_EQ(err, -ERANGE, "prog_detach");
1933 	assert_mprog_count(target, 3);
1934 
1935 	LIBBPF_OPTS_RESET(optd,
1936 		.flags = BPF_F_BEFORE,
1937 		.relative_fd = fd1,
1938 	);
1939 
1940 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1941 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1942 	assert_mprog_count(target, 3);
1943 
1944 	LIBBPF_OPTS_RESET(optd,
1945 		.flags = BPF_F_BEFORE,
1946 		.relative_fd = fd3,
1947 	);
1948 
1949 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1950 	ASSERT_OK(err, "prog_detach");
1951 
1952 	assert_mprog_count(target, 2);
1953 
1954 	memset(prog_ids, 0, sizeof(prog_ids));
1955 	optq.count = ARRAY_SIZE(prog_ids);
1956 
1957 	err = bpf_prog_query_opts(loopback, target, &optq);
1958 	if (!ASSERT_OK(err, "prog_query"))
1959 		goto cleanup4;
1960 
1961 	ASSERT_EQ(optq.count, 2, "count");
1962 	ASSERT_EQ(optq.revision, 7, "revision");
1963 	ASSERT_EQ(optq.prog_ids[0], id3, "prog_ids[0]");
1964 	ASSERT_EQ(optq.prog_ids[1], id4, "prog_ids[1]");
1965 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1966 
1967 	LIBBPF_OPTS_RESET(optd,
1968 		.flags = BPF_F_BEFORE,
1969 		.relative_fd = fd4,
1970 	);
1971 
1972 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1973 	ASSERT_OK(err, "prog_detach");
1974 
1975 	assert_mprog_count(target, 1);
1976 
1977 	memset(prog_ids, 0, sizeof(prog_ids));
1978 	optq.count = ARRAY_SIZE(prog_ids);
1979 
1980 	err = bpf_prog_query_opts(loopback, target, &optq);
1981 	if (!ASSERT_OK(err, "prog_query"))
1982 		goto cleanup4;
1983 
1984 	ASSERT_EQ(optq.count, 1, "count");
1985 	ASSERT_EQ(optq.revision, 8, "revision");
1986 	ASSERT_EQ(optq.prog_ids[0], id4, "prog_ids[0]");
1987 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
1988 
1989 	LIBBPF_OPTS_RESET(optd,
1990 		.flags = BPF_F_BEFORE,
1991 	);
1992 
1993 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1994 	ASSERT_OK(err, "prog_detach");
1995 
1996 	assert_mprog_count(target, 0);
1997 	goto cleanup;
1998 
1999 cleanup4:
2000 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
2001 	ASSERT_OK(err, "prog_detach");
2002 	assert_mprog_count(target, 3);
2003 
2004 cleanup3:
2005 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2006 	ASSERT_OK(err, "prog_detach");
2007 	assert_mprog_count(target, 2);
2008 
2009 cleanup2:
2010 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2011 	ASSERT_OK(err, "prog_detach");
2012 	assert_mprog_count(target, 1);
2013 
2014 cleanup1:
2015 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2016 	ASSERT_OK(err, "prog_detach");
2017 	assert_mprog_count(target, 0);
2018 
2019 cleanup:
2020 	test_tc_link__destroy(skel);
2021 }
2022 
2023 void serial_test_tc_opts_detach_before(void)
2024 {
2025 	test_tc_opts_detach_before_target(BPF_TCX_INGRESS);
2026 	test_tc_opts_detach_before_target(BPF_TCX_EGRESS);
2027 }
2028 
2029 static void test_tc_opts_detach_after_target(int target)
2030 {
2031 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2032 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2033 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
2034 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
2035 	struct test_tc_link *skel;
2036 	__u32 prog_ids[5];
2037 	int err;
2038 
2039 	skel = test_tc_link__open_and_load();
2040 	if (!ASSERT_OK_PTR(skel, "skel_load"))
2041 		goto cleanup;
2042 
2043 	fd1 = bpf_program__fd(skel->progs.tc1);
2044 	fd2 = bpf_program__fd(skel->progs.tc2);
2045 	fd3 = bpf_program__fd(skel->progs.tc3);
2046 	fd4 = bpf_program__fd(skel->progs.tc4);
2047 
2048 	id1 = id_from_prog_fd(fd1);
2049 	id2 = id_from_prog_fd(fd2);
2050 	id3 = id_from_prog_fd(fd3);
2051 	id4 = id_from_prog_fd(fd4);
2052 
2053 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
2054 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
2055 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
2056 
2057 	assert_mprog_count(target, 0);
2058 
2059 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
2060 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2061 		goto cleanup;
2062 
2063 	assert_mprog_count(target, 1);
2064 
2065 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
2066 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2067 		goto cleanup1;
2068 
2069 	assert_mprog_count(target, 2);
2070 
2071 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
2072 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2073 		goto cleanup2;
2074 
2075 	assert_mprog_count(target, 3);
2076 
2077 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
2078 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2079 		goto cleanup3;
2080 
2081 	assert_mprog_count(target, 4);
2082 
2083 	optq.prog_ids = prog_ids;
2084 
2085 	memset(prog_ids, 0, sizeof(prog_ids));
2086 	optq.count = ARRAY_SIZE(prog_ids);
2087 
2088 	err = bpf_prog_query_opts(loopback, target, &optq);
2089 	if (!ASSERT_OK(err, "prog_query"))
2090 		goto cleanup4;
2091 
2092 	ASSERT_EQ(optq.count, 4, "count");
2093 	ASSERT_EQ(optq.revision, 5, "revision");
2094 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2095 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
2096 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
2097 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
2098 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
2099 
2100 	LIBBPF_OPTS_RESET(optd,
2101 		.flags = BPF_F_AFTER,
2102 		.relative_fd = fd1,
2103 	);
2104 
2105 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2106 	ASSERT_OK(err, "prog_detach");
2107 
2108 	assert_mprog_count(target, 3);
2109 
2110 	memset(prog_ids, 0, sizeof(prog_ids));
2111 	optq.count = ARRAY_SIZE(prog_ids);
2112 
2113 	err = bpf_prog_query_opts(loopback, target, &optq);
2114 	if (!ASSERT_OK(err, "prog_query"))
2115 		goto cleanup4;
2116 
2117 	ASSERT_EQ(optq.count, 3, "count");
2118 	ASSERT_EQ(optq.revision, 6, "revision");
2119 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2120 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
2121 	ASSERT_EQ(optq.prog_ids[2], id4, "prog_ids[2]");
2122 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
2123 
2124 	LIBBPF_OPTS_RESET(optd,
2125 		.flags = BPF_F_AFTER,
2126 		.relative_fd = fd1,
2127 	);
2128 
2129 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2130 	ASSERT_EQ(err, -ENOENT, "prog_detach");
2131 	assert_mprog_count(target, 3);
2132 
2133 	LIBBPF_OPTS_RESET(optd,
2134 		.flags = BPF_F_AFTER,
2135 		.relative_fd = fd4,
2136 	);
2137 
2138 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2139 	ASSERT_EQ(err, -ERANGE, "prog_detach");
2140 	assert_mprog_count(target, 3);
2141 
2142 	LIBBPF_OPTS_RESET(optd,
2143 		.flags = BPF_F_AFTER,
2144 		.relative_fd = fd3,
2145 	);
2146 
2147 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2148 	ASSERT_EQ(err, -ERANGE, "prog_detach");
2149 	assert_mprog_count(target, 3);
2150 
2151 	LIBBPF_OPTS_RESET(optd,
2152 		.flags = BPF_F_AFTER,
2153 		.relative_fd = fd1,
2154 	);
2155 
2156 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2157 	ASSERT_EQ(err, -ERANGE, "prog_detach");
2158 	assert_mprog_count(target, 3);
2159 
2160 	LIBBPF_OPTS_RESET(optd,
2161 		.flags = BPF_F_AFTER,
2162 		.relative_fd = fd1,
2163 	);
2164 
2165 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2166 	ASSERT_OK(err, "prog_detach");
2167 
2168 	assert_mprog_count(target, 2);
2169 
2170 	memset(prog_ids, 0, sizeof(prog_ids));
2171 	optq.count = ARRAY_SIZE(prog_ids);
2172 
2173 	err = bpf_prog_query_opts(loopback, target, &optq);
2174 	if (!ASSERT_OK(err, "prog_query"))
2175 		goto cleanup4;
2176 
2177 	ASSERT_EQ(optq.count, 2, "count");
2178 	ASSERT_EQ(optq.revision, 7, "revision");
2179 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2180 	ASSERT_EQ(optq.prog_ids[1], id4, "prog_ids[1]");
2181 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
2182 
2183 	LIBBPF_OPTS_RESET(optd,
2184 		.flags = BPF_F_AFTER,
2185 		.relative_fd = fd1,
2186 	);
2187 
2188 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
2189 	ASSERT_OK(err, "prog_detach");
2190 
2191 	assert_mprog_count(target, 1);
2192 
2193 	memset(prog_ids, 0, sizeof(prog_ids));
2194 	optq.count = ARRAY_SIZE(prog_ids);
2195 
2196 	err = bpf_prog_query_opts(loopback, target, &optq);
2197 	if (!ASSERT_OK(err, "prog_query"))
2198 		goto cleanup4;
2199 
2200 	ASSERT_EQ(optq.count, 1, "count");
2201 	ASSERT_EQ(optq.revision, 8, "revision");
2202 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2203 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
2204 
2205 	LIBBPF_OPTS_RESET(optd,
2206 		.flags = BPF_F_AFTER,
2207 	);
2208 
2209 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
2210 	ASSERT_OK(err, "prog_detach");
2211 
2212 	assert_mprog_count(target, 0);
2213 	goto cleanup;
2214 
2215 cleanup4:
2216 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
2217 	ASSERT_OK(err, "prog_detach");
2218 	assert_mprog_count(target, 3);
2219 
2220 cleanup3:
2221 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2222 	ASSERT_OK(err, "prog_detach");
2223 	assert_mprog_count(target, 2);
2224 
2225 cleanup2:
2226 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2227 	ASSERT_OK(err, "prog_detach");
2228 	assert_mprog_count(target, 1);
2229 
2230 cleanup1:
2231 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2232 	ASSERT_OK(err, "prog_detach");
2233 	assert_mprog_count(target, 0);
2234 
2235 cleanup:
2236 	test_tc_link__destroy(skel);
2237 }
2238 
2239 void serial_test_tc_opts_detach_after(void)
2240 {
2241 	test_tc_opts_detach_after_target(BPF_TCX_INGRESS);
2242 	test_tc_opts_detach_after_target(BPF_TCX_EGRESS);
2243 }
2244 
2245 static void test_tc_opts_delete_empty(int target, bool chain_tc_old)
2246 {
2247 	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback);
2248 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2249 	int err;
2250 
2251 	assert_mprog_count(target, 0);
2252 	if (chain_tc_old) {
2253 		tc_hook.attach_point = target == BPF_TCX_INGRESS ?
2254 				       BPF_TC_INGRESS : BPF_TC_EGRESS;
2255 		err = bpf_tc_hook_create(&tc_hook);
2256 		ASSERT_OK(err, "bpf_tc_hook_create");
2257 		assert_mprog_count(target, 0);
2258 	}
2259 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
2260 	ASSERT_EQ(err, -ENOENT, "prog_detach");
2261 	if (chain_tc_old) {
2262 		tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
2263 		bpf_tc_hook_destroy(&tc_hook);
2264 	}
2265 	assert_mprog_count(target, 0);
2266 }
2267 
2268 void serial_test_tc_opts_delete_empty(void)
2269 {
2270 	test_tc_opts_delete_empty(BPF_TCX_INGRESS, false);
2271 	test_tc_opts_delete_empty(BPF_TCX_EGRESS, false);
2272 	test_tc_opts_delete_empty(BPF_TCX_INGRESS, true);
2273 	test_tc_opts_delete_empty(BPF_TCX_EGRESS, true);
2274 }
2275 
2276 static void test_tc_chain_mixed(int target)
2277 {
2278 	LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
2279 	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback);
2280 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2281 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2282 	__u32 fd1, fd2, fd3, id1, id2, id3;
2283 	struct test_tc_link *skel;
2284 	int err, detach_fd;
2285 
2286 	skel = test_tc_link__open_and_load();
2287 	if (!ASSERT_OK_PTR(skel, "skel_load"))
2288 		goto cleanup;
2289 
2290 	fd1 = bpf_program__fd(skel->progs.tc4);
2291 	fd2 = bpf_program__fd(skel->progs.tc5);
2292 	fd3 = bpf_program__fd(skel->progs.tc6);
2293 
2294 	id1 = id_from_prog_fd(fd1);
2295 	id2 = id_from_prog_fd(fd2);
2296 	id3 = id_from_prog_fd(fd3);
2297 
2298 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
2299 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
2300 
2301 	assert_mprog_count(target, 0);
2302 
2303 	tc_hook.attach_point = target == BPF_TCX_INGRESS ?
2304 			       BPF_TC_INGRESS : BPF_TC_EGRESS;
2305 	err = bpf_tc_hook_create(&tc_hook);
2306 	err = err == -EEXIST ? 0 : err;
2307 	if (!ASSERT_OK(err, "bpf_tc_hook_create"))
2308 		goto cleanup;
2309 
2310 	tc_opts.prog_fd = fd2;
2311 	err = bpf_tc_attach(&tc_hook, &tc_opts);
2312 	if (!ASSERT_OK(err, "bpf_tc_attach"))
2313 		goto cleanup_hook;
2314 
2315 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
2316 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2317 		goto cleanup_filter;
2318 
2319 	detach_fd = fd3;
2320 
2321 	assert_mprog_count(target, 1);
2322 
2323 	tc_skel_reset_all_seen(skel);
2324 	ASSERT_OK(system(ping_cmd), ping_cmd);
2325 
2326 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
2327 	ASSERT_EQ(skel->bss->seen_tc5, false, "seen_tc5");
2328 	ASSERT_EQ(skel->bss->seen_tc6, true, "seen_tc6");
2329 
2330 	LIBBPF_OPTS_RESET(opta,
2331 		.flags = BPF_F_REPLACE,
2332 		.replace_prog_fd = fd3,
2333 	);
2334 
2335 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
2336 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2337 		goto cleanup_opts;
2338 
2339 	detach_fd = fd1;
2340 
2341 	assert_mprog_count(target, 1);
2342 
2343 	tc_skel_reset_all_seen(skel);
2344 	ASSERT_OK(system(ping_cmd), ping_cmd);
2345 
2346 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
2347 	ASSERT_EQ(skel->bss->seen_tc5, true, "seen_tc5");
2348 	ASSERT_EQ(skel->bss->seen_tc6, false, "seen_tc6");
2349 
2350 cleanup_opts:
2351 	err = bpf_prog_detach_opts(detach_fd, loopback, target, &optd);
2352 	ASSERT_OK(err, "prog_detach");
2353 	assert_mprog_count(target, 0);
2354 
2355 	tc_skel_reset_all_seen(skel);
2356 	ASSERT_OK(system(ping_cmd), ping_cmd);
2357 
2358 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
2359 	ASSERT_EQ(skel->bss->seen_tc5, true, "seen_tc5");
2360 	ASSERT_EQ(skel->bss->seen_tc6, false, "seen_tc6");
2361 
2362 cleanup_filter:
2363 	tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0;
2364 	err = bpf_tc_detach(&tc_hook, &tc_opts);
2365 	ASSERT_OK(err, "bpf_tc_detach");
2366 
2367 cleanup_hook:
2368 	tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
2369 	bpf_tc_hook_destroy(&tc_hook);
2370 
2371 cleanup:
2372 	test_tc_link__destroy(skel);
2373 }
2374 
2375 void serial_test_tc_opts_chain_mixed(void)
2376 {
2377 	test_tc_chain_mixed(BPF_TCX_INGRESS);
2378 	test_tc_chain_mixed(BPF_TCX_EGRESS);
2379 }
2380 
2381 static int generate_dummy_prog(void)
2382 {
2383 	const struct bpf_insn prog_insns[] = {
2384 		BPF_MOV64_IMM(BPF_REG_0, 0),
2385 		BPF_EXIT_INSN(),
2386 	};
2387 	const size_t prog_insn_cnt = sizeof(prog_insns) / sizeof(struct bpf_insn);
2388 	LIBBPF_OPTS(bpf_prog_load_opts, opts);
2389 	const size_t log_buf_sz = 256;
2390 	char log_buf[log_buf_sz];
2391 	int fd = -1;
2392 
2393 	opts.log_buf = log_buf;
2394 	opts.log_size = log_buf_sz;
2395 
2396 	log_buf[0] = '\0';
2397 	opts.log_level = 0;
2398 	fd = bpf_prog_load(BPF_PROG_TYPE_SCHED_CLS, "tcx_prog", "GPL",
2399 			   prog_insns, prog_insn_cnt, &opts);
2400 	ASSERT_STREQ(log_buf, "", "log_0");
2401 	ASSERT_GE(fd, 0, "prog_fd");
2402 	return fd;
2403 }
2404 
2405 static void test_tc_opts_max_target(int target, int flags, bool relative)
2406 {
2407 	int err, ifindex, i, prog_fd, last_fd = -1;
2408 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2409 	const int max_progs = 63;
2410 
2411 	ASSERT_OK(system("ip link add dev tcx_opts1 type veth peer name tcx_opts2"), "add veth");
2412 	ifindex = if_nametoindex("tcx_opts1");
2413 	ASSERT_NEQ(ifindex, 0, "non_zero_ifindex");
2414 
2415 	assert_mprog_count_ifindex(ifindex, target, 0);
2416 
2417 	for (i = 0; i < max_progs; i++) {
2418 		prog_fd = generate_dummy_prog();
2419 		if (!ASSERT_GE(prog_fd, 0, "dummy_prog"))
2420 			goto cleanup;
2421 		err = bpf_prog_attach_opts(prog_fd, ifindex, target, &opta);
2422 		if (!ASSERT_EQ(err, 0, "prog_attach"))
2423 			goto cleanup;
2424 		assert_mprog_count_ifindex(ifindex, target, i + 1);
2425 		if (i == max_progs - 1 && relative)
2426 			last_fd = prog_fd;
2427 		else
2428 			close(prog_fd);
2429 	}
2430 
2431 	prog_fd = generate_dummy_prog();
2432 	if (!ASSERT_GE(prog_fd, 0, "dummy_prog"))
2433 		goto cleanup;
2434 	opta.flags = flags;
2435 	if (last_fd > 0)
2436 		opta.relative_fd = last_fd;
2437 	err = bpf_prog_attach_opts(prog_fd, ifindex, target, &opta);
2438 	ASSERT_EQ(err, -ERANGE, "prog_64_attach");
2439 	assert_mprog_count_ifindex(ifindex, target, max_progs);
2440 	close(prog_fd);
2441 cleanup:
2442 	if (last_fd > 0)
2443 		close(last_fd);
2444 	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
2445 	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
2446 	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
2447 }
2448 
2449 void serial_test_tc_opts_max(void)
2450 {
2451 	test_tc_opts_max_target(BPF_TCX_INGRESS, 0, false);
2452 	test_tc_opts_max_target(BPF_TCX_EGRESS, 0, false);
2453 
2454 	test_tc_opts_max_target(BPF_TCX_INGRESS, BPF_F_BEFORE, false);
2455 	test_tc_opts_max_target(BPF_TCX_EGRESS, BPF_F_BEFORE, true);
2456 
2457 	test_tc_opts_max_target(BPF_TCX_INGRESS, BPF_F_AFTER, true);
2458 	test_tc_opts_max_target(BPF_TCX_EGRESS, BPF_F_AFTER, false);
2459 }
2460 
2461 static void test_tc_opts_query_target(int target)
2462 {
2463 	const size_t attr_size = offsetofend(union bpf_attr, query);
2464 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2465 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2466 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
2467 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
2468 	struct test_tc_link *skel;
2469 	union bpf_attr attr;
2470 	__u32 prog_ids[10];
2471 	int err;
2472 
2473 	skel = test_tc_link__open_and_load();
2474 	if (!ASSERT_OK_PTR(skel, "skel_load"))
2475 		goto cleanup;
2476 
2477 	fd1 = bpf_program__fd(skel->progs.tc1);
2478 	fd2 = bpf_program__fd(skel->progs.tc2);
2479 	fd3 = bpf_program__fd(skel->progs.tc3);
2480 	fd4 = bpf_program__fd(skel->progs.tc4);
2481 
2482 	id1 = id_from_prog_fd(fd1);
2483 	id2 = id_from_prog_fd(fd2);
2484 	id3 = id_from_prog_fd(fd3);
2485 	id4 = id_from_prog_fd(fd4);
2486 
2487 	assert_mprog_count(target, 0);
2488 
2489 	LIBBPF_OPTS_RESET(opta,
2490 		.expected_revision = 1,
2491 	);
2492 
2493 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
2494 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2495 		goto cleanup;
2496 
2497 	assert_mprog_count(target, 1);
2498 
2499 	LIBBPF_OPTS_RESET(opta,
2500 		.expected_revision = 2,
2501 	);
2502 
2503 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
2504 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2505 		goto cleanup1;
2506 
2507 	assert_mprog_count(target, 2);
2508 
2509 	LIBBPF_OPTS_RESET(opta,
2510 		.expected_revision = 3,
2511 	);
2512 
2513 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
2514 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2515 		goto cleanup2;
2516 
2517 	assert_mprog_count(target, 3);
2518 
2519 	LIBBPF_OPTS_RESET(opta,
2520 		.expected_revision = 4,
2521 	);
2522 
2523 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
2524 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2525 		goto cleanup3;
2526 
2527 	assert_mprog_count(target, 4);
2528 
2529 	/* Test 1: Double query via libbpf API */
2530 	err = bpf_prog_query_opts(loopback, target, &optq);
2531 	if (!ASSERT_OK(err, "prog_query"))
2532 		goto cleanup4;
2533 
2534 	ASSERT_EQ(optq.count, 4, "count");
2535 	ASSERT_EQ(optq.revision, 5, "revision");
2536 	ASSERT_EQ(optq.prog_ids, NULL, "prog_ids");
2537 	ASSERT_EQ(optq.link_ids, NULL, "link_ids");
2538 
2539 	memset(prog_ids, 0, sizeof(prog_ids));
2540 	optq.prog_ids = prog_ids;
2541 
2542 	err = bpf_prog_query_opts(loopback, target, &optq);
2543 	if (!ASSERT_OK(err, "prog_query"))
2544 		goto cleanup4;
2545 
2546 	ASSERT_EQ(optq.count, 4, "count");
2547 	ASSERT_EQ(optq.revision, 5, "revision");
2548 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2549 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
2550 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
2551 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
2552 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
2553 	ASSERT_EQ(optq.link_ids, NULL, "link_ids");
2554 
2555 	/* Test 2: Double query via bpf_attr & bpf(2) directly */
2556 	memset(&attr, 0, attr_size);
2557 	attr.query.target_ifindex = loopback;
2558 	attr.query.attach_type = target;
2559 
2560 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2561 	if (!ASSERT_OK(err, "prog_query"))
2562 		goto cleanup4;
2563 
2564 	ASSERT_EQ(attr.query.count, 4, "count");
2565 	ASSERT_EQ(attr.query.revision, 5, "revision");
2566 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2567 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2568 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2569 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2570 	ASSERT_EQ(attr.query.prog_ids, 0, "prog_ids");
2571 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2572 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2573 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2574 
2575 	memset(prog_ids, 0, sizeof(prog_ids));
2576 	attr.query.prog_ids = ptr_to_u64(prog_ids);
2577 
2578 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2579 	if (!ASSERT_OK(err, "prog_query"))
2580 		goto cleanup4;
2581 
2582 	ASSERT_EQ(attr.query.count, 4, "count");
2583 	ASSERT_EQ(attr.query.revision, 5, "revision");
2584 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2585 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2586 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2587 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2588 	ASSERT_EQ(attr.query.prog_ids, ptr_to_u64(prog_ids), "prog_ids");
2589 	ASSERT_EQ(prog_ids[0], id1, "prog_ids[0]");
2590 	ASSERT_EQ(prog_ids[1], id2, "prog_ids[1]");
2591 	ASSERT_EQ(prog_ids[2], id3, "prog_ids[2]");
2592 	ASSERT_EQ(prog_ids[3], id4, "prog_ids[3]");
2593 	ASSERT_EQ(prog_ids[4], 0, "prog_ids[4]");
2594 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2595 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2596 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2597 
2598 	/* Test 3: Query with smaller prog_ids array */
2599 	memset(&attr, 0, attr_size);
2600 	attr.query.target_ifindex = loopback;
2601 	attr.query.attach_type = target;
2602 
2603 	memset(prog_ids, 0, sizeof(prog_ids));
2604 	attr.query.prog_ids = ptr_to_u64(prog_ids);
2605 	attr.query.count = 2;
2606 
2607 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2608 	ASSERT_EQ(err, -1, "prog_query_should_fail");
2609 	ASSERT_EQ(errno, ENOSPC, "prog_query_should_fail");
2610 
2611 	ASSERT_EQ(attr.query.count, 4, "count");
2612 	ASSERT_EQ(attr.query.revision, 5, "revision");
2613 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2614 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2615 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2616 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2617 	ASSERT_EQ(attr.query.prog_ids, ptr_to_u64(prog_ids), "prog_ids");
2618 	ASSERT_EQ(prog_ids[0], id1, "prog_ids[0]");
2619 	ASSERT_EQ(prog_ids[1], id2, "prog_ids[1]");
2620 	ASSERT_EQ(prog_ids[2], 0, "prog_ids[2]");
2621 	ASSERT_EQ(prog_ids[3], 0, "prog_ids[3]");
2622 	ASSERT_EQ(prog_ids[4], 0, "prog_ids[4]");
2623 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2624 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2625 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2626 
2627 	/* Test 4: Query with larger prog_ids array */
2628 	memset(&attr, 0, attr_size);
2629 	attr.query.target_ifindex = loopback;
2630 	attr.query.attach_type = target;
2631 
2632 	memset(prog_ids, 0, sizeof(prog_ids));
2633 	attr.query.prog_ids = ptr_to_u64(prog_ids);
2634 	attr.query.count = 10;
2635 
2636 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2637 	if (!ASSERT_OK(err, "prog_query"))
2638 		goto cleanup4;
2639 
2640 	ASSERT_EQ(attr.query.count, 4, "count");
2641 	ASSERT_EQ(attr.query.revision, 5, "revision");
2642 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2643 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2644 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2645 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2646 	ASSERT_EQ(attr.query.prog_ids, ptr_to_u64(prog_ids), "prog_ids");
2647 	ASSERT_EQ(prog_ids[0], id1, "prog_ids[0]");
2648 	ASSERT_EQ(prog_ids[1], id2, "prog_ids[1]");
2649 	ASSERT_EQ(prog_ids[2], id3, "prog_ids[2]");
2650 	ASSERT_EQ(prog_ids[3], id4, "prog_ids[3]");
2651 	ASSERT_EQ(prog_ids[4], 0, "prog_ids[4]");
2652 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2653 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2654 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2655 
2656 	/* Test 5: Query with NULL prog_ids array but with count > 0 */
2657 	memset(&attr, 0, attr_size);
2658 	attr.query.target_ifindex = loopback;
2659 	attr.query.attach_type = target;
2660 
2661 	memset(prog_ids, 0, sizeof(prog_ids));
2662 	attr.query.count = sizeof(prog_ids);
2663 
2664 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2665 	if (!ASSERT_OK(err, "prog_query"))
2666 		goto cleanup4;
2667 
2668 	ASSERT_EQ(attr.query.count, 4, "count");
2669 	ASSERT_EQ(attr.query.revision, 5, "revision");
2670 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2671 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2672 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2673 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2674 	ASSERT_EQ(prog_ids[0], 0, "prog_ids[0]");
2675 	ASSERT_EQ(prog_ids[1], 0, "prog_ids[1]");
2676 	ASSERT_EQ(prog_ids[2], 0, "prog_ids[2]");
2677 	ASSERT_EQ(prog_ids[3], 0, "prog_ids[3]");
2678 	ASSERT_EQ(prog_ids[4], 0, "prog_ids[4]");
2679 	ASSERT_EQ(attr.query.prog_ids, 0, "prog_ids");
2680 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2681 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2682 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2683 
2684 	/* Test 6: Query with non-NULL prog_ids array but with count == 0 */
2685 	memset(&attr, 0, attr_size);
2686 	attr.query.target_ifindex = loopback;
2687 	attr.query.attach_type = target;
2688 
2689 	memset(prog_ids, 0, sizeof(prog_ids));
2690 	attr.query.prog_ids = ptr_to_u64(prog_ids);
2691 
2692 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2693 	if (!ASSERT_OK(err, "prog_query"))
2694 		goto cleanup4;
2695 
2696 	ASSERT_EQ(attr.query.count, 4, "count");
2697 	ASSERT_EQ(attr.query.revision, 5, "revision");
2698 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2699 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2700 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2701 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2702 	ASSERT_EQ(prog_ids[0], 0, "prog_ids[0]");
2703 	ASSERT_EQ(prog_ids[1], 0, "prog_ids[1]");
2704 	ASSERT_EQ(prog_ids[2], 0, "prog_ids[2]");
2705 	ASSERT_EQ(prog_ids[3], 0, "prog_ids[3]");
2706 	ASSERT_EQ(prog_ids[4], 0, "prog_ids[4]");
2707 	ASSERT_EQ(attr.query.prog_ids, ptr_to_u64(prog_ids), "prog_ids");
2708 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2709 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2710 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2711 
2712 	/* Test 7: Query with invalid flags */
2713 	attr.query.attach_flags = 0;
2714 	attr.query.query_flags = 1;
2715 
2716 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2717 	ASSERT_EQ(err, -1, "prog_query_should_fail");
2718 	ASSERT_EQ(errno, EINVAL, "prog_query_should_fail");
2719 
2720 	attr.query.attach_flags = 1;
2721 	attr.query.query_flags = 0;
2722 
2723 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2724 	ASSERT_EQ(err, -1, "prog_query_should_fail");
2725 	ASSERT_EQ(errno, EINVAL, "prog_query_should_fail");
2726 
2727 cleanup4:
2728 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
2729 	ASSERT_OK(err, "prog_detach");
2730 	assert_mprog_count(target, 3);
2731 
2732 cleanup3:
2733 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2734 	ASSERT_OK(err, "prog_detach");
2735 	assert_mprog_count(target, 2);
2736 
2737 cleanup2:
2738 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2739 	ASSERT_OK(err, "prog_detach");
2740 	assert_mprog_count(target, 1);
2741 
2742 cleanup1:
2743 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2744 	ASSERT_OK(err, "prog_detach");
2745 	assert_mprog_count(target, 0);
2746 
2747 cleanup:
2748 	test_tc_link__destroy(skel);
2749 }
2750 
2751 void serial_test_tc_opts_query(void)
2752 {
2753 	test_tc_opts_query_target(BPF_TCX_INGRESS);
2754 	test_tc_opts_query_target(BPF_TCX_EGRESS);
2755 }
2756 
2757 static void test_tc_opts_query_attach_target(int target)
2758 {
2759 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2760 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2761 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
2762 	struct test_tc_link *skel;
2763 	__u32 prog_ids[2];
2764 	__u32 fd1, id1;
2765 	int err;
2766 
2767 	skel = test_tc_link__open_and_load();
2768 	if (!ASSERT_OK_PTR(skel, "skel_load"))
2769 		goto cleanup;
2770 
2771 	fd1 = bpf_program__fd(skel->progs.tc1);
2772 	id1 = id_from_prog_fd(fd1);
2773 
2774 	err = bpf_prog_query_opts(loopback, target, &optq);
2775 	if (!ASSERT_OK(err, "prog_query"))
2776 		goto cleanup;
2777 
2778 	ASSERT_EQ(optq.count, 0, "count");
2779 	ASSERT_EQ(optq.revision, 1, "revision");
2780 
2781 	LIBBPF_OPTS_RESET(opta,
2782 		.expected_revision = optq.revision,
2783 	);
2784 
2785 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
2786 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2787 		goto cleanup;
2788 
2789 	memset(prog_ids, 0, sizeof(prog_ids));
2790 	optq.prog_ids = prog_ids;
2791 	optq.count = ARRAY_SIZE(prog_ids);
2792 
2793 	err = bpf_prog_query_opts(loopback, target, &optq);
2794 	if (!ASSERT_OK(err, "prog_query"))
2795 		goto cleanup1;
2796 
2797 	ASSERT_EQ(optq.count, 1, "count");
2798 	ASSERT_EQ(optq.revision, 2, "revision");
2799 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2800 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
2801 
2802 cleanup1:
2803 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2804 	ASSERT_OK(err, "prog_detach");
2805 	assert_mprog_count(target, 0);
2806 cleanup:
2807 	test_tc_link__destroy(skel);
2808 }
2809 
2810 void serial_test_tc_opts_query_attach(void)
2811 {
2812 	test_tc_opts_query_attach_target(BPF_TCX_INGRESS);
2813 	test_tc_opts_query_attach_target(BPF_TCX_EGRESS);
2814 }
2815