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