diff options
Diffstat (limited to 'common/native/bpf_headers/BpfMapTest.cpp')
| -rw-r--r-- | common/native/bpf_headers/BpfMapTest.cpp | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/common/native/bpf_headers/BpfMapTest.cpp b/common/native/bpf_headers/BpfMapTest.cpp new file mode 100644 index 0000000..d0737b0 --- /dev/null +++ b/common/native/bpf_headers/BpfMapTest.cpp @@ -0,0 +1,244 @@ +/* + * 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. + */ + +#include <fstream> +#include <iostream> +#include <string> +#include <vector> + +#include <fcntl.h> +#include <inttypes.h> +#include <linux/inet_diag.h> +#include <linux/sock_diag.h> +#include <net/if.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include <gtest/gtest.h> + +#include <android-base/stringprintf.h> +#include <android-base/strings.h> + +#include "bpf/BpfMap.h" +#include "bpf/BpfUtils.h" + +using ::testing::Test; + +namespace android { +namespace bpf { + +using base::Result; +using base::unique_fd; + +constexpr uint32_t TEST_MAP_SIZE = 10; +constexpr uint32_t TEST_KEY1 = 1; +constexpr uint32_t TEST_VALUE1 = 10; +constexpr const char PINNED_MAP_PATH[] = "/sys/fs/bpf/testMap"; + +class BpfMapTest : public testing::Test { + protected: + BpfMapTest() {} + + void SetUp() { + EXPECT_EQ(0, setrlimitForTest()); + if (!access(PINNED_MAP_PATH, R_OK)) { + EXPECT_EQ(0, remove(PINNED_MAP_PATH)); + } + } + + void TearDown() { + if (!access(PINNED_MAP_PATH, R_OK)) { + EXPECT_EQ(0, remove(PINNED_MAP_PATH)); + } + } + + void checkMapInvalid(BpfMap<uint32_t, uint32_t>& map) { + EXPECT_FALSE(map.isValid()); + EXPECT_EQ(-1, map.getMap().get()); + } + + void checkMapValid(BpfMap<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) { + ASSERT_RESULT_OK(map.writeValue(key, value, BPF_ANY)); + uint32_t value_read; + ASSERT_EQ(0, findMapEntry(map.getMap(), &key, &value_read)); + checkValueAndStatus(value, value_read); + } + + void checkValueAndStatus(uint32_t refValue, Result<uint32_t> value) { + ASSERT_RESULT_OK(value); + ASSERT_EQ(refValue, value.value()); + } + + void populateMap(uint32_t total, BpfMap<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) { + 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); + + BpfMap<uint32_t, uint32_t> testMap2(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC); + checkMapValid(testMap2); +} + +TEST_F(BpfMapTest, basicHelpers) { + BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC); + uint32_t key = TEST_KEY1; + uint32_t value_write = TEST_VALUE1; + writeToMapAndCheck(testMap, key, value_write); + Result<uint32_t> value_read = testMap.readValue(key); + checkValueAndStatus(value_write, value_read); + Result<uint32_t> key_read = testMap.getFirstKey(); + checkValueAndStatus(key, key_read); + ASSERT_RESULT_OK(testMap.deleteValue(key)); + ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_read)); + ASSERT_EQ(ENOENT, errno); +} + +TEST_F(BpfMapTest, reset) { + BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC); + uint32_t key = TEST_KEY1; + uint32_t value_write = TEST_VALUE1; + writeToMapAndCheck(testMap, key, value_write); + + testMap.reset(-1); + checkMapInvalid(testMap); + ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_write)); + ASSERT_EQ(EBADF, errno); +} + +TEST_F(BpfMapTest, moveConstructor) { + BpfMap<uint32_t, uint32_t> testMap1(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC); + BpfMap<uint32_t, uint32_t> testMap2; + testMap2 = std::move(testMap1); + uint32_t key = TEST_KEY1; + checkMapInvalid(testMap1); + uint32_t value = TEST_VALUE1; + writeToMapAndCheck(testMap2, key, value); +} + +TEST_F(BpfMapTest, SetUpMap) { + EXPECT_NE(0, access(PINNED_MAP_PATH, R_OK)); + BpfMap<uint32_t, uint32_t> testMap1(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; + EXPECT_RESULT_OK(testMap2.init(PINNED_MAP_PATH)); + checkMapValid(testMap2); + uint32_t key = TEST_KEY1; + uint32_t value = TEST_VALUE1; + writeToMapAndCheck(testMap1, key, value); + Result<uint32_t> value_read = testMap2.readValue(key); + checkValueAndStatus(value, value_read); +} + +TEST_F(BpfMapTest, iterate) { + BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC); + populateMap(TEST_MAP_SIZE, testMap); + int totalCount = 0; + int totalSum = 0; + const auto iterateWithDeletion = [&totalCount, &totalSum](const uint32_t& key, + BpfMap<uint32_t, uint32_t>& map) { + EXPECT_GE((uint32_t)TEST_MAP_SIZE, key); + totalCount++; + totalSum += key; + return map.deleteValue(key); + }; + EXPECT_RESULT_OK(testMap.iterate(iterateWithDeletion)); + EXPECT_EQ((int)TEST_MAP_SIZE, totalCount); + EXPECT_EQ(((1 + TEST_MAP_SIZE - 1) * (TEST_MAP_SIZE - 1)) / 2, (uint32_t)totalSum); + expectMapEmpty(testMap); +} + +TEST_F(BpfMapTest, iterateWithValue) { + BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC); + populateMap(TEST_MAP_SIZE, testMap); + int totalCount = 0; + int totalSum = 0; + const auto iterateWithDeletion = [&totalCount, &totalSum](const uint32_t& key, + const uint32_t& value, + BpfMap<uint32_t, uint32_t>& map) { + EXPECT_GE((uint32_t)TEST_MAP_SIZE, key); + EXPECT_EQ(value, key * 10); + totalCount++; + totalSum += value; + return map.deleteValue(key); + }; + EXPECT_RESULT_OK(testMap.iterateWithValue(iterateWithDeletion)); + EXPECT_EQ((int)TEST_MAP_SIZE, totalCount); + EXPECT_EQ(((1 + TEST_MAP_SIZE - 1) * (TEST_MAP_SIZE - 1)) * 5, (uint32_t)totalSum); + expectMapEmpty(testMap); +} + +TEST_F(BpfMapTest, mapIsEmpty) { + BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC); + expectMapEmpty(testMap); + uint32_t key = TEST_KEY1; + uint32_t value_write = TEST_VALUE1; + writeToMapAndCheck(testMap, key, value_write); + Result<bool> isEmpty = testMap.isEmpty(); + ASSERT_RESULT_OK(isEmpty); + ASSERT_FALSE(isEmpty.value()); + ASSERT_RESULT_OK(testMap.deleteValue(key)); + ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_write)); + ASSERT_EQ(ENOENT, errno); + expectMapEmpty(testMap); + int entriesSeen = 0; + EXPECT_RESULT_OK(testMap.iterate( + [&entriesSeen](const unsigned int&, + const BpfMap<unsigned int, unsigned int>&) -> Result<void> { + entriesSeen++; + return {}; + })); + EXPECT_EQ(0, entriesSeen); + EXPECT_RESULT_OK(testMap.iterateWithValue( + [&entriesSeen](const unsigned int&, const unsigned int&, + const BpfMap<unsigned int, unsigned int>&) -> Result<void> { + entriesSeen++; + return {}; + })); + EXPECT_EQ(0, entriesSeen); +} + +TEST_F(BpfMapTest, mapClear) { + BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC); + populateMap(TEST_MAP_SIZE, testMap); + Result<bool> isEmpty = testMap.isEmpty(); + ASSERT_RESULT_OK(isEmpty); + ASSERT_FALSE(*isEmpty); + ASSERT_RESULT_OK(testMap.clear()); + expectMapEmpty(testMap); +} + +} // namespace bpf +} // namespace android |
