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
13void 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
93cleanup_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
100cleanup_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
107cleanup:
108	test_tc_link__destroy(skel);
109}
110
111static 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
233cleanup_target4:
234	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
235	ASSERT_OK(err, "prog_detach");
236	assert_mprog_count(target, 3);
237
238cleanup_target3:
239	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
240	ASSERT_OK(err, "prog_detach");
241	assert_mprog_count(target, 2);
242
243cleanup_target2:
244	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
245	ASSERT_OK(err, "prog_detach");
246	assert_mprog_count(target, 1);
247
248cleanup_target:
249	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
250	ASSERT_OK(err, "prog_detach");
251	assert_mprog_count(target, 0);
252
253cleanup:
254	test_tc_link__destroy(skel);
255}
256
257void 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
263static 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
385cleanup_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
404cleanup_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
422cleanup_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
439cleanup_target:
440	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
441	ASSERT_OK(err, "prog_detach");
442	assert_mprog_count(target, 0);
443
444cleanup:
445	test_tc_link__destroy(skel);
446}
447
448void 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
454static 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
537cleanup_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
546cleanup_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
553cleanup:
554	test_tc_link__destroy(skel);
555}
556
557void 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
563static 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
638cleanup_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);
644cleanup:
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
658void 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
666static 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
851cleanup_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
856cleanup_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
863cleanup:
864	test_tc_link__destroy(skel);
865}
866
867void 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
873static 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);
1016cleanup:
1017	test_tc_link__destroy(skel);
1018}
1019
1020void 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
1026static 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
1136cleanup_target4:
1137	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1138	ASSERT_OK(err, "prog_detach");
1139	assert_mprog_count(target, 3);
1140
1141cleanup_target3:
1142	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1143	ASSERT_OK(err, "prog_detach");
1144	assert_mprog_count(target, 2);
1145
1146cleanup_target2:
1147	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1148	ASSERT_OK(err, "prog_detach");
1149	assert_mprog_count(target, 1);
1150
1151cleanup_target:
1152	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1153	ASSERT_OK(err, "prog_detach");
1154	assert_mprog_count(target, 0);
1155
1156cleanup:
1157	test_tc_link__destroy(skel);
1158}
1159
1160void 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
1166static 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
1276cleanup_target4:
1277	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1278	ASSERT_OK(err, "prog_detach");
1279	assert_mprog_count(target, 3);
1280
1281cleanup_target3:
1282	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1283	ASSERT_OK(err, "prog_detach");
1284	assert_mprog_count(target, 2);
1285
1286cleanup_target2:
1287	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1288	ASSERT_OK(err, "prog_detach");
1289	assert_mprog_count(target, 1);
1290
1291cleanup_target:
1292	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1293	ASSERT_OK(err, "prog_detach");
1294	assert_mprog_count(target, 0);
1295
1296cleanup:
1297	test_tc_link__destroy(skel);
1298}
1299
1300void 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
1306static 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;
1367cleanup3:
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);
1372cleanup2:
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);
1377cleanup1:
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);
1382cleanup:
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
1390void 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
1396static 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
1556cleanup1:
1557	err = bpf_prog_detach_opts(detach_fd, loopback, target, &optd);
1558	ASSERT_OK(err, "prog_detach");
1559	assert_mprog_count(target, 2);
1560
1561cleanup:
1562	test_tc_link__destroy(skel);
1563	assert_mprog_count(target, 0);
1564}
1565
1566void 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
1572static 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
1634cleanup1:
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
1640cleanup:
1641	test_tc_link__destroy(skel);
1642	assert_mprog_count(target, 0);
1643}
1644
1645void 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
1651static 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
1792cleanup4:
1793	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
1794	ASSERT_OK(err, "prog_detach");
1795	assert_mprog_count(target, 3);
1796
1797cleanup3:
1798	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
1799	ASSERT_OK(err, "prog_detach");
1800	assert_mprog_count(target, 2);
1801
1802cleanup2:
1803	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
1804	ASSERT_OK(err, "prog_detach");
1805	assert_mprog_count(target, 1);
1806
1807cleanup1:
1808	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
1809	ASSERT_OK(err, "prog_detach");
1810	assert_mprog_count(target, 0);
1811
1812cleanup:
1813	test_tc_link__destroy(skel);
1814}
1815
1816void 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
1822static 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
1999cleanup4:
2000	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
2001	ASSERT_OK(err, "prog_detach");
2002	assert_mprog_count(target, 3);
2003
2004cleanup3:
2005	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2006	ASSERT_OK(err, "prog_detach");
2007	assert_mprog_count(target, 2);
2008
2009cleanup2:
2010	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2011	ASSERT_OK(err, "prog_detach");
2012	assert_mprog_count(target, 1);
2013
2014cleanup1:
2015	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2016	ASSERT_OK(err, "prog_detach");
2017	assert_mprog_count(target, 0);
2018
2019cleanup:
2020	test_tc_link__destroy(skel);
2021}
2022
2023void 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
2029static 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
2215cleanup4:
2216	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
2217	ASSERT_OK(err, "prog_detach");
2218	assert_mprog_count(target, 3);
2219
2220cleanup3:
2221	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2222	ASSERT_OK(err, "prog_detach");
2223	assert_mprog_count(target, 2);
2224
2225cleanup2:
2226	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2227	ASSERT_OK(err, "prog_detach");
2228	assert_mprog_count(target, 1);
2229
2230cleanup1:
2231	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2232	ASSERT_OK(err, "prog_detach");
2233	assert_mprog_count(target, 0);
2234
2235cleanup:
2236	test_tc_link__destroy(skel);
2237}
2238
2239void 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
2245static 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
2268void 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
2276static 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
2350cleanup_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
2362cleanup_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
2367cleanup_hook:
2368	tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
2369	bpf_tc_hook_destroy(&tc_hook);
2370
2371cleanup:
2372	test_tc_link__destroy(skel);
2373}
2374
2375void 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
2381static 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
2405static 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);
2441cleanup:
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
2449void 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
2461static 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
2727cleanup4:
2728	err = bpf_prog_detach_opts(fd4, loopback, target, &optd);
2729	ASSERT_OK(err, "prog_detach");
2730	assert_mprog_count(target, 3);
2731
2732cleanup3:
2733	err = bpf_prog_detach_opts(fd3, loopback, target, &optd);
2734	ASSERT_OK(err, "prog_detach");
2735	assert_mprog_count(target, 2);
2736
2737cleanup2:
2738	err = bpf_prog_detach_opts(fd2, loopback, target, &optd);
2739	ASSERT_OK(err, "prog_detach");
2740	assert_mprog_count(target, 1);
2741
2742cleanup1:
2743	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2744	ASSERT_OK(err, "prog_detach");
2745	assert_mprog_count(target, 0);
2746
2747cleanup:
2748	test_tc_link__destroy(skel);
2749}
2750
2751void 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
2757static 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
2802cleanup1:
2803	err = bpf_prog_detach_opts(fd1, loopback, target, &optd);
2804	ASSERT_OK(err, "prog_detach");
2805	assert_mprog_count(target, 0);
2806cleanup:
2807	test_tc_link__destroy(skel);
2808}
2809
2810void 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