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