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