summaryrefslogtreecommitdiff
path: root/common/native/bpf_headers/include/bpf/BpfMap.h
diff options
context:
space:
mode:
Diffstat (limited to 'common/native/bpf_headers/include/bpf/BpfMap.h')
-rw-r--r--common/native/bpf_headers/include/bpf/BpfMap.h330
1 files changed, 0 insertions, 330 deletions
diff --git a/common/native/bpf_headers/include/bpf/BpfMap.h b/common/native/bpf_headers/include/bpf/BpfMap.h
deleted file mode 100644
index 847083e5..00000000
--- a/common/native/bpf_headers/include/bpf/BpfMap.h
+++ /dev/null
@@ -1,330 +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 "BpfSyscallWrappers.h"
-#include "bpf/BpfUtils.h"
-
-namespace android {
-namespace bpf {
-
-using base::Result;
-using base::unique_fd;
-using std::function;
-
-// 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>() {};
-
- // explicitly force no copy constructor, since it would need to dup the fd
- // (later on, for testing, we still make available a copy assignment operator)
- BpfMap<Key, Value>(const BpfMap<Key, Value>&) = delete;
-
- private:
- void abortOnKeyOrValueSizeMismatch() {
- if (!mMapFd.ok()) abort();
- if (isAtLeastKernelVersion(4, 14, 0)) {
- if (bpfGetFdKeySize(mMapFd) != sizeof(Key)) abort();
- if (bpfGetFdValueSize(mMapFd) != sizeof(Value)) abort();
- }
- }
-
- 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) {
- mMapFd.reset(mapRetrieve(pathname, flags));
- abortOnKeyOrValueSizeMismatch();
- }
-
- public:
- explicit BpfMap<Key, Value>(const char* pathname) : BpfMap<Key, Value>(pathname, 0) {}
-
-#ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
- // All bpf maps should be created by the bpfloader, so this constructor
- // is reserved for tests
- BpfMap<Key, Value>(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags = 0) {
- mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags));
- if (!mMapFd.ok()) abort();
- }
-#endif
-
- Result<Key> getFirstKey() const {
- Key firstKey;
- if (getFirstMapKey(mMapFd, &firstKey)) {
- return ErrnoErrorf("Get firstKey map {} failed", mMapFd.get());
- }
- return firstKey;
- }
-
- 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;
- }
-
- 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 {};
- }
-
- 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;
- }
-
- Result<void> deleteValue(const Key& key) {
- if (deleteMapEntry(mMapFd, &key)) {
- return ErrnoErrorf("Delete entry from map {} failed", mMapFd.get());
- }
- return {};
- }
-
- protected:
- [[clang::reinitializes]] Result<void> init(const char* path, int fd) {
- mMapFd.reset(fd);
- if (!mMapFd.ok()) {
- return ErrnoErrorf("Pinned map not accessible or does not exist: ({})", path);
- }
- // Normally we should return an error here instead of calling abort,
- // but this cannot happen at runtime without a massive code bug (K/V type mismatch)
- // and as such it's better to just blow the system up and let the developer fix it.
- // Crashes are much more likely to be noticed than logs and missing functionality.
- abortOnKeyOrValueSizeMismatch();
- return {};
- }
-
- public:
- // Function that tries to get map from a pinned path.
- [[clang::reinitializes]] Result<void> init(const char* path) {
- return init(path, mapRetrieveRW(path));
- }
-
-
-#ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
- // due to Android SELinux limitations which prevent map creation by anyone besides the bpfloader
- // this should only ever be used by test code, it is equivalent to:
- // .reset(createMap(type, keysize, valuesize, max_entries, map_flags)
- // TODO: derive map_flags from BpfMap vs BpfMapRO
- [[clang::reinitializes]] Result<void> resetMap(bpf_map_type map_type,
- uint32_t max_entries,
- uint32_t map_flags = 0) {
- mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags));
- if (!mMapFd.ok()) return ErrnoErrorf("Unable to create map.");
- return {};
- }
-#endif
-
- // Iterate through the map and handle each key retrieved based on the filter
- // without modification of map content.
- Result<void> iterate(
- const function<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.
- Result<void> iterateWithValue(
- const function<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
- Result<void> iterate(
- const function<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.
- Result<void> iterateWithValue(
- const function<Result<void>(const Key& key, const Value& value,
- BpfMap<Key, Value>& map)>& filter);
-
-#ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
- const unique_fd& getMap() const { return mMapFd; };
-
- // Copy assignment operator - due to need for fd duping, should not be used in non-test code.
- BpfMap<Key, Value>& operator=(const BpfMap<Key, Value>& other) {
- if (this != &other) mMapFd.reset(fcntl(other.mMapFd.get(), F_DUPFD_CLOEXEC, 0));
- return *this;
- }
-#else
- BpfMap<Key, Value>& operator=(const BpfMap<Key, Value>&) = delete;
-#endif
-
- // Move assignment operator
- BpfMap<Key, Value>& operator=(BpfMap<Key, Value>&& other) noexcept {
- if (this != &other) {
- mMapFd = std::move(other.mMapFd);
- other.reset();
- }
- return *this;
- }
-
- void reset(unique_fd fd) = delete;
-
-#ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
- // Note that unique_fd.reset() carefully saves and restores the errno,
- // and BpfMap.reset() won't touch the errno if passed in fd is negative either,
- // hence you can do something like BpfMap.reset(systemcall()) and then
- // check BpfMap.isValid() and look at errno and see why systemcall() failed.
- [[clang::reinitializes]] void reset(int fd) {
- mMapFd.reset(fd);
- if (mMapFd.ok()) abortOnKeyOrValueSizeMismatch();
- }
-#endif
-
- [[clang::reinitializes]] void reset() {
- mMapFd.reset();
- }
-
- bool isValid() const { return mMapFd.ok(); }
-
- 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();
- }
- }
- }
-
- Result<bool> isEmpty() const {
- auto key = getFirstKey();
- if (key.ok()) return false;
- if (key.error().code() == ENOENT) return true;
- return key.error();
- }
-
- private:
- unique_fd mMapFd;
-};
-
-template <class Key, class Value>
-Result<void> BpfMap<Key, Value>::iterate(
- const function<Result<void>(const Key& key,
- const BpfMap<Key, Value>& map)>& filter) const {
- Result<Key> curKey = getFirstKey();
- while (curKey.ok()) {
- const Result<Key>& nextKey = getNextKey(curKey.value());
- 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>
-Result<void> BpfMap<Key, Value>::iterateWithValue(
- const function<Result<void>(const Key& key, const Value& value,
- const BpfMap<Key, Value>& map)>& filter) const {
- Result<Key> curKey = getFirstKey();
- while (curKey.ok()) {
- const Result<Key>& nextKey = getNextKey(curKey.value());
- Result<Value> curValue = readValue(curKey.value());
- if (!curValue.ok()) return curValue.error();
- 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>
-Result<void> BpfMap<Key, Value>::iterate(
- const function<Result<void>(const Key& key,
- BpfMap<Key, Value>& map)>& filter) {
- Result<Key> curKey = getFirstKey();
- while (curKey.ok()) {
- const Result<Key>& nextKey = getNextKey(curKey.value());
- 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>
-Result<void> BpfMap<Key, Value>::iterateWithValue(
- const function<Result<void>(const Key& key, const Value& value,
- BpfMap<Key, Value>& map)>& filter) {
- Result<Key> curKey = getFirstKey();
- while (curKey.ok()) {
- const Result<Key>& nextKey = getNextKey(curKey.value());
- Result<Value> curValue = readValue(curKey.value());
- if (!curValue.ok()) return curValue.error();
- 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:
- BpfMapRO<Key, Value>() {};
-
- explicit BpfMapRO<Key, Value>(const char* pathname)
- : BpfMap<Key, Value>(pathname, BPF_F_RDONLY) {}
-
- // Function that tries to get map from a pinned path.
- [[clang::reinitializes]] Result<void> init(const char* path) {
- return BpfMap<Key,Value>::init(path, mapRetrieveRO(path));
- }
-};
-
-} // namespace bpf
-} // namespace android