summaryrefslogtreecommitdiff
path: root/libbpf_android
diff options
context:
space:
mode:
authorKen Chen <cken@google.com>2021-10-25 21:11:22 +0800
committerMaciej Żenczykowski <maze@google.com>2021-12-04 03:42:55 +0000
commitb1d4888ec208915a726c363ef70ab4df9f986cb5 (patch)
treed186c305b3cbe8d1abb992e9fb2ac3381336521b /libbpf_android
parentb81aef32e5e73ea00294639a8fcd24071269feb0 (diff)
downloadbpf-b1d4888ec208915a726c363ef70ab4df9f986cb5.tar.gz
[NETD-BPF#14] Move BPF map definition and utils to frameworks/libs/net/
Move BPF map definition and utilities to a common place that easy to be referenced from both mainline module and platform code. Bug: 202086915 Test: m; flash; boot Test: cd system/netd/ && atest Test: cd packages/modules/Connectivity && atest Test: m gpuservice_unittest libtimeinstate_test bpf_module_test CtsAppOpsTestCases libbpf_load_test VtsBootconfigTest vts_test_binary_bpf_module bpf_benchmark libbpf_load_test libbpf_android_test Change-Id: Ib15cf78c2da97bff835fb406c866676eec77c013
Diffstat (limited to 'libbpf_android')
-rw-r--r--libbpf_android/Android.bp9
-rw-r--r--libbpf_android/BpfLoadTest.cpp4
-rw-r--r--libbpf_android/include/WaitForProgsLoaded.h39
-rw-r--r--libbpf_android/include/bpf/BpfMap.h260
-rw-r--r--libbpf_android/include/bpf/BpfUtils.h140
5 files changed, 9 insertions, 443 deletions
diff --git a/libbpf_android/Android.bp b/libbpf_android/Android.bp
index b3ab84c..7ab4c47 100644
--- a/libbpf_android/Android.bp
+++ b/libbpf_android/Android.bp
@@ -61,9 +61,13 @@ cc_library {
"libbpf",
],
header_libs: [
- "libbpf_android_headers"
+ "bpf_map_utils",
+ "libbpf_android_headers",
+ ],
+ export_header_lib_headers: [
+ "libbpf_android_headers",
+ "bpf_map_utils",
],
- export_header_lib_headers: ["libbpf_android_headers"],
export_shared_lib_headers: ["libbpf"],
local_include_dirs: ["include"],
@@ -77,6 +81,7 @@ cc_library {
cc_test {
name: "libbpf_load_test",
+ header_libs: ["bpf_map_utils"],
srcs: [
"BpfLoadTest.cpp",
],
diff --git a/libbpf_android/BpfLoadTest.cpp b/libbpf_android/BpfLoadTest.cpp
index 09cd36c..a058263 100644
--- a/libbpf_android/BpfLoadTest.cpp
+++ b/libbpf_android/BpfLoadTest.cpp
@@ -19,8 +19,8 @@
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
-#include "include/bpf/BpfMap.h"
-#include "include/bpf/BpfUtils.h"
+#include "bpf/BpfMap.h"
+#include "bpf/BpfUtils.h"
#include "include/libbpf_android.h"
using ::testing::Test;
diff --git a/libbpf_android/include/WaitForProgsLoaded.h b/libbpf_android/include/WaitForProgsLoaded.h
deleted file mode 100644
index bc4168e..0000000
--- a/libbpf_android/include/WaitForProgsLoaded.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- * Android BPF library - public API
- *
- * 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 <log/log.h>
-
-#include <android-base/properties.h>
-
-namespace android {
-namespace bpf {
-
-// 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/libbpf_android/include/bpf/BpfMap.h b/libbpf_android/include/bpf/BpfMap.h
deleted file mode 100644
index bdffc0f..0000000
--- a/libbpf_android/include/bpf/BpfMap.h
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 8f1b9a2..0000000
--- a/libbpf_android/include/bpf/BpfUtils.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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 <linux/pfkeyv2.h>
-#include <net/if.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
-#include <sys/utsname.h>
-
-#include <string>
-
-#include <android-base/unique_fd.h>
-#include <log/log.h>
-
-#include "BpfSyscallWrappers.h"
-
-// 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 {
-
-constexpr const int OVERFLOW_COUNTERSET = 2;
-
-constexpr const uint64_t NONEXISTENT_COOKIE = 0;
-
-static inline 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;
-}
-
-static inline 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;
-}
-
-static inline 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;
-}
-
-#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
-
-static inline unsigned kernelVersion() {
- struct utsname buf;
- int ret = uname(&buf);
- if (ret) return 0;
-
- unsigned kver_major;
- unsigned kver_minor;
- unsigned kver_sub;
- char unused;
- ret = sscanf(buf.release, "%u.%u.%u%c", &kver_major, &kver_minor, &kver_sub, &unused);
- // Check the device kernel version
- if (ret < 3) return 0;
-
- return KVER(kver_major, kver_minor, kver_sub);
-}
-
-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_SKIP() << "Skip: bpf is supported."; \
- } while (0)
-
-#define SKIP_IF_BPF_NOT_SUPPORTED \
- do { \
- if (!android::bpf::isAtLeastKernelVersion(4, 9, 0)) \
- GTEST_SKIP() << "Skip: bpf is not supported."; \
- } while (0)
-
-#define SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED \
- do { \
- if (!android::bpf::isAtLeastKernelVersion(4, 14, 0)) \
- GTEST_SKIP() << "Skip: extended bpf feature not supported."; \
- } while (0)
-
-#define SKIP_IF_XDP_NOT_SUPPORTED \
- do { \
- if (!android::bpf::isAtLeastKernelVersion(5, 9, 0)) \
- GTEST_SKIP() << "Skip: xdp not supported."; \
- } while (0)
-
-} // namespace bpf
-} // namespace android