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