blob: eb20e0e507ed19a6a02a3c67b905e6e25b28ac9b [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) {
Chenbo Fengeac6c472018-02-05 15:06:23 -0800146 StatsValue value1 = {.rxBytes = TEST_BYTES0,
147 .rxPackets = TEST_PACKET0,
148 .txBytes = TEST_BYTES1,
149 .txPackets = TEST_PACKET1,};
Chenbo Fengdc4e3252017-12-22 11:00:52 -0800150 populateFakeStats(TEST_UID1, 0, IFACE0, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
151 populateFakeStats(TEST_UID1, 0, IFACE0, TEST_COUNTERSET1, &value1, mFakeUidStatsMap);
152 populateFakeStats(TEST_UID2, 0, IFACE0, TEST_COUNTERSET1, &value1, mFakeUidStatsMap);
153 Stats result1 = {};
154 ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID1, &result1, mFakeUidStatsMap));
155 ASSERT_EQ((TEST_PACKET0 + TEST_PACKET2) * 2, result1.rxPackets);
156 ASSERT_EQ((TEST_BYTES0 + TEST_BYTES2) * 2, result1.rxBytes);
157 ASSERT_EQ((TEST_PACKET1 + TEST_PACKET3) * 2, result1.txPackets);
158 ASSERT_EQ((TEST_BYTES1 + TEST_BYTES3) * 2, result1.txBytes);
159 Stats result2 = {};
160 ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID2, &result2, mFakeUidStatsMap));
161 ASSERT_EQ((TEST_PACKET0 + TEST_PACKET2), result2.rxPackets);
162 ASSERT_EQ((TEST_BYTES0 + TEST_BYTES2), result2.rxBytes);
163 ASSERT_EQ((TEST_PACKET1 + TEST_PACKET3), result2.txPackets);
164 ASSERT_EQ((TEST_BYTES1 + TEST_BYTES3), result2.txBytes);
165 std::vector<stats_line> lines;
166 std::vector<std::string> ifaces;
167 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, TEST_UID1, mFakeUidStatsMap));
168 ASSERT_EQ((unsigned long)2, lines.size());
169 lines.clear();
170 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, TEST_UID2, mFakeUidStatsMap));
171 ASSERT_EQ((unsigned long)1, lines.size());
172}
173
174TEST_F(BpfNetworkStatsHelperTest, TestGetIfaceStatsInternal) {
175 const char* fakeFilePath = "/data/local/tmp/testIface.txt";
176 std::ofstream fakeProcFile(fakeFilePath);
177 ASSERT_TRUE(fakeProcFile.is_open());
178 fakeProcFile << "Inter-| Receive | Transmit "
179 " \n";
180 fakeProcFile << " face |bytes packets errs drop fifo frame compressed multicast|bytes "
181 "packets errs drop fifo colls carrier compressed\n";
182 fakeProcFile << " lo: 8308 116 0 0 0 0 0 0 8308 "
183 " 116 0 0 0 0 0 0\n";
184 fakeProcFile << "rmnet0: 1507570 2205 0 0 0 0 0 0 489339 "
185 "2237 0 0 0 0 0 0\n";
186 fakeProcFile << " ifb0: 52454 151 0 151 0 0 0 0 0 "
187 " 0 0 0 0 0 0 0\n";
188 fakeProcFile << " ifb1: 52454 151 0 151 0 0 0 0 0 "
189 " 0 0 0 0 0 0 0\n";
190 fakeProcFile << " sit0: 0 0 0 0 0 0 0 0 0 "
191 " 0 148 0 0 0 0 0\n";
192 fakeProcFile << "ip6tnl0: 0 0 0 0 0 0 0 0 0 "
193 " 0 151 151 0 0 0 0\n";
194 fakeProcFile.close();
195 const char* iface = "lo";
196 Stats result1 = {};
197 ASSERT_EQ(0, bpfGetIfaceStatsInternal(iface, &result1, fakeFilePath));
198 EXPECT_EQ(116UL, result1.rxPackets);
199 EXPECT_EQ(8308UL, result1.rxBytes);
200 EXPECT_EQ(116UL, result1.txPackets);
201 EXPECT_EQ(8308UL, result1.txBytes);
202 Stats result2 = {};
203 const char* iface2 = "rmnet0";
204 EXPECT_EQ(0, bpfGetIfaceStatsInternal(iface2, &result2, fakeFilePath));
205 EXPECT_EQ(2205UL, result2.rxPackets);
206 EXPECT_EQ(1507570UL, result2.rxBytes);
207 EXPECT_EQ(2237UL, result2.txPackets);
208 EXPECT_EQ(489339UL, result2.txBytes);
209}
210
211TEST_F(BpfNetworkStatsHelperTest, TestGetStatsDetail) {
212 const char* iface = "lo";
213 int ifaceIndex = if_nametoindex(iface);
214 ASSERT_LT(0, ifaceIndex);
Chenbo Fengeac6c472018-02-05 15:06:23 -0800215 StatsValue value1 = {.rxBytes = TEST_BYTES0,
216 .rxPackets = TEST_PACKET0,
217 .txBytes = TEST_BYTES1,
218 .txPackets = TEST_PACKET1,};
Chenbo Fengdc4e3252017-12-22 11:00:52 -0800219 populateFakeStats(0, 0, 0, COUNTERSETS_LIMIT, &value1, mFakeTagStatsMap);
220 populateFakeStats(TEST_UID1, TEST_TAG, ifaceIndex, TEST_COUNTERSET0, &value1, mFakeTagStatsMap);
221 populateFakeStats(TEST_UID1, TEST_TAG, ifaceIndex + 1, TEST_COUNTERSET0, &value1,
222 mFakeTagStatsMap);
223 populateFakeStats(TEST_UID1, TEST_TAG + 1, ifaceIndex, TEST_COUNTERSET0, &value1,
224 mFakeTagStatsMap);
225 populateFakeStats(TEST_UID2, TEST_TAG, ifaceIndex, TEST_COUNTERSET0, &value1, mFakeTagStatsMap);
226 std::vector<stats_line> lines;
227 std::vector<std::string> ifaces;
228 ASSERT_EQ(0, parseBpfTagStatsDetail(&lines, ifaces, TAG_ALL, UID_ALL, mFakeTagStatsMap));
229 ASSERT_EQ((unsigned long)4, lines.size());
230 lines.clear();
231 ASSERT_EQ(0, parseBpfTagStatsDetail(&lines, ifaces, TAG_ALL, TEST_UID1, mFakeTagStatsMap));
232 ASSERT_EQ((unsigned long)3, lines.size());
233 lines.clear();
234 ASSERT_EQ(0, parseBpfTagStatsDetail(&lines, ifaces, TEST_TAG, TEST_UID1, mFakeTagStatsMap));
235 ASSERT_EQ((unsigned long)2, lines.size());
236 lines.clear();
237 ifaces.push_back(std::string(iface));
238 ASSERT_EQ(0, parseBpfTagStatsDetail(&lines, ifaces, TEST_TAG, TEST_UID1, mFakeTagStatsMap));
239 ASSERT_EQ((unsigned long)1, lines.size());
240}
241
242TEST_F(BpfNetworkStatsHelperTest, TestGetStatsWithSkippedIface) {
243 const char* iface = "lo";
244 int ifaceIndex = if_nametoindex(iface);
245 ASSERT_LT(0, ifaceIndex);
Chenbo Fengeac6c472018-02-05 15:06:23 -0800246 StatsValue value1 = {.rxBytes = TEST_BYTES0,
247 .rxPackets = TEST_PACKET0,
248 .txBytes = TEST_BYTES1,
249 .txPackets = TEST_PACKET1,};
Chenbo Fengdc4e3252017-12-22 11:00:52 -0800250 populateFakeStats(0, 0, 0, COUNTERSETS_LIMIT, &value1, mFakeUidStatsMap);
251 populateFakeStats(TEST_UID1, 0, ifaceIndex, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
252 populateFakeStats(TEST_UID1, 0, ifaceIndex + 1, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
253 populateFakeStats(TEST_UID1, 0, ifaceIndex, TEST_COUNTERSET1, &value1, mFakeUidStatsMap);
254 populateFakeStats(TEST_UID2, 0, ifaceIndex, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
255 std::vector<stats_line> lines;
256 std::vector<std::string> ifaces;
257 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, -1, mFakeUidStatsMap));
258 ASSERT_EQ((unsigned long)4, lines.size());
259 lines.clear();
260 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, TEST_UID1, mFakeUidStatsMap));
261 ASSERT_EQ((unsigned long)3, lines.size());
262 lines.clear();
263 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, TEST_UID2, mFakeUidStatsMap));
264 ASSERT_EQ((unsigned long)1, lines.size());
265 lines.clear();
266 ifaces.push_back(std::string(iface));
267 ASSERT_EQ(0, parseBpfUidStatsDetail(&lines, ifaces, TEST_UID1, mFakeUidStatsMap));
268 ASSERT_EQ((unsigned long)2, lines.size());
269}
270
271} // namespace bpf
272} // namespace android