summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-06-15 21:50:48 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-06-15 21:50:48 +0000
commit1262aba43bc7983fcc4beb1c40417a7bd96f4be2 (patch)
tree1d33651c0c1c3185ce4a63b1fd27e562151400bd
parent1250962df75c600ea01c9ec4f41fdbb329451f7a (diff)
parente802bac986e70007581bfd9f3a39119bdd347121 (diff)
downloadbpf-aml_tz3_314012010.tar.gz
Change-Id: I87601d64438fb25c154385fb00b5bf38beb49542
-rw-r--r--OWNERS7
-rw-r--r--OWNERS_bpf5
-rw-r--r--bpfloader/Android.bp2
-rw-r--r--bpfloader/BpfLoader.cpp61
-rw-r--r--bpfloader/bpfloader.rc27
-rw-r--r--libbpf_android/Android.bp35
-rw-r--r--libbpf_android/BpfLoadTest.cpp75
-rw-r--r--libbpf_android/BpfUtils.cpp114
-rw-r--r--libbpf_android/Loader.cpp292
-rw-r--r--libbpf_android/TEST_MAPPING12
-rw-r--r--libbpf_android/include/bpf/BpfMap.h260
-rw-r--r--libbpf_android/include/bpf/BpfUtils.h83
-rw-r--r--libbpf_android/include/libbpf_android.h17
-rw-r--r--progs/include/bpf_helpers.h201
-rw-r--r--progs/include/bpf_map_def.h177
-rw-r--r--progs/include/test/mock_bpf_helpers.h94
16 files changed, 972 insertions, 490 deletions
diff --git a/OWNERS b/OWNERS
index e58fb39..094de96 100644
--- a/OWNERS
+++ b/OWNERS
@@ -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)