1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <fstream>
18 #include <iostream>
19 #include <string>
20 #include <vector>
21
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <linux/inet_diag.h>
25 #include <linux/sock_diag.h>
26 #include <net/if.h>
27 #include <sys/socket.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 #include <gtest/gtest.h>
32
33 #include <android-base/stringprintf.h>
34 #include <android-base/strings.h>
35
36 #define BPF_MAP_MAKE_VISIBLE_FOR_TESTING
37 #include "bpf/BpfMap.h"
38 #include "bpf/BpfUtils.h"
39
40 using ::testing::Test;
41
42 namespace android {
43 namespace bpf {
44
45 using base::Result;
46 using base::unique_fd;
47
48 constexpr uint32_t TEST_MAP_SIZE = 10;
49 constexpr uint32_t TEST_KEY1 = 1;
50 constexpr uint32_t TEST_VALUE1 = 10;
51 constexpr const char PINNED_MAP_PATH[] = "/sys/fs/bpf/testMap";
52
53 class BpfMapTest : public testing::Test {
54 protected:
BpfMapTest()55 BpfMapTest() {}
56
SetUp()57 void SetUp() {
58 EXPECT_EQ(0, setrlimitForTest());
59 if (!access(PINNED_MAP_PATH, R_OK)) {
60 EXPECT_EQ(0, remove(PINNED_MAP_PATH));
61 }
62 }
63
TearDown()64 void TearDown() {
65 if (!access(PINNED_MAP_PATH, R_OK)) {
66 EXPECT_EQ(0, remove(PINNED_MAP_PATH));
67 }
68 }
69
checkMapInvalid(BpfMap<uint32_t,uint32_t> & map)70 void checkMapInvalid(BpfMap<uint32_t, uint32_t>& map) {
71 EXPECT_FALSE(map.isValid());
72 EXPECT_EQ(-1, map.getMap().get());
73 }
74
checkMapValid(BpfMap<uint32_t,uint32_t> & map)75 void checkMapValid(BpfMap<uint32_t, uint32_t>& map) {
76 EXPECT_LE(0, map.getMap().get());
77 EXPECT_TRUE(map.isValid());
78 }
79
writeToMapAndCheck(BpfMap<uint32_t,uint32_t> & map,uint32_t key,uint32_t value)80 void writeToMapAndCheck(BpfMap<uint32_t, uint32_t>& map, uint32_t key, uint32_t value) {
81 ASSERT_RESULT_OK(map.writeValue(key, value, BPF_ANY));
82 uint32_t value_read;
83 ASSERT_EQ(0, findMapEntry(map.getMap(), &key, &value_read));
84 checkValueAndStatus(value, value_read);
85 }
86
checkValueAndStatus(uint32_t refValue,Result<uint32_t> value)87 void checkValueAndStatus(uint32_t refValue, Result<uint32_t> value) {
88 ASSERT_RESULT_OK(value);
89 ASSERT_EQ(refValue, value.value());
90 }
91
populateMap(uint32_t total,BpfMap<uint32_t,uint32_t> & map)92 void populateMap(uint32_t total, BpfMap<uint32_t, uint32_t>& map) {
93 for (uint32_t key = 0; key < total; key++) {
94 uint32_t value = key * 10;
95 EXPECT_RESULT_OK(map.writeValue(key, value, BPF_ANY));
96 }
97 }
98
expectMapEmpty(BpfMap<uint32_t,uint32_t> & map)99 void expectMapEmpty(BpfMap<uint32_t, uint32_t>& map) {
100 Result<bool> isEmpty = map.isEmpty();
101 ASSERT_RESULT_OK(isEmpty);
102 ASSERT_TRUE(isEmpty.value());
103 }
104 };
105
TEST_F(BpfMapTest,constructor)106 TEST_F(BpfMapTest, constructor) {
107 BpfMap<uint32_t, uint32_t> testMap1;
108 checkMapInvalid(testMap1);
109
110 BpfMap<uint32_t, uint32_t> testMap2;
111 ASSERT_RESULT_OK(testMap2.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
112 checkMapValid(testMap2);
113 }
114
TEST_F(BpfMapTest,basicHelpers)115 TEST_F(BpfMapTest, basicHelpers) {
116 BpfMap<uint32_t, uint32_t> testMap;
117 ASSERT_RESULT_OK(testMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
118 uint32_t key = TEST_KEY1;
119 uint32_t value_write = TEST_VALUE1;
120 writeToMapAndCheck(testMap, key, value_write);
121 Result<uint32_t> value_read = testMap.readValue(key);
122 checkValueAndStatus(value_write, value_read);
123 Result<uint32_t> key_read = testMap.getFirstKey();
124 checkValueAndStatus(key, key_read);
125 ASSERT_RESULT_OK(testMap.deleteValue(key));
126 ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_read));
127 ASSERT_EQ(ENOENT, errno);
128 }
129
TEST_F(BpfMapTest,reset)130 TEST_F(BpfMapTest, reset) {
131 BpfMap<uint32_t, uint32_t> testMap;
132 ASSERT_RESULT_OK(testMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
133 uint32_t key = TEST_KEY1;
134 uint32_t value_write = TEST_VALUE1;
135 writeToMapAndCheck(testMap, key, value_write);
136
137 testMap.reset(-1);
138 checkMapInvalid(testMap);
139 ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_write));
140 ASSERT_EQ(EBADF, errno);
141 }
142
TEST_F(BpfMapTest,moveConstructor)143 TEST_F(BpfMapTest, moveConstructor) {
144 BpfMap<uint32_t, uint32_t> testMap1;
145 ASSERT_RESULT_OK(testMap1.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
146 BpfMap<uint32_t, uint32_t> testMap2;
147 testMap2 = std::move(testMap1);
148 uint32_t key = TEST_KEY1;
149 checkMapInvalid(testMap1);
150 uint32_t value = TEST_VALUE1;
151 writeToMapAndCheck(testMap2, key, value);
152 }
153
TEST_F(BpfMapTest,SetUpMap)154 TEST_F(BpfMapTest, SetUpMap) {
155 EXPECT_NE(0, access(PINNED_MAP_PATH, R_OK));
156 BpfMap<uint32_t, uint32_t> testMap1;
157 ASSERT_RESULT_OK(testMap1.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
158 ASSERT_EQ(0, bpfFdPin(testMap1.getMap(), PINNED_MAP_PATH));
159 EXPECT_EQ(0, access(PINNED_MAP_PATH, R_OK));
160 checkMapValid(testMap1);
161 BpfMap<uint32_t, uint32_t> testMap2;
162 EXPECT_RESULT_OK(testMap2.init(PINNED_MAP_PATH));
163 checkMapValid(testMap2);
164 uint32_t key = TEST_KEY1;
165 uint32_t value = TEST_VALUE1;
166 writeToMapAndCheck(testMap1, key, value);
167 Result<uint32_t> value_read = testMap2.readValue(key);
168 checkValueAndStatus(value, value_read);
169 }
170
TEST_F(BpfMapTest,iterate)171 TEST_F(BpfMapTest, iterate) {
172 BpfMap<uint32_t, uint32_t> testMap;
173 ASSERT_RESULT_OK(testMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
174 populateMap(TEST_MAP_SIZE, testMap);
175 int totalCount = 0;
176 int totalSum = 0;
177 const auto iterateWithDeletion = [&totalCount, &totalSum](const uint32_t& key,
178 BpfMap<uint32_t, uint32_t>& map) {
179 EXPECT_GE((uint32_t)TEST_MAP_SIZE, key);
180 totalCount++;
181 totalSum += key;
182 return map.deleteValue(key);
183 };
184 EXPECT_RESULT_OK(testMap.iterate(iterateWithDeletion));
185 EXPECT_EQ((int)TEST_MAP_SIZE, totalCount);
186 EXPECT_EQ(((1 + TEST_MAP_SIZE - 1) * (TEST_MAP_SIZE - 1)) / 2, (uint32_t)totalSum);
187 expectMapEmpty(testMap);
188 }
189
TEST_F(BpfMapTest,iterateWithValue)190 TEST_F(BpfMapTest, iterateWithValue) {
191 BpfMap<uint32_t, uint32_t> testMap;
192 ASSERT_RESULT_OK(testMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
193 populateMap(TEST_MAP_SIZE, testMap);
194 int totalCount = 0;
195 int totalSum = 0;
196 const auto iterateWithDeletion = [&totalCount, &totalSum](const uint32_t& key,
197 const uint32_t& value,
198 BpfMap<uint32_t, uint32_t>& map) {
199 EXPECT_GE((uint32_t)TEST_MAP_SIZE, key);
200 EXPECT_EQ(value, key * 10);
201 totalCount++;
202 totalSum += value;
203 return map.deleteValue(key);
204 };
205 EXPECT_RESULT_OK(testMap.iterateWithValue(iterateWithDeletion));
206 EXPECT_EQ((int)TEST_MAP_SIZE, totalCount);
207 EXPECT_EQ(((1 + TEST_MAP_SIZE - 1) * (TEST_MAP_SIZE - 1)) * 5, (uint32_t)totalSum);
208 expectMapEmpty(testMap);
209 }
210
TEST_F(BpfMapTest,mapIsEmpty)211 TEST_F(BpfMapTest, mapIsEmpty) {
212 BpfMap<uint32_t, uint32_t> testMap;
213 ASSERT_RESULT_OK(testMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC));
214 expectMapEmpty(testMap);
215 uint32_t key = TEST_KEY1;
216 uint32_t value_write = TEST_VALUE1;
217 writeToMapAndCheck(testMap, key, value_write);
218 Result<bool> isEmpty = testMap.isEmpty();
219 ASSERT_RESULT_OK(isEmpty);
220 ASSERT_FALSE(isEmpty.value());
221 ASSERT_RESULT_OK(testMap.deleteValue(key));
222 ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_write));
223 ASSERT_EQ(ENOENT, errno);
224 expectMapEmpty(testMap);
225 int entriesSeen = 0;
226 EXPECT_RESULT_OK(testMap.iterate(
227 [&entriesSeen](const unsigned int&,
228 const BpfMap<unsigned int, unsigned int>&) -> Result<void> {
229 entriesSeen++;
230 return {};
231 }));
232 EXPECT_EQ(0, entriesSeen);
233 EXPECT_RESULT_OK(testMap.iterateWithValue(
234 [&entriesSeen](const unsigned int&, const unsigned int&,
235 const BpfMap<unsigned int, unsigned int>&) -> Result<void> {
236 entriesSeen++;
237 return {};
238 }));
239 EXPECT_EQ(0, entriesSeen);
240 }
241
TEST_F(BpfMapTest,mapClear)242 TEST_F(BpfMapTest, mapClear) {
243 BpfMap<uint32_t, uint32_t> testMap;
244 ASSERT_RESULT_OK(testMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE));
245 populateMap(TEST_MAP_SIZE, testMap);
246 Result<bool> isEmpty = testMap.isEmpty();
247 ASSERT_RESULT_OK(isEmpty);
248 ASSERT_FALSE(*isEmpty);
249 ASSERT_RESULT_OK(testMap.clear());
250 expectMapEmpty(testMap);
251 }
252
253 } // namespace bpf
254 } // namespace android
255