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