xref: /linux/tools/testing/selftests/bpf/prog_tests/tc_opts.c (revision 1fd1dc41724319406b0aff221a352a400b0ddfc5)
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 test_ns_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 test_ns_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 test_ns_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 test_ns_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 test_ns_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 test_ns_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 test_ns_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 test_ns_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 test_ns_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 	goto cleanup;
1364 
1365 cleanup3:
1366 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1367 	ASSERT_OK(err, "prog_detach");
1368 
1369 	assert_mprog_count_ifindex(ifindex, target, 2);
1370 cleanup2:
1371 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1372 	ASSERT_OK(err, "prog_detach");
1373 
1374 	assert_mprog_count_ifindex(ifindex, target, 1);
1375 cleanup1:
1376 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1377 	ASSERT_OK(err, "prog_detach");
1378 
1379 	assert_mprog_count_ifindex(ifindex, target, 0);
1380 cleanup:
1381 	test_tc_link__destroy(skel);
1382 
1383 	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1384 	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1385 	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1386 }
1387 
1388 void test_ns_tc_opts_dev_cleanup(void)
1389 {
1390 	test_tc_opts_dev_cleanup_target(BPF_TCX_INGRESS);
1391 	test_tc_opts_dev_cleanup_target(BPF_TCX_EGRESS);
1392 }
1393 
1394 static void test_tc_opts_mixed_target(int target)
1395 {
1396 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1397 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1398 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1399 	LIBBPF_OPTS(bpf_tcx_opts, optl);
1400 	__u32 pid1, pid2, pid3, pid4, lid2, lid4;
1401 	__u32 prog_flags[4], link_flags[4];
1402 	__u32 prog_ids[4], link_ids[4];
1403 	struct test_tc_link *skel;
1404 	struct bpf_link *link;
1405 	int err, detach_fd;
1406 
1407 	skel = test_tc_link__open();
1408 	if (!ASSERT_OK_PTR(skel, "skel_open"))
1409 		goto cleanup;
1410 
1411 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1412 		  0, "tc1_attach_type");
1413 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1414 		  0, "tc2_attach_type");
1415 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
1416 		  0, "tc3_attach_type");
1417 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1418 		  0, "tc4_attach_type");
1419 
1420 	err = test_tc_link__load(skel);
1421 	if (!ASSERT_OK(err, "skel_load"))
1422 		goto cleanup;
1423 
1424 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1425 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1426 	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1427 	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1428 
1429 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1430 	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
1431 	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1432 
1433 	assert_mprog_count(target, 0);
1434 
1435 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc1),
1436 				   loopback, target, &opta);
1437 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1438 		goto cleanup;
1439 
1440 	detach_fd = bpf_program__fd(skel->progs.tc1);
1441 
1442 	assert_mprog_count(target, 1);
1443 
1444 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1445 	if (!ASSERT_OK_PTR(link, "link_attach"))
1446 		goto cleanup1;
1447 	skel->links.tc2 = link;
1448 
1449 	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
1450 
1451 	assert_mprog_count(target, 2);
1452 
1453 	LIBBPF_OPTS_RESET(opta,
1454 		.flags = BPF_F_REPLACE,
1455 		.replace_prog_fd = bpf_program__fd(skel->progs.tc1),
1456 	);
1457 
1458 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc2),
1459 				   loopback, target, &opta);
1460 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1461 
1462 	assert_mprog_count(target, 2);
1463 
1464 	LIBBPF_OPTS_RESET(opta,
1465 		.flags = BPF_F_REPLACE,
1466 		.replace_prog_fd = bpf_program__fd(skel->progs.tc2),
1467 	);
1468 
1469 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc1),
1470 				   loopback, target, &opta);
1471 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1472 
1473 	assert_mprog_count(target, 2);
1474 
1475 	LIBBPF_OPTS_RESET(opta,
1476 		.flags = BPF_F_REPLACE,
1477 		.replace_prog_fd = bpf_program__fd(skel->progs.tc2),
1478 	);
1479 
1480 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc3),
1481 				   loopback, target, &opta);
1482 	ASSERT_EQ(err, -EBUSY, "prog_attach");
1483 
1484 	assert_mprog_count(target, 2);
1485 
1486 	LIBBPF_OPTS_RESET(opta,
1487 		.flags = BPF_F_REPLACE,
1488 		.replace_prog_fd = bpf_program__fd(skel->progs.tc1),
1489 	);
1490 
1491 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc3),
1492 				   loopback, target, &opta);
1493 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1494 		goto cleanup1;
1495 
1496 	detach_fd = bpf_program__fd(skel->progs.tc3);
1497 
1498 	assert_mprog_count(target, 2);
1499 
1500 	link = bpf_program__attach_tcx(skel->progs.tc4, loopback, &optl);
1501 	if (!ASSERT_OK_PTR(link, "link_attach"))
1502 		goto cleanup1;
1503 	skel->links.tc4 = link;
1504 
1505 	lid4 = id_from_link_fd(bpf_link__fd(skel->links.tc4));
1506 
1507 	assert_mprog_count(target, 3);
1508 
1509 	LIBBPF_OPTS_RESET(opta,
1510 		.flags = BPF_F_REPLACE,
1511 		.replace_prog_fd = bpf_program__fd(skel->progs.tc4),
1512 	);
1513 
1514 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc2),
1515 				   loopback, target, &opta);
1516 	ASSERT_EQ(err, -EEXIST, "prog_attach");
1517 
1518 	optq.prog_ids = prog_ids;
1519 	optq.prog_attach_flags = prog_flags;
1520 	optq.link_ids = link_ids;
1521 	optq.link_attach_flags = link_flags;
1522 
1523 	memset(prog_ids, 0, sizeof(prog_ids));
1524 	memset(prog_flags, 0, sizeof(prog_flags));
1525 	memset(link_ids, 0, sizeof(link_ids));
1526 	memset(link_flags, 0, sizeof(link_flags));
1527 	optq.count = ARRAY_SIZE(prog_ids);
1528 
1529 	err = bpf_prog_query_opts(loopback, target, &optq);
1530 	if (!ASSERT_OK(err, "prog_query"))
1531 		goto cleanup1;
1532 
1533 	ASSERT_EQ(optq.count, 3, "count");
1534 	ASSERT_EQ(optq.revision, 5, "revision");
1535 	ASSERT_EQ(optq.prog_ids[0], pid3, "prog_ids[0]");
1536 	ASSERT_EQ(optq.prog_attach_flags[0], 0, "prog_flags[0]");
1537 	ASSERT_EQ(optq.link_ids[0], 0, "link_ids[0]");
1538 	ASSERT_EQ(optq.link_attach_flags[0], 0, "link_flags[0]");
1539 	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
1540 	ASSERT_EQ(optq.prog_attach_flags[1], 0, "prog_flags[1]");
1541 	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
1542 	ASSERT_EQ(optq.link_attach_flags[1], 0, "link_flags[1]");
1543 	ASSERT_EQ(optq.prog_ids[2], pid4, "prog_ids[2]");
1544 	ASSERT_EQ(optq.prog_attach_flags[2], 0, "prog_flags[2]");
1545 	ASSERT_EQ(optq.link_ids[2], lid4, "link_ids[2]");
1546 	ASSERT_EQ(optq.link_attach_flags[2], 0, "link_flags[2]");
1547 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
1548 	ASSERT_EQ(optq.prog_attach_flags[3], 0, "prog_flags[3]");
1549 	ASSERT_EQ(optq.link_ids[3], 0, "link_ids[3]");
1550 	ASSERT_EQ(optq.link_attach_flags[3], 0, "link_flags[3]");
1551 
1552 	ASSERT_OK(system(ping_cmd), ping_cmd);
1553 
1554 cleanup1:
1555 	err = bpf_prog_detach_opts(detach_fd, loopback, target, &optd);
1556 	ASSERT_OK(err, "prog_detach");
1557 	assert_mprog_count(target, 2);
1558 
1559 cleanup:
1560 	test_tc_link__destroy(skel);
1561 	assert_mprog_count(target, 0);
1562 }
1563 
1564 void test_ns_tc_opts_mixed(void)
1565 {
1566 	test_tc_opts_mixed_target(BPF_TCX_INGRESS);
1567 	test_tc_opts_mixed_target(BPF_TCX_EGRESS);
1568 }
1569 
1570 static void test_tc_opts_demixed_target(int target)
1571 {
1572 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1573 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1574 	LIBBPF_OPTS(bpf_tcx_opts, optl);
1575 	struct test_tc_link *skel;
1576 	struct bpf_link *link;
1577 	__u32 pid1, pid2;
1578 	int err;
1579 
1580 	skel = test_tc_link__open();
1581 	if (!ASSERT_OK_PTR(skel, "skel_open"))
1582 		goto cleanup;
1583 
1584 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1585 		  0, "tc1_attach_type");
1586 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1587 		  0, "tc2_attach_type");
1588 
1589 	err = test_tc_link__load(skel);
1590 	if (!ASSERT_OK(err, "skel_load"))
1591 		goto cleanup;
1592 
1593 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1594 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1595 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1596 
1597 	assert_mprog_count(target, 0);
1598 
1599 	err = bpf_prog_attach_opts(bpf_program__fd(skel->progs.tc1),
1600 				   loopback, target, &opta);
1601 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1602 		goto cleanup;
1603 
1604 	assert_mprog_count(target, 1);
1605 
1606 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1607 	if (!ASSERT_OK_PTR(link, "link_attach"))
1608 		goto cleanup1;
1609 	skel->links.tc2 = link;
1610 
1611 	assert_mprog_count(target, 2);
1612 
1613 	LIBBPF_OPTS_RESET(optd,
1614 		.flags = BPF_F_AFTER,
1615 	);
1616 
1617 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1618 	ASSERT_EQ(err, -EBUSY, "prog_detach");
1619 
1620 	assert_mprog_count(target, 2);
1621 
1622 	LIBBPF_OPTS_RESET(optd,
1623 		.flags = BPF_F_BEFORE,
1624 	);
1625 
1626 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1627 	ASSERT_OK(err, "prog_detach");
1628 
1629 	assert_mprog_count(target, 1);
1630 	goto cleanup;
1631 
1632 cleanup1:
1633 	err = bpf_prog_detach_opts(bpf_program__fd(skel->progs.tc1),
1634 				   loopback, target, &optd);
1635 	ASSERT_OK(err, "prog_detach");
1636 	assert_mprog_count(target, 2);
1637 
1638 cleanup:
1639 	test_tc_link__destroy(skel);
1640 	assert_mprog_count(target, 0);
1641 }
1642 
1643 void test_ns_tc_opts_demixed(void)
1644 {
1645 	test_tc_opts_demixed_target(BPF_TCX_INGRESS);
1646 	test_tc_opts_demixed_target(BPF_TCX_EGRESS);
1647 }
1648 
1649 static void test_tc_opts_detach_target(int target)
1650 {
1651 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1652 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1653 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1654 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1655 	struct test_tc_link *skel;
1656 	__u32 prog_ids[5];
1657 	int err;
1658 
1659 	skel = test_tc_link__open_and_load();
1660 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1661 		goto cleanup;
1662 
1663 	fd1 = bpf_program__fd(skel->progs.tc1);
1664 	fd2 = bpf_program__fd(skel->progs.tc2);
1665 	fd3 = bpf_program__fd(skel->progs.tc3);
1666 	fd4 = bpf_program__fd(skel->progs.tc4);
1667 
1668 	id1 = id_from_prog_fd(fd1);
1669 	id2 = id_from_prog_fd(fd2);
1670 	id3 = id_from_prog_fd(fd3);
1671 	id4 = id_from_prog_fd(fd4);
1672 
1673 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1674 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1675 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1676 
1677 	assert_mprog_count(target, 0);
1678 
1679 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1680 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1681 		goto cleanup;
1682 
1683 	assert_mprog_count(target, 1);
1684 
1685 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
1686 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1687 		goto cleanup1;
1688 
1689 	assert_mprog_count(target, 2);
1690 
1691 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
1692 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1693 		goto cleanup2;
1694 
1695 	assert_mprog_count(target, 3);
1696 
1697 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
1698 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1699 		goto cleanup3;
1700 
1701 	assert_mprog_count(target, 4);
1702 
1703 	optq.prog_ids = prog_ids;
1704 
1705 	memset(prog_ids, 0, sizeof(prog_ids));
1706 	optq.count = ARRAY_SIZE(prog_ids);
1707 
1708 	err = bpf_prog_query_opts(loopback, target, &optq);
1709 	if (!ASSERT_OK(err, "prog_query"))
1710 		goto cleanup4;
1711 
1712 	ASSERT_EQ(optq.count, 4, "count");
1713 	ASSERT_EQ(optq.revision, 5, "revision");
1714 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
1715 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
1716 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
1717 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
1718 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1719 
1720 	LIBBPF_OPTS_RESET(optd,
1721 		.flags = BPF_F_BEFORE,
1722 	);
1723 
1724 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1725 	ASSERT_OK(err, "prog_detach");
1726 
1727 	assert_mprog_count(target, 3);
1728 
1729 	memset(prog_ids, 0, sizeof(prog_ids));
1730 	optq.count = ARRAY_SIZE(prog_ids);
1731 
1732 	err = bpf_prog_query_opts(loopback, target, &optq);
1733 	if (!ASSERT_OK(err, "prog_query"))
1734 		goto cleanup4;
1735 
1736 	ASSERT_EQ(optq.count, 3, "count");
1737 	ASSERT_EQ(optq.revision, 6, "revision");
1738 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1739 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1740 	ASSERT_EQ(optq.prog_ids[2], id4, "prog_ids[2]");
1741 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
1742 
1743 	LIBBPF_OPTS_RESET(optd,
1744 		.flags = BPF_F_AFTER,
1745 	);
1746 
1747 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1748 	ASSERT_OK(err, "prog_detach");
1749 
1750 	assert_mprog_count(target, 2);
1751 
1752 	memset(prog_ids, 0, sizeof(prog_ids));
1753 	optq.count = ARRAY_SIZE(prog_ids);
1754 
1755 	err = bpf_prog_query_opts(loopback, target, &optq);
1756 	if (!ASSERT_OK(err, "prog_query"))
1757 		goto cleanup4;
1758 
1759 	ASSERT_EQ(optq.count, 2, "count");
1760 	ASSERT_EQ(optq.revision, 7, "revision");
1761 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1762 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1763 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1764 
1765 	LIBBPF_OPTS_RESET(optd);
1766 
1767 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1768 	ASSERT_OK(err, "prog_detach");
1769 	assert_mprog_count(target, 1);
1770 
1771 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1772 	ASSERT_OK(err, "prog_detach");
1773 	assert_mprog_count(target, 0);
1774 
1775 	LIBBPF_OPTS_RESET(optd,
1776 		.flags = BPF_F_BEFORE,
1777 	);
1778 
1779 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1780 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1781 
1782 	LIBBPF_OPTS_RESET(optd,
1783 		.flags = BPF_F_AFTER,
1784 	);
1785 
1786 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1787 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1788 	goto cleanup;
1789 
1790 cleanup4:
1791 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1792 	ASSERT_OK(err, "prog_detach");
1793 	assert_mprog_count(target, 3);
1794 
1795 cleanup3:
1796 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1797 	ASSERT_OK(err, "prog_detach");
1798 	assert_mprog_count(target, 2);
1799 
1800 cleanup2:
1801 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1802 	ASSERT_OK(err, "prog_detach");
1803 	assert_mprog_count(target, 1);
1804 
1805 cleanup1:
1806 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1807 	ASSERT_OK(err, "prog_detach");
1808 	assert_mprog_count(target, 0);
1809 
1810 cleanup:
1811 	test_tc_link__destroy(skel);
1812 }
1813 
1814 void test_ns_tc_opts_detach(void)
1815 {
1816 	test_tc_opts_detach_target(BPF_TCX_INGRESS);
1817 	test_tc_opts_detach_target(BPF_TCX_EGRESS);
1818 }
1819 
1820 static void test_tc_opts_detach_before_target(int target)
1821 {
1822 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
1823 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
1824 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1825 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
1826 	struct test_tc_link *skel;
1827 	__u32 prog_ids[5];
1828 	int err;
1829 
1830 	skel = test_tc_link__open_and_load();
1831 	if (!ASSERT_OK_PTR(skel, "skel_load"))
1832 		goto cleanup;
1833 
1834 	fd1 = bpf_program__fd(skel->progs.tc1);
1835 	fd2 = bpf_program__fd(skel->progs.tc2);
1836 	fd3 = bpf_program__fd(skel->progs.tc3);
1837 	fd4 = bpf_program__fd(skel->progs.tc4);
1838 
1839 	id1 = id_from_prog_fd(fd1);
1840 	id2 = id_from_prog_fd(fd2);
1841 	id3 = id_from_prog_fd(fd3);
1842 	id4 = id_from_prog_fd(fd4);
1843 
1844 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
1845 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
1846 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
1847 
1848 	assert_mprog_count(target, 0);
1849 
1850 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
1851 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1852 		goto cleanup;
1853 
1854 	assert_mprog_count(target, 1);
1855 
1856 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
1857 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1858 		goto cleanup1;
1859 
1860 	assert_mprog_count(target, 2);
1861 
1862 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
1863 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1864 		goto cleanup2;
1865 
1866 	assert_mprog_count(target, 3);
1867 
1868 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
1869 	if (!ASSERT_EQ(err, 0, "prog_attach"))
1870 		goto cleanup3;
1871 
1872 	assert_mprog_count(target, 4);
1873 
1874 	optq.prog_ids = prog_ids;
1875 
1876 	memset(prog_ids, 0, sizeof(prog_ids));
1877 	optq.count = ARRAY_SIZE(prog_ids);
1878 
1879 	err = bpf_prog_query_opts(loopback, target, &optq);
1880 	if (!ASSERT_OK(err, "prog_query"))
1881 		goto cleanup4;
1882 
1883 	ASSERT_EQ(optq.count, 4, "count");
1884 	ASSERT_EQ(optq.revision, 5, "revision");
1885 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
1886 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
1887 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
1888 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
1889 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1890 
1891 	LIBBPF_OPTS_RESET(optd,
1892 		.flags = BPF_F_BEFORE,
1893 		.relative_fd = fd2,
1894 	);
1895 
1896 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1897 	ASSERT_OK(err, "prog_detach");
1898 
1899 	assert_mprog_count(target, 3);
1900 
1901 	memset(prog_ids, 0, sizeof(prog_ids));
1902 	optq.count = ARRAY_SIZE(prog_ids);
1903 
1904 	err = bpf_prog_query_opts(loopback, target, &optq);
1905 	if (!ASSERT_OK(err, "prog_query"))
1906 		goto cleanup4;
1907 
1908 	ASSERT_EQ(optq.count, 3, "count");
1909 	ASSERT_EQ(optq.revision, 6, "revision");
1910 	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
1911 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
1912 	ASSERT_EQ(optq.prog_ids[2], id4, "prog_ids[2]");
1913 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
1914 
1915 	LIBBPF_OPTS_RESET(optd,
1916 		.flags = BPF_F_BEFORE,
1917 		.relative_fd = fd2,
1918 	);
1919 
1920 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1921 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1922 	assert_mprog_count(target, 3);
1923 
1924 	LIBBPF_OPTS_RESET(optd,
1925 		.flags = BPF_F_BEFORE,
1926 		.relative_fd = fd4,
1927 	);
1928 
1929 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1930 	ASSERT_EQ(err, -ERANGE, "prog_detach");
1931 	assert_mprog_count(target, 3);
1932 
1933 	LIBBPF_OPTS_RESET(optd,
1934 		.flags = BPF_F_BEFORE,
1935 		.relative_fd = fd1,
1936 	);
1937 
1938 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1939 	ASSERT_EQ(err, -ENOENT, "prog_detach");
1940 	assert_mprog_count(target, 3);
1941 
1942 	LIBBPF_OPTS_RESET(optd,
1943 		.flags = BPF_F_BEFORE,
1944 		.relative_fd = fd3,
1945 	);
1946 
1947 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1948 	ASSERT_OK(err, "prog_detach");
1949 
1950 	assert_mprog_count(target, 2);
1951 
1952 	memset(prog_ids, 0, sizeof(prog_ids));
1953 	optq.count = ARRAY_SIZE(prog_ids);
1954 
1955 	err = bpf_prog_query_opts(loopback, target, &optq);
1956 	if (!ASSERT_OK(err, "prog_query"))
1957 		goto cleanup4;
1958 
1959 	ASSERT_EQ(optq.count, 2, "count");
1960 	ASSERT_EQ(optq.revision, 7, "revision");
1961 	ASSERT_EQ(optq.prog_ids[0], id3, "prog_ids[0]");
1962 	ASSERT_EQ(optq.prog_ids[1], id4, "prog_ids[1]");
1963 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1964 
1965 	LIBBPF_OPTS_RESET(optd,
1966 		.flags = BPF_F_BEFORE,
1967 		.relative_fd = fd4,
1968 	);
1969 
1970 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1971 	ASSERT_OK(err, "prog_detach");
1972 
1973 	assert_mprog_count(target, 1);
1974 
1975 	memset(prog_ids, 0, sizeof(prog_ids));
1976 	optq.count = ARRAY_SIZE(prog_ids);
1977 
1978 	err = bpf_prog_query_opts(loopback, target, &optq);
1979 	if (!ASSERT_OK(err, "prog_query"))
1980 		goto cleanup4;
1981 
1982 	ASSERT_EQ(optq.count, 1, "count");
1983 	ASSERT_EQ(optq.revision, 8, "revision");
1984 	ASSERT_EQ(optq.prog_ids[0], id4, "prog_ids[0]");
1985 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
1986 
1987 	LIBBPF_OPTS_RESET(optd,
1988 		.flags = BPF_F_BEFORE,
1989 	);
1990 
1991 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
1992 	ASSERT_OK(err, "prog_detach");
1993 
1994 	assert_mprog_count(target, 0);
1995 	goto cleanup;
1996 
1997 cleanup4:
1998 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1999 	ASSERT_OK(err, "prog_detach");
2000 	assert_mprog_count(target, 3);
2001 
2002 cleanup3:
2003 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2004 	ASSERT_OK(err, "prog_detach");
2005 	assert_mprog_count(target, 2);
2006 
2007 cleanup2:
2008 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2009 	ASSERT_OK(err, "prog_detach");
2010 	assert_mprog_count(target, 1);
2011 
2012 cleanup1:
2013 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2014 	ASSERT_OK(err, "prog_detach");
2015 	assert_mprog_count(target, 0);
2016 
2017 cleanup:
2018 	test_tc_link__destroy(skel);
2019 }
2020 
2021 void test_ns_tc_opts_detach_before(void)
2022 {
2023 	test_tc_opts_detach_before_target(BPF_TCX_INGRESS);
2024 	test_tc_opts_detach_before_target(BPF_TCX_EGRESS);
2025 }
2026 
2027 static void test_tc_opts_detach_after_target(int target)
2028 {
2029 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2030 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2031 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
2032 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
2033 	struct test_tc_link *skel;
2034 	__u32 prog_ids[5];
2035 	int err;
2036 
2037 	skel = test_tc_link__open_and_load();
2038 	if (!ASSERT_OK_PTR(skel, "skel_load"))
2039 		goto cleanup;
2040 
2041 	fd1 = bpf_program__fd(skel->progs.tc1);
2042 	fd2 = bpf_program__fd(skel->progs.tc2);
2043 	fd3 = bpf_program__fd(skel->progs.tc3);
2044 	fd4 = bpf_program__fd(skel->progs.tc4);
2045 
2046 	id1 = id_from_prog_fd(fd1);
2047 	id2 = id_from_prog_fd(fd2);
2048 	id3 = id_from_prog_fd(fd3);
2049 	id4 = id_from_prog_fd(fd4);
2050 
2051 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
2052 	ASSERT_NEQ(id3, id4, "prog_ids_3_4");
2053 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
2054 
2055 	assert_mprog_count(target, 0);
2056 
2057 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
2058 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2059 		goto cleanup;
2060 
2061 	assert_mprog_count(target, 1);
2062 
2063 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
2064 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2065 		goto cleanup1;
2066 
2067 	assert_mprog_count(target, 2);
2068 
2069 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
2070 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2071 		goto cleanup2;
2072 
2073 	assert_mprog_count(target, 3);
2074 
2075 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
2076 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2077 		goto cleanup3;
2078 
2079 	assert_mprog_count(target, 4);
2080 
2081 	optq.prog_ids = prog_ids;
2082 
2083 	memset(prog_ids, 0, sizeof(prog_ids));
2084 	optq.count = ARRAY_SIZE(prog_ids);
2085 
2086 	err = bpf_prog_query_opts(loopback, target, &optq);
2087 	if (!ASSERT_OK(err, "prog_query"))
2088 		goto cleanup4;
2089 
2090 	ASSERT_EQ(optq.count, 4, "count");
2091 	ASSERT_EQ(optq.revision, 5, "revision");
2092 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2093 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
2094 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
2095 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
2096 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
2097 
2098 	LIBBPF_OPTS_RESET(optd,
2099 		.flags = BPF_F_AFTER,
2100 		.relative_fd = fd1,
2101 	);
2102 
2103 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2104 	ASSERT_OK(err, "prog_detach");
2105 
2106 	assert_mprog_count(target, 3);
2107 
2108 	memset(prog_ids, 0, sizeof(prog_ids));
2109 	optq.count = ARRAY_SIZE(prog_ids);
2110 
2111 	err = bpf_prog_query_opts(loopback, target, &optq);
2112 	if (!ASSERT_OK(err, "prog_query"))
2113 		goto cleanup4;
2114 
2115 	ASSERT_EQ(optq.count, 3, "count");
2116 	ASSERT_EQ(optq.revision, 6, "revision");
2117 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2118 	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
2119 	ASSERT_EQ(optq.prog_ids[2], id4, "prog_ids[2]");
2120 	ASSERT_EQ(optq.prog_ids[3], 0, "prog_ids[3]");
2121 
2122 	LIBBPF_OPTS_RESET(optd,
2123 		.flags = BPF_F_AFTER,
2124 		.relative_fd = fd1,
2125 	);
2126 
2127 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2128 	ASSERT_EQ(err, -ENOENT, "prog_detach");
2129 	assert_mprog_count(target, 3);
2130 
2131 	LIBBPF_OPTS_RESET(optd,
2132 		.flags = BPF_F_AFTER,
2133 		.relative_fd = fd4,
2134 	);
2135 
2136 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2137 	ASSERT_EQ(err, -ERANGE, "prog_detach");
2138 	assert_mprog_count(target, 3);
2139 
2140 	LIBBPF_OPTS_RESET(optd,
2141 		.flags = BPF_F_AFTER,
2142 		.relative_fd = fd3,
2143 	);
2144 
2145 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2146 	ASSERT_EQ(err, -ERANGE, "prog_detach");
2147 	assert_mprog_count(target, 3);
2148 
2149 	LIBBPF_OPTS_RESET(optd,
2150 		.flags = BPF_F_AFTER,
2151 		.relative_fd = fd1,
2152 	);
2153 
2154 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2155 	ASSERT_EQ(err, -ERANGE, "prog_detach");
2156 	assert_mprog_count(target, 3);
2157 
2158 	LIBBPF_OPTS_RESET(optd,
2159 		.flags = BPF_F_AFTER,
2160 		.relative_fd = fd1,
2161 	);
2162 
2163 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2164 	ASSERT_OK(err, "prog_detach");
2165 
2166 	assert_mprog_count(target, 2);
2167 
2168 	memset(prog_ids, 0, sizeof(prog_ids));
2169 	optq.count = ARRAY_SIZE(prog_ids);
2170 
2171 	err = bpf_prog_query_opts(loopback, target, &optq);
2172 	if (!ASSERT_OK(err, "prog_query"))
2173 		goto cleanup4;
2174 
2175 	ASSERT_EQ(optq.count, 2, "count");
2176 	ASSERT_EQ(optq.revision, 7, "revision");
2177 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2178 	ASSERT_EQ(optq.prog_ids[1], id4, "prog_ids[1]");
2179 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
2180 
2181 	LIBBPF_OPTS_RESET(optd,
2182 		.flags = BPF_F_AFTER,
2183 		.relative_fd = fd1,
2184 	);
2185 
2186 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
2187 	ASSERT_OK(err, "prog_detach");
2188 
2189 	assert_mprog_count(target, 1);
2190 
2191 	memset(prog_ids, 0, sizeof(prog_ids));
2192 	optq.count = ARRAY_SIZE(prog_ids);
2193 
2194 	err = bpf_prog_query_opts(loopback, target, &optq);
2195 	if (!ASSERT_OK(err, "prog_query"))
2196 		goto cleanup4;
2197 
2198 	ASSERT_EQ(optq.count, 1, "count");
2199 	ASSERT_EQ(optq.revision, 8, "revision");
2200 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2201 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
2202 
2203 	LIBBPF_OPTS_RESET(optd,
2204 		.flags = BPF_F_AFTER,
2205 	);
2206 
2207 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
2208 	ASSERT_OK(err, "prog_detach");
2209 
2210 	assert_mprog_count(target, 0);
2211 	goto cleanup;
2212 
2213 cleanup4:
2214 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
2215 	ASSERT_OK(err, "prog_detach");
2216 	assert_mprog_count(target, 3);
2217 
2218 cleanup3:
2219 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2220 	ASSERT_OK(err, "prog_detach");
2221 	assert_mprog_count(target, 2);
2222 
2223 cleanup2:
2224 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2225 	ASSERT_OK(err, "prog_detach");
2226 	assert_mprog_count(target, 1);
2227 
2228 cleanup1:
2229 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2230 	ASSERT_OK(err, "prog_detach");
2231 	assert_mprog_count(target, 0);
2232 
2233 cleanup:
2234 	test_tc_link__destroy(skel);
2235 }
2236 
2237 void test_ns_tc_opts_detach_after(void)
2238 {
2239 	test_tc_opts_detach_after_target(BPF_TCX_INGRESS);
2240 	test_tc_opts_detach_after_target(BPF_TCX_EGRESS);
2241 }
2242 
2243 static void test_tc_opts_delete_empty(int target, bool chain_tc_old)
2244 {
2245 	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback);
2246 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2247 	int err;
2248 
2249 	assert_mprog_count(target, 0);
2250 	if (chain_tc_old) {
2251 		tc_hook.attach_point = target == BPF_TCX_INGRESS ?
2252 				       BPF_TC_INGRESS : BPF_TC_EGRESS;
2253 		err = bpf_tc_hook_create(&tc_hook);
2254 		ASSERT_OK(err, "bpf_tc_hook_create");
2255 		assert_mprog_count(target, 0);
2256 	}
2257 	err = bpf_prog_detach_opts(0, loopback, target, &optd);
2258 	ASSERT_EQ(err, -ENOENT, "prog_detach");
2259 	if (chain_tc_old) {
2260 		tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
2261 		bpf_tc_hook_destroy(&tc_hook);
2262 	}
2263 	assert_mprog_count(target, 0);
2264 }
2265 
2266 void test_ns_tc_opts_delete_empty(void)
2267 {
2268 	test_tc_opts_delete_empty(BPF_TCX_INGRESS, false);
2269 	test_tc_opts_delete_empty(BPF_TCX_EGRESS, false);
2270 	test_tc_opts_delete_empty(BPF_TCX_INGRESS, true);
2271 	test_tc_opts_delete_empty(BPF_TCX_EGRESS, true);
2272 }
2273 
2274 static void test_tc_chain_mixed(int target)
2275 {
2276 	LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
2277 	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback);
2278 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2279 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2280 	__u32 fd1, fd2, fd3, id1, id2, id3;
2281 	struct test_tc_link *skel;
2282 	int err, detach_fd;
2283 
2284 	skel = test_tc_link__open_and_load();
2285 	if (!ASSERT_OK_PTR(skel, "skel_load"))
2286 		goto cleanup;
2287 
2288 	fd1 = bpf_program__fd(skel->progs.tc4);
2289 	fd2 = bpf_program__fd(skel->progs.tc5);
2290 	fd3 = bpf_program__fd(skel->progs.tc6);
2291 
2292 	id1 = id_from_prog_fd(fd1);
2293 	id2 = id_from_prog_fd(fd2);
2294 	id3 = id_from_prog_fd(fd3);
2295 
2296 	ASSERT_NEQ(id1, id2, "prog_ids_1_2");
2297 	ASSERT_NEQ(id2, id3, "prog_ids_2_3");
2298 
2299 	assert_mprog_count(target, 0);
2300 
2301 	tc_hook.attach_point = target == BPF_TCX_INGRESS ?
2302 			       BPF_TC_INGRESS : BPF_TC_EGRESS;
2303 	err = bpf_tc_hook_create(&tc_hook);
2304 	err = err == -EEXIST ? 0 : err;
2305 	if (!ASSERT_OK(err, "bpf_tc_hook_create"))
2306 		goto cleanup;
2307 
2308 	tc_opts.prog_fd = fd2;
2309 	err = bpf_tc_attach(&tc_hook, &tc_opts);
2310 	if (!ASSERT_OK(err, "bpf_tc_attach"))
2311 		goto cleanup_hook;
2312 
2313 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
2314 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2315 		goto cleanup_filter;
2316 
2317 	detach_fd = fd3;
2318 
2319 	assert_mprog_count(target, 1);
2320 
2321 	tc_skel_reset_all_seen(skel);
2322 	ASSERT_OK(system(ping_cmd), ping_cmd);
2323 
2324 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
2325 	ASSERT_EQ(skel->bss->seen_tc5, false, "seen_tc5");
2326 	ASSERT_EQ(skel->bss->seen_tc6, true, "seen_tc6");
2327 
2328 	LIBBPF_OPTS_RESET(opta,
2329 		.flags = BPF_F_REPLACE,
2330 		.replace_prog_fd = fd3,
2331 	);
2332 
2333 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
2334 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2335 		goto cleanup_opts;
2336 
2337 	detach_fd = fd1;
2338 
2339 	assert_mprog_count(target, 1);
2340 
2341 	tc_skel_reset_all_seen(skel);
2342 	ASSERT_OK(system(ping_cmd), ping_cmd);
2343 
2344 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
2345 	ASSERT_EQ(skel->bss->seen_tc5, true, "seen_tc5");
2346 	ASSERT_EQ(skel->bss->seen_tc6, false, "seen_tc6");
2347 
2348 cleanup_opts:
2349 	err = bpf_prog_detach_opts(detach_fd, loopback, target, &optd);
2350 	ASSERT_OK(err, "prog_detach");
2351 	assert_mprog_count(target, 0);
2352 
2353 	tc_skel_reset_all_seen(skel);
2354 	ASSERT_OK(system(ping_cmd), ping_cmd);
2355 
2356 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
2357 	ASSERT_EQ(skel->bss->seen_tc5, true, "seen_tc5");
2358 	ASSERT_EQ(skel->bss->seen_tc6, false, "seen_tc6");
2359 
2360 cleanup_filter:
2361 	tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0;
2362 	err = bpf_tc_detach(&tc_hook, &tc_opts);
2363 	ASSERT_OK(err, "bpf_tc_detach");
2364 
2365 cleanup_hook:
2366 	tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
2367 	bpf_tc_hook_destroy(&tc_hook);
2368 
2369 cleanup:
2370 	test_tc_link__destroy(skel);
2371 }
2372 
2373 void test_ns_tc_opts_chain_mixed(void)
2374 {
2375 	test_tc_chain_mixed(BPF_TCX_INGRESS);
2376 	test_tc_chain_mixed(BPF_TCX_EGRESS);
2377 }
2378 
2379 static int generate_dummy_prog(void)
2380 {
2381 	const struct bpf_insn prog_insns[] = {
2382 		BPF_MOV64_IMM(BPF_REG_0, 0),
2383 		BPF_EXIT_INSN(),
2384 	};
2385 	const size_t prog_insn_cnt = ARRAY_SIZE(prog_insns);
2386 	LIBBPF_OPTS(bpf_prog_load_opts, opts);
2387 	const size_t log_buf_sz = 256;
2388 	char log_buf[log_buf_sz];
2389 	int fd = -1;
2390 
2391 	opts.log_buf = log_buf;
2392 	opts.log_size = log_buf_sz;
2393 
2394 	log_buf[0] = '\0';
2395 	opts.log_level = 0;
2396 	fd = bpf_prog_load(BPF_PROG_TYPE_SCHED_CLS, "tcx_prog", "GPL",
2397 			   prog_insns, prog_insn_cnt, &opts);
2398 	ASSERT_STREQ(log_buf, "", "log_0");
2399 	ASSERT_GE(fd, 0, "prog_fd");
2400 	return fd;
2401 }
2402 
2403 static void test_tc_opts_max_target(int target, int flags, bool relative)
2404 {
2405 	int err, ifindex, i, prog_fd, last_fd = -1;
2406 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2407 	const int max_progs = 63;
2408 
2409 	ASSERT_OK(system("ip link add dev tcx_opts1 type veth peer name tcx_opts2"), "add veth");
2410 	ifindex = if_nametoindex("tcx_opts1");
2411 	ASSERT_NEQ(ifindex, 0, "non_zero_ifindex");
2412 
2413 	assert_mprog_count_ifindex(ifindex, target, 0);
2414 
2415 	for (i = 0; i < max_progs; i++) {
2416 		prog_fd = generate_dummy_prog();
2417 		if (!ASSERT_GE(prog_fd, 0, "dummy_prog"))
2418 			goto cleanup;
2419 		err = bpf_prog_attach_opts(prog_fd, ifindex, target, &opta);
2420 		if (!ASSERT_EQ(err, 0, "prog_attach"))
2421 			goto cleanup;
2422 		assert_mprog_count_ifindex(ifindex, target, i + 1);
2423 		if (i == max_progs - 1 && relative)
2424 			last_fd = prog_fd;
2425 		else
2426 			close(prog_fd);
2427 	}
2428 
2429 	prog_fd = generate_dummy_prog();
2430 	if (!ASSERT_GE(prog_fd, 0, "dummy_prog"))
2431 		goto cleanup;
2432 	opta.flags = flags;
2433 	if (last_fd > 0)
2434 		opta.relative_fd = last_fd;
2435 	err = bpf_prog_attach_opts(prog_fd, ifindex, target, &opta);
2436 	ASSERT_EQ(err, -ERANGE, "prog_64_attach");
2437 	assert_mprog_count_ifindex(ifindex, target, max_progs);
2438 	close(prog_fd);
2439 cleanup:
2440 	if (last_fd > 0)
2441 		close(last_fd);
2442 	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
2443 	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
2444 	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
2445 }
2446 
2447 void test_ns_tc_opts_max(void)
2448 {
2449 	test_tc_opts_max_target(BPF_TCX_INGRESS, 0, false);
2450 	test_tc_opts_max_target(BPF_TCX_EGRESS, 0, false);
2451 
2452 	test_tc_opts_max_target(BPF_TCX_INGRESS, BPF_F_BEFORE, false);
2453 	test_tc_opts_max_target(BPF_TCX_EGRESS, BPF_F_BEFORE, true);
2454 
2455 	test_tc_opts_max_target(BPF_TCX_INGRESS, BPF_F_AFTER, true);
2456 	test_tc_opts_max_target(BPF_TCX_EGRESS, BPF_F_AFTER, false);
2457 }
2458 
2459 static void test_tc_opts_query_target(int target)
2460 {
2461 	const size_t attr_size = offsetofend(union bpf_attr, query);
2462 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2463 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2464 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
2465 	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
2466 	struct test_tc_link *skel;
2467 	union bpf_attr attr;
2468 	__u32 prog_ids[10];
2469 	int err;
2470 
2471 	skel = test_tc_link__open_and_load();
2472 	if (!ASSERT_OK_PTR(skel, "skel_load"))
2473 		goto cleanup;
2474 
2475 	fd1 = bpf_program__fd(skel->progs.tc1);
2476 	fd2 = bpf_program__fd(skel->progs.tc2);
2477 	fd3 = bpf_program__fd(skel->progs.tc3);
2478 	fd4 = bpf_program__fd(skel->progs.tc4);
2479 
2480 	id1 = id_from_prog_fd(fd1);
2481 	id2 = id_from_prog_fd(fd2);
2482 	id3 = id_from_prog_fd(fd3);
2483 	id4 = id_from_prog_fd(fd4);
2484 
2485 	assert_mprog_count(target, 0);
2486 
2487 	LIBBPF_OPTS_RESET(opta,
2488 		.expected_revision = 1,
2489 	);
2490 
2491 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
2492 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2493 		goto cleanup;
2494 
2495 	assert_mprog_count(target, 1);
2496 
2497 	LIBBPF_OPTS_RESET(opta,
2498 		.expected_revision = 2,
2499 	);
2500 
2501 	err = bpf_prog_attach_opts(fd2, loopback, target, &opta);
2502 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2503 		goto cleanup1;
2504 
2505 	assert_mprog_count(target, 2);
2506 
2507 	LIBBPF_OPTS_RESET(opta,
2508 		.expected_revision = 3,
2509 	);
2510 
2511 	err = bpf_prog_attach_opts(fd3, loopback, target, &opta);
2512 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2513 		goto cleanup2;
2514 
2515 	assert_mprog_count(target, 3);
2516 
2517 	LIBBPF_OPTS_RESET(opta,
2518 		.expected_revision = 4,
2519 	);
2520 
2521 	err = bpf_prog_attach_opts(fd4, loopback, target, &opta);
2522 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2523 		goto cleanup3;
2524 
2525 	assert_mprog_count(target, 4);
2526 
2527 	/* Test 1: Double query via libbpf API */
2528 	err = bpf_prog_query_opts(loopback, target, &optq);
2529 	if (!ASSERT_OK(err, "prog_query"))
2530 		goto cleanup4;
2531 
2532 	ASSERT_EQ(optq.count, 4, "count");
2533 	ASSERT_EQ(optq.revision, 5, "revision");
2534 	ASSERT_EQ(optq.prog_ids, NULL, "prog_ids");
2535 	ASSERT_EQ(optq.link_ids, NULL, "link_ids");
2536 
2537 	memset(prog_ids, 0, sizeof(prog_ids));
2538 	optq.prog_ids = prog_ids;
2539 
2540 	err = bpf_prog_query_opts(loopback, target, &optq);
2541 	if (!ASSERT_OK(err, "prog_query"))
2542 		goto cleanup4;
2543 
2544 	ASSERT_EQ(optq.count, 4, "count");
2545 	ASSERT_EQ(optq.revision, 5, "revision");
2546 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2547 	ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]");
2548 	ASSERT_EQ(optq.prog_ids[2], id3, "prog_ids[2]");
2549 	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
2550 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
2551 	ASSERT_EQ(optq.link_ids, NULL, "link_ids");
2552 
2553 	/* Test 2: Double query via bpf_attr & bpf(2) directly */
2554 	memset(&attr, 0, attr_size);
2555 	attr.query.target_ifindex = loopback;
2556 	attr.query.attach_type = target;
2557 
2558 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2559 	if (!ASSERT_OK(err, "prog_query"))
2560 		goto cleanup4;
2561 
2562 	ASSERT_EQ(attr.query.count, 4, "count");
2563 	ASSERT_EQ(attr.query.revision, 5, "revision");
2564 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2565 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2566 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2567 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2568 	ASSERT_EQ(attr.query.prog_ids, 0, "prog_ids");
2569 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2570 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2571 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2572 
2573 	memset(prog_ids, 0, sizeof(prog_ids));
2574 	attr.query.prog_ids = ptr_to_u64(prog_ids);
2575 
2576 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2577 	if (!ASSERT_OK(err, "prog_query"))
2578 		goto cleanup4;
2579 
2580 	ASSERT_EQ(attr.query.count, 4, "count");
2581 	ASSERT_EQ(attr.query.revision, 5, "revision");
2582 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2583 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2584 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2585 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2586 	ASSERT_EQ(attr.query.prog_ids, ptr_to_u64(prog_ids), "prog_ids");
2587 	ASSERT_EQ(prog_ids[0], id1, "prog_ids[0]");
2588 	ASSERT_EQ(prog_ids[1], id2, "prog_ids[1]");
2589 	ASSERT_EQ(prog_ids[2], id3, "prog_ids[2]");
2590 	ASSERT_EQ(prog_ids[3], id4, "prog_ids[3]");
2591 	ASSERT_EQ(prog_ids[4], 0, "prog_ids[4]");
2592 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2593 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2594 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2595 
2596 	/* Test 3: Query with smaller prog_ids array */
2597 	memset(&attr, 0, attr_size);
2598 	attr.query.target_ifindex = loopback;
2599 	attr.query.attach_type = target;
2600 
2601 	memset(prog_ids, 0, sizeof(prog_ids));
2602 	attr.query.prog_ids = ptr_to_u64(prog_ids);
2603 	attr.query.count = 2;
2604 
2605 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2606 	ASSERT_EQ(err, -1, "prog_query_should_fail");
2607 	ASSERT_EQ(errno, ENOSPC, "prog_query_should_fail");
2608 
2609 	ASSERT_EQ(attr.query.count, 4, "count");
2610 	ASSERT_EQ(attr.query.revision, 5, "revision");
2611 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2612 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2613 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2614 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2615 	ASSERT_EQ(attr.query.prog_ids, ptr_to_u64(prog_ids), "prog_ids");
2616 	ASSERT_EQ(prog_ids[0], id1, "prog_ids[0]");
2617 	ASSERT_EQ(prog_ids[1], id2, "prog_ids[1]");
2618 	ASSERT_EQ(prog_ids[2], 0, "prog_ids[2]");
2619 	ASSERT_EQ(prog_ids[3], 0, "prog_ids[3]");
2620 	ASSERT_EQ(prog_ids[4], 0, "prog_ids[4]");
2621 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2622 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2623 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2624 
2625 	/* Test 4: Query with larger prog_ids array */
2626 	memset(&attr, 0, attr_size);
2627 	attr.query.target_ifindex = loopback;
2628 	attr.query.attach_type = target;
2629 
2630 	memset(prog_ids, 0, sizeof(prog_ids));
2631 	attr.query.prog_ids = ptr_to_u64(prog_ids);
2632 	attr.query.count = 10;
2633 
2634 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2635 	if (!ASSERT_OK(err, "prog_query"))
2636 		goto cleanup4;
2637 
2638 	ASSERT_EQ(attr.query.count, 4, "count");
2639 	ASSERT_EQ(attr.query.revision, 5, "revision");
2640 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2641 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2642 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2643 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2644 	ASSERT_EQ(attr.query.prog_ids, ptr_to_u64(prog_ids), "prog_ids");
2645 	ASSERT_EQ(prog_ids[0], id1, "prog_ids[0]");
2646 	ASSERT_EQ(prog_ids[1], id2, "prog_ids[1]");
2647 	ASSERT_EQ(prog_ids[2], id3, "prog_ids[2]");
2648 	ASSERT_EQ(prog_ids[3], id4, "prog_ids[3]");
2649 	ASSERT_EQ(prog_ids[4], 0, "prog_ids[4]");
2650 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2651 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2652 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2653 
2654 	/* Test 5: Query with NULL prog_ids array but with count > 0 */
2655 	memset(&attr, 0, attr_size);
2656 	attr.query.target_ifindex = loopback;
2657 	attr.query.attach_type = target;
2658 
2659 	memset(prog_ids, 0, sizeof(prog_ids));
2660 	attr.query.count = sizeof(prog_ids);
2661 
2662 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2663 	if (!ASSERT_OK(err, "prog_query"))
2664 		goto cleanup4;
2665 
2666 	ASSERT_EQ(attr.query.count, 4, "count");
2667 	ASSERT_EQ(attr.query.revision, 5, "revision");
2668 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2669 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2670 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2671 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2672 	ASSERT_EQ(prog_ids[0], 0, "prog_ids[0]");
2673 	ASSERT_EQ(prog_ids[1], 0, "prog_ids[1]");
2674 	ASSERT_EQ(prog_ids[2], 0, "prog_ids[2]");
2675 	ASSERT_EQ(prog_ids[3], 0, "prog_ids[3]");
2676 	ASSERT_EQ(prog_ids[4], 0, "prog_ids[4]");
2677 	ASSERT_EQ(attr.query.prog_ids, 0, "prog_ids");
2678 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2679 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2680 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2681 
2682 	/* Test 6: Query with non-NULL prog_ids array but with count == 0 */
2683 	memset(&attr, 0, attr_size);
2684 	attr.query.target_ifindex = loopback;
2685 	attr.query.attach_type = target;
2686 
2687 	memset(prog_ids, 0, sizeof(prog_ids));
2688 	attr.query.prog_ids = ptr_to_u64(prog_ids);
2689 
2690 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2691 	if (!ASSERT_OK(err, "prog_query"))
2692 		goto cleanup4;
2693 
2694 	ASSERT_EQ(attr.query.count, 4, "count");
2695 	ASSERT_EQ(attr.query.revision, 5, "revision");
2696 	ASSERT_EQ(attr.query.query_flags, 0, "query_flags");
2697 	ASSERT_EQ(attr.query.attach_flags, 0, "attach_flags");
2698 	ASSERT_EQ(attr.query.target_ifindex, loopback, "target_ifindex");
2699 	ASSERT_EQ(attr.query.attach_type, target, "attach_type");
2700 	ASSERT_EQ(prog_ids[0], 0, "prog_ids[0]");
2701 	ASSERT_EQ(prog_ids[1], 0, "prog_ids[1]");
2702 	ASSERT_EQ(prog_ids[2], 0, "prog_ids[2]");
2703 	ASSERT_EQ(prog_ids[3], 0, "prog_ids[3]");
2704 	ASSERT_EQ(prog_ids[4], 0, "prog_ids[4]");
2705 	ASSERT_EQ(attr.query.prog_ids, ptr_to_u64(prog_ids), "prog_ids");
2706 	ASSERT_EQ(attr.query.prog_attach_flags, 0, "prog_attach_flags");
2707 	ASSERT_EQ(attr.query.link_ids, 0, "link_ids");
2708 	ASSERT_EQ(attr.query.link_attach_flags, 0, "link_attach_flags");
2709 
2710 	/* Test 7: Query with invalid flags */
2711 	attr.query.attach_flags = 0;
2712 	attr.query.query_flags = 1;
2713 
2714 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2715 	ASSERT_EQ(err, -1, "prog_query_should_fail");
2716 	ASSERT_EQ(errno, EINVAL, "prog_query_should_fail");
2717 
2718 	attr.query.attach_flags = 1;
2719 	attr.query.query_flags = 0;
2720 
2721 	err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, attr_size);
2722 	ASSERT_EQ(err, -1, "prog_query_should_fail");
2723 	ASSERT_EQ(errno, EINVAL, "prog_query_should_fail");
2724 
2725 cleanup4:
2726 	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
2727 	ASSERT_OK(err, "prog_detach");
2728 	assert_mprog_count(target, 3);
2729 
2730 cleanup3:
2731 	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2732 	ASSERT_OK(err, "prog_detach");
2733 	assert_mprog_count(target, 2);
2734 
2735 cleanup2:
2736 	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2737 	ASSERT_OK(err, "prog_detach");
2738 	assert_mprog_count(target, 1);
2739 
2740 cleanup1:
2741 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2742 	ASSERT_OK(err, "prog_detach");
2743 	assert_mprog_count(target, 0);
2744 
2745 cleanup:
2746 	test_tc_link__destroy(skel);
2747 }
2748 
2749 void test_ns_tc_opts_query(void)
2750 {
2751 	test_tc_opts_query_target(BPF_TCX_INGRESS);
2752 	test_tc_opts_query_target(BPF_TCX_EGRESS);
2753 }
2754 
2755 static void test_tc_opts_query_attach_target(int target)
2756 {
2757 	LIBBPF_OPTS(bpf_prog_attach_opts, opta);
2758 	LIBBPF_OPTS(bpf_prog_detach_opts, optd);
2759 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
2760 	struct test_tc_link *skel;
2761 	__u32 prog_ids[2];
2762 	__u32 fd1, id1;
2763 	int err;
2764 
2765 	skel = test_tc_link__open_and_load();
2766 	if (!ASSERT_OK_PTR(skel, "skel_load"))
2767 		goto cleanup;
2768 
2769 	fd1 = bpf_program__fd(skel->progs.tc1);
2770 	id1 = id_from_prog_fd(fd1);
2771 
2772 	err = bpf_prog_query_opts(loopback, target, &optq);
2773 	if (!ASSERT_OK(err, "prog_query"))
2774 		goto cleanup;
2775 
2776 	ASSERT_EQ(optq.count, 0, "count");
2777 	ASSERT_EQ(optq.revision, 1, "revision");
2778 
2779 	LIBBPF_OPTS_RESET(opta,
2780 		.expected_revision = optq.revision,
2781 	);
2782 
2783 	err = bpf_prog_attach_opts(fd1, loopback, target, &opta);
2784 	if (!ASSERT_EQ(err, 0, "prog_attach"))
2785 		goto cleanup;
2786 
2787 	memset(prog_ids, 0, sizeof(prog_ids));
2788 	optq.prog_ids = prog_ids;
2789 	optq.count = ARRAY_SIZE(prog_ids);
2790 
2791 	err = bpf_prog_query_opts(loopback, target, &optq);
2792 	if (!ASSERT_OK(err, "prog_query"))
2793 		goto cleanup1;
2794 
2795 	ASSERT_EQ(optq.count, 1, "count");
2796 	ASSERT_EQ(optq.revision, 2, "revision");
2797 	ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]");
2798 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
2799 
2800 cleanup1:
2801 	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2802 	ASSERT_OK(err, "prog_detach");
2803 	assert_mprog_count(target, 0);
2804 cleanup:
2805 	test_tc_link__destroy(skel);
2806 }
2807 
2808 void test_ns_tc_opts_query_attach(void)
2809 {
2810 	test_tc_opts_query_attach_target(BPF_TCX_INGRESS);
2811 	test_tc_opts_query_attach_target(BPF_TCX_EGRESS);
2812 }
2813