BpfMap.h: introduce BpfMapRW
which allows writes, but not deletes. This is technically what bpf map arrays are capable of. Test: TreeHugger Flag: EXEMPT mainline Signed-off-by: Maciej Żenczykowski <maze@google.com> Change-Id: I1f73bea5428c190af17442539c296282ce9e0786
This commit is contained in:
parent
70bdbb79ce
commit
8b96286200
9 changed files with 70 additions and 50 deletions
|
|
@ -67,17 +67,17 @@ class BpfMapTest : public testing::Test {
|
|||
}
|
||||
}
|
||||
|
||||
void checkMapInvalid(BpfMap<uint32_t, uint32_t>& map) {
|
||||
void checkMapInvalid(BpfMapRO<uint32_t, uint32_t>& map) {
|
||||
EXPECT_FALSE(map.isValid());
|
||||
EXPECT_EQ(-1, map.getMap().get());
|
||||
}
|
||||
|
||||
void checkMapValid(BpfMap<uint32_t, uint32_t>& map) {
|
||||
void checkMapValid(BpfMapRO<uint32_t, uint32_t>& map) {
|
||||
EXPECT_LE(0, map.getMap().get());
|
||||
EXPECT_TRUE(map.isValid());
|
||||
}
|
||||
|
||||
void writeToMapAndCheck(BpfMap<uint32_t, uint32_t>& map, uint32_t key, uint32_t value) {
|
||||
void writeToMapAndCheck(BpfMapRW<uint32_t, uint32_t>& map, uint32_t key, uint32_t value) {
|
||||
ASSERT_RESULT_OK(map.writeValue(key, value, BPF_ANY));
|
||||
uint32_t value_read;
|
||||
ASSERT_EQ(0, findMapEntry(map.getMap(), &key, &value_read));
|
||||
|
|
@ -89,27 +89,39 @@ class BpfMapTest : public testing::Test {
|
|||
ASSERT_EQ(refValue, value.value());
|
||||
}
|
||||
|
||||
void populateMap(uint32_t total, BpfMap<uint32_t, uint32_t>& map) {
|
||||
void populateMap(uint32_t total, BpfMapRW<uint32_t, uint32_t>& map) {
|
||||
for (uint32_t key = 0; key < total; key++) {
|
||||
uint32_t value = key * 10;
|
||||
EXPECT_RESULT_OK(map.writeValue(key, value, BPF_ANY));
|
||||
}
|
||||
}
|
||||
|
||||
void expectMapEmpty(BpfMap<uint32_t, uint32_t>& map) {
|
||||
void expectMapEmpty(BpfMapRO<uint32_t, uint32_t>& map) {
|
||||
Result<bool> isEmpty = map.isEmpty();
|
||||
ASSERT_RESULT_OK(isEmpty);
|
||||
ASSERT_TRUE(isEmpty.value());
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(BpfMapTest, constructor) {
|
||||
BpfMap<uint32_t, uint32_t> testMap1;
|
||||
checkMapInvalid(testMap1);
|
||||
TEST_F(BpfMapTest, constructorRO) {
|
||||
BpfMapRO<uint32_t, uint32_t> testMap;
|
||||
checkMapInvalid(testMap);
|
||||
}
|
||||
|
||||
BpfMap<uint32_t, uint32_t> testMap2;
|
||||
ASSERT_RESULT_OK(testMap2.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
|
||||
checkMapValid(testMap2);
|
||||
TEST_F(BpfMapTest, constructorRW) {
|
||||
BpfMapRW<uint32_t, uint32_t> testMap;
|
||||
checkMapInvalid(testMap);
|
||||
|
||||
ASSERT_RESULT_OK(testMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
|
||||
checkMapValid(testMap);
|
||||
}
|
||||
|
||||
TEST_F(BpfMapTest, constructor) {
|
||||
BpfMap<uint32_t, uint32_t> testMap;
|
||||
checkMapInvalid(testMap);
|
||||
|
||||
ASSERT_RESULT_OK(testMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
|
||||
checkMapValid(testMap);
|
||||
}
|
||||
|
||||
TEST_F(BpfMapTest, basicHelpers) {
|
||||
|
|
@ -128,7 +140,7 @@ TEST_F(BpfMapTest, basicHelpers) {
|
|||
}
|
||||
|
||||
TEST_F(BpfMapTest, reset) {
|
||||
BpfMap<uint32_t, uint32_t> testMap;
|
||||
BpfMapRW<uint32_t, uint32_t> testMap;
|
||||
ASSERT_RESULT_OK(testMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
|
||||
uint32_t key = TEST_KEY1;
|
||||
uint32_t value_write = TEST_VALUE1;
|
||||
|
|
@ -141,9 +153,9 @@ TEST_F(BpfMapTest, reset) {
|
|||
}
|
||||
|
||||
TEST_F(BpfMapTest, moveConstructor) {
|
||||
BpfMap<uint32_t, uint32_t> testMap1;
|
||||
BpfMapRW<uint32_t, uint32_t> testMap1;
|
||||
ASSERT_RESULT_OK(testMap1.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
|
||||
BpfMap<uint32_t, uint32_t> testMap2;
|
||||
BpfMapRW<uint32_t, uint32_t> testMap2;
|
||||
testMap2 = std::move(testMap1);
|
||||
uint32_t key = TEST_KEY1;
|
||||
checkMapInvalid(testMap1);
|
||||
|
|
@ -153,12 +165,12 @@ TEST_F(BpfMapTest, moveConstructor) {
|
|||
|
||||
TEST_F(BpfMapTest, SetUpMap) {
|
||||
EXPECT_NE(0, access(PINNED_MAP_PATH, R_OK));
|
||||
BpfMap<uint32_t, uint32_t> testMap1;
|
||||
BpfMapRW<uint32_t, uint32_t> testMap1;
|
||||
ASSERT_RESULT_OK(testMap1.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
|
||||
ASSERT_EQ(0, bpfFdPin(testMap1.getMap(), PINNED_MAP_PATH));
|
||||
EXPECT_EQ(0, access(PINNED_MAP_PATH, R_OK));
|
||||
checkMapValid(testMap1);
|
||||
BpfMap<uint32_t, uint32_t> testMap2;
|
||||
BpfMapRW<uint32_t, uint32_t> testMap2;
|
||||
EXPECT_RESULT_OK(testMap2.init(PINNED_MAP_PATH));
|
||||
checkMapValid(testMap2);
|
||||
uint32_t key = TEST_KEY1;
|
||||
|
|
@ -250,7 +262,7 @@ TEST_F(BpfMapTest, mapClear) {
|
|||
}
|
||||
|
||||
TEST_F(BpfMapTest, testGTSbitmapTestOpen) {
|
||||
BpfMap<int, uint64_t> bitmap;
|
||||
BpfMapRW<int, uint64_t> bitmap;
|
||||
ASSERT_RESULT_OK(bitmap.init("/sys/fs/bpf/tethering/map_test_bitmap"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -257,19 +257,15 @@ class BpfMapRO {
|
|||
};
|
||||
|
||||
template <class Key, class Value>
|
||||
class BpfMap : public BpfMapRO<Key, Value> {
|
||||
class BpfMapRW : public BpfMapRO<Key, Value> {
|
||||
protected:
|
||||
using BpfMapRO<Key, Value>::mMapFd;
|
||||
using BpfMapRO<Key, Value>::abortOnMismatch;
|
||||
|
||||
public:
|
||||
using BpfMapRO<Key, Value>::getFirstKey;
|
||||
using BpfMapRO<Key, Value>::getNextKey;
|
||||
using BpfMapRO<Key, Value>::readValue;
|
||||
using BpfMapRO<Key, Value>::BpfMapRO;
|
||||
|
||||
BpfMap<Key, Value>() {};
|
||||
|
||||
explicit BpfMap<Key, Value>(const char* pathname) {
|
||||
explicit BpfMapRW<Key, Value>(const char* pathname) {
|
||||
mMapFd.reset(mapRetrieveRW(pathname));
|
||||
abortOnMismatch(/* writable */ true);
|
||||
}
|
||||
|
|
@ -286,6 +282,30 @@ class BpfMap : public BpfMapRO<Key, Value> {
|
|||
return {};
|
||||
}
|
||||
|
||||
#ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
|
||||
[[clang::reinitializes]] Result<void> resetMap(bpf_map_type map_type,
|
||||
uint32_t max_entries,
|
||||
uint32_t map_flags = 0) {
|
||||
if (map_flags & BPF_F_WRONLY) Abort(0, "map_flags is write-only");
|
||||
if (map_flags & BPF_F_RDONLY) Abort(0, "map_flags is read-only");
|
||||
mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries,
|
||||
map_flags));
|
||||
if (!mMapFd.ok()) return ErrnoErrorf("BpfMap::resetMap() failed");
|
||||
abortOnMismatch(/* writable */ true);
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <class Key, class Value>
|
||||
class BpfMap : public BpfMapRW<Key, Value> {
|
||||
protected:
|
||||
using BpfMapRW<Key, Value>::mMapFd;
|
||||
|
||||
public:
|
||||
using BpfMapRW<Key, Value>::BpfMapRW;
|
||||
using BpfMapRW<Key, Value>::getFirstKey;
|
||||
|
||||
Result<void> deleteValue(const Key& key) {
|
||||
if (deleteMapEntry(mMapFd, &key)) {
|
||||
return ErrnoErrorf("BpfMap::deleteValue() failed");
|
||||
|
|
@ -309,20 +329,6 @@ class BpfMap : public BpfMapRO<Key, Value> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
|
||||
[[clang::reinitializes]] Result<void> resetMap(bpf_map_type map_type,
|
||||
uint32_t max_entries,
|
||||
uint32_t map_flags = 0) {
|
||||
if (map_flags & BPF_F_WRONLY) Abort(0, "map_flags is write-only");
|
||||
if (map_flags & BPF_F_RDONLY) Abort(0, "map_flags is read-only");
|
||||
mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries,
|
||||
map_flags));
|
||||
if (!mMapFd.ok()) return ErrnoErrorf("BpfMap::resetMap() failed");
|
||||
abortOnMismatch(/* writable */ true);
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace bpf
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class BpfBasicTest : public NetNativeTestBase {
|
|||
};
|
||||
|
||||
TEST_F(BpfBasicTest, TestTagSocket) {
|
||||
BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH);
|
||||
BpfMapRO<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH);
|
||||
ASSERT_TRUE(cookieTagMap.isValid());
|
||||
int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
ASSERT_LE(0, sock);
|
||||
|
|
@ -72,7 +72,7 @@ TEST_F(BpfBasicTest, TestTagSocket) {
|
|||
}
|
||||
|
||||
TEST_F(BpfBasicTest, TestCloseSocketWithoutUntag) {
|
||||
BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH);
|
||||
BpfMapRO<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH);
|
||||
ASSERT_TRUE(cookieTagMap.isValid());
|
||||
int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
ASSERT_LE(0, sock);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
using android::bpf::BpfMap;
|
||||
using android::bpf::BpfMapRO;
|
||||
using android::bpf::BpfMapRW;
|
||||
|
||||
namespace android {
|
||||
namespace net {
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ namespace bpf {
|
|||
|
||||
using base::Result;
|
||||
|
||||
BpfMap<uint32_t, IfaceValue>& getIfaceIndexNameMap() {
|
||||
static BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
|
||||
BpfMapRW<uint32_t, IfaceValue>& getIfaceIndexNameMap() {
|
||||
static BpfMapRW<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
|
||||
return ifaceIndexNameMap;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,10 +72,10 @@ class BpfNetworkStatsHelperTest : public testing::Test {
|
|||
protected:
|
||||
BpfNetworkStatsHelperTest() {}
|
||||
BpfMap<uint64_t, UidTagValue> mFakeCookieTagMap;
|
||||
BpfMap<uint32_t, StatsValue> mFakeAppUidStatsMap;
|
||||
BpfMap<StatsKey, StatsValue> mFakeStatsMap;
|
||||
BpfMap<uint32_t, IfaceValue> mFakeIfaceIndexNameMap;
|
||||
BpfMap<uint32_t, StatsValue> mFakeIfaceStatsMap;
|
||||
BpfMapRW<uint32_t, StatsValue> mFakeAppUidStatsMap;
|
||||
BpfMapRW<StatsKey, StatsValue> mFakeStatsMap;
|
||||
BpfMapRW<uint32_t, IfaceValue> mFakeIfaceIndexNameMap;
|
||||
BpfMapRW<uint32_t, StatsValue> mFakeIfaceStatsMap;
|
||||
|
||||
IfIndexToNameFunc mIfIndex2Name = [this](const uint32_t ifindex){
|
||||
return mFakeIfaceIndexNameMap.readValue(ifindex);
|
||||
|
|
@ -108,7 +108,7 @@ class BpfNetworkStatsHelperTest : public testing::Test {
|
|||
}
|
||||
|
||||
void populateFakeStats(uid_t uid, uint32_t tag, uint32_t ifaceIndex, uint32_t counterSet,
|
||||
StatsValue value, BpfMap<StatsKey, StatsValue>& map) {
|
||||
StatsValue value, BpfMapRW<StatsKey, StatsValue>& map) {
|
||||
StatsKey key = {
|
||||
.uid = (uint32_t)uid, .tag = tag, .counterSet = counterSet, .ifaceIndex = ifaceIndex};
|
||||
EXPECT_RESULT_OK(map.writeValue(key, value, BPF_ANY));
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ bool operator<(const stats_line& lhs, const stats_line& rhs);
|
|||
|
||||
// This mirrors BpfMap.h's:
|
||||
// Result<Value> readValue(const Key key) const
|
||||
// for a BpfMap<uint32_t, IfaceValue>
|
||||
// for a BpfMapRW<uint32_t, IfaceValue>
|
||||
using IfIndexToNameFunc = std::function<Result<IfaceValue>(const uint32_t)>;
|
||||
|
||||
// For test only
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class NetworkTracePoller {
|
|||
std::unique_ptr<BpfRingbuf<PacketTrace>> mRingBuffer GUARDED_BY(mBufferMutex);
|
||||
|
||||
// The packet tracing config map (really a 1-element array).
|
||||
BpfMap<uint32_t, bool> mConfigurationMap GUARDED_BY(mMutex);
|
||||
BpfMapRW<uint32_t, bool> mConfigurationMap GUARDED_BY(mMutex);
|
||||
|
||||
// This must be the last member, causing it to be the first deleted. If it is
|
||||
// not, members required for callbacks can be deleted before it's stopped.
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
using android::base::Result;
|
||||
using android::bpf::BpfMap;
|
||||
using android::bpf::BpfMapRW;
|
||||
|
||||
class Firewall {
|
||||
public:
|
||||
|
|
@ -37,8 +38,8 @@ class Firewall {
|
|||
Result<bool> getDataSaverSetting();
|
||||
Result<void> setDataSaver(bool enabled);
|
||||
private:
|
||||
BpfMap<uint32_t, uint32_t> mConfigurationMap GUARDED_BY(mMutex);
|
||||
BpfMapRW<uint32_t, uint32_t> mConfigurationMap GUARDED_BY(mMutex);
|
||||
BpfMap<uint32_t, UidOwnerValue> mUidOwnerMap GUARDED_BY(mMutex);
|
||||
BpfMap<uint32_t, bool> mDataSaverEnabledMap GUARDED_BY(mMutex);
|
||||
BpfMapRW<uint32_t, bool> mDataSaverEnabledMap GUARDED_BY(mMutex);
|
||||
std::mutex mMutex;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue