blob: 951bda94263a1afb22baa4f1e76213ba34f017ba [file] [log] [blame]
Chenbo Fengdc4e3252017-12-22 11:00:52 -08001/*
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#include <netdutils/MockSyscalls.h>
37#include "bpf/BpfNetworkStats.h"
38#include "bpf/BpfUtils.h"
39
40using namespace android::bpf;
41
42using ::testing::_;
43using ::testing::ByMove;
44using ::testing::Invoke;
45using ::testing::Return;
46using ::testing::StrictMock;
47using ::testing::Test;
48
49namespace android {
50namespace bpf {
51
52using base::unique_fd;
53using netdutils::status::ok;
54
55constexpr int TEST_MAP_SIZE = 10;
56constexpr uid_t TEST_UID1 = 10086;
57constexpr uid_t TEST_UID2 = 12345;
58constexpr uint32_t TEST_TAG = 42;
59constexpr int TEST_COUNTERSET0 = 0;
60constexpr int TEST_COUNTERSET1 = 1;
61constexpr int DEFAULT_COUNTERSET = 0;
62constexpr const int COUNTERSETS_LIMIT = 2;
63constexpr uint64_t TEST_BYTES0 = 1000;
64constexpr uint64_t TEST_BYTES1 = 2000;
65constexpr uint64_t TEST_BYTES2 = 3000;
66constexpr uint64_t TEST_BYTES3 = 4000;
67constexpr uint64_t TEST_PACKET0 = 100;
68constexpr uint64_t TEST_PACKET1 = 200;
69constexpr uint64_t TEST_PACKET2 = 200;
70constexpr uint64_t TEST_PACKET3 = 400;
71constexpr uint32_t IFACE0 = 1;
72constexpr uint32_t IFACE1 = 2;
73
74class BpfNetworkStatsHelperTest : public testing::Test {
75 protected:
76 BpfNetworkStatsHelperTest() {}
77 unique_fd mFakeCookieTagMap;
78 unique_fd mFakeUidStatsMap;
79 unique_fd mFakeTagStatsMap;
80
81 void SetUp() {
82 mFakeCookieTagMap = unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(uint64_t),
83 sizeof(struct UidTag), TEST_MAP_SIZE, 0));
84 ASSERT_LE(0, mFakeCookieTagMap);
85
86 mFakeUidStatsMap = unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
87 sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
88 ASSERT_LE(0, mFakeUidStatsMap);
89
90 mFakeTagStatsMap = unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
91 sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
92 ASSERT_LE(0, mFakeTagStatsMap);
93 }
94
95 void TearDown() {
96 mFakeCookieTagMap.reset();
97 mFakeUidStatsMap.reset();
98 mFakeTagStatsMap.reset();
99 }
100
101 void expectUidTag(uint64_t cookie, uid_t uid, uint32_t tag) {
102 struct UidTag tagResult;
103 EXPECT_EQ(0, findMapEntry(mFakeCookieTagMap, &cookie, &tagResult));
104 EXPECT_EQ(uid, tagResult.uid);
105 EXPECT_EQ(tag, tagResult.tag);
106 }
107
108 void populateFakeStats(uid_t uid, uint32_t tag, uint32_t ifaceIndex, uint32_t counterSet,
109 StatsValue* value, const base::unique_fd& map_fd) {
110 StatsKey key = {
111 .uid = (uint32_t)uid, .tag = tag, .counterSet = counterSet, .ifaceIndex = ifaceIndex};
112 EXPECT_EQ(0, writeToMapEntry(map_fd, &key, value, BPF_ANY));
113 }
114};
115
116// TEST to verify the behavior of bpf map when cocurrent deletion happens when
117// iterating the same map.
118TEST_F(BpfNetworkStatsHelperTest, TestIterateMapWithDeletion) {
119 for (int i = 0; i < 5; i++) {
120 uint64_t cookie = i + 1;
121 struct UidTag tag = {.uid = TEST_UID1, .tag = TEST_TAG};
122 EXPECT_EQ(0, writeToMapEntry(mFakeCookieTagMap, &cookie, &tag, BPF_ANY));
123 }
124 uint64_t curCookie = 0;
125 uint64_t nextCookie = 0;
126 struct UidTag tagResult;
127 EXPECT_EQ(0, getNextMapKey(mFakeCookieTagMap, &curCookie, &nextCookie));
128 uint64_t headOfMap = nextCookie;
129 curCookie = nextCookie;
130 // Find the second entry in the map, then immediately delete it.
131 EXPECT_EQ(0, getNextMapKey(mFakeCookieTagMap, &curCookie, &nextCookie));
132 EXPECT_EQ(0, deleteMapEntry(mFakeCookieTagMap, &nextCookie));
133 // Find the entry that is now immediately after headOfMap, then delete that.
134 EXPECT_EQ(0, getNextMapKey(mFakeCookieTagMap, &curCookie, &nextCookie));
135 EXPECT_EQ(0, deleteMapEntry(mFakeCookieTagMap, &nextCookie));
136 // Attempting to read an entry that has been deleted fails with ENOENT.
137 curCookie = nextCookie;
138 EXPECT_EQ(-1, findMapEntry(mFakeCookieTagMap, &curCookie, &tagResult));
139 EXPECT_EQ(ENOENT, errno);
140 // Finding the entry after our deleted entry restarts iteration from the beginning of the map.
141 EXPECT_EQ(0, getNextMapKey(mFakeCookieTagMap, &curCookie, &nextCookie));
142 EXPECT_EQ(headOfMap, nextCookie);
143}
144
145TEST_F(BpfNetworkStatsHelperTest, TestGetUidStatsTotal) {
146 StatsValue value1 = {.rxTcpBytes = TEST_BYTES0,
147 .rxTcpPackets = TEST_PACKET0,
148 .txTcpBytes = TEST_BYTES1,
149 .txTcpPackets = TEST_PACKET1,
150 .rxUdpPackets = 0,
151 .rxUdpBytes = 0,
152 .txUdpPackets = 0,
153 .txUdpBytes = 0,
154 .rxOtherBytes = TEST_BYTES2,
155 .rxOtherPackets = TEST_PACKET2,
156 .txOtherBytes = TEST_BYTES3,
157 .txOtherPackets = TEST_PACKET3};
158 populateFakeStats(TEST_UID1, 0, IFACE0, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
159 populateFakeStats(TEST_UID1, 0, IFACE0, TEST_COUNTERSET1, &value1, mFakeUidStatsMap);
160 populateFakeStats(TEST_UID2, 0, IFACE0, TEST_COUNTERSET1, &value1, mFakeUidStatsMap);
161 Stats result1 = {};
162 ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID1, &result1, mFakeUidStatsMap));
163 ASSERT_EQ((TEST_PACKET0 + TEST_PACKET2) * 2, result1.rxPackets);
164 ASSERT_EQ((TEST_BYTES0 + TEST_BYTES2) * 2, result1.rxBytes);
165 ASSERT_EQ((TEST_PACKET1 + TEST_PACKET3) * 2, result1.txPackets);
166 ASSERT_EQ((TEST_BYTES1 + TEST_BYTES3) * 2, result1.txBytes);
167 Stats result2 = {};
168 ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID2, &result2, mFakeUidStatsMap));
169 ASSERT_EQ((TEST_PACKET0 + TEST_PACKET2), result2.rxPackets);
170 ASSERT_EQ((TEST_BYTES0 + TEST_BYTES2), result2.rxBytes);
171 ASSERT_EQ((TEST_PACKET1 + TEST_PACKET3), result2.txPackets);
172 ASSERT_EQ((TEST_BYTES1 + TEST_BYTES3), result2.txBytes);
173 std::vector<stats_line> lines;
174 std::vector<std::string> ifaces;
175 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, TEST_UID1, mFakeUidStatsMap));
176 ASSERT_EQ((unsigned long)2, lines.size());
177 lines.clear();
178 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, TEST_UID2, mFakeUidStatsMap));
179 ASSERT_EQ((unsigned long)1, lines.size());
180}
181
182TEST_F(BpfNetworkStatsHelperTest, TestGetIfaceStatsInternal) {
183 const char* fakeFilePath = "/data/local/tmp/testIface.txt";
184 std::ofstream fakeProcFile(fakeFilePath);
185 ASSERT_TRUE(fakeProcFile.is_open());
186 fakeProcFile << "Inter-| Receive | Transmit "
187 " \n";
188 fakeProcFile << " face |bytes packets errs drop fifo frame compressed multicast|bytes "
189 "packets errs drop fifo colls carrier compressed\n";
190 fakeProcFile << " lo: 8308 116 0 0 0 0 0 0 8308 "
191 " 116 0 0 0 0 0 0\n";
192 fakeProcFile << "rmnet0: 1507570 2205 0 0 0 0 0 0 489339 "
193 "2237 0 0 0 0 0 0\n";
194 fakeProcFile << " ifb0: 52454 151 0 151 0 0 0 0 0 "
195 " 0 0 0 0 0 0 0\n";
196 fakeProcFile << " ifb1: 52454 151 0 151 0 0 0 0 0 "
197 " 0 0 0 0 0 0 0\n";
198 fakeProcFile << " sit0: 0 0 0 0 0 0 0 0 0 "
199 " 0 148 0 0 0 0 0\n";
200 fakeProcFile << "ip6tnl0: 0 0 0 0 0 0 0 0 0 "
201 " 0 151 151 0 0 0 0\n";
202 fakeProcFile.close();
203 const char* iface = "lo";
204 Stats result1 = {};
205 ASSERT_EQ(0, bpfGetIfaceStatsInternal(iface, &result1, fakeFilePath));
206 EXPECT_EQ(116UL, result1.rxPackets);
207 EXPECT_EQ(8308UL, result1.rxBytes);
208 EXPECT_EQ(116UL, result1.txPackets);
209 EXPECT_EQ(8308UL, result1.txBytes);
210 Stats result2 = {};
211 const char* iface2 = "rmnet0";
212 EXPECT_EQ(0, bpfGetIfaceStatsInternal(iface2, &result2, fakeFilePath));
213 EXPECT_EQ(2205UL, result2.rxPackets);
214 EXPECT_EQ(1507570UL, result2.rxBytes);
215 EXPECT_EQ(2237UL, result2.txPackets);
216 EXPECT_EQ(489339UL, result2.txBytes);
217}
218
219TEST_F(BpfNetworkStatsHelperTest, TestGetStatsDetail) {
220 const char* iface = "lo";
221 int ifaceIndex = if_nametoindex(iface);
222 ASSERT_LT(0, ifaceIndex);
223 StatsValue value1 = {.rxTcpBytes = TEST_BYTES0,
224 .rxTcpPackets = TEST_PACKET0,
225 .txTcpBytes = TEST_BYTES1,
226 .txTcpPackets = TEST_PACKET1,
227 .rxUdpPackets = 0,
228 .rxUdpBytes = 0,
229 .txUdpPackets = 0,
230 .txUdpBytes = 0,
231 .rxOtherBytes = TEST_BYTES2,
232 .rxOtherPackets = TEST_PACKET2,
233 .txOtherBytes = TEST_BYTES3,
234 .txOtherPackets = TEST_PACKET3};
235 populateFakeStats(0, 0, 0, COUNTERSETS_LIMIT, &value1, mFakeTagStatsMap);
236 populateFakeStats(TEST_UID1, TEST_TAG, ifaceIndex, TEST_COUNTERSET0, &value1, mFakeTagStatsMap);
237 populateFakeStats(TEST_UID1, TEST_TAG, ifaceIndex + 1, TEST_COUNTERSET0, &value1,
238 mFakeTagStatsMap);
239 populateFakeStats(TEST_UID1, TEST_TAG + 1, ifaceIndex, TEST_COUNTERSET0, &value1,
240 mFakeTagStatsMap);
241 populateFakeStats(TEST_UID2, TEST_TAG, ifaceIndex, TEST_COUNTERSET0, &value1, mFakeTagStatsMap);
242 std::vector<stats_line> lines;
243 std::vector<std::string> ifaces;
244 ASSERT_EQ(0, parseBpfTagStatsDetail(&lines, ifaces, TAG_ALL, UID_ALL, mFakeTagStatsMap));
245 ASSERT_EQ((unsigned long)4, lines.size());
246 lines.clear();
247 ASSERT_EQ(0, parseBpfTagStatsDetail(&lines, ifaces, TAG_ALL, TEST_UID1, mFakeTagStatsMap));
248 ASSERT_EQ((unsigned long)3, lines.size());
249 lines.clear();
250 ASSERT_EQ(0, parseBpfTagStatsDetail(&lines, ifaces, TEST_TAG, TEST_UID1, mFakeTagStatsMap));
251 ASSERT_EQ((unsigned long)2, lines.size());
252 lines.clear();
253 ifaces.push_back(std::string(iface));
254 ASSERT_EQ(0, parseBpfTagStatsDetail(&lines, ifaces, TEST_TAG, TEST_UID1, mFakeTagStatsMap));
255 ASSERT_EQ((unsigned long)1, lines.size());
256}
257
258TEST_F(BpfNetworkStatsHelperTest, TestGetStatsWithSkippedIface) {
259 const char* iface = "lo";
260 int ifaceIndex = if_nametoindex(iface);
261 ASSERT_LT(0, ifaceIndex);
262 StatsValue value1 = {.rxTcpBytes = TEST_BYTES0,
263 .rxTcpPackets = TEST_PACKET0,
264 .txTcpBytes = TEST_BYTES1,
265 .txTcpPackets = TEST_PACKET1,
266 .rxUdpPackets = 0,
267 .rxUdpBytes = 0,
268 .txUdpPackets = 0,
269 .txUdpBytes = 0,
270 .rxOtherBytes = TEST_BYTES2,
271 .rxOtherPackets = TEST_PACKET2,
272 .txOtherBytes = TEST_BYTES3,
273 .txOtherPackets = TEST_PACKET3};
274 populateFakeStats(0, 0, 0, COUNTERSETS_LIMIT, &value1, mFakeUidStatsMap);
275 populateFakeStats(TEST_UID1, 0, ifaceIndex, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
276 populateFakeStats(TEST_UID1, 0, ifaceIndex + 1, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
277 populateFakeStats(TEST_UID1, 0, ifaceIndex, TEST_COUNTERSET1, &value1, mFakeUidStatsMap);
278 populateFakeStats(TEST_UID2, 0, ifaceIndex, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
279 std::vector<stats_line> lines;
280 std::vector<std::string> ifaces;
281 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, -1, mFakeUidStatsMap));
282 ASSERT_EQ((unsigned long)4, lines.size());
283 lines.clear();
284 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, TEST_UID1, mFakeUidStatsMap));
285 ASSERT_EQ((unsigned long)3, lines.size());
286 lines.clear();
287 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, TEST_UID2, mFakeUidStatsMap));
288 ASSERT_EQ((unsigned long)1, lines.size());
289 lines.clear();
290 ifaces.push_back(std::string(iface));
291 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, TEST_UID1, mFakeUidStatsMap));
292 ASSERT_EQ((unsigned long)2, lines.size());
293}
294
295} // namespace bpf
296} // namespace android