679 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Tests for attaching, detaching, and replacing flow_dissector BPF program.4 */5 6#define _GNU_SOURCE7#include <errno.h>8#include <fcntl.h>9#include <sched.h>10#include <stdbool.h>11#include <sys/stat.h>12#include <unistd.h>13 14#include <linux/bpf.h>15#include <bpf/bpf.h>16 17#include "test_progs.h"18 19static int init_net = -1;20 21static __u32 query_attached_prog_id(int netns)22{23 __u32 prog_ids[1] = {};24 __u32 prog_cnt = ARRAY_SIZE(prog_ids);25 int err;26 27 err = bpf_prog_query(netns, BPF_FLOW_DISSECTOR, 0, NULL,28 prog_ids, &prog_cnt);29 if (CHECK_FAIL(err)) {30 perror("bpf_prog_query");31 return 0;32 }33 34 return prog_cnt == 1 ? prog_ids[0] : 0;35}36 37static bool prog_is_attached(int netns)38{39 return query_attached_prog_id(netns) > 0;40}41 42static int load_prog(enum bpf_prog_type type)43{44 struct bpf_insn prog[] = {45 BPF_MOV64_IMM(BPF_REG_0, BPF_OK),46 BPF_EXIT_INSN(),47 };48 int fd;49 50 fd = bpf_test_load_program(type, prog, ARRAY_SIZE(prog), "GPL", 0, NULL, 0);51 if (CHECK_FAIL(fd < 0))52 perror("bpf_test_load_program");53 54 return fd;55}56 57static __u32 query_prog_id(int prog)58{59 struct bpf_prog_info info = {};60 __u32 info_len = sizeof(info);61 int err;62 63 err = bpf_prog_get_info_by_fd(prog, &info, &info_len);64 if (CHECK_FAIL(err || info_len != sizeof(info))) {65 perror("bpf_prog_get_info_by_fd");66 return 0;67 }68 69 return info.id;70}71 72static int unshare_net(int old_net)73{74 int err, new_net;75 76 err = unshare(CLONE_NEWNET);77 if (CHECK_FAIL(err)) {78 perror("unshare(CLONE_NEWNET)");79 return -1;80 }81 new_net = open("/proc/self/ns/net", O_RDONLY);82 if (CHECK_FAIL(new_net < 0)) {83 perror("open(/proc/self/ns/net)");84 setns(old_net, CLONE_NEWNET);85 return -1;86 }87 return new_net;88}89 90static void test_prog_attach_prog_attach(int netns, int prog1, int prog2)91{92 int err;93 94 err = bpf_prog_attach(prog1, 0, BPF_FLOW_DISSECTOR, 0);95 if (CHECK_FAIL(err)) {96 perror("bpf_prog_attach(prog1)");97 return;98 }99 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));100 101 /* Expect success when attaching a different program */102 err = bpf_prog_attach(prog2, 0, BPF_FLOW_DISSECTOR, 0);103 if (CHECK_FAIL(err)) {104 perror("bpf_prog_attach(prog2) #1");105 goto out_detach;106 }107 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog2));108 109 /* Expect failure when attaching the same program twice */110 err = bpf_prog_attach(prog2, 0, BPF_FLOW_DISSECTOR, 0);111 if (CHECK_FAIL(!err || errno != EINVAL))112 perror("bpf_prog_attach(prog2) #2");113 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog2));114 115out_detach:116 err = bpf_prog_detach2(prog2, 0, BPF_FLOW_DISSECTOR);117 if (CHECK_FAIL(err))118 perror("bpf_prog_detach");119 CHECK_FAIL(prog_is_attached(netns));120}121 122static void test_link_create_link_create(int netns, int prog1, int prog2)123{124 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);125 int link1, link2;126 127 link1 = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &opts);128 if (CHECK_FAIL(link < 0)) {129 perror("bpf_link_create(prog1)");130 return;131 }132 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));133 134 /* Expect failure creating link when another link exists */135 errno = 0;136 link2 = bpf_link_create(prog2, netns, BPF_FLOW_DISSECTOR, &opts);137 if (CHECK_FAIL(link2 >= 0 || errno != E2BIG))138 perror("bpf_prog_attach(prog2) expected E2BIG");139 if (link2 >= 0)140 close(link2);141 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));142 143 close(link1);144 CHECK_FAIL(prog_is_attached(netns));145}146 147static void test_prog_attach_link_create(int netns, int prog1, int prog2)148{149 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);150 int err, link;151 152 err = bpf_prog_attach(prog1, 0, BPF_FLOW_DISSECTOR, 0);153 if (CHECK_FAIL(err)) {154 perror("bpf_prog_attach(prog1)");155 return;156 }157 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));158 159 /* Expect failure creating link when prog attached */160 errno = 0;161 link = bpf_link_create(prog2, netns, BPF_FLOW_DISSECTOR, &opts);162 if (CHECK_FAIL(link >= 0 || errno != EEXIST))163 perror("bpf_link_create(prog2) expected EEXIST");164 if (link >= 0)165 close(link);166 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));167 168 err = bpf_prog_detach2(prog1, 0, BPF_FLOW_DISSECTOR);169 if (CHECK_FAIL(err))170 perror("bpf_prog_detach");171 CHECK_FAIL(prog_is_attached(netns));172}173 174static void test_link_create_prog_attach(int netns, int prog1, int prog2)175{176 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);177 int err, link;178 179 link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &opts);180 if (CHECK_FAIL(link < 0)) {181 perror("bpf_link_create(prog1)");182 return;183 }184 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));185 186 /* Expect failure attaching prog when link exists */187 errno = 0;188 err = bpf_prog_attach(prog2, 0, BPF_FLOW_DISSECTOR, 0);189 if (CHECK_FAIL(!err || errno != EEXIST))190 perror("bpf_prog_attach(prog2) expected EEXIST");191 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));192 193 close(link);194 CHECK_FAIL(prog_is_attached(netns));195}196 197static void test_link_create_prog_detach(int netns, int prog1, int prog2)198{199 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);200 int err, link;201 202 link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &opts);203 if (CHECK_FAIL(link < 0)) {204 perror("bpf_link_create(prog1)");205 return;206 }207 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));208 209 /* Expect failure detaching prog when link exists */210 errno = 0;211 err = bpf_prog_detach2(prog1, 0, BPF_FLOW_DISSECTOR);212 if (CHECK_FAIL(!err || errno != EINVAL))213 perror("bpf_prog_detach expected EINVAL");214 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));215 216 close(link);217 CHECK_FAIL(prog_is_attached(netns));218}219 220static void test_prog_attach_detach_query(int netns, int prog1, int prog2)221{222 int err;223 224 err = bpf_prog_attach(prog1, 0, BPF_FLOW_DISSECTOR, 0);225 if (CHECK_FAIL(err)) {226 perror("bpf_prog_attach(prog1)");227 return;228 }229 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));230 231 err = bpf_prog_detach2(prog1, 0, BPF_FLOW_DISSECTOR);232 if (CHECK_FAIL(err)) {233 perror("bpf_prog_detach");234 return;235 }236 237 /* Expect no prog attached after successful detach */238 CHECK_FAIL(prog_is_attached(netns));239}240 241static void test_link_create_close_query(int netns, int prog1, int prog2)242{243 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);244 int link;245 246 link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &opts);247 if (CHECK_FAIL(link < 0)) {248 perror("bpf_link_create(prog1)");249 return;250 }251 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));252 253 close(link);254 /* Expect no prog attached after closing last link FD */255 CHECK_FAIL(prog_is_attached(netns));256}257 258static void test_link_update_no_old_prog(int netns, int prog1, int prog2)259{260 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);261 DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);262 int err, link;263 264 link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);265 if (CHECK_FAIL(link < 0)) {266 perror("bpf_link_create(prog1)");267 return;268 }269 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));270 271 /* Expect success replacing the prog when old prog not specified */272 update_opts.flags = 0;273 update_opts.old_prog_fd = 0;274 err = bpf_link_update(link, prog2, &update_opts);275 if (CHECK_FAIL(err))276 perror("bpf_link_update");277 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog2));278 279 close(link);280 CHECK_FAIL(prog_is_attached(netns));281}282 283static void test_link_update_replace_old_prog(int netns, int prog1, int prog2)284{285 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);286 DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);287 int err, link;288 289 link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);290 if (CHECK_FAIL(link < 0)) {291 perror("bpf_link_create(prog1)");292 return;293 }294 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));295 296 /* Expect success F_REPLACE and old prog specified to succeed */297 update_opts.flags = BPF_F_REPLACE;298 update_opts.old_prog_fd = prog1;299 err = bpf_link_update(link, prog2, &update_opts);300 if (CHECK_FAIL(err))301 perror("bpf_link_update");302 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog2));303 304 close(link);305 CHECK_FAIL(prog_is_attached(netns));306}307 308static void test_link_update_same_prog(int netns, int prog1, int prog2)309{310 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);311 DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);312 int err, link;313 314 link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);315 if (CHECK_FAIL(link < 0)) {316 perror("bpf_link_create(prog1)");317 return;318 }319 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));320 321 /* Expect success updating the prog with the same one */322 update_opts.flags = 0;323 update_opts.old_prog_fd = 0;324 err = bpf_link_update(link, prog1, &update_opts);325 if (CHECK_FAIL(err))326 perror("bpf_link_update");327 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));328 329 close(link);330 CHECK_FAIL(prog_is_attached(netns));331}332 333static void test_link_update_invalid_opts(int netns, int prog1, int prog2)334{335 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);336 DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);337 int err, link;338 339 link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);340 if (CHECK_FAIL(link < 0)) {341 perror("bpf_link_create(prog1)");342 return;343 }344 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));345 346 /* Expect update to fail w/ old prog FD but w/o F_REPLACE*/347 errno = 0;348 update_opts.flags = 0;349 update_opts.old_prog_fd = prog1;350 err = bpf_link_update(link, prog2, &update_opts);351 if (CHECK_FAIL(!err || errno != EINVAL)) {352 perror("bpf_link_update expected EINVAL");353 goto out_close;354 }355 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));356 357 /* Expect update to fail on old prog FD mismatch */358 errno = 0;359 update_opts.flags = BPF_F_REPLACE;360 update_opts.old_prog_fd = prog2;361 err = bpf_link_update(link, prog2, &update_opts);362 if (CHECK_FAIL(!err || errno != EPERM)) {363 perror("bpf_link_update expected EPERM");364 goto out_close;365 }366 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));367 368 /* Expect update to fail for invalid old prog FD */369 errno = 0;370 update_opts.flags = BPF_F_REPLACE;371 update_opts.old_prog_fd = -1;372 err = bpf_link_update(link, prog2, &update_opts);373 if (CHECK_FAIL(!err || errno != EBADF)) {374 perror("bpf_link_update expected EBADF");375 goto out_close;376 }377 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));378 379 /* Expect update to fail with invalid flags */380 errno = 0;381 update_opts.flags = BPF_F_ALLOW_MULTI;382 update_opts.old_prog_fd = 0;383 err = bpf_link_update(link, prog2, &update_opts);384 if (CHECK_FAIL(!err || errno != EINVAL))385 perror("bpf_link_update expected EINVAL");386 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));387 388out_close:389 close(link);390 CHECK_FAIL(prog_is_attached(netns));391}392 393static void test_link_update_invalid_prog(int netns, int prog1, int prog2)394{395 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);396 DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);397 int err, link, prog3;398 399 link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);400 if (CHECK_FAIL(link < 0)) {401 perror("bpf_link_create(prog1)");402 return;403 }404 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));405 406 /* Expect failure when new prog FD is not valid */407 errno = 0;408 update_opts.flags = 0;409 update_opts.old_prog_fd = 0;410 err = bpf_link_update(link, -1, &update_opts);411 if (CHECK_FAIL(!err || errno != EBADF)) {412 perror("bpf_link_update expected EINVAL");413 goto out_close_link;414 }415 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));416 417 prog3 = load_prog(BPF_PROG_TYPE_SOCKET_FILTER);418 if (prog3 < 0)419 goto out_close_link;420 421 /* Expect failure when new prog FD type doesn't match */422 errno = 0;423 update_opts.flags = 0;424 update_opts.old_prog_fd = 0;425 err = bpf_link_update(link, prog3, &update_opts);426 if (CHECK_FAIL(!err || errno != EINVAL))427 perror("bpf_link_update expected EINVAL");428 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));429 430 close(prog3);431out_close_link:432 close(link);433 CHECK_FAIL(prog_is_attached(netns));434}435 436static void test_link_update_netns_gone(int netns, int prog1, int prog2)437{438 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);439 DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);440 int err, link, old_net;441 442 old_net = netns;443 netns = unshare_net(old_net);444 if (netns < 0)445 return;446 447 link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);448 if (CHECK_FAIL(link < 0)) {449 perror("bpf_link_create(prog1)");450 return;451 }452 CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));453 454 close(netns);455 err = setns(old_net, CLONE_NEWNET);456 if (CHECK_FAIL(err)) {457 perror("setns(CLONE_NEWNET)");458 close(link);459 return;460 }461 462 /* Expect failure when netns destroyed */463 errno = 0;464 update_opts.flags = 0;465 update_opts.old_prog_fd = 0;466 err = bpf_link_update(link, prog2, &update_opts);467 if (CHECK_FAIL(!err || errno != ENOLINK))468 perror("bpf_link_update");469 470 close(link);471}472 473static void test_link_get_info(int netns, int prog1, int prog2)474{475 DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);476 DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);477 struct bpf_link_info info = {};478 struct stat netns_stat = {};479 __u32 info_len, link_id;480 int err, link, old_net;481 482 old_net = netns;483 netns = unshare_net(old_net);484 if (netns < 0)485 return;486 487 err = fstat(netns, &netns_stat);488 if (CHECK_FAIL(err)) {489 perror("stat(netns)");490 goto out_resetns;491 }492 493 link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);494 if (CHECK_FAIL(link < 0)) {495 perror("bpf_link_create(prog1)");496 goto out_resetns;497 }498 499 info_len = sizeof(info);500 err = bpf_link_get_info_by_fd(link, &info, &info_len);501 if (CHECK_FAIL(err)) {502 perror("bpf_obj_get_info");503 goto out_unlink;504 }505 CHECK_FAIL(info_len != sizeof(info));506 507 /* Expect link info to be sane and match prog and netns details */508 CHECK_FAIL(info.type != BPF_LINK_TYPE_NETNS);509 CHECK_FAIL(info.id == 0);510 CHECK_FAIL(info.prog_id != query_prog_id(prog1));511 CHECK_FAIL(info.netns.netns_ino != netns_stat.st_ino);512 CHECK_FAIL(info.netns.attach_type != BPF_FLOW_DISSECTOR);513 514 update_opts.flags = 0;515 update_opts.old_prog_fd = 0;516 err = bpf_link_update(link, prog2, &update_opts);517 if (CHECK_FAIL(err)) {518 perror("bpf_link_update(prog2)");519 goto out_unlink;520 }521 522 link_id = info.id;523 info_len = sizeof(info);524 err = bpf_link_get_info_by_fd(link, &info, &info_len);525 if (CHECK_FAIL(err)) {526 perror("bpf_obj_get_info");527 goto out_unlink;528 }529 CHECK_FAIL(info_len != sizeof(info));530 531 /* Expect no info change after update except in prog id */532 CHECK_FAIL(info.type != BPF_LINK_TYPE_NETNS);533 CHECK_FAIL(info.id != link_id);534 CHECK_FAIL(info.prog_id != query_prog_id(prog2));535 CHECK_FAIL(info.netns.netns_ino != netns_stat.st_ino);536 CHECK_FAIL(info.netns.attach_type != BPF_FLOW_DISSECTOR);537 538 /* Leave netns link is attached to and close last FD to it */539 err = setns(old_net, CLONE_NEWNET);540 if (CHECK_FAIL(err)) {541 perror("setns(NEWNET)");542 goto out_unlink;543 }544 close(netns);545 old_net = -1;546 netns = -1;547 548 info_len = sizeof(info);549 err = bpf_link_get_info_by_fd(link, &info, &info_len);550 if (CHECK_FAIL(err)) {551 perror("bpf_obj_get_info");552 goto out_unlink;553 }554 CHECK_FAIL(info_len != sizeof(info));555 556 /* Expect netns_ino to change to 0 */557 CHECK_FAIL(info.type != BPF_LINK_TYPE_NETNS);558 CHECK_FAIL(info.id != link_id);559 CHECK_FAIL(info.prog_id != query_prog_id(prog2));560 CHECK_FAIL(info.netns.netns_ino != 0);561 CHECK_FAIL(info.netns.attach_type != BPF_FLOW_DISSECTOR);562 563out_unlink:564 close(link);565out_resetns:566 if (old_net != -1)567 setns(old_net, CLONE_NEWNET);568 if (netns != -1)569 close(netns);570}571 572static void run_tests(int netns)573{574 struct test {575 const char *test_name;576 void (*test_func)(int netns, int prog1, int prog2);577 } tests[] = {578 { "prog attach, prog attach",579 test_prog_attach_prog_attach },580 { "link create, link create",581 test_link_create_link_create },582 { "prog attach, link create",583 test_prog_attach_link_create },584 { "link create, prog attach",585 test_link_create_prog_attach },586 { "link create, prog detach",587 test_link_create_prog_detach },588 { "prog attach, detach, query",589 test_prog_attach_detach_query },590 { "link create, close, query",591 test_link_create_close_query },592 { "link update no old prog",593 test_link_update_no_old_prog },594 { "link update with replace old prog",595 test_link_update_replace_old_prog },596 { "link update with same prog",597 test_link_update_same_prog },598 { "link update invalid opts",599 test_link_update_invalid_opts },600 { "link update invalid prog",601 test_link_update_invalid_prog },602 { "link update netns gone",603 test_link_update_netns_gone },604 { "link get info",605 test_link_get_info },606 };607 int i, progs[2] = { -1, -1 };608 char test_name[80];609 610 for (i = 0; i < ARRAY_SIZE(progs); i++) {611 progs[i] = load_prog(BPF_PROG_TYPE_FLOW_DISSECTOR);612 if (progs[i] < 0)613 goto out_close;614 }615 616 for (i = 0; i < ARRAY_SIZE(tests); i++) {617 snprintf(test_name, sizeof(test_name),618 "flow dissector %s%s",619 tests[i].test_name,620 netns == init_net ? " (init_net)" : "");621 if (test__start_subtest(test_name))622 tests[i].test_func(netns, progs[0], progs[1]);623 }624out_close:625 for (i = 0; i < ARRAY_SIZE(progs); i++) {626 if (progs[i] >= 0)627 CHECK_FAIL(close(progs[i]));628 }629}630 631void serial_test_flow_dissector_reattach(void)632{633 int err, new_net, saved_net;634 635 saved_net = open("/proc/self/ns/net", O_RDONLY);636 if (CHECK_FAIL(saved_net < 0)) {637 perror("open(/proc/self/ns/net");638 return;639 }640 641 init_net = open("/proc/1/ns/net", O_RDONLY);642 if (CHECK_FAIL(init_net < 0)) {643 perror("open(/proc/1/ns/net)");644 goto out_close;645 }646 647 err = setns(init_net, CLONE_NEWNET);648 if (CHECK_FAIL(err)) {649 perror("setns(/proc/1/ns/net)");650 goto out_close;651 }652 653 if (prog_is_attached(init_net)) {654 test__skip();655 printf("Can't test with flow dissector attached to init_net\n");656 goto out_setns;657 }658 659 /* First run tests in root network namespace */660 run_tests(init_net);661 662 /* Then repeat tests in a non-root namespace */663 new_net = unshare_net(init_net);664 if (new_net < 0)665 goto out_setns;666 run_tests(new_net);667 close(new_net);668 669out_setns:670 /* Move back to netns we started in. */671 err = setns(saved_net, CLONE_NEWNET);672 if (CHECK_FAIL(err))673 perror("setns(/proc/self/ns/net)");674 675out_close:676 close(init_net);677 close(saved_net);678}679