diff --git a/bpfloader/Android.bp b/bpfloader/Android.bp index 7e7139f..c754e8d 100644 --- a/bpfloader/Android.bp +++ b/bpfloader/Android.bp @@ -39,6 +39,7 @@ cc_binary { memtag_heap: true, }, clang: true, + header_libs: ["bpf_map_utils"], shared_libs: [ "libcutils", "libbpf_android", 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 #include #include -#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 - -#include - -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 - -#include -#include -#include -#include -#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 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 BpfMap { - public: - BpfMap() {}; - - protected: - // flag must be within BPF_OBJ_FLAG_MASK, ie. 0, BPF_F_RDONLY, BPF_F_WRONLY - BpfMap(const char* pathname, uint32_t flags) { - int map_fd = mapRetrieve(pathname, flags); - if (map_fd >= 0) mMapFd.reset(map_fd); - } - - public: - explicit BpfMap(const char* pathname) : BpfMap(pathname, 0) {} - - BpfMap(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 getFirstKey() const { - Key firstKey; - if (getFirstMapKey(mMapFd, &firstKey)) { - return ErrnoErrorf("Get firstKey map {} failed", mMapFd.get()); - } - return firstKey; - } - - base::Result 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 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 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 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 init(const char* path); - - // Iterate through the map and handle each key retrieved based on the filter - // without modification of map content. - base::Result iterate( - const std::function(const Key& key, const BpfMap& map)>& - filter) const; - - // Iterate through the map and get each pair, handle each pair based on the filter without modification of map content. - base::Result iterateWithValue( - const std::function(const Key& key, const Value& value, - const BpfMap& map)>& filter) const; - - // Iterate through the map and handle each key retrieved based on the filter - base::Result iterate( - const std::function(const Key& key, BpfMap& map)>& - filter); - - // Iterate through the map and get each pair, handle each pair based on the filter. - base::Result iterateWithValue( - const std::function(const Key& key, const Value& value, - BpfMap& map)>& filter); - - const base::unique_fd& getMap() const { return mMapFd; }; - - // Copy assignment operator - BpfMap& operator=(const BpfMap& other) { - if (this != &other) mMapFd.reset(fcntl(other.mMapFd.get(), F_DUPFD_CLOEXEC, 0)); - return *this; - } - - // Move assignment operator - BpfMap& operator=(BpfMap&& 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 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 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 -base::Result BpfMap::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 -base::Result BpfMap::iterate( - const std::function(const Key& key, const BpfMap& map)>& - filter) const { - base::Result curKey = getFirstKey(); - while (curKey.ok()) { - const base::Result& nextKey = getNextKey(curKey.value()); - base::Result status = filter(curKey.value(), *this); - if (!status.ok()) return status; - curKey = nextKey; - } - if (curKey.error().code() == ENOENT) return {}; - return curKey.error(); -} - -template -base::Result BpfMap::iterateWithValue( - const std::function(const Key& key, const Value& value, - const BpfMap& map)>& filter) const { - base::Result curKey = getFirstKey(); - while (curKey.ok()) { - const base::Result& nextKey = getNextKey(curKey.value()); - base::Result curValue = readValue(curKey.value()); - if (!curValue.ok()) return curValue.error(); - base::Result status = filter(curKey.value(), curValue.value(), *this); - if (!status.ok()) return status; - curKey = nextKey; - } - if (curKey.error().code() == ENOENT) return {}; - return curKey.error(); -} - -template -base::Result BpfMap::iterate( - const std::function(const Key& key, BpfMap& map)>& filter) { - base::Result curKey = getFirstKey(); - while (curKey.ok()) { - const base::Result& nextKey = getNextKey(curKey.value()); - base::Result status = filter(curKey.value(), *this); - if (!status.ok()) return status; - curKey = nextKey; - } - if (curKey.error().code() == ENOENT) return {}; - return curKey.error(); -} - -template -base::Result BpfMap::iterateWithValue( - const std::function(const Key& key, const Value& value, - BpfMap& map)>& filter) { - base::Result curKey = getFirstKey(); - while (curKey.ok()) { - const base::Result& nextKey = getNextKey(curKey.value()); - base::Result curValue = readValue(curKey.value()); - if (!curValue.ok()) return curValue.error(); - base::Result 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 BpfMapRO : public BpfMap { - public: - explicit BpfMapRO(const char* pathname) - : BpfMap(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 -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#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