diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-15 21:50:48 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-15 21:50:48 +0000 |
commit | 1262aba43bc7983fcc4beb1c40417a7bd96f4be2 (patch) | |
tree | 1d33651c0c1c3185ce4a63b1fd27e562151400bd | |
parent | 1250962df75c600ea01c9ec4f41fdbb329451f7a (diff) | |
parent | e802bac986e70007581bfd9f3a39119bdd347121 (diff) | |
download | bpf-aml_tz3_314012010.tar.gz |
Snap for 8730993 from e802bac986e70007581bfd9f3a39119bdd347121 to mainline-tzdata3-releaseaml_tz3_314012070aml_tz3_314012050aml_tz3_314012010aml_tz3_313110000aml_tz3_312511020aml_tz3_312511010aml_tz3_312410020aml_tz3_312410010android12-mainline-tzdata3-releaseaml_tz3_314012010
Change-Id: I87601d64438fb25c154385fb00b5bf38beb49542
-rw-r--r-- | OWNERS | 7 | ||||
-rw-r--r-- | OWNERS_bpf | 5 | ||||
-rw-r--r-- | bpfloader/Android.bp | 2 | ||||
-rw-r--r-- | bpfloader/BpfLoader.cpp | 61 | ||||
-rw-r--r-- | bpfloader/bpfloader.rc | 27 | ||||
-rw-r--r-- | libbpf_android/Android.bp | 35 | ||||
-rw-r--r-- | libbpf_android/BpfLoadTest.cpp | 75 | ||||
-rw-r--r-- | libbpf_android/BpfUtils.cpp | 114 | ||||
-rw-r--r-- | libbpf_android/Loader.cpp | 292 | ||||
-rw-r--r-- | libbpf_android/TEST_MAPPING | 12 | ||||
-rw-r--r-- | libbpf_android/include/bpf/BpfMap.h | 260 | ||||
-rw-r--r-- | libbpf_android/include/bpf/BpfUtils.h | 83 | ||||
-rw-r--r-- | libbpf_android/include/libbpf_android.h | 17 | ||||
-rw-r--r-- | progs/include/bpf_helpers.h | 201 | ||||
-rw-r--r-- | progs/include/bpf_map_def.h | 177 | ||||
-rw-r--r-- | progs/include/test/mock_bpf_helpers.h | 94 |
16 files changed, 972 insertions, 490 deletions
@@ -1,2 +1,5 @@ -set noparent -file:platform/system/bpf:master:/OWNERS_bpf +connoro@google.com +lorenzo@google.com +maze@google.com +smoreland@google.com +sspatil@google.com diff --git a/OWNERS_bpf b/OWNERS_bpf deleted file mode 100644 index 094de96..0000000 --- a/OWNERS_bpf +++ /dev/null @@ -1,5 +0,0 @@ -connoro@google.com -lorenzo@google.com -maze@google.com -smoreland@google.com -sspatil@google.com diff --git a/bpfloader/Android.bp b/bpfloader/Android.bp index 4dbabfa..6582755 100644 --- a/bpfloader/Android.bp +++ b/bpfloader/Android.bp @@ -39,7 +39,6 @@ cc_binary { memtag_heap: true, }, clang: true, - header_libs: ["bpf_headers"], shared_libs: [ "libcutils", "libbpf_android", @@ -55,7 +54,6 @@ cc_binary { init_rc: ["bpfloader.rc"], required: [ - "btfloader", "time_in_state.o" ], diff --git a/bpfloader/BpfLoader.cpp b/bpfloader/BpfLoader.cpp index 739932d..7a68894 100644 --- a/bpfloader/BpfLoader.cpp +++ b/bpfloader/BpfLoader.cpp @@ -38,8 +38,6 @@ #include <sys/stat.h> #include <sys/types.h> -#include <android-base/logging.h> -#include <android-base/macros.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> @@ -53,73 +51,37 @@ using android::base::EndsWith; using std::string; -// see b/162057235. For arbitrary program types, the concern is that due to the lack of -// SELinux access controls over BPF program attachpoints, we have no way to control the -// attachment of programs to shared resources (or to detect when a shared resource -// has one BPF program replace another that is attached there) -constexpr bpf_prog_type kVendorAllowedProgTypes[] = { - BPF_PROG_TYPE_SOCKET_FILTER, -}; - -struct Location { +struct { const char* const dir; const char* const prefix; - const bpf_prog_type* allowedProgTypes = nullptr; - size_t allowedProgTypesLength = 0; -}; - -const Location locations[] = { - // Tethering mainline module: tether offload +} locations[] = { + // Tethering mainline module { .dir = "/apex/com.android.tethering/etc/bpf/", .prefix = "tethering/", }, - // Tethering mainline module (shared with netd & system server) - { - .dir = "/apex/com.android.tethering/etc/bpf/netd_shared/", - .prefix = "netd_shared/", - }, - // Tethering mainline module (shared with system server) - { - .dir = "/apex/com.android.tethering/etc/bpf/net_shared/", - .prefix = "net_shared/", - }, - // Tethering mainline module (not shared) - { - .dir = "/apex/com.android.tethering/etc/bpf/net_private/", - .prefix = "net_private/", - }, // Core operating system { .dir = "/system/etc/bpf/", .prefix = "", }, - // Vendor operating system - { - .dir = "/vendor/etc/bpf/", - .prefix = "vendor/", - .allowedProgTypes = kVendorAllowedProgTypes, - .allowedProgTypesLength = arraysize(kVendorAllowedProgTypes), - }, }; -int loadAllElfObjects(const Location& location) { +int loadAllElfObjects(const char* const progDir, const char* const prefix) { int retVal = 0; DIR* dir; struct dirent* ent; - if ((dir = opendir(location.dir)) != NULL) { + if ((dir = opendir(progDir)) != NULL) { while ((ent = readdir(dir)) != NULL) { string s = ent->d_name; if (!EndsWith(s, ".o")) continue; - string progPath(location.dir); + string progPath(progDir); progPath += s; bool critical; - int ret = android::bpf::loadProg(progPath.c_str(), &critical, location.prefix, - location.allowedProgTypes, - location.allowedProgTypesLength); + int ret = android::bpf::loadProg(progPath.c_str(), &critical, prefix); if (ret) { if (critical) retVal = ret; ALOGE("Failed to load object: %s, ret: %s", progPath.c_str(), std::strerror(-ret)); @@ -149,14 +111,11 @@ void createSysFsBpfSubDir(const char* const prefix) { } } -int main(int argc, char** argv) { - (void)argc; - android::base::InitLogging(argv, &android::base::KernelLogger); - +int main() { // Load all ELF objects, create programs and maps, and pin them - for (const auto& location : locations) { + for (const auto location : locations) { createSysFsBpfSubDir(location.prefix); - if (loadAllElfObjects(location) != 0) { + if (loadAllElfObjects(location.dir, location.prefix) != 0) { ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS FROM %s ===", location.dir); ALOGE("If this triggers reliably, you're probably missing kernel options or patches."); ALOGE("If this triggers randomly, you might be hitting some memory allocation " diff --git a/bpfloader/bpfloader.rc b/bpfloader/bpfloader.rc index 0d92cd8..3c56c43 100644 --- a/bpfloader/bpfloader.rc +++ b/bpfloader/bpfloader.rc @@ -15,9 +15,6 @@ # considered to have booted successfully. # on load_bpf_programs - # Linux 5.16-rc1 has changed the default to 2 (disabled but changeable), - # but we need 0 - write /proc/sys/kernel/unprivileged_bpf_disabled 0 # Enable the eBPF JIT -- but do note that on 64-bit kernels it is likely # already force enabled by the kernel config option BPF_JIT_ALWAYS_ON write /proc/sys/net/core/bpf_jit_enable 1 @@ -57,30 +54,6 @@ service bpfloader /system/bin/bpfloader # rlimit memlock 1073741824 1073741824 oneshot - # - # How to debug bootloops caused by 'bpfloader-failed'. - # - # 1. On some lower RAM devices (like wembley) you may need to first enable developer mode - # (from the Settings app UI), and change the developer option "Logger buffer sizes" - # from the default (wembley: 64kB) to the maximum (1M) per log buffer. - # Otherwise buffer will overflow before you manage to dump it and you'll get useless logs. - # - # 2. comment out 'reboot_on_failure reboot,bpfloader-failed' below - # 3. rebuild/reflash/reboot - # 4. as the device is booting up capture bpfloader logs via: - # adb logcat -s 'bpfloader:*' 'LibBpfLoader:*' - # - # something like: - # $ adb reboot; sleep 1; adb wait-for-device; adb root; sleep 1; adb wait-for-device; adb logcat -s 'bpfloader:*' 'LibBpfLoader:*' - # will take care of capturing logs as early as possible - # - # 5. look through the logs from the kernel's bpf verifier that bpfloader dumps out, - # it usually makes sense to search back from the end and find the particular - # bpf verifier failure that caused bpfloader to terminate early with an error code. - # This will probably be something along the lines of 'too many jumps' or - # 'cannot prove return value is 0 or 1' or 'unsupported / unknown operation / helper', - # 'invalid bpf_context access', etc. - # reboot_on_failure reboot,bpfloader-failed # we're not really updatable, but want to be able to load bpf programs shipped in apexes updatable diff --git a/libbpf_android/Android.bp b/libbpf_android/Android.bp index 1fac19d..526926a 100644 --- a/libbpf_android/Android.bp +++ b/libbpf_android/Android.bp @@ -22,6 +22,22 @@ package { default_applicable_licenses: ["system_bpf_license"], } +cc_library_headers { + name: "libbpf_android_headers", + vendor_available: false, + host_supported: false, + native_bridge_supported: true, + export_include_dirs: ["include"], + target: { + linux_bionic: { + enabled: true, + }, + }, + sdk_version: "30", + header_libs: ["bpf_syscall_wrappers"], + export_header_lib_headers: ["bpf_syscall_wrappers"], +} + cc_library { name: "libbpf_android", vendor_available: false, @@ -30,6 +46,7 @@ cc_library { target: { android: { srcs: [ + "BpfUtils.cpp", "Loader.cpp", ], sanitize: { @@ -41,18 +58,16 @@ cc_library { shared_libs: [ "libbase", "libutils", + "libprocessgroup", "liblog", "libbpf_bcc", - "libbpf_minimal", ], header_libs: [ - "bpf_headers", - ], - export_header_lib_headers: [ - "bpf_headers", + "libbpf_android_headers" ], + export_header_lib_headers: ["libbpf_android_headers"], export_shared_lib_headers: ["libbpf_bcc"], - export_include_dirs: ["include"], + local_include_dirs: ["include"], defaults: ["bpf_defaults"], cflags: [ @@ -64,8 +79,6 @@ cc_library { cc_test { name: "libbpf_load_test", - test_suites: ["general-tests"], - header_libs: ["bpf_headers"], srcs: [ "BpfLoadTest.cpp", ], @@ -79,15 +92,13 @@ cc_test { shared_libs: [ "libbpf_android", "libbpf_bcc", - "libbpf_minimal", "libbase", "liblog", "libutils", ], - data: [ - ":bpf_load_tp_prog.o", - ":bpf_load_tp_prog_btf.o", + required: [ + "bpf_load_tp_prog.o", ], require_root: true, } diff --git a/libbpf_android/BpfLoadTest.cpp b/libbpf_android/BpfLoadTest.cpp index db45da1..09cd36c 100644 --- a/libbpf_android/BpfLoadTest.cpp +++ b/libbpf_android/BpfLoadTest.cpp @@ -14,53 +14,38 @@ * limitations under the License. */ -#include <android-base/file.h> #include <android-base/macros.h> #include <gtest/gtest.h> #include <stdlib.h> #include <unistd.h> #include <iostream> -#include "bpf/BpfMap.h" -#include "bpf/BpfUtils.h" +#include "include/bpf/BpfMap.h" +#include "include/bpf/BpfUtils.h" #include "include/libbpf_android.h" -using ::testing::TestWithParam; +using ::testing::Test; + +constexpr const char tp_prog_path[] = + "/sys/fs/bpf/prog_bpf_load_tp_prog_tracepoint_sched_sched_switch"; +constexpr const char tp_map_path[] = "/sys/fs/bpf/map_bpf_load_tp_prog_cpu_pid_map"; namespace android { namespace bpf { -class BpfLoadTest : public TestWithParam<std::string> { +class BpfLoadTest : public testing::Test { protected: BpfLoadTest() {} int mProgFd; - std::string mTpProgPath; - std::string mTpNeverLoadProgPath; - std::string mTpMapPath;; void SetUp() { - mTpProgPath = "/sys/fs/bpf/prog_" + GetParam() + "_tracepoint_sched_sched_switch"; - unlink(mTpProgPath.c_str()); - - mTpNeverLoadProgPath = "/sys/fs/bpf/prog_" + GetParam() + "_tracepoint_sched_sched_wakeup"; - unlink(mTpNeverLoadProgPath.c_str()); - - mTpMapPath = "/sys/fs/bpf/map_" + GetParam() + "_cpu_pid_map"; - unlink(mTpMapPath.c_str()); + unlink(tp_prog_path); + unlink(tp_map_path); - auto progPath = android::base::GetExecutableDirectory() + "/" + GetParam() + ".o"; bool critical = true; - - bpf_prog_type kAllowed[] = { - BPF_PROG_TYPE_UNSPEC, - }; - EXPECT_EQ(android::bpf::loadProg(progPath.c_str(), &critical, "", kAllowed, - arraysize(kAllowed)), - -1); - - EXPECT_EQ(android::bpf::loadProg(progPath.c_str(), &critical), 0); + EXPECT_EQ(android::bpf::loadProg("/system/etc/bpf/bpf_load_tp_prog.o", &critical), 0); EXPECT_EQ(false, critical); - mProgFd = bpf_obj_get(mTpProgPath.c_str()); + mProgFd = bpf_obj_get(tp_prog_path); EXPECT_GT(mProgFd, 0); int ret = bpf_attach_tracepoint(mProgFd, "sched", "sched_switch"); @@ -69,14 +54,14 @@ class BpfLoadTest : public TestWithParam<std::string> { void TearDown() { close(mProgFd); - unlink(mTpProgPath.c_str()); - unlink(mTpMapPath.c_str()); + unlink(tp_prog_path); + unlink(tp_map_path); } void checkMapNonZero() { // The test program installs a tracepoint on sched:sched_switch // and expects the kernel to populate a PID corresponding to CPU - android::bpf::BpfMap<uint32_t, uint32_t> m(mTpMapPath.c_str()); + android::bpf::BpfMap<uint32_t, uint32_t> m(tp_map_path); // Wait for program to run a little sleep(1); @@ -96,39 +81,11 @@ class BpfLoadTest : public TestWithParam<std::string> { EXPECT_RESULT_OK(m.iterateWithValue(iterFunc)); EXPECT_EQ(non_zero, 1); } - - void checkMapBtf() { - // Earlier kernels lack BPF_BTF_LOAD support - if (!isAtLeastKernelVersion(4, 19, 0)) GTEST_SKIP() << "pre-4.19 kernel does not support BTF"; - - const bool haveBtf = GetParam().find("btf") != std::string::npos; - - std::string str; - EXPECT_EQ(android::base::ReadFileToString(mTpMapPath, &str), haveBtf); - if (haveBtf) EXPECT_FALSE(str.empty()); - } - - void checkKernelVersionEnforced() { - EXPECT_EQ(bpf_obj_get(mTpNeverLoadProgPath.c_str()), -1); - EXPECT_EQ(errno, ENOENT); - } }; -INSTANTIATE_TEST_SUITE_P(BpfLoadTests, BpfLoadTest, - ::testing::Values("bpf_load_tp_prog", - "bpf_load_tp_prog_btf")); - -TEST_P(BpfLoadTest, bpfCheckMap) { +TEST_F(BpfLoadTest, bpfCheckMap) { checkMapNonZero(); } -TEST_P(BpfLoadTest, bpfCheckBtf) { - checkMapBtf(); -} - -TEST_P(BpfLoadTest, bpfCheckMinKernelVersionEnforced) { - checkKernelVersionEnforced(); -} - } // namespace bpf } // namespace android diff --git a/libbpf_android/BpfUtils.cpp b/libbpf_android/BpfUtils.cpp new file mode 100644 index 0000000..8689192 --- /dev/null +++ b/libbpf_android/BpfUtils.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BpfUtils" + +#include "bpf/BpfUtils.h" + +#include <elf.h> +#include <inttypes.h> +#include <linux/bpf.h> +#include <linux/if_ether.h> +#include <linux/in.h> +#include <linux/pfkeyv2.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#include <sstream> +#include <string> + +#include <android-base/unique_fd.h> +#include <log/log.h> +#include <processgroup/processgroup.h> + +using android::base::unique_fd; + +// The buffer size for the buffer that records program loading logs, needs to be large enough for +// the largest kernel program. + +namespace android { +namespace bpf { + +uint64_t getSocketCookie(int sockFd) { + uint64_t sock_cookie; + socklen_t cookie_len = sizeof(sock_cookie); + int res = getsockopt(sockFd, SOL_SOCKET, SO_COOKIE, &sock_cookie, &cookie_len); + if (res < 0) { + res = -errno; + ALOGE("Failed to get socket cookie: %s\n", strerror(errno)); + errno = -res; + // 0 is an invalid cookie. See sock_gen_cookie. + return NONEXISTENT_COOKIE; + } + return sock_cookie; +} + +int synchronizeKernelRCU() { + // This is a temporary hack for network stats map swap on devices running + // 4.9 kernels. The kernel code of socket release on pf_key socket will + // explicitly call synchronize_rcu() which is exactly what we need. + int pfSocket = socket(AF_KEY, SOCK_RAW | SOCK_CLOEXEC, PF_KEY_V2); + + if (pfSocket < 0) { + int ret = -errno; + ALOGE("create PF_KEY socket failed: %s", strerror(errno)); + return ret; + } + + // When closing socket, synchronize_rcu() gets called in sock_release(). + if (close(pfSocket)) { + int ret = -errno; + ALOGE("failed to close the PF_KEY socket: %s", strerror(errno)); + return ret; + } + return 0; +} + +int setrlimitForTest() { + // Set the memory rlimit for the test process if the default MEMLOCK rlimit is not enough. + struct rlimit limit = { + .rlim_cur = 1073741824, // 1 GiB + .rlim_max = 1073741824, // 1 GiB + }; + int res = setrlimit(RLIMIT_MEMLOCK, &limit); + if (res) { + ALOGE("Failed to set the default MEMLOCK rlimit: %s", strerror(errno)); + } + return res; +} + +unsigned kernelVersion() { + struct utsname buf; + int ret = uname(&buf); + if (ret) return 0; + + unsigned kver_major; + unsigned kver_minor; + unsigned kver_sub; + char dummy; + ret = sscanf(buf.release, "%u.%u.%u%c", &kver_major, &kver_minor, &kver_sub, &dummy); + // Check the device kernel version + if (ret < 3) return 0; + + return KVER(kver_major, kver_minor, kver_sub); +} + +} // namespace bpf +} // namespace android diff --git a/libbpf_android/Loader.cpp b/libbpf_android/Loader.cpp index 5f2bc70..259068a 100644 --- a/libbpf_android/Loader.cpp +++ b/libbpf_android/Loader.cpp @@ -24,43 +24,33 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sysexits.h> #include <sys/stat.h> #include <sys/utsname.h> -#include <sys/wait.h> #include <unistd.h> -// This is BpfLoader v0.14 +// This is BpfLoader v0.2 #define BPFLOADER_VERSION_MAJOR 0u -#define BPFLOADER_VERSION_MINOR 14u +#define BPFLOADER_VERSION_MINOR 2u #define BPFLOADER_VERSION ((BPFLOADER_VERSION_MAJOR << 16) | BPFLOADER_VERSION_MINOR) +#include "../progs/include/bpf_map_def.h" #include "bpf/BpfUtils.h" -#include "bpf/bpf_map_def.h" #include "include/libbpf_android.h" -#include <bpf/bpf.h> - #include <cstdlib> #include <fstream> #include <iostream> #include <optional> #include <string> -#include <unordered_map> #include <vector> -#include <android-base/cmsg.h> -#include <android-base/file.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #define BPF_FS_PATH "/sys/fs/bpf/" // Size of the BPF log buffer for verifier logging -#define BPF_LOAD_LOG_SZ 0xfffff - -// Unspecified attach type is 0 which is BPF_CGROUP_INET_INGRESS. -#define BPF_ATTACH_TYPE_UNSPEC BPF_CGROUP_INET_INGRESS +#define BPF_LOAD_LOG_SZ 0x1ffff using android::base::StartsWith; using android::base::unique_fd; @@ -87,35 +77,30 @@ static string pathToFilename(const string& path, bool noext = false) { typedef struct { const char* name; enum bpf_prog_type type; - enum bpf_attach_type expected_attach_type; } sectionType; /* * Map section name prefixes to program types, the section name will be: - * SECTION(<prefix>/<name-of-program>) + * SEC(<prefix>/<name-of-program>) * For example: - * SECTION("tracepoint/sched_switch_func") where sched_switch_funcs + * SEC("tracepoint/sched_switch_func") where sched_switch_funcs * is the name of the program, and tracepoint is the type. - * - * However, be aware that you should not be directly using the SECTION() macro. - * Instead use the DEFINE_(BPF|XDP)_(PROG|MAP)... & LICENSE/CRITICAL macros. */ sectionType sectionNameTypes[] = { - {"bind4/", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_BIND}, - {"bind6/", BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_BIND}, - {"cgroupskb/", BPF_PROG_TYPE_CGROUP_SKB, BPF_ATTACH_TYPE_UNSPEC}, - {"cgroupsock/", BPF_PROG_TYPE_CGROUP_SOCK, BPF_ATTACH_TYPE_UNSPEC}, - {"kprobe/", BPF_PROG_TYPE_KPROBE, BPF_ATTACH_TYPE_UNSPEC}, - {"schedact/", BPF_PROG_TYPE_SCHED_ACT, BPF_ATTACH_TYPE_UNSPEC}, - {"schedcls/", BPF_PROG_TYPE_SCHED_CLS, BPF_ATTACH_TYPE_UNSPEC}, - {"skfilter/", BPF_PROG_TYPE_SOCKET_FILTER, BPF_ATTACH_TYPE_UNSPEC}, - {"tracepoint/", BPF_PROG_TYPE_TRACEPOINT, BPF_ATTACH_TYPE_UNSPEC}, - {"xdp/", BPF_PROG_TYPE_XDP, BPF_ATTACH_TYPE_UNSPEC}, + {"kprobe", BPF_PROG_TYPE_KPROBE}, + {"tracepoint", BPF_PROG_TYPE_TRACEPOINT}, + {"skfilter", BPF_PROG_TYPE_SOCKET_FILTER}, + {"cgroupskb", BPF_PROG_TYPE_CGROUP_SKB}, + {"schedcls", BPF_PROG_TYPE_SCHED_CLS}, + {"cgroupsock", BPF_PROG_TYPE_CGROUP_SOCK}, + {"xdp", BPF_PROG_TYPE_XDP}, + + /* End of table */ + {"END", BPF_PROG_TYPE_UNSPEC}, }; typedef struct { enum bpf_prog_type type; - enum bpf_attach_type expected_attach_type; string name; vector<char> data; vector<char> rel_data; @@ -293,32 +278,35 @@ static int readSymTab(ifstream& elfFile, int sort, vector<Elf64_Sym>& data) { } static enum bpf_prog_type getSectionType(string& name) { - for (auto& snt : sectionNameTypes) - if (StartsWith(name, snt.name)) return snt.type; - - // TODO Remove this code when fuse-bpf is upstream and this BPF_PROG_TYPE_FUSE is fixed - if (StartsWith(name, "fuse/")) { - int result = BPF_PROG_TYPE_UNSPEC; - ifstream("/sys/fs/fuse/bpf_prog_type_fuse") >> result; - return static_cast<bpf_prog_type>(result); - } + for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++) + if (StartsWith(name, sectionNameTypes[i].name)) return sectionNameTypes[i].type; return BPF_PROG_TYPE_UNSPEC; } -static enum bpf_attach_type getExpectedAttachType(string& name) { - for (auto& snt : sectionNameTypes) - if (StartsWith(name, snt.name)) return snt.expected_attach_type; - return BPF_ATTACH_TYPE_UNSPEC; -} - +/* If ever needed static string getSectionName(enum bpf_prog_type type) { - for (auto& snt : sectionNameTypes) - if (snt.type == type) - return string(snt.name); + for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++) + if (sectionNameTypes[i].type == type) + return string(sectionNameTypes[i].name); - return "UNKNOWN SECTION NAME " + std::to_string(type); + return NULL; +} +*/ + +static bool isRelSection(codeSection& cs, string& name) { + for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++) { + sectionType st = sectionNameTypes[i]; + + if (st.type != cs.type) continue; + + if (StartsWith(name, string(".rel") + st.name + "/")) + return true; + else + return false; + } + return false; } static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd, @@ -354,8 +342,7 @@ static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd, return 0; } -static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vector<string>& names, - optional<unsigned> symbolType = std::nullopt) { +static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vector<string>& names) { int ret; string name; vector<Elf64_Sym> symtab; @@ -386,8 +373,6 @@ static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vect } for (int i = 0; i < (int)symtab.size(); i++) { - if (symbolType.has_value() && ELF_ST_TYPE(symtab[i].st_info) != symbolType) continue; - if (symtab[i].st_shndx == sec_idx) { string s; ret = getSymName(elfFile, symtab[i].st_name, s); @@ -399,19 +384,8 @@ static int getSectionSymNames(ifstream& elfFile, const string& sectionName, vect return 0; } -static bool IsAllowed(bpf_prog_type type, const bpf_prog_type* allowed, size_t numAllowed) { - if (allowed == nullptr) return true; - - for (size_t i = 0; i < numAllowed; i++) { - if (type == allowed[i]) return true; - } - - return false; -} - /* Read a section by its index - for ex to get sec hdr strtab blob */ -static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t sizeOfBpfProgDef, - const bpf_prog_type* allowed, size_t numAllowed) { +static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t sizeOfBpfProgDef) { vector<Elf64_Shdr> shTable; int entries, ret = 0; @@ -435,36 +409,27 @@ static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t s if (ret) return ret; enum bpf_prog_type ptype = getSectionType(name); + if (ptype != BPF_PROG_TYPE_UNSPEC) { + string oldName = name; - if (ptype == BPF_PROG_TYPE_UNSPEC) continue; - - if (!IsAllowed(ptype, allowed, numAllowed)) { - ALOGE("Program type %s not permitted here", getSectionName(ptype).c_str()); - return -1; - } - - // This must be done before '/' is replaced with '_'. - cs_temp.expected_attach_type = getExpectedAttachType(name); + // convert all slashes to underscores + std::replace(name.begin(), name.end(), '/', '_'); - string oldName = name; + cs_temp.type = ptype; + cs_temp.name = name; - // convert all slashes to underscores - std::replace(name.begin(), name.end(), '/', '_'); - - cs_temp.type = ptype; - cs_temp.name = name; - - ret = readSectionByIdx(elfFile, i, cs_temp.data); - if (ret) return ret; - ALOGD("Loaded code section %d (%s)\n", i, name.c_str()); - - vector<string> csSymNames; - ret = getSectionSymNames(elfFile, oldName, csSymNames, STT_FUNC); - if (ret || !csSymNames.size()) return ret; - for (size_t i = 0; i < progDefNames.size(); ++i) { - if (!progDefNames[i].compare(csSymNames[0] + "_def")) { - cs_temp.prog_def = pd[i]; - break; + ret = readSectionByIdx(elfFile, i, cs_temp.data); + if (ret) return ret; + ALOGD("Loaded code section %d (%s)\n", i, name.c_str()); + + vector<string> csSymNames; + ret = getSectionSymNames(elfFile, oldName, csSymNames); + if (ret || !csSymNames.size()) return ret; + for (size_t i = 0; i < progDefNames.size(); ++i) { + if (!progDefNames[i].compare(csSymNames[0] + "_def")) { + cs_temp.prog_def = pd[i]; + break; + } } } @@ -473,7 +438,7 @@ static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t s ret = getSymName(elfFile, shTable[i + 1].sh_name, name); if (ret) return ret; - if (name == (".rel" + oldName)) { + if (isRelSection(cs_temp, name)) { ret = readSectionByIdx(elfFile, i + 1, cs_temp.rel_data); if (ret) return ret; ALOGD("Loaded relo section %d (%s)\n", i, name.c_str()); @@ -500,88 +465,12 @@ static int getSymNameByIdx(ifstream& elfFile, int index, string& name) { return getSymName(elfFile, symtab[index].st_name, name); } -static bool waitpidTimeout(pid_t pid, int timeoutMs) { - // Add SIGCHLD to the signal set. - sigset_t child_mask, original_mask; - sigemptyset(&child_mask); - sigaddset(&child_mask, SIGCHLD); - if (sigprocmask(SIG_BLOCK, &child_mask, &original_mask) == -1) return false; - - // Wait for a SIGCHLD notification. - errno = 0; - timespec ts = {0, timeoutMs * 1000000}; - int wait_result = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts)); - - // Restore the original signal set. - sigprocmask(SIG_SETMASK, &original_mask, nullptr); - - if (wait_result == -1) return false; - - int status; - return TEMP_FAILURE_RETRY(waitpid(pid, &status, WNOHANG)) == pid; -} - -static std::optional<unique_fd> getMapBtfInfo(const char* elfPath, - std::unordered_map<string, std::pair<uint32_t, uint32_t>> &btfTypeIds) { - unique_fd bpfloaderSocket, btfloaderSocket; - if (!android::base::Socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, &bpfloaderSocket, - &btfloaderSocket)) { - return {}; - } - - unique_fd pipeRead, pipeWrite; - if (!android::base::Pipe(&pipeRead, &pipeWrite, O_NONBLOCK)) { - return {}; - } - - pid_t pid = fork(); - if (pid < 0) return {}; - if (!pid) { - bpfloaderSocket.reset(); - pipeRead.reset(); - auto socketFdStr = std::to_string(btfloaderSocket.release()); - auto pipeFdStr = std::to_string(pipeWrite.release()); - - if (execl("/system/bin/btfloader", "/system/bin/btfloader", socketFdStr.c_str(), - pipeFdStr.c_str(), elfPath, NULL) == -1) { - ALOGW("exec btfloader failed with errno %d (%s)\n", errno, strerror(errno)); - exit(EX_UNAVAILABLE); - } - } - btfloaderSocket.reset(); - pipeWrite.reset(); - if (!waitpidTimeout(pid, 100)) { - kill(pid, SIGKILL); - return {}; - } - - unique_fd btfFd; - if (android::base::ReceiveFileDescriptors(bpfloaderSocket, nullptr, 0, &btfFd)) return {}; - - std::string btfTypeIdStr; - if (!android::base::ReadFdToString(pipeRead, &btfTypeIdStr)) return {}; - if (btfFd.get() < 0) return {}; - - const auto mapTypeIdLines = android::base::Split(btfTypeIdStr, "\n"); - for (const auto &line : mapTypeIdLines) { - const auto vec = android::base::Split(line, " "); - // Splitting on newline will give us one empty line - if (vec.size() != 3) continue; - const int kTid = atoi(vec[1].c_str()); - const int vTid = atoi(vec[2].c_str()); - if (!kTid || !vTid) return {}; - btfTypeIds[vec[0]] = std::make_pair(kTid, vTid); - } - return btfFd; -} - static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds, const char* prefix, size_t sizeOfBpfMapDef) { int ret; - vector<char> mdData, btfData; + vector<char> mdData; vector<struct bpf_map_def> md; vector<string> mapNames; - std::unordered_map<string, std::pair<uint32_t, uint32_t>> btfTypeIdMap; string fname = pathToFilename(string(elfPath), true); ret = readSectionByName("maps", elfFile, mdData); @@ -614,11 +503,6 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& ret = getSectionSymNames(elfFile, "maps", mapNames); if (ret) return ret; - std::optional<unique_fd> btfFd; - if (!readSectionByName(".BTF", elfFile, btfData)) { - btfFd = getMapBtfInfo(elfPath, btfTypeIdMap); - } - unsigned kvers = kernelVersion(); for (int i = 0; i < (int)mapNames.size(); i++) { @@ -684,20 +568,8 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& // programs as being 5.4+... type = BPF_MAP_TYPE_HASH; } - struct bpf_create_map_attr attr = { - .name = mapNames[i].c_str(), - .map_type = type, - .map_flags = md[i].map_flags, - .key_size = md[i].key_size, - .value_size = md[i].value_size, - .max_entries = md[i].max_entries, - }; - if (btfFd.has_value() && btfTypeIdMap.find(mapNames[i]) != btfTypeIdMap.end()) { - attr.btf_fd = btfFd->get(); - attr.btf_key_type_id = btfTypeIdMap.at(mapNames[i]).first; - attr.btf_value_type_id = btfTypeIdMap.at(mapNames[i]).second; - } - fd.reset(bcc_create_map_xattr(&attr, true)); + fd.reset(bpf_create_map(type, mapNames[i].c_str(), md[i].key_size, md[i].value_size, + md[i].max_entries, md[i].map_flags)); saved_errno = errno; ALOGD("bpf_create_map name %s, ret: %d\n", mapNames[i].c_str(), fd.get()); } @@ -713,15 +585,6 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& if (ret) return -errno; } - struct bpf_map_info map_info = {}; - __u32 map_info_len = sizeof(map_info); - int rv = bpf_obj_get_info_by_fd(fd, &map_info, &map_info_len); - if (rv) { - ALOGE("bpf_obj_get_info_by_fd failed, ret: %d [%d]\n", rv, errno); - } else { - ALOGI("map %s id %d\n", mapPinLoc.c_str(), map_info.id); - } - mapFds.push_back(std::move(fd)); } @@ -854,18 +717,9 @@ static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const } else { vector<char> log_buf(BPF_LOAD_LOG_SZ, 0); - struct bpf_load_program_attr attr = { - .prog_type = cs[i].type, - .name = name.c_str(), - .insns = (struct bpf_insn*)cs[i].data.data(), - .license = license.c_str(), - .log_level = 0, - .expected_attach_type = cs[i].expected_attach_type, - }; - - fd = bcc_prog_load_xattr(&attr, cs[i].data.size(), log_buf.data(), log_buf.size(), - true); - + fd = bpf_prog_load(cs[i].type, name.c_str(), (struct bpf_insn*)cs[i].data.data(), + cs[i].data.size(), license.c_str(), kvers, 0, log_buf.data(), + log_buf.size()); ALOGD("bpf_prog_load lib call for %s (%s) returned fd: %d (%s)\n", elfPath, cs[i].name.c_str(), fd, (fd < 0 ? std::strerror(errno) : "no error")); @@ -890,22 +744,13 @@ static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const if (!reuse) { ret = bpf_obj_pin(fd, progPinLoc.c_str()); if (ret) return -errno; - if (chmod(progPinLoc.c_str(), 0440)) return -errno; if (cs[i].prog_def.has_value()) { if (chown(progPinLoc.c_str(), (uid_t)cs[i].prog_def->uid, (gid_t)cs[i].prog_def->gid)) { return -errno; } } - } - - struct bpf_prog_info prog_info = {}; - __u32 prog_info_len = sizeof(prog_info); - int rv = bpf_obj_get_info_by_fd(fd, &prog_info, &prog_info_len); - if (rv) { - ALOGE("bpf_obj_get_info_by_fd failed, ret: %d [%d]\n", rv, errno); - } else { - ALOGI("prog %s id %d\n", progPinLoc.c_str(), prog_info.id); + if (chmod(progPinLoc.c_str(), 0440)) return -errno; } cs[i].prog_fd.reset(fd); @@ -914,8 +759,7 @@ static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const return 0; } -int loadProg(const char* elfPath, bool* isCritical, const char* prefix, - const bpf_prog_type* allowed, size_t numAllowed) { +int loadProg(const char* elfPath, bool* isCritical, const char* prefix) { vector<char> license; vector<char> critical; vector<codeSection> cs; @@ -980,7 +824,7 @@ int loadProg(const char* elfPath, bool* isCritical, const char* prefix, return -1; } - ret = readCodeSections(elfFile, cs, sizeOfBpfProgDef, allowed, numAllowed); + ret = readCodeSections(elfFile, cs, sizeOfBpfProgDef); if (ret) { ALOGE("Couldn't read all code sections in %s\n", elfPath); return ret; diff --git a/libbpf_android/TEST_MAPPING b/libbpf_android/TEST_MAPPING deleted file mode 100644 index b6a940b..0000000 --- a/libbpf_android/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "libbpf_load_test" - } - ], - "hwasan-postsubmit": [ - { - "name": "libbpf_load_test" - } - ] -} diff --git a/libbpf_android/include/bpf/BpfMap.h b/libbpf_android/include/bpf/BpfMap.h new file mode 100644 index 0000000..bdffc0f --- /dev/null +++ b/libbpf_android/include/bpf/BpfMap.h @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <linux/bpf.h> + +#include <android-base/result.h> +#include <android-base/stringprintf.h> +#include <android-base/unique_fd.h> +#include <utils/Log.h> +#include "bpf/BpfUtils.h" + +namespace android { +namespace bpf { + +// This is a class wrapper for eBPF maps. The eBPF map is a special in-kernel +// data structure that stores data in <Key, Value> pairs. It can be read/write +// from userspace by passing syscalls with the map file descriptor. This class +// is used to generalize the procedure of interacting with eBPF maps and hide +// the implementation detail from other process. Besides the basic syscalls +// wrapper, it also provides some useful helper functions as well as an iterator +// nested class to iterate the map more easily. +// +// NOTE: A kernel eBPF map may be accessed by both kernel and userspace +// processes at the same time. Or if the map is pinned as a virtual file, it can +// be obtained by multiple eBPF map class object and accessed concurrently. +// Though the map class object and the underlying kernel map are thread safe, it +// is not safe to iterate over a map while another thread or process is deleting +// from it. In this case the iteration can return duplicate entries. +template <class Key, class Value> +class BpfMap { + public: + BpfMap<Key, Value>() {}; + + protected: + // flag must be within BPF_OBJ_FLAG_MASK, ie. 0, BPF_F_RDONLY, BPF_F_WRONLY + BpfMap<Key, Value>(const char* pathname, uint32_t flags) { + int map_fd = mapRetrieve(pathname, flags); + if (map_fd >= 0) mMapFd.reset(map_fd); + } + + public: + explicit BpfMap<Key, Value>(const char* pathname) : BpfMap<Key, Value>(pathname, 0) {} + + BpfMap<Key, Value>(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags = 0) { + int map_fd = createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags); + if (map_fd >= 0) mMapFd.reset(map_fd); + } + + base::Result<Key> getFirstKey() const { + Key firstKey; + if (getFirstMapKey(mMapFd, &firstKey)) { + return ErrnoErrorf("Get firstKey map {} failed", mMapFd.get()); + } + return firstKey; + } + + base::Result<Key> getNextKey(const Key& key) const { + Key nextKey; + if (getNextMapKey(mMapFd, &key, &nextKey)) { + return ErrnoErrorf("Get next key of map {} failed", mMapFd.get()); + } + return nextKey; + } + + base::Result<void> writeValue(const Key& key, const Value& value, uint64_t flags) { + if (writeToMapEntry(mMapFd, &key, &value, flags)) { + return ErrnoErrorf("Write to map {} failed", mMapFd.get()); + } + return {}; + } + + base::Result<Value> readValue(const Key key) const { + Value value; + if (findMapEntry(mMapFd, &key, &value)) { + return ErrnoErrorf("Read value of map {} failed", mMapFd.get()); + } + return value; + } + + base::Result<void> deleteValue(const Key& key) { + if (deleteMapEntry(mMapFd, &key)) { + return ErrnoErrorf("Delete entry from map {} failed", mMapFd.get()); + } + return {}; + } + + // Function that tries to get map from a pinned path. + base::Result<void> init(const char* path); + + // Iterate through the map and handle each key retrieved based on the filter + // without modification of map content. + base::Result<void> iterate( + const std::function<base::Result<void>(const Key& key, const BpfMap<Key, Value>& map)>& + filter) const; + + // Iterate through the map and get each <key, value> pair, handle each <key, + // value> pair based on the filter without modification of map content. + base::Result<void> iterateWithValue( + const std::function<base::Result<void>(const Key& key, const Value& value, + const BpfMap<Key, Value>& map)>& filter) const; + + // Iterate through the map and handle each key retrieved based on the filter + base::Result<void> iterate( + const std::function<base::Result<void>(const Key& key, BpfMap<Key, Value>& map)>& + filter); + + // Iterate through the map and get each <key, value> pair, handle each <key, + // value> pair based on the filter. + base::Result<void> iterateWithValue( + const std::function<base::Result<void>(const Key& key, const Value& value, + BpfMap<Key, Value>& map)>& filter); + + const base::unique_fd& getMap() const { return mMapFd; }; + + // Copy assignment operator + BpfMap<Key, Value>& operator=(const BpfMap<Key, Value>& other) { + if (this != &other) mMapFd.reset(fcntl(other.mMapFd.get(), F_DUPFD_CLOEXEC, 0)); + return *this; + } + + // Move assignment operator + BpfMap<Key, Value>& operator=(BpfMap<Key, Value>&& other) noexcept { + mMapFd = std::move(other.mMapFd); + other.reset(-1); + return *this; + } + + void reset(base::unique_fd fd) = delete; + + void reset(int fd) { mMapFd.reset(fd); } + + bool isValid() const { return mMapFd != -1; } + + base::Result<void> clear() { + while (true) { + auto key = getFirstKey(); + if (!key.ok()) { + if (key.error().code() == ENOENT) return {}; // empty: success + return key.error(); // Anything else is an error + } + auto res = deleteValue(key.value()); + if (!res.ok()) { + // Someone else could have deleted the key, so ignore ENOENT + if (res.error().code() == ENOENT) continue; + ALOGE("Failed to delete data %s", strerror(res.error().code())); + return res.error(); + } + } + } + + base::Result<bool> isEmpty() const { + auto key = getFirstKey(); + if (!key.ok()) { + // Return error code ENOENT means the map is empty + if (key.error().code() == ENOENT) return true; + return key.error(); + } + return false; + } + + private: + base::unique_fd mMapFd; +}; + +template <class Key, class Value> +base::Result<void> BpfMap<Key, Value>::init(const char* path) { + mMapFd = base::unique_fd(mapRetrieveRW(path)); + if (mMapFd == -1) { + return ErrnoErrorf("Pinned map not accessible or does not exist: ({})", path); + } + return {}; +} + +template <class Key, class Value> +base::Result<void> BpfMap<Key, Value>::iterate( + const std::function<base::Result<void>(const Key& key, const BpfMap<Key, Value>& map)>& + filter) const { + base::Result<Key> curKey = getFirstKey(); + while (curKey.ok()) { + const base::Result<Key>& nextKey = getNextKey(curKey.value()); + base::Result<void> status = filter(curKey.value(), *this); + if (!status.ok()) return status; + curKey = nextKey; + } + if (curKey.error().code() == ENOENT) return {}; + return curKey.error(); +} + +template <class Key, class Value> +base::Result<void> BpfMap<Key, Value>::iterateWithValue( + const std::function<base::Result<void>(const Key& key, const Value& value, + const BpfMap<Key, Value>& map)>& filter) const { + base::Result<Key> curKey = getFirstKey(); + while (curKey.ok()) { + const base::Result<Key>& nextKey = getNextKey(curKey.value()); + base::Result<Value> curValue = readValue(curKey.value()); + if (!curValue.ok()) return curValue.error(); + base::Result<void> status = filter(curKey.value(), curValue.value(), *this); + if (!status.ok()) return status; + curKey = nextKey; + } + if (curKey.error().code() == ENOENT) return {}; + return curKey.error(); +} + +template <class Key, class Value> +base::Result<void> BpfMap<Key, Value>::iterate( + const std::function<base::Result<void>(const Key& key, BpfMap<Key, Value>& map)>& filter) { + base::Result<Key> curKey = getFirstKey(); + while (curKey.ok()) { + const base::Result<Key>& nextKey = getNextKey(curKey.value()); + base::Result<void> status = filter(curKey.value(), *this); + if (!status.ok()) return status; + curKey = nextKey; + } + if (curKey.error().code() == ENOENT) return {}; + return curKey.error(); +} + +template <class Key, class Value> +base::Result<void> BpfMap<Key, Value>::iterateWithValue( + const std::function<base::Result<void>(const Key& key, const Value& value, + BpfMap<Key, Value>& map)>& filter) { + base::Result<Key> curKey = getFirstKey(); + while (curKey.ok()) { + const base::Result<Key>& nextKey = getNextKey(curKey.value()); + base::Result<Value> curValue = readValue(curKey.value()); + if (!curValue.ok()) return curValue.error(); + base::Result<void> status = filter(curKey.value(), curValue.value(), *this); + if (!status.ok()) return status; + curKey = nextKey; + } + if (curKey.error().code() == ENOENT) return {}; + return curKey.error(); +} + +template <class Key, class Value> +class BpfMapRO : public BpfMap<Key, Value> { + public: + explicit BpfMapRO<Key, Value>(const char* pathname) + : BpfMap<Key, Value>(pathname, BPF_F_RDONLY) {} +}; + +} // namespace bpf +} // namespace android diff --git a/libbpf_android/include/bpf/BpfUtils.h b/libbpf_android/include/bpf/BpfUtils.h new file mode 100644 index 0000000..8fd2a4a --- /dev/null +++ b/libbpf_android/include/bpf/BpfUtils.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <linux/if_ether.h> +#include <net/if.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> + +#include <string> + +#include "BpfSyscallWrappers.h" + +namespace android { +namespace bpf { + +constexpr const int OVERFLOW_COUNTERSET = 2; + +constexpr const uint64_t NONEXISTENT_COOKIE = 0; + +uint64_t getSocketCookie(int sockFd); +int synchronizeKernelRCU(); +int setrlimitForTest(); + +#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c)) + +unsigned kernelVersion(); + +static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) { + return kernelVersion() >= KVER(major, minor, sub); +} + +#define SKIP_IF_BPF_SUPPORTED \ + do { \ + if (android::bpf::isAtLeastKernelVersion(4, 9, 0)) { \ + GTEST_LOG_(INFO) << "This test is skipped since bpf is supported\n"; \ + return; \ + } \ + } while (0) + +#define SKIP_IF_BPF_NOT_SUPPORTED \ + do { \ + if (!android::bpf::isAtLeastKernelVersion(4, 9, 0)) { \ + GTEST_LOG_(INFO) << "This test is skipped since bpf is not supported\n"; \ + return; \ + } \ + } while (0) + +#define SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED \ + do { \ + if (!android::bpf::isAtLeastKernelVersion(4, 14, 0)) { \ + GTEST_LOG_(INFO) << "This test is skipped since extended bpf feature" \ + << "not supported\n"; \ + return; \ + } \ + } while (0) + +#define SKIP_IF_XDP_NOT_SUPPORTED \ + do { \ + if (!android::bpf::isAtLeastKernelVersion(5, 9, 0)) { \ + GTEST_LOG_(INFO) << "This test is skipped since xdp" \ + << "not supported\n"; \ + return; \ + } \ + } while (0) + +} // namespace bpf +} // namespace android diff --git a/libbpf_android/include/libbpf_android.h b/libbpf_android/include/libbpf_android.h index 3fb777b..640f35b 100644 --- a/libbpf_android/include/libbpf_android.h +++ b/libbpf_android/include/libbpf_android.h @@ -19,16 +19,29 @@ #include <libbpf.h> #include <linux/bpf.h> +#include <log/log.h> + +#include <android-base/properties.h> namespace android { namespace bpf { // BPF loader implementation. Loads an eBPF ELF object -int loadProg(const char* elfPath, bool* isCritical, const char* prefix = "", - const bpf_prog_type* allowed = nullptr, size_t numAllowed = 0); +int loadProg(const char* elfPath, bool* isCritical, const char* prefix = ""); // Exposed for testing unsigned int readSectionUint(const char* name, std::ifstream& elfFile, unsigned int defVal); +// Wait for bpfloader to load BPF programs. +static inline void waitForProgsLoaded() { + // infinite loop until success with 5/10/20/40/60/60/60... delay + for (int delay = 5;; delay *= 2) { + if (delay > 60) delay = 60; + if (android::base::WaitForProperty("bpf.progs_loaded", "1", std::chrono::seconds(delay))) + return; + ALOGW("Waited %ds for bpf.progs_loaded, still waiting...", delay); + } +} + } // namespace bpf } // namespace android diff --git a/progs/include/bpf_helpers.h b/progs/include/bpf_helpers.h new file mode 100644 index 0000000..abd19c6 --- /dev/null +++ b/progs/include/bpf_helpers.h @@ -0,0 +1,201 @@ +/* Common BPF helpers to be used by all BPF programs loaded by Android */ + +#include <linux/bpf.h> +#include <stdbool.h> +#include <stdint.h> + +#include "bpf_map_def.h" + +/****************************************************************************** + * WARNING: CHANGES TO THIS FILE OUTSIDE OF AOSP/MASTER ARE LIKELY TO BREAK * + * DEVICE COMPATIBILITY WITH MAINLINE MODULES SHIPPING EBPF CODE. * + * * + * THIS WILL LIKELY RESULT IN BRICKED DEVICES AT SOME ARBITRARY FUTURE TIME * + * * + * THAT GOES ESPECIALLY FOR THE 'SEC' 'LICENSE' AND 'CRITICAL' MACRO DEFINES * + * * + * We strongly suggest that if you need changes to bpfloader functionality * + * you get your changes reviewed and accepted into aosp/master. * + * * + ******************************************************************************/ + +/* place things in different elf sections */ +#define SEC(NAME) __attribute__((section(NAME), used)) + +/* Must be present in every program, example usage: + * LICENSE("GPL"); or LICENSE("Apache 2.0"); + * + * We also take this opportunity to embed a bunch of other useful values in + * the resulting .o (This is to enable some limited forward compatibility + * with mainline module shipped ebpf programs) + * + * The bpfloader_{min/max}_ver defines the [min, max) range of bpfloader + * versions that should load this .o file (bpfloaders outside of this range + * will simply ignore/skip this *entire* .o) + * The [inclusive,exclusive) matches what we do for kernel ver dependencies. + * + * The size_of_bpf_{map,prog}_def allow the bpfloader to load programs where + * these structures have been extended with additional fields (they will of + * course simply be ignored then). + * + * If missing, bpfloader_{min/max}_ver default to 0/0x10000 ie. [v0.0, v1.0), + * while size_of_bpf_{map/prog}_def default to 32/20 which are the v0.0 sizes. + */ +#define LICENSE(NAME) \ + unsigned int _bpfloader_min_ver SEC("bpfloader_min_ver") = DEFAULT_BPFLOADER_MIN_VER; \ + unsigned int _bpfloader_max_ver SEC("bpfloader_max_ver") = DEFAULT_BPFLOADER_MAX_VER; \ + size_t _size_of_bpf_map_def SEC("size_of_bpf_map_def") = sizeof(struct bpf_map_def); \ + size_t _size_of_bpf_prog_def SEC("size_of_bpf_prog_def") = sizeof(struct bpf_prog_def); \ + char _license[] SEC("license") = (NAME) + +/* flag the resulting bpf .o file as critical to system functionality, + * loading all kernel version appropriate programs in it must succeed + * for bpfloader success + */ +#define CRITICAL(REASON) char _critical[] SEC("critical") = (REASON) + +/* + * Helper functions called from eBPF programs written in C. These are + * implemented in the kernel sources. + */ + +#define KVER_NONE 0 +#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c)) +#define KVER_INF 0xFFFFFFFFu + +/* generic functions */ + +/* + * Type-unsafe bpf map functions - avoid if possible. + * + * Using these it is possible to pass in keys/values of the wrong type/size, + * or, for 'bpf_map_lookup_elem_unsafe' receive into a pointer to the wrong type. + * You will not get a compile time failure, and for certain types of errors you + * might not even get a failure from the kernel's ebpf verifier during program load, + * instead stuff might just not work right at runtime. + * + * Instead please use: + * DEFINE_BPF_MAP(foo_map, TYPE, KeyType, ValueType, num_entries) + * where TYPE can be something like HASH or ARRAY, and num_entries is an integer. + * + * This defines the map (hence this should not be used in a header file included + * from multiple locations) and provides type safe accessors: + * ValueType * bpf_foo_map_lookup_elem(const KeyType *) + * int bpf_foo_map_update_elem(const KeyType *, const ValueType *, flags) + * int bpf_foo_map_delete_elem(const KeyType *) + * + * This will make sure that if you change the type of a map you'll get compile + * errors at any spots you forget to update with the new type. + * + * Note: these all take pointers to const map because from the C/eBPF point of view + * the map struct is really just a readonly map definition of the in kernel object. + * Runtime modification of the map defining struct is meaningless, since + * the contents is only ever used during bpf program loading & map creation + * by the bpf loader, and not by the eBPF program itself. + */ +static void* (*bpf_map_lookup_elem_unsafe)(const struct bpf_map_def* map, + const void* key) = (void*)BPF_FUNC_map_lookup_elem; +static int (*bpf_map_update_elem_unsafe)(const struct bpf_map_def* map, const void* key, + const void* value, unsigned long long flags) = (void*) + BPF_FUNC_map_update_elem; +static int (*bpf_map_delete_elem_unsafe)(const struct bpf_map_def* map, + const void* key) = (void*)BPF_FUNC_map_delete_elem; + +/* type safe macro to declare a map and related accessor functions */ +#define DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, usr, grp, md) \ + const struct bpf_map_def SEC("maps") the_map = { \ + .type = BPF_MAP_TYPE_##TYPE, \ + .key_size = sizeof(TypeOfKey), \ + .value_size = sizeof(TypeOfValue), \ + .max_entries = (num_entries), \ + .map_flags = 0, \ + .uid = (usr), \ + .gid = (grp), \ + .mode = (md), \ + .bpfloader_min_ver = DEFAULT_BPFLOADER_MIN_VER, \ + .bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER, \ + .min_kver = KVER_NONE, \ + .max_kver = KVER_INF, \ + }; \ + \ + static inline __always_inline __unused TypeOfValue* bpf_##the_map##_lookup_elem( \ + const TypeOfKey* k) { \ + return bpf_map_lookup_elem_unsafe(&the_map, k); \ + }; \ + \ + static inline __always_inline __unused int bpf_##the_map##_update_elem( \ + const TypeOfKey* k, const TypeOfValue* v, unsigned long long flags) { \ + return bpf_map_update_elem_unsafe(&the_map, k, v, flags); \ + }; \ + \ + static inline __always_inline __unused int bpf_##the_map##_delete_elem(const TypeOfKey* k) { \ + return bpf_map_delete_elem_unsafe(&the_map, k); \ + }; + +#define DEFINE_BPF_MAP(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \ + DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, AID_ROOT, AID_ROOT, 0600) + +#define DEFINE_BPF_MAP_GWO(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, gid) \ + DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, AID_ROOT, gid, 0620) + +#define DEFINE_BPF_MAP_GRO(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, gid) \ + DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, AID_ROOT, gid, 0640) + +#define DEFINE_BPF_MAP_GRW(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, gid) \ + DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, AID_ROOT, gid, 0660) + +static int (*bpf_probe_read)(void* dst, int size, void* unsafe_ptr) = (void*) BPF_FUNC_probe_read; +static int (*bpf_probe_read_str)(void* dst, int size, void* unsafe_ptr) = (void*) BPF_FUNC_probe_read_str; +static unsigned long long (*bpf_ktime_get_ns)(void) = (void*) BPF_FUNC_ktime_get_ns; +static unsigned long long (*bpf_ktime_get_boot_ns)(void) = (void*)BPF_FUNC_ktime_get_boot_ns; +static int (*bpf_trace_printk)(const char* fmt, int fmt_size, ...) = (void*) BPF_FUNC_trace_printk; +static unsigned long long (*bpf_get_current_pid_tgid)(void) = (void*) BPF_FUNC_get_current_pid_tgid; +static unsigned long long (*bpf_get_current_uid_gid)(void) = (void*) BPF_FUNC_get_current_uid_gid; +static unsigned long long (*bpf_get_smp_processor_id)(void) = (void*) BPF_FUNC_get_smp_processor_id; + +#define DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv, \ + opt) \ + const struct bpf_prog_def SEC("progs") the_prog##_def = { \ + .uid = (prog_uid), \ + .gid = (prog_gid), \ + .min_kver = (min_kv), \ + .max_kver = (max_kv), \ + .optional = (opt), \ + .bpfloader_min_ver = DEFAULT_BPFLOADER_MIN_VER, \ + .bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER, \ + }; \ + SEC(SECTION_NAME) \ + int the_prog + +// Programs (here used in the sense of functions/sections) marked optional are allowed to fail +// to load (for example due to missing kernel patches). +// The bpfloader will just ignore these failures and continue processing the next section. +// +// A non-optional program (function/section) failing to load causes a failure and aborts +// processing of the entire .o, if the .o is additionally marked critical, this will result +// in the entire bpfloader process terminating with a failure and not setting the bpf.progs_loaded +// system property. This in turn results in waitForProgsLoaded() never finishing. +// +// ie. a non-optional program in a critical .o is mandatory for kernels matching the min/max kver. + +// programs requiring a kernel version >= min_kv && < max_kv +#define DEFINE_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv) \ + DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv, \ + false) +#define DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, \ + max_kv) \ + DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv, true) + +// programs requiring a kernel version >= min_kv +#define DEFINE_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv) \ + DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF, \ + false) +#define DEFINE_OPTIONAL_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv) \ + DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF, \ + true) + +// programs with no kernel version requirements +#define DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \ + DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, 0, KVER_INF, false) +#define DEFINE_OPTIONAL_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \ + DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, 0, KVER_INF, true) diff --git a/progs/include/bpf_map_def.h b/progs/include/bpf_map_def.h new file mode 100644 index 0000000..647c813 --- /dev/null +++ b/progs/include/bpf_map_def.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +/* This file is separate because it's included both by eBPF programs (via include + * in bpf_helpers.h) and directly by the boot time bpfloader (Loader.cpp). + */ + +#include <linux/bpf.h> + +// Pull in AID_* constants from //system/core/libcutils/include/private/android_filesystem_config.h +#include <private/android_filesystem_config.h> + +/****************************************************************************** + * * + * ! ! ! W A R N I N G ! ! ! * + * * + * CHANGES TO THESE STRUCTURE DEFINITIONS OUTSIDE OF AOSP/MASTER *WILL* BREAK * + * MAINLINE MODULE COMPATIBILITY * + * * + * AND THUS MAY RESULT IN YOUR DEVICE BRICKING AT SOME ARBITRARY POINT IN * + * THE FUTURE * + * * + * (and even in aosp/master you may only append new fields at the very end, * + * you may *never* delete fields, change their types, ordering, insert in * + * the middle, etc. If a mainline module using the old definition has * + * already shipped (which happens roughly monthly), then it's set in stone) * + * * + ******************************************************************************/ + +// These are the values used if these fields are missing +#define DEFAULT_BPFLOADER_MIN_VER 0u // v0.0 (this is inclusive ie. >= v0.0) +#define DEFAULT_BPFLOADER_MAX_VER 0x10000u // v1.0 (this is exclusive ie. < v1.0) +#define DEFAULT_SIZEOF_BPF_MAP_DEF 32 // v0.0 struct: enum (uint sized) + 7 uint +#define DEFAULT_SIZEOF_BPF_PROG_DEF 20 // v0.0 struct: 4 uint + bool + 3 byte alignment pad + +/* + * The bpf_{map,prog}_def structures are compiled for different architectures. + * Once by the BPF compiler for the BPF architecture, and once by a C++ + * compiler for the native Android architecture for the bpfloader. + * + * For things to work, their layout must be the same between the two. + * The BPF architecture is platform independent ('64-bit LSB bpf'). + * So this effectively means these structures must be the same layout + * on 5 architectures, all of them little endian: + * 64-bit BPF, x86_64, arm and 32-bit x86 and arm + * + * As such for any types we use inside of these structs we must make sure that + * the size and alignment are the same, so the same amount of padding is used. + * + * Currently we only use: bool, enum bpf_map_type and unsigned int. + * Additionally we use char for padding. + * + * !!! WARNING: HERE BE DRAGONS !!! + * + * Be particularly careful with 64-bit integers. + * You will need to manually override their alignment to 8 bytes. + * + * To quote some parts of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69560 + * + * Some types have weaker alignment requirements when they are structure members. + * + * unsigned long long on x86 is such a type. + * + * C distinguishes C11 _Alignof (the minimum alignment the type is guaranteed + * to have in all contexts, so 4, see min_align_of_type) from GNU C __alignof + * (the normal alignment of the type, so 8). + * + * alignof / _Alignof == minimum alignment required by target ABI + * __alignof / __alignof__ == preferred alignment + * + * When in a struct, apparently the minimum alignment is used. + */ + +_Static_assert(sizeof(bool) == 1, "sizeof bool != 1"); +_Static_assert(__alignof__(bool) == 1, "__alignof__ bool != 1"); +_Static_assert(_Alignof(bool) == 1, "_Alignof bool != 1"); + +_Static_assert(sizeof(char) == 1, "sizeof char != 1"); +_Static_assert(__alignof__(char) == 1, "__alignof__ char != 1"); +_Static_assert(_Alignof(char) == 1, "_Alignof char != 1"); + +// This basically verifies that an enum is 'just' a 32-bit int +_Static_assert(sizeof(enum bpf_map_type) == 4, "sizeof enum bpf_map_type != 4"); +_Static_assert(__alignof__(enum bpf_map_type) == 4, "__alignof__ enum bpf_map_type != 4"); +_Static_assert(_Alignof(enum bpf_map_type) == 4, "_Alignof enum bpf_map_type != 4"); + +// Linux kernel requires sizeof(int) == 4, sizeof(void*) == sizeof(long), sizeof(long long) == 8 +_Static_assert(sizeof(unsigned int) == 4, "sizeof unsigned int != 4"); +_Static_assert(__alignof__(unsigned int) == 4, "__alignof__ unsigned int != 4"); +_Static_assert(_Alignof(unsigned int) == 4, "_Alignof unsigned int != 4"); + +// We don't currently use any 64-bit types in these structs, so this is purely to document issue. +// Here sizeof & __alignof__ are consistent, but _Alignof is not: compile for 'aosp_cf_x86_phone' +_Static_assert(sizeof(unsigned long long) == 8, "sizeof unsigned long long != 8"); +_Static_assert(__alignof__(unsigned long long) == 8, "__alignof__ unsigned long long != 8"); +// BPF wants 8, but 32-bit x86 wants 4 +//_Static_assert(_Alignof(unsigned long long) == 8, "_Alignof unsigned long long != 8"); + +/* + * Map structure to be used by Android eBPF C programs. The Android eBPF loader + * uses this structure from eBPF object to create maps at boot time. + * + * The eBPF C program should define structure in the maps section using + * SEC("maps") otherwise it will be ignored by the eBPF loader. + * + * For example: + * const struct bpf_map_def SEC("maps") mymap { .type=... , .key_size=... } + * + * See 'bpf_helpers.h' for helpful macros for eBPF program use. + */ +struct bpf_map_def { + enum bpf_map_type type; + unsigned int key_size; + unsigned int value_size; + unsigned int max_entries; + unsigned int map_flags; + + // The following are not supported by the Android bpfloader: + // unsigned int inner_map_idx; + // unsigned int numa_node; + + unsigned int uid; // uid_t + unsigned int gid; // gid_t + unsigned int mode; // mode_t + + // The following fields were added in version 0.1 + unsigned int bpfloader_min_ver; // if missing, defaults to 0, ie. v0.0 + unsigned int bpfloader_max_ver; // if missing, defaults to 0x10000, ie. v1.0 + + // The following fields were added in version 0.2 + // kernelVersion() must be >= min_kver and < max_kver + unsigned int min_kver; + unsigned int max_kver; +}; + +// This needs to be updated whenever the above structure definition is expanded. +_Static_assert(sizeof(struct bpf_map_def) == 48, "sizeof struct bpf_map_def != 48"); +_Static_assert(__alignof__(struct bpf_map_def) == 4, "__alignof__ struct bpf_map_def != 4"); +_Static_assert(_Alignof(struct bpf_map_def) == 4, "_Alignof struct bpf_map_def != 4"); + +struct bpf_prog_def { + unsigned int uid; + unsigned int gid; + + // kernelVersion() must be >= min_kver and < max_kver + unsigned int min_kver; + unsigned int max_kver; + + bool optional; // program section (ie. function) may fail to load, continue onto next func. + char pad0[3]; + + // The following fields were added in version 0.1 + unsigned int bpfloader_min_ver; // if missing, defaults to 0, ie. v0.0 + unsigned int bpfloader_max_ver; // if missing, defaults to 0x10000, ie. v1.0 + + // No new fields in version 0.2 +}; + +// This needs to be updated whenever the above structure definition is expanded. +_Static_assert(sizeof(struct bpf_prog_def) == 28, "sizeof struct bpf_prog_def != 28"); +_Static_assert(__alignof__(struct bpf_prog_def) == 4, "__alignof__ struct bpf_prog_def != 4"); +_Static_assert(_Alignof(struct bpf_prog_def) == 4, "_Alignof struct bpf_prog_def != 4"); diff --git a/progs/include/test/mock_bpf_helpers.h b/progs/include/test/mock_bpf_helpers.h deleted file mode 100644 index 141ee4f..0000000 --- a/progs/include/test/mock_bpf_helpers.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -/* Mock BPF helpers to be used for testing of BPF programs loaded by Android */ - -#include <linux/bpf.h> -#include <stdbool.h> -#include <stdint.h> - -#include <cutils/android_filesystem_config.h> - -typedef void* mock_bpf_map_t; - -/* type safe macro to declare a map and related accessor functions */ -#define DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, usr, grp, md) \ - mock_bpf_map_t mock_bpf_map_##the_map; \ - \ - mock_bpf_map_t get_mock_bpf_map_##the_map() { \ - if (mock_bpf_map_##the_map == 0) { \ - mock_bpf_map_##the_map = mock_bpf_map_create(sizeof(TypeOfKey), sizeof(TypeOfValue), \ - BPF_MAP_TYPE_##TYPE); \ - } \ - return mock_bpf_map_##the_map; \ - } \ - __unused TypeOfValue* bpf_##the_map##_lookup_elem(const TypeOfKey* k) { \ - return (TypeOfValue*)mock_bpf_lookup_elem(get_mock_bpf_map_##the_map(), (void*)k); \ - }; \ - \ - __unused int bpf_##the_map##_update_elem(const TypeOfKey* k, const TypeOfValue* v, \ - uint64_t flags) { \ - return mock_bpf_update_elem(get_mock_bpf_map_##the_map(), (void*)k, (void*)v, flags); \ - }; \ - \ - __unused int bpf_##the_map##_delete_elem(const TypeOfKey* k) { \ - return mock_bpf_delete_elem(get_mock_bpf_map_##the_map(), (void*)k); \ - }; - -#define DEFINE_BPF_MAP(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \ - DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, AID_ROOT, AID_ROOT, 0600) - -#define DEFINE_BPF_MAP_GWO(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, gid) \ - DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, AID_ROOT, gid, 0620) - -#define DEFINE_BPF_MAP_GRO(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, gid) \ - DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, AID_ROOT, gid, 0640) - -#define DEFINE_BPF_MAP_GRW(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, gid) \ - DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, AID_ROOT, gid, 0660) - -#define DEFINE_BPF_PROG(section, owner, group, name) int name - -#ifdef __cplusplus -extern "C" { -#endif - -mock_bpf_map_t mock_bpf_map_create(uint32_t key_size, uint32_t value_size, uint32_t type); -void* mock_bpf_lookup_elem(mock_bpf_map_t map, void* key); -int mock_bpf_update_elem(mock_bpf_map_t map, void* key, void* value, uint64_t flags); -int mock_bpf_delete_elem(mock_bpf_map_t map, void* key); - -uint64_t bpf_ktime_get_ns(); -uint64_t bpf_get_smp_processor_id(); -uint64_t bpf_get_current_uid_gid(); -uint64_t bpf_get_current_pid_tgid(); - -void mock_bpf_set_ktime_ns(uint64_t time_ns); -void mock_bpf_set_smp_processor_id(uint32_t cpu); -void mock_bpf_set_current_uid_gid(uint32_t uid); -void mock_bpf_set_current_pid_tgid(uint64_t pid_tgid); - -#ifdef __cplusplus -} // extern "C" -#endif - -/* place things in different elf sections */ -#define SECTION(NAME) __attribute__((section(NAME), used)) - -/* Example use: LICENSE("GPL"); or LICENSE("Apache 2.0"); */ -#define LICENSE(NAME) char _license[] SECTION("license") = (NAME) |