From 033c3cc955c70d18c796ca155cd95d5ed0bee227 Mon Sep 17 00:00:00 2001 From: Dave Thaler Date: Mon, 2 May 2022 16:40:00 -0700 Subject: [PATCH] Add more bpf() api tests (#1043) Fixes #1021 Signed-off-by: Dave Thaler --- libs/api/bpf_syscall.cpp | 4 ++ tests/unit/libbpf_test.cpp | 123 ++++++++++++++++++++++++++++++++++++- 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/libs/api/bpf_syscall.cpp b/libs/api/bpf_syscall.cpp index d190be661..597531e2a 100644 --- a/libs/api/bpf_syscall.cpp +++ b/libs/api/bpf_syscall.cpp @@ -46,6 +46,10 @@ bpf(int cmd, union bpf_attr* attr, unsigned int size) return bpf_map_update_elem(attr->map_fd, (const void*)attr->key, (const void*)attr->value, attr->flags); case BPF_OBJ_GET: CHECK_SIZE(bpf_fd); + if (attr->bpf_fd != 0) { + errno = EINVAL; + return -1; + } return bpf_obj_get((const char*)attr->pathname); case BPF_OBJ_GET_INFO_BY_FD: CHECK_SIZE(info.info_len); diff --git a/tests/unit/libbpf_test.cpp b/tests/unit/libbpf_test.cpp index e2047686a..35bbcea8d 100644 --- a/tests/unit/libbpf_test.cpp +++ b/tests/unit/libbpf_test.cpp @@ -1302,6 +1302,113 @@ TEST_CASE("enumerate link IDs", "[libbpf]") REQUIRE(errno == ENOENT); } +TEST_CASE("enumerate link IDs with bpf", "[libbpf]") +{ + _test_helper_end_to_end test_helper; + program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP); + program_info_provider_t bind_program_info(EBPF_PROGRAM_TYPE_BIND); + single_instance_hook_t xdp_hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP); + single_instance_hook_t bind_hook(EBPF_PROGRAM_TYPE_BIND, EBPF_ATTACH_TYPE_BIND); + + // Verify the enumeration is empty. + union bpf_attr attr; + memset(&attr, 0, sizeof(attr)); + REQUIRE(bpf(BPF_LINK_GET_NEXT_ID, &attr, sizeof(attr)) < 0); + REQUIRE(errno == ENOENT); + + memset(&attr, 0, sizeof(attr)); + attr.link_id = EBPF_ID_NONE; + REQUIRE(bpf(BPF_LINK_GET_NEXT_ID, &attr, sizeof(attr)) < 0); + REQUIRE(errno == ENOENT); + + // Load and attach some programs. + uint32_t ifindex = 1; + program_load_attach_helper_t xdp_helper( + "droppacket.o", EBPF_PROGRAM_TYPE_XDP, "DropPacket", EBPF_EXECUTION_JIT, &ifindex, sizeof(ifindex), xdp_hook); + program_load_attach_helper_t bind_helper( + "bindmonitor.o", EBPF_PROGRAM_TYPE_BIND, "BindMonitor", EBPF_EXECUTION_JIT, nullptr, 0, bind_hook); + + // Now enumerate the IDs. + memset(&attr, 0, sizeof(attr)); + REQUIRE(bpf(BPF_LINK_GET_NEXT_ID, &attr, sizeof(attr)) == 0); + uint32_t id1 = attr.next_id; + + memset(&attr, 0, sizeof(attr)); + attr.link_id = id1; + fd_t fd1 = bpf(BPF_LINK_GET_FD_BY_ID, &attr, sizeof(attr)); + REQUIRE(fd1 >= 0); + + REQUIRE(bpf(BPF_LINK_GET_NEXT_ID, &attr, sizeof(attr)) == 0); + uint32_t id2 = attr.next_id; + + memset(&attr, 0, sizeof(attr)); + attr.link_id = id2; + fd_t fd2 = bpf(BPF_LINK_GET_FD_BY_ID, &attr, sizeof(attr)); + REQUIRE(fd2 >= 0); + + REQUIRE(bpf(BPF_LINK_GET_NEXT_ID, &attr, sizeof(attr)) < 0); + REQUIRE(errno == ENOENT); + + // Get info on the first link. + memset(&attr, 0, sizeof(attr)); + bpf_link_info info; + attr.info.bpf_fd = fd1; + attr.info.info = (uintptr_t)&info; + attr.info.info_len = sizeof(info); + REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); + REQUIRE(info.attach_type_uuid == EBPF_ATTACH_TYPE_XDP); + REQUIRE(info.xdp.ifindex == ifindex); + + // Detach the first link. + memset(&attr, 0, sizeof(attr)); + attr.link_detach.link_fd = fd1; + REQUIRE(bpf(BPF_LINK_DETACH, &attr, sizeof(attr)) == 0); + + // Get info on the detached link. + memset(&attr, 0, sizeof(attr)); + attr.info.bpf_fd = fd1; + attr.info.info = (uintptr_t)&info; + attr.info.info_len = sizeof(info); + REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); + REQUIRE(info.attach_type_uuid == EBPF_ATTACH_TYPE_XDP); + REQUIRE(info.xdp.ifindex == 0); + + // Pin the detached link. + memset(&attr, 0, sizeof(attr)); + attr.bpf_fd = fd1; + attr.pathname = (uintptr_t) "MyPath"; + REQUIRE(bpf(BPF_OBJ_PIN, &attr, sizeof(attr)) == 0); + + // Verify that bpf_fd must be 0 when calling BPF_OBJ_GET. + REQUIRE(bpf(BPF_OBJ_GET, &attr, sizeof(attr)) < 0); + REQUIRE(errno == EINVAL); + + // Retrieve a new fd from the pin path. + attr.bpf_fd = 0; + fd_t fd3 = bpf(BPF_OBJ_GET, &attr, sizeof(attr)); + REQUIRE(fd3 > 0); + + // Get info on the new fd. + memset(&attr, 0, sizeof(attr)); + attr.info.bpf_fd = fd3; + attr.info.info = (uintptr_t)&info; + attr.info.info_len = sizeof(info); + REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); + REQUIRE(info.id == id1); + REQUIRE(info.xdp.ifindex == 0); + + // And for completeness, try an invalid bpf() call. + REQUIRE(bpf(-1, &attr, sizeof(attr)) < 0); + REQUIRE(errno == EINVAL); + + // Unpin the link. + REQUIRE(ebpf_object_unpin("MyPath") == EBPF_SUCCESS); + + Platform::_close(fd1); + Platform::_close(fd2); + Platform::_close(fd3); +} + TEST_CASE("bpf_prog_attach", "[libbpf]") { _test_helper_libbpf test_helper; @@ -1524,7 +1631,7 @@ TEST_CASE("bpf_object__load", "[libbpf]") // Test bpf() with the following command ids: // BPF_PROG_LOAD, BPF_OBJ_GET_INFO_BY_FD, BPF_PROG_GET_NEXT_ID, // BPF_MAP_CREATE, BPF_MAP_GET_NEXT_ID, BPF_PROG_BIND_MAP, -// and BPF_MAP_GET_FD_BY_ID. +// BPF_PROG_GET_FD_BY_ID, BPF_MAP_GET_FD_BY_ID, and BPF_MAP_GET_FD_BY_ID. TEST_CASE("BPF_PROG_BIND_MAP etc.", "[libbpf]") { _test_helper_libbpf test_helper; @@ -1559,6 +1666,13 @@ TEST_CASE("BPF_PROG_BIND_MAP etc.", "[libbpf]") REQUIRE(bpf(BPF_PROG_GET_NEXT_ID, &attr, sizeof(attr)) == 0); REQUIRE(attr.next_id == program_info.id); + // Verify we can convert the program id to an fd. + memset(&attr, 0, sizeof(attr)); + attr.prog_id = program_info.id; + int prog_fd2 = bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr)); + REQUIRE(prog_fd2 > 0); + Platform::_close(prog_fd2); + // Create a map. memset(&attr, 0, sizeof(attr)); attr.map_type = BPF_MAP_TYPE_ARRAY; @@ -1584,6 +1698,13 @@ TEST_CASE("BPF_PROG_BIND_MAP etc.", "[libbpf]") REQUIRE(bpf(BPF_MAP_GET_NEXT_ID, &attr, sizeof(attr)) == 0); REQUIRE(attr.next_id == map_id); + // Verify we can convert the map id to an fd. + memset(&attr, 0, sizeof(attr)); + attr.map_id = map_id; + int map_fd2 = bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr)); + REQUIRE(map_fd2 > 0); + Platform::_close(map_fd2); + // Bind it to the program. memset(&attr, 0, sizeof(attr)); attr.prog_bind_map.prog_fd = program_fd;