diff options
author | Ken Chen <cken@google.com> | 2021-10-25 21:11:22 +0800 |
---|---|---|
committer | Maciej Żenczykowski <maze@google.com> | 2021-12-04 03:42:55 +0000 |
commit | b1d4888ec208915a726c363ef70ab4df9f986cb5 (patch) | |
tree | d186c305b3cbe8d1abb992e9fb2ac3381336521b /libbpf_android | |
parent | b81aef32e5e73ea00294639a8fcd24071269feb0 (diff) | |
download | bpf-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.bp | 9 | ||||
-rw-r--r-- | libbpf_android/BpfLoadTest.cpp | 4 | ||||
-rw-r--r-- | libbpf_android/include/WaitForProgsLoaded.h | 39 | ||||
-rw-r--r-- | libbpf_android/include/bpf/BpfMap.h | 260 | ||||
-rw-r--r-- | libbpf_android/include/bpf/BpfUtils.h | 140 |
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 |