1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2023 Isovalent */
3 #include <uapi/linux/if_link.h>
4 #include <net/if.h>
5 #include <test_progs.h>
6 
7 #define loopback 1
8 #define ping_cmd "ping -q -c1 -w1 127.0.0.1 > /dev/null"
9 
10 #include "test_tc_link.skel.h"
11 #include "tc_helpers.h"
12 
13 void serial_test_tc_links_basic(void)
14 {
15 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
16 	LIBBPF_OPTS(bpf_tcx_opts, optl);
17 	__u32 prog_ids[2], link_ids[2];
18 	__u32 pid1, pid2, lid1, lid2;
19 	struct test_tc_link *skel;
20 	struct bpf_link *link;
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 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
28 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
29 
30 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
31 
32 	assert_mprog_count(BPF_TCX_INGRESS, 0);
33 	assert_mprog_count(BPF_TCX_EGRESS, 0);
34 
35 	ASSERT_EQ(skel->bss->seen_tc1, false, "seen_tc1");
36 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
37 
38 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
39 	if (!ASSERT_OK_PTR(link, "link_attach"))
40 		goto cleanup;
41 
42 	skel->links.tc1 = link;
43 
44 	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
45 
46 	assert_mprog_count(BPF_TCX_INGRESS, 1);
47 	assert_mprog_count(BPF_TCX_EGRESS, 0);
48 
49 	optq.prog_ids = prog_ids;
50 	optq.link_ids = link_ids;
51 
52 	memset(prog_ids, 0, sizeof(prog_ids));
53 	memset(link_ids, 0, sizeof(link_ids));
54 	optq.count = ARRAY_SIZE(prog_ids);
55 
56 	err = bpf_prog_query_opts(loopback, BPF_TCX_INGRESS, &optq);
57 	if (!ASSERT_OK(err, "prog_query"))
58 		goto cleanup;
59 
60 	ASSERT_EQ(optq.count, 1, "count");
61 	ASSERT_EQ(optq.revision, 2, "revision");
62 	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
63 	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
64 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
65 	ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
66 
67 	ASSERT_OK(system(ping_cmd), ping_cmd);
68 
69 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
70 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
71 
72 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
73 	if (!ASSERT_OK_PTR(link, "link_attach"))
74 		goto cleanup;
75 
76 	skel->links.tc2 = link;
77 
78 	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
79 	ASSERT_NEQ(lid1, lid2, "link_ids_1_2");
80 
81 	assert_mprog_count(BPF_TCX_INGRESS, 1);
82 	assert_mprog_count(BPF_TCX_EGRESS, 1);
83 
84 	memset(prog_ids, 0, sizeof(prog_ids));
85 	memset(link_ids, 0, sizeof(link_ids));
86 	optq.count = ARRAY_SIZE(prog_ids);
87 
88 	err = bpf_prog_query_opts(loopback, BPF_TCX_EGRESS, &optq);
89 	if (!ASSERT_OK(err, "prog_query"))
90 		goto cleanup;
91 
92 	ASSERT_EQ(optq.count, 1, "count");
93 	ASSERT_EQ(optq.revision, 2, "revision");
94 	ASSERT_EQ(optq.prog_ids[0], pid2, "prog_ids[0]");
95 	ASSERT_EQ(optq.link_ids[0], lid2, "link_ids[0]");
96 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
97 	ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
98 
99 	ASSERT_OK(system(ping_cmd), ping_cmd);
100 
101 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
102 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
103 cleanup:
104 	test_tc_link__destroy(skel);
105 
106 	assert_mprog_count(BPF_TCX_INGRESS, 0);
107 	assert_mprog_count(BPF_TCX_EGRESS, 0);
108 }
109 
110 static void test_tc_links_before_target(int target)
111 {
112 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
113 	LIBBPF_OPTS(bpf_tcx_opts, optl);
114 	__u32 prog_ids[5], link_ids[5];
115 	__u32 pid1, pid2, pid3, pid4;
116 	__u32 lid1, lid2, lid3, lid4;
117 	struct test_tc_link *skel;
118 	struct bpf_link *link;
119 	int err;
120 
121 	skel = test_tc_link__open();
122 	if (!ASSERT_OK_PTR(skel, "skel_open"))
123 		goto cleanup;
124 
125 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
126 		  0, "tc1_attach_type");
127 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
128 		  0, "tc2_attach_type");
129 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
130 		  0, "tc3_attach_type");
131 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
132 		  0, "tc4_attach_type");
133 
134 	err = test_tc_link__load(skel);
135 	if (!ASSERT_OK(err, "skel_load"))
136 		goto cleanup;
137 
138 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
139 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
140 	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
141 	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
142 
143 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
144 	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
145 	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
146 
147 	assert_mprog_count(target, 0);
148 
149 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
150 	if (!ASSERT_OK_PTR(link, "link_attach"))
151 		goto cleanup;
152 
153 	skel->links.tc1 = link;
154 
155 	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
156 
157 	assert_mprog_count(target, 1);
158 
159 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
160 	if (!ASSERT_OK_PTR(link, "link_attach"))
161 		goto cleanup;
162 
163 	skel->links.tc2 = link;
164 
165 	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
166 
167 	assert_mprog_count(target, 2);
168 
169 	optq.prog_ids = prog_ids;
170 	optq.link_ids = link_ids;
171 
172 	memset(prog_ids, 0, sizeof(prog_ids));
173 	memset(link_ids, 0, sizeof(link_ids));
174 	optq.count = ARRAY_SIZE(prog_ids);
175 
176 	err = bpf_prog_query_opts(loopback, target, &optq);
177 	if (!ASSERT_OK(err, "prog_query"))
178 		goto cleanup;
179 
180 	ASSERT_EQ(optq.count, 2, "count");
181 	ASSERT_EQ(optq.revision, 3, "revision");
182 	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
183 	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
184 	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
185 	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
186 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
187 	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
188 
189 	ASSERT_OK(system(ping_cmd), ping_cmd);
190 
191 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
192 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
193 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
194 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
195 
196 	skel->bss->seen_tc1 = false;
197 	skel->bss->seen_tc2 = false;
198 
199 	LIBBPF_OPTS_RESET(optl,
200 		.flags = BPF_F_BEFORE,
201 		.relative_fd = bpf_program__fd(skel->progs.tc2),
202 	);
203 
204 	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
205 	if (!ASSERT_OK_PTR(link, "link_attach"))
206 		goto cleanup;
207 
208 	skel->links.tc3 = link;
209 
210 	lid3 = id_from_link_fd(bpf_link__fd(skel->links.tc3));
211 
212 	LIBBPF_OPTS_RESET(optl,
213 		.flags = BPF_F_BEFORE | BPF_F_LINK,
214 		.relative_id = lid1,
215 	);
216 
217 	link = bpf_program__attach_tcx(skel->progs.tc4, loopback, &optl);
218 	if (!ASSERT_OK_PTR(link, "link_attach"))
219 		goto cleanup;
220 
221 	skel->links.tc4 = link;
222 
223 	lid4 = id_from_link_fd(bpf_link__fd(skel->links.tc4));
224 
225 	assert_mprog_count(target, 4);
226 
227 	memset(prog_ids, 0, sizeof(prog_ids));
228 	memset(link_ids, 0, sizeof(link_ids));
229 	optq.count = ARRAY_SIZE(prog_ids);
230 
231 	err = bpf_prog_query_opts(loopback, target, &optq);
232 	if (!ASSERT_OK(err, "prog_query"))
233 		goto cleanup;
234 
235 	ASSERT_EQ(optq.count, 4, "count");
236 	ASSERT_EQ(optq.revision, 5, "revision");
237 	ASSERT_EQ(optq.prog_ids[0], pid4, "prog_ids[0]");
238 	ASSERT_EQ(optq.link_ids[0], lid4, "link_ids[0]");
239 	ASSERT_EQ(optq.prog_ids[1], pid1, "prog_ids[1]");
240 	ASSERT_EQ(optq.link_ids[1], lid1, "link_ids[1]");
241 	ASSERT_EQ(optq.prog_ids[2], pid3, "prog_ids[2]");
242 	ASSERT_EQ(optq.link_ids[2], lid3, "link_ids[2]");
243 	ASSERT_EQ(optq.prog_ids[3], pid2, "prog_ids[3]");
244 	ASSERT_EQ(optq.link_ids[3], lid2, "link_ids[3]");
245 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
246 	ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
247 
248 	ASSERT_OK(system(ping_cmd), ping_cmd);
249 
250 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
251 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
252 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
253 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
254 cleanup:
255 	test_tc_link__destroy(skel);
256 	assert_mprog_count(target, 0);
257 }
258 
259 void serial_test_tc_links_before(void)
260 {
261 	test_tc_links_before_target(BPF_TCX_INGRESS);
262 	test_tc_links_before_target(BPF_TCX_EGRESS);
263 }
264 
265 static void test_tc_links_after_target(int target)
266 {
267 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
268 	LIBBPF_OPTS(bpf_tcx_opts, optl);
269 	__u32 prog_ids[5], link_ids[5];
270 	__u32 pid1, pid2, pid3, pid4;
271 	__u32 lid1, lid2, lid3, lid4;
272 	struct test_tc_link *skel;
273 	struct bpf_link *link;
274 	int err;
275 
276 	skel = test_tc_link__open();
277 	if (!ASSERT_OK_PTR(skel, "skel_open"))
278 		goto cleanup;
279 
280 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
281 		  0, "tc1_attach_type");
282 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
283 		  0, "tc2_attach_type");
284 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
285 		  0, "tc3_attach_type");
286 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
287 		  0, "tc4_attach_type");
288 
289 	err = test_tc_link__load(skel);
290 	if (!ASSERT_OK(err, "skel_load"))
291 		goto cleanup;
292 
293 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
294 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
295 	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
296 	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
297 
298 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
299 	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
300 	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
301 
302 	assert_mprog_count(target, 0);
303 
304 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
305 	if (!ASSERT_OK_PTR(link, "link_attach"))
306 		goto cleanup;
307 
308 	skel->links.tc1 = link;
309 
310 	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
311 
312 	assert_mprog_count(target, 1);
313 
314 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
315 	if (!ASSERT_OK_PTR(link, "link_attach"))
316 		goto cleanup;
317 
318 	skel->links.tc2 = link;
319 
320 	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
321 
322 	assert_mprog_count(target, 2);
323 
324 	optq.prog_ids = prog_ids;
325 	optq.link_ids = link_ids;
326 
327 	memset(prog_ids, 0, sizeof(prog_ids));
328 	memset(link_ids, 0, sizeof(link_ids));
329 	optq.count = ARRAY_SIZE(prog_ids);
330 
331 	err = bpf_prog_query_opts(loopback, target, &optq);
332 	if (!ASSERT_OK(err, "prog_query"))
333 		goto cleanup;
334 
335 	ASSERT_EQ(optq.count, 2, "count");
336 	ASSERT_EQ(optq.revision, 3, "revision");
337 	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
338 	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
339 	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
340 	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
341 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
342 	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
343 
344 	ASSERT_OK(system(ping_cmd), ping_cmd);
345 
346 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
347 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
348 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
349 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
350 
351 	skel->bss->seen_tc1 = false;
352 	skel->bss->seen_tc2 = false;
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 	ASSERT_OK(system(ping_cmd), ping_cmd);
404 
405 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
406 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
407 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
408 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
409 cleanup:
410 	test_tc_link__destroy(skel);
411 	assert_mprog_count(target, 0);
412 }
413 
414 void serial_test_tc_links_after(void)
415 {
416 	test_tc_links_after_target(BPF_TCX_INGRESS);
417 	test_tc_links_after_target(BPF_TCX_EGRESS);
418 }
419 
420 static void test_tc_links_revision_target(int target)
421 {
422 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
423 	LIBBPF_OPTS(bpf_tcx_opts, optl);
424 	__u32 prog_ids[3], link_ids[3];
425 	__u32 pid1, pid2, lid1, lid2;
426 	struct test_tc_link *skel;
427 	struct bpf_link *link;
428 	int err;
429 
430 	skel = test_tc_link__open();
431 	if (!ASSERT_OK_PTR(skel, "skel_open"))
432 		goto cleanup;
433 
434 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
435 		  0, "tc1_attach_type");
436 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
437 		  0, "tc2_attach_type");
438 
439 	err = test_tc_link__load(skel);
440 	if (!ASSERT_OK(err, "skel_load"))
441 		goto cleanup;
442 
443 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
444 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
445 
446 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
447 
448 	assert_mprog_count(target, 0);
449 
450 	optl.expected_revision = 1;
451 
452 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
453 	if (!ASSERT_OK_PTR(link, "link_attach"))
454 		goto cleanup;
455 
456 	skel->links.tc1 = link;
457 
458 	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
459 
460 	assert_mprog_count(target, 1);
461 
462 	optl.expected_revision = 1;
463 
464 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
465 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
466 		bpf_link__destroy(link);
467 		goto cleanup;
468 	}
469 
470 	assert_mprog_count(target, 1);
471 
472 	optl.expected_revision = 2;
473 
474 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
475 	if (!ASSERT_OK_PTR(link, "link_attach"))
476 		goto cleanup;
477 
478 	skel->links.tc2 = link;
479 
480 	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
481 
482 	assert_mprog_count(target, 2);
483 
484 	optq.prog_ids = prog_ids;
485 	optq.link_ids = link_ids;
486 
487 	memset(prog_ids, 0, sizeof(prog_ids));
488 	memset(link_ids, 0, sizeof(link_ids));
489 	optq.count = ARRAY_SIZE(prog_ids);
490 
491 	err = bpf_prog_query_opts(loopback, target, &optq);
492 	if (!ASSERT_OK(err, "prog_query"))
493 		goto cleanup;
494 
495 	ASSERT_EQ(optq.count, 2, "count");
496 	ASSERT_EQ(optq.revision, 3, "revision");
497 	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
498 	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
499 	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
500 	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
501 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
502 	ASSERT_EQ(optq.link_ids[2], 0, "prog_ids[2]");
503 
504 	ASSERT_OK(system(ping_cmd), ping_cmd);
505 
506 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
507 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
508 cleanup:
509 	test_tc_link__destroy(skel);
510 	assert_mprog_count(target, 0);
511 }
512 
513 void serial_test_tc_links_revision(void)
514 {
515 	test_tc_links_revision_target(BPF_TCX_INGRESS);
516 	test_tc_links_revision_target(BPF_TCX_EGRESS);
517 }
518 
519 static void test_tc_chain_classic(int target, bool chain_tc_old)
520 {
521 	LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
522 	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback);
523 	bool hook_created = false, tc_attached = false;
524 	LIBBPF_OPTS(bpf_tcx_opts, optl);
525 	__u32 pid1, pid2, pid3;
526 	struct test_tc_link *skel;
527 	struct bpf_link *link;
528 	int err;
529 
530 	skel = test_tc_link__open();
531 	if (!ASSERT_OK_PTR(skel, "skel_open"))
532 		goto cleanup;
533 
534 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
535 		  0, "tc1_attach_type");
536 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
537 		  0, "tc2_attach_type");
538 
539 	err = test_tc_link__load(skel);
540 	if (!ASSERT_OK(err, "skel_load"))
541 		goto cleanup;
542 
543 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
544 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
545 	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
546 
547 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
548 	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
549 
550 	assert_mprog_count(target, 0);
551 
552 	if (chain_tc_old) {
553 		tc_hook.attach_point = target == BPF_TCX_INGRESS ?
554 				       BPF_TC_INGRESS : BPF_TC_EGRESS;
555 		err = bpf_tc_hook_create(&tc_hook);
556 		if (err == 0)
557 			hook_created = true;
558 		err = err == -EEXIST ? 0 : err;
559 		if (!ASSERT_OK(err, "bpf_tc_hook_create"))
560 			goto cleanup;
561 
562 		tc_opts.prog_fd = bpf_program__fd(skel->progs.tc3);
563 		err = bpf_tc_attach(&tc_hook, &tc_opts);
564 		if (!ASSERT_OK(err, "bpf_tc_attach"))
565 			goto cleanup;
566 		tc_attached = true;
567 	}
568 
569 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
570 	if (!ASSERT_OK_PTR(link, "link_attach"))
571 		goto cleanup;
572 
573 	skel->links.tc1 = link;
574 
575 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
576 	if (!ASSERT_OK_PTR(link, "link_attach"))
577 		goto cleanup;
578 
579 	skel->links.tc2 = link;
580 
581 	assert_mprog_count(target, 2);
582 
583 	ASSERT_OK(system(ping_cmd), ping_cmd);
584 
585 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
586 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
587 	ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3");
588 
589 	skel->bss->seen_tc1 = false;
590 	skel->bss->seen_tc2 = false;
591 	skel->bss->seen_tc3 = false;
592 
593 	err = bpf_link__detach(skel->links.tc2);
594 	if (!ASSERT_OK(err, "prog_detach"))
595 		goto cleanup;
596 
597 	assert_mprog_count(target, 1);
598 
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 	ASSERT_OK(system(ping_cmd), ping_cmd);
710 
711 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
712 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
713 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
714 
715 	skel->bss->seen_tc1 = false;
716 	skel->bss->seen_tc2 = false;
717 	skel->bss->seen_tc3 = false;
718 
719 	LIBBPF_OPTS_RESET(optl,
720 		.flags = BPF_F_REPLACE,
721 		.relative_fd = bpf_program__fd(skel->progs.tc2),
722 		.expected_revision = 3,
723 	);
724 
725 	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
726 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
727 		bpf_link__destroy(link);
728 		goto cleanup;
729 	}
730 
731 	assert_mprog_count(target, 2);
732 
733 	LIBBPF_OPTS_RESET(optl,
734 		.flags = BPF_F_REPLACE | BPF_F_LINK,
735 		.relative_fd = bpf_link__fd(skel->links.tc2),
736 		.expected_revision = 3,
737 	);
738 
739 	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
740 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
741 		bpf_link__destroy(link);
742 		goto cleanup;
743 	}
744 
745 	assert_mprog_count(target, 2);
746 
747 	LIBBPF_OPTS_RESET(optl,
748 		.flags = BPF_F_REPLACE | BPF_F_LINK | BPF_F_AFTER,
749 		.relative_id = lid2,
750 	);
751 
752 	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
753 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
754 		bpf_link__destroy(link);
755 		goto cleanup;
756 	}
757 
758 	assert_mprog_count(target, 2);
759 
760 	err = bpf_link__update_program(skel->links.tc2, skel->progs.tc3);
761 	if (!ASSERT_OK(err, "link_update"))
762 		goto cleanup;
763 
764 	assert_mprog_count(target, 2);
765 
766 	memset(prog_ids, 0, sizeof(prog_ids));
767 	memset(link_ids, 0, sizeof(link_ids));
768 	optq.count = ARRAY_SIZE(prog_ids);
769 
770 	err = bpf_prog_query_opts(loopback, target, &optq);
771 	if (!ASSERT_OK(err, "prog_query"))
772 		goto cleanup;
773 
774 	ASSERT_EQ(optq.count, 2, "count");
775 	ASSERT_EQ(optq.revision, 4, "revision");
776 	ASSERT_EQ(optq.prog_ids[0], pid3, "prog_ids[0]");
777 	ASSERT_EQ(optq.link_ids[0], lid2, "link_ids[0]");
778 	ASSERT_EQ(optq.prog_ids[1], pid1, "prog_ids[1]");
779 	ASSERT_EQ(optq.link_ids[1], lid1, "link_ids[1]");
780 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
781 	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
782 
783 	ASSERT_OK(system(ping_cmd), ping_cmd);
784 
785 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
786 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
787 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
788 
789 	skel->bss->seen_tc1 = false;
790 	skel->bss->seen_tc2 = false;
791 	skel->bss->seen_tc3 = false;
792 
793 	err = bpf_link__detach(skel->links.tc2);
794 	if (!ASSERT_OK(err, "link_detach"))
795 		goto cleanup;
796 
797 	assert_mprog_count(target, 1);
798 
799 	memset(prog_ids, 0, sizeof(prog_ids));
800 	memset(link_ids, 0, sizeof(link_ids));
801 	optq.count = ARRAY_SIZE(prog_ids);
802 
803 	err = bpf_prog_query_opts(loopback, target, &optq);
804 	if (!ASSERT_OK(err, "prog_query"))
805 		goto cleanup;
806 
807 	ASSERT_EQ(optq.count, 1, "count");
808 	ASSERT_EQ(optq.revision, 5, "revision");
809 	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
810 	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
811 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
812 	ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
813 
814 	ASSERT_OK(system(ping_cmd), ping_cmd);
815 
816 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
817 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
818 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
819 
820 	skel->bss->seen_tc1 = false;
821 	skel->bss->seen_tc2 = false;
822 	skel->bss->seen_tc3 = false;
823 
824 	err = bpf_link__update_program(skel->links.tc1, skel->progs.tc1);
825 	if (!ASSERT_OK(err, "link_update_self"))
826 		goto cleanup;
827 
828 	assert_mprog_count(target, 1);
829 
830 	memset(prog_ids, 0, sizeof(prog_ids));
831 	memset(link_ids, 0, sizeof(link_ids));
832 	optq.count = ARRAY_SIZE(prog_ids);
833 
834 	err = bpf_prog_query_opts(loopback, target, &optq);
835 	if (!ASSERT_OK(err, "prog_query"))
836 		goto cleanup;
837 
838 	ASSERT_EQ(optq.count, 1, "count");
839 	ASSERT_EQ(optq.revision, 5, "revision");
840 	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
841 	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
842 	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
843 	ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
844 
845 	ASSERT_OK(system(ping_cmd), ping_cmd);
846 
847 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
848 	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
849 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
850 cleanup:
851 	test_tc_link__destroy(skel);
852 	assert_mprog_count(target, 0);
853 }
854 
855 void serial_test_tc_links_replace(void)
856 {
857 	test_tc_links_replace_target(BPF_TCX_INGRESS);
858 	test_tc_links_replace_target(BPF_TCX_EGRESS);
859 }
860 
861 static void test_tc_links_invalid_target(int target)
862 {
863 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
864 	LIBBPF_OPTS(bpf_tcx_opts, optl);
865 	__u32 pid1, pid2, lid1;
866 	struct test_tc_link *skel;
867 	struct bpf_link *link;
868 	int err;
869 
870 	skel = test_tc_link__open();
871 	if (!ASSERT_OK_PTR(skel, "skel_open"))
872 		goto cleanup;
873 
874 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
875 		  0, "tc1_attach_type");
876 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
877 		  0, "tc2_attach_type");
878 
879 	err = test_tc_link__load(skel);
880 	if (!ASSERT_OK(err, "skel_load"))
881 		goto cleanup;
882 
883 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
884 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
885 
886 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
887 
888 	assert_mprog_count(target, 0);
889 
890 	optl.flags = BPF_F_BEFORE | BPF_F_AFTER;
891 
892 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
893 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
894 		bpf_link__destroy(link);
895 		goto cleanup;
896 	}
897 
898 	assert_mprog_count(target, 0);
899 
900 	LIBBPF_OPTS_RESET(optl,
901 		.flags = BPF_F_BEFORE | BPF_F_ID,
902 	);
903 
904 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
905 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
906 		bpf_link__destroy(link);
907 		goto cleanup;
908 	}
909 
910 	assert_mprog_count(target, 0);
911 
912 	LIBBPF_OPTS_RESET(optl,
913 		.flags = BPF_F_AFTER | BPF_F_ID,
914 	);
915 
916 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
917 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
918 		bpf_link__destroy(link);
919 		goto cleanup;
920 	}
921 
922 	assert_mprog_count(target, 0);
923 
924 	LIBBPF_OPTS_RESET(optl,
925 		.flags = BPF_F_ID,
926 	);
927 
928 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
929 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
930 		bpf_link__destroy(link);
931 		goto cleanup;
932 	}
933 
934 	assert_mprog_count(target, 0);
935 
936 	LIBBPF_OPTS_RESET(optl,
937 		.flags = BPF_F_LINK,
938 		.relative_fd = bpf_program__fd(skel->progs.tc2),
939 	);
940 
941 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
942 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
943 		bpf_link__destroy(link);
944 		goto cleanup;
945 	}
946 
947 	assert_mprog_count(target, 0);
948 
949 	LIBBPF_OPTS_RESET(optl,
950 		.flags = BPF_F_LINK,
951 	);
952 
953 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
954 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
955 		bpf_link__destroy(link);
956 		goto cleanup;
957 	}
958 
959 	assert_mprog_count(target, 0);
960 
961 	LIBBPF_OPTS_RESET(optl,
962 		.relative_fd = bpf_program__fd(skel->progs.tc2),
963 	);
964 
965 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
966 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
967 		bpf_link__destroy(link);
968 		goto cleanup;
969 	}
970 
971 	assert_mprog_count(target, 0);
972 
973 	LIBBPF_OPTS_RESET(optl,
974 		.flags = BPF_F_BEFORE | BPF_F_AFTER,
975 		.relative_fd = bpf_program__fd(skel->progs.tc2),
976 	);
977 
978 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
979 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
980 		bpf_link__destroy(link);
981 		goto cleanup;
982 	}
983 
984 	assert_mprog_count(target, 0);
985 
986 	LIBBPF_OPTS_RESET(optl,
987 		.flags = BPF_F_BEFORE,
988 		.relative_fd = bpf_program__fd(skel->progs.tc1),
989 	);
990 
991 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
992 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
993 		bpf_link__destroy(link);
994 		goto cleanup;
995 	}
996 
997 	assert_mprog_count(target, 0);
998 
999 	LIBBPF_OPTS_RESET(optl,
1000 		.flags = BPF_F_ID,
1001 		.relative_id = pid2,
1002 	);
1003 
1004 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1005 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1006 		bpf_link__destroy(link);
1007 		goto cleanup;
1008 	}
1009 
1010 	assert_mprog_count(target, 0);
1011 
1012 	LIBBPF_OPTS_RESET(optl,
1013 		.flags = BPF_F_ID,
1014 		.relative_id = 42,
1015 	);
1016 
1017 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1018 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1019 		bpf_link__destroy(link);
1020 		goto cleanup;
1021 	}
1022 
1023 	assert_mprog_count(target, 0);
1024 
1025 	LIBBPF_OPTS_RESET(optl,
1026 		.flags = BPF_F_BEFORE,
1027 		.relative_fd = bpf_program__fd(skel->progs.tc1),
1028 	);
1029 
1030 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1031 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1032 		bpf_link__destroy(link);
1033 		goto cleanup;
1034 	}
1035 
1036 	assert_mprog_count(target, 0);
1037 
1038 	LIBBPF_OPTS_RESET(optl,
1039 		.flags = BPF_F_BEFORE | BPF_F_LINK,
1040 		.relative_fd = bpf_program__fd(skel->progs.tc1),
1041 	);
1042 
1043 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1044 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1045 		bpf_link__destroy(link);
1046 		goto cleanup;
1047 	}
1048 
1049 	assert_mprog_count(target, 0);
1050 
1051 	LIBBPF_OPTS_RESET(optl,
1052 		.flags = BPF_F_AFTER,
1053 		.relative_fd = bpf_program__fd(skel->progs.tc1),
1054 	);
1055 
1056 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1057 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1058 		bpf_link__destroy(link);
1059 		goto cleanup;
1060 	}
1061 
1062 	assert_mprog_count(target, 0);
1063 
1064 	LIBBPF_OPTS_RESET(optl);
1065 
1066 	link = bpf_program__attach_tcx(skel->progs.tc1, 0, &optl);
1067 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1068 		bpf_link__destroy(link);
1069 		goto cleanup;
1070 	}
1071 
1072 	assert_mprog_count(target, 0);
1073 
1074 	LIBBPF_OPTS_RESET(optl,
1075 		.flags = BPF_F_AFTER | BPF_F_LINK,
1076 		.relative_fd = bpf_program__fd(skel->progs.tc1),
1077 	);
1078 
1079 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1080 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1081 		bpf_link__destroy(link);
1082 		goto cleanup;
1083 	}
1084 
1085 	assert_mprog_count(target, 0);
1086 
1087 	LIBBPF_OPTS_RESET(optl);
1088 
1089 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1090 	if (!ASSERT_OK_PTR(link, "link_attach"))
1091 		goto cleanup;
1092 
1093 	skel->links.tc1 = link;
1094 
1095 	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
1096 
1097 	assert_mprog_count(target, 1);
1098 
1099 	LIBBPF_OPTS_RESET(optl,
1100 		.flags = BPF_F_AFTER | BPF_F_LINK,
1101 		.relative_fd = bpf_program__fd(skel->progs.tc1),
1102 	);
1103 
1104 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1105 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1106 		bpf_link__destroy(link);
1107 		goto cleanup;
1108 	}
1109 
1110 	assert_mprog_count(target, 1);
1111 
1112 	LIBBPF_OPTS_RESET(optl,
1113 		.flags = BPF_F_BEFORE | BPF_F_LINK | BPF_F_ID,
1114 		.relative_id = ~0,
1115 	);
1116 
1117 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1118 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1119 		bpf_link__destroy(link);
1120 		goto cleanup;
1121 	}
1122 
1123 	assert_mprog_count(target, 1);
1124 
1125 	LIBBPF_OPTS_RESET(optl,
1126 		.flags = BPF_F_BEFORE | BPF_F_LINK | BPF_F_ID,
1127 		.relative_id = lid1,
1128 	);
1129 
1130 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1131 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1132 		bpf_link__destroy(link);
1133 		goto cleanup;
1134 	}
1135 
1136 	assert_mprog_count(target, 1);
1137 
1138 	LIBBPF_OPTS_RESET(optl,
1139 		.flags = BPF_F_BEFORE | BPF_F_ID,
1140 		.relative_id = pid1,
1141 	);
1142 
1143 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1144 	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1145 		bpf_link__destroy(link);
1146 		goto cleanup;
1147 	}
1148 	assert_mprog_count(target, 1);
1149 
1150 	LIBBPF_OPTS_RESET(optl,
1151 		.flags = BPF_F_BEFORE | BPF_F_LINK | BPF_F_ID,
1152 		.relative_id = lid1,
1153 	);
1154 
1155 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1156 	if (!ASSERT_OK_PTR(link, "link_attach"))
1157 		goto cleanup;
1158 
1159 	skel->links.tc2 = link;
1160 
1161 	assert_mprog_count(target, 2);
1162 cleanup:
1163 	test_tc_link__destroy(skel);
1164 	assert_mprog_count(target, 0);
1165 }
1166 
1167 void serial_test_tc_links_invalid(void)
1168 {
1169 	test_tc_links_invalid_target(BPF_TCX_INGRESS);
1170 	test_tc_links_invalid_target(BPF_TCX_EGRESS);
1171 }
1172 
1173 static void test_tc_links_prepend_target(int target)
1174 {
1175 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1176 	LIBBPF_OPTS(bpf_tcx_opts, optl);
1177 	__u32 prog_ids[5], link_ids[5];
1178 	__u32 pid1, pid2, pid3, pid4;
1179 	__u32 lid1, lid2, lid3, lid4;
1180 	struct test_tc_link *skel;
1181 	struct bpf_link *link;
1182 	int err;
1183 
1184 	skel = test_tc_link__open();
1185 	if (!ASSERT_OK_PTR(skel, "skel_open"))
1186 		goto cleanup;
1187 
1188 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1189 		  0, "tc1_attach_type");
1190 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1191 		  0, "tc2_attach_type");
1192 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
1193 		  0, "tc3_attach_type");
1194 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1195 		  0, "tc4_attach_type");
1196 
1197 	err = test_tc_link__load(skel);
1198 	if (!ASSERT_OK(err, "skel_load"))
1199 		goto cleanup;
1200 
1201 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1202 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1203 	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1204 	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1205 
1206 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1207 	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
1208 	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1209 
1210 	assert_mprog_count(target, 0);
1211 
1212 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1213 	if (!ASSERT_OK_PTR(link, "link_attach"))
1214 		goto cleanup;
1215 
1216 	skel->links.tc1 = link;
1217 
1218 	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
1219 
1220 	assert_mprog_count(target, 1);
1221 
1222 	LIBBPF_OPTS_RESET(optl,
1223 		.flags = BPF_F_BEFORE,
1224 	);
1225 
1226 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1227 	if (!ASSERT_OK_PTR(link, "link_attach"))
1228 		goto cleanup;
1229 
1230 	skel->links.tc2 = link;
1231 
1232 	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
1233 
1234 	assert_mprog_count(target, 2);
1235 
1236 	optq.prog_ids = prog_ids;
1237 	optq.link_ids = link_ids;
1238 
1239 	memset(prog_ids, 0, sizeof(prog_ids));
1240 	memset(link_ids, 0, sizeof(link_ids));
1241 	optq.count = ARRAY_SIZE(prog_ids);
1242 
1243 	err = bpf_prog_query_opts(loopback, target, &optq);
1244 	if (!ASSERT_OK(err, "prog_query"))
1245 		goto cleanup;
1246 
1247 	ASSERT_EQ(optq.count, 2, "count");
1248 	ASSERT_EQ(optq.revision, 3, "revision");
1249 	ASSERT_EQ(optq.prog_ids[0], pid2, "prog_ids[0]");
1250 	ASSERT_EQ(optq.link_ids[0], lid2, "link_ids[0]");
1251 	ASSERT_EQ(optq.prog_ids[1], pid1, "prog_ids[1]");
1252 	ASSERT_EQ(optq.link_ids[1], lid1, "link_ids[1]");
1253 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1254 	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
1255 
1256 	ASSERT_OK(system(ping_cmd), ping_cmd);
1257 
1258 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1259 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1260 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
1261 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
1262 
1263 	skel->bss->seen_tc1 = false;
1264 	skel->bss->seen_tc2 = false;
1265 
1266 	LIBBPF_OPTS_RESET(optl,
1267 		.flags = BPF_F_BEFORE,
1268 	);
1269 
1270 	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
1271 	if (!ASSERT_OK_PTR(link, "link_attach"))
1272 		goto cleanup;
1273 
1274 	skel->links.tc3 = link;
1275 
1276 	lid3 = id_from_link_fd(bpf_link__fd(skel->links.tc3));
1277 
1278 	LIBBPF_OPTS_RESET(optl,
1279 		.flags = BPF_F_BEFORE,
1280 	);
1281 
1282 	link = bpf_program__attach_tcx(skel->progs.tc4, loopback, &optl);
1283 	if (!ASSERT_OK_PTR(link, "link_attach"))
1284 		goto cleanup;
1285 
1286 	skel->links.tc4 = link;
1287 
1288 	lid4 = id_from_link_fd(bpf_link__fd(skel->links.tc4));
1289 
1290 	assert_mprog_count(target, 4);
1291 
1292 	memset(prog_ids, 0, sizeof(prog_ids));
1293 	memset(link_ids, 0, sizeof(link_ids));
1294 	optq.count = ARRAY_SIZE(prog_ids);
1295 
1296 	err = bpf_prog_query_opts(loopback, target, &optq);
1297 	if (!ASSERT_OK(err, "prog_query"))
1298 		goto cleanup;
1299 
1300 	ASSERT_EQ(optq.count, 4, "count");
1301 	ASSERT_EQ(optq.revision, 5, "revision");
1302 	ASSERT_EQ(optq.prog_ids[0], pid4, "prog_ids[0]");
1303 	ASSERT_EQ(optq.link_ids[0], lid4, "link_ids[0]");
1304 	ASSERT_EQ(optq.prog_ids[1], pid3, "prog_ids[1]");
1305 	ASSERT_EQ(optq.link_ids[1], lid3, "link_ids[1]");
1306 	ASSERT_EQ(optq.prog_ids[2], pid2, "prog_ids[2]");
1307 	ASSERT_EQ(optq.link_ids[2], lid2, "link_ids[2]");
1308 	ASSERT_EQ(optq.prog_ids[3], pid1, "prog_ids[3]");
1309 	ASSERT_EQ(optq.link_ids[3], lid1, "link_ids[3]");
1310 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1311 	ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
1312 
1313 	ASSERT_OK(system(ping_cmd), ping_cmd);
1314 
1315 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1316 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1317 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
1318 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
1319 cleanup:
1320 	test_tc_link__destroy(skel);
1321 	assert_mprog_count(target, 0);
1322 }
1323 
1324 void serial_test_tc_links_prepend(void)
1325 {
1326 	test_tc_links_prepend_target(BPF_TCX_INGRESS);
1327 	test_tc_links_prepend_target(BPF_TCX_EGRESS);
1328 }
1329 
1330 static void test_tc_links_append_target(int target)
1331 {
1332 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1333 	LIBBPF_OPTS(bpf_tcx_opts, optl);
1334 	__u32 prog_ids[5], link_ids[5];
1335 	__u32 pid1, pid2, pid3, pid4;
1336 	__u32 lid1, lid2, lid3, lid4;
1337 	struct test_tc_link *skel;
1338 	struct bpf_link *link;
1339 	int err;
1340 
1341 	skel = test_tc_link__open();
1342 	if (!ASSERT_OK_PTR(skel, "skel_open"))
1343 		goto cleanup;
1344 
1345 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1346 		  0, "tc1_attach_type");
1347 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1348 		  0, "tc2_attach_type");
1349 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
1350 		  0, "tc3_attach_type");
1351 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1352 		  0, "tc4_attach_type");
1353 
1354 	err = test_tc_link__load(skel);
1355 	if (!ASSERT_OK(err, "skel_load"))
1356 		goto cleanup;
1357 
1358 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1359 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1360 	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1361 	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1362 
1363 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1364 	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
1365 	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1366 
1367 	assert_mprog_count(target, 0);
1368 
1369 	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1370 	if (!ASSERT_OK_PTR(link, "link_attach"))
1371 		goto cleanup;
1372 
1373 	skel->links.tc1 = link;
1374 
1375 	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
1376 
1377 	assert_mprog_count(target, 1);
1378 
1379 	LIBBPF_OPTS_RESET(optl,
1380 		.flags = BPF_F_AFTER,
1381 	);
1382 
1383 	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1384 	if (!ASSERT_OK_PTR(link, "link_attach"))
1385 		goto cleanup;
1386 
1387 	skel->links.tc2 = link;
1388 
1389 	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
1390 
1391 	assert_mprog_count(target, 2);
1392 
1393 	optq.prog_ids = prog_ids;
1394 	optq.link_ids = link_ids;
1395 
1396 	memset(prog_ids, 0, sizeof(prog_ids));
1397 	memset(link_ids, 0, sizeof(link_ids));
1398 	optq.count = ARRAY_SIZE(prog_ids);
1399 
1400 	err = bpf_prog_query_opts(loopback, target, &optq);
1401 	if (!ASSERT_OK(err, "prog_query"))
1402 		goto cleanup;
1403 
1404 	ASSERT_EQ(optq.count, 2, "count");
1405 	ASSERT_EQ(optq.revision, 3, "revision");
1406 	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
1407 	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
1408 	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
1409 	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
1410 	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1411 	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
1412 
1413 	ASSERT_OK(system(ping_cmd), ping_cmd);
1414 
1415 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1416 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1417 	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
1418 	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
1419 
1420 	skel->bss->seen_tc1 = false;
1421 	skel->bss->seen_tc2 = false;
1422 
1423 	LIBBPF_OPTS_RESET(optl,
1424 		.flags = BPF_F_AFTER,
1425 	);
1426 
1427 	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
1428 	if (!ASSERT_OK_PTR(link, "link_attach"))
1429 		goto cleanup;
1430 
1431 	skel->links.tc3 = link;
1432 
1433 	lid3 = id_from_link_fd(bpf_link__fd(skel->links.tc3));
1434 
1435 	LIBBPF_OPTS_RESET(optl,
1436 		.flags = BPF_F_AFTER,
1437 	);
1438 
1439 	link = bpf_program__attach_tcx(skel->progs.tc4, loopback, &optl);
1440 	if (!ASSERT_OK_PTR(link, "link_attach"))
1441 		goto cleanup;
1442 
1443 	skel->links.tc4 = link;
1444 
1445 	lid4 = id_from_link_fd(bpf_link__fd(skel->links.tc4));
1446 
1447 	assert_mprog_count(target, 4);
1448 
1449 	memset(prog_ids, 0, sizeof(prog_ids));
1450 	memset(link_ids, 0, sizeof(link_ids));
1451 	optq.count = ARRAY_SIZE(prog_ids);
1452 
1453 	err = bpf_prog_query_opts(loopback, target, &optq);
1454 	if (!ASSERT_OK(err, "prog_query"))
1455 		goto cleanup;
1456 
1457 	ASSERT_EQ(optq.count, 4, "count");
1458 	ASSERT_EQ(optq.revision, 5, "revision");
1459 	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
1460 	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
1461 	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
1462 	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
1463 	ASSERT_EQ(optq.prog_ids[2], pid3, "prog_ids[2]");
1464 	ASSERT_EQ(optq.link_ids[2], lid3, "link_ids[2]");
1465 	ASSERT_EQ(optq.prog_ids[3], pid4, "prog_ids[3]");
1466 	ASSERT_EQ(optq.link_ids[3], lid4, "link_ids[3]");
1467 	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1468 	ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
1469 
1470 	ASSERT_OK(system(ping_cmd), ping_cmd);
1471 
1472 	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1473 	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1474 	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
1475 	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
1476 cleanup:
1477 	test_tc_link__destroy(skel);
1478 	assert_mprog_count(target, 0);
1479 }
1480 
1481 void serial_test_tc_links_append(void)
1482 {
1483 	test_tc_links_append_target(BPF_TCX_INGRESS);
1484 	test_tc_links_append_target(BPF_TCX_EGRESS);
1485 }
1486 
1487 static void test_tc_links_dev_cleanup_target(int target)
1488 {
1489 	LIBBPF_OPTS(bpf_tcx_opts, optl);
1490 	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1491 	__u32 pid1, pid2, pid3, pid4;
1492 	struct test_tc_link *skel;
1493 	struct bpf_link *link;
1494 	int err, ifindex;
1495 
1496 	ASSERT_OK(system("ip link add dev tcx_opts1 type veth peer name tcx_opts2"), "add veth");
1497 	ifindex = if_nametoindex("tcx_opts1");
1498 	ASSERT_NEQ(ifindex, 0, "non_zero_ifindex");
1499 
1500 	skel = test_tc_link__open();
1501 	if (!ASSERT_OK_PTR(skel, "skel_open"))
1502 		goto cleanup;
1503 
1504 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1505 		  0, "tc1_attach_type");
1506 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1507 		  0, "tc2_attach_type");
1508 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
1509 		  0, "tc3_attach_type");
1510 	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1511 		  0, "tc4_attach_type");
1512 
1513 	err = test_tc_link__load(skel);
1514 	if (!ASSERT_OK(err, "skel_load"))
1515 		goto cleanup;
1516 
1517 	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1518 	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1519 	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1520 	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1521 
1522 	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1523 	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
1524 	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1525 
1526 	assert_mprog_count(target, 0);
1527 
1528 	link = bpf_program__attach_tcx(skel->progs.tc1, ifindex, &optl);
1529 	if (!ASSERT_OK_PTR(link, "link_attach"))
1530 		goto cleanup;
1531 
1532 	skel->links.tc1 = link;
1533 
1534 	assert_mprog_count_ifindex(ifindex, target, 1);
1535 
1536 	link = bpf_program__attach_tcx(skel->progs.tc2, ifindex, &optl);
1537 	if (!ASSERT_OK_PTR(link, "link_attach"))
1538 		goto cleanup;
1539 
1540 	skel->links.tc2 = link;
1541 
1542 	assert_mprog_count_ifindex(ifindex, target, 2);
1543 
1544 	link = bpf_program__attach_tcx(skel->progs.tc3, ifindex, &optl);
1545 	if (!ASSERT_OK_PTR(link, "link_attach"))
1546 		goto cleanup;
1547 
1548 	skel->links.tc3 = link;
1549 
1550 	assert_mprog_count_ifindex(ifindex, target, 3);
1551 
1552 	link = bpf_program__attach_tcx(skel->progs.tc4, ifindex, &optl);
1553 	if (!ASSERT_OK_PTR(link, "link_attach"))
1554 		goto cleanup;
1555 
1556 	skel->links.tc4 = link;
1557 
1558 	assert_mprog_count_ifindex(ifindex, target, 4);
1559 
1560 	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1561 	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1562 	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1563 
1564 	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc1)), 0, "tc1_ifindex");
1565 	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc2)), 0, "tc2_ifindex");
1566 	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc3)), 0, "tc3_ifindex");
1567 	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc4)), 0, "tc4_ifindex");
1568 
1569 	test_tc_link__destroy(skel);
1570 	return;
1571 cleanup:
1572 	test_tc_link__destroy(skel);
1573 
1574 	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1575 	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1576 	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1577 }
1578 
1579 void serial_test_tc_links_dev_cleanup(void)
1580 {
1581 	test_tc_links_dev_cleanup_target(BPF_TCX_INGRESS);
1582 	test_tc_links_dev_cleanup_target(BPF_TCX_EGRESS);
1583 }
1584