blob: acde1e65999ccab6a825b962846f0d19d40bd84e [file] [log] [blame]
Chenbo Fengf43bf812017-12-15 18:27:22 -08001/*
2 * Copyright (C) 2017 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 <inttypes.h>
18#include <net/if.h>
19#include <string.h>
20#include <unordered_set>
21
22#include <utils/Log.h>
23#include <utils/misc.h>
24
25#include "android-base/file.h"
26#include "android-base/strings.h"
27#include "android-base/unique_fd.h"
28#include "bpf/BpfNetworkStats.h"
29#include "bpf/BpfUtils.h"
30
Lorenzo Colittif9c654c2018-03-01 18:02:15 +090031#ifdef LOG_TAG
32#undef LOG_TAG
33#endif
34
35#define LOG_TAG "BpfNetworkStats"
36
Chenbo Fengf43bf812017-12-15 18:27:22 -080037namespace android {
38namespace bpf {
39
Chenbo Fengf43bf812017-12-15 18:27:22 -080040
Chenbo Feng7e974052018-02-28 22:57:21 -080041// The limit for stats received by a unknown interface;
Chenbo Feng33a4de12018-03-16 18:10:07 -070042static const int64_t MAX_UNKNOWN_IFACE_BYTES = 100*1000;
Chenbo Feng7e974052018-02-28 22:57:21 -080043
Chenbo Feng06c73642018-03-05 04:10:38 -080044static constexpr uint32_t BPF_OPEN_FLAGS = BPF_F_RDONLY;
Lorenzo Colittif9c654c2018-03-01 18:02:15 +090045
Chenbo Fengf43bf812017-12-15 18:27:22 -080046int bpfGetUidStatsInternal(uid_t uid, Stats* stats, const base::unique_fd& map_fd) {
Chenbo Feng16513482018-03-15 17:59:58 -070047 struct StatsKey nonExistentKey = NONEXISTENT_STATSKEY;
48 struct StatsValue dummyValue;
49 auto processUidStats = [uid, stats](void *key, const base::unique_fd& map_fd) {
50 if (((StatsKey *) key)->uid != uid) {
Chenbo Fengef1cab32018-04-13 19:50:49 -070051 return BPF_CONTINUE;
Chenbo Fengf43bf812017-12-15 18:27:22 -080052 }
Chenbo Feng16513482018-03-15 17:59:58 -070053 StatsValue statsEntry;
54 int ret = bpf::findMapEntry(map_fd, key, &statsEntry);
55 if (ret) return -errno;
56 stats->rxPackets += statsEntry.rxPackets;
57 stats->txPackets += statsEntry.txPackets;
58 stats->rxBytes += statsEntry.rxBytes;
59 stats->txBytes += statsEntry.txBytes;
Chenbo Fengef1cab32018-04-13 19:50:49 -070060 return BPF_CONTINUE;
Chenbo Feng16513482018-03-15 17:59:58 -070061 };
62 return bpfIterateMap(nonExistentKey, dummyValue, map_fd, processUidStats);
Chenbo Fengf43bf812017-12-15 18:27:22 -080063}
64
65int bpfGetUidStats(uid_t uid, Stats* stats) {
Lorenzo Colittif9c654c2018-03-01 18:02:15 +090066 base::unique_fd uidStatsMap(bpf::mapRetrieve(UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Fengf43bf812017-12-15 18:27:22 -080067 if (uidStatsMap < 0) {
68 int ret = -errno;
Lorenzo Colittif9c654c2018-03-01 18:02:15 +090069 ALOGE("Opening map fd from %s failed: %s", UID_STATS_MAP_PATH, strerror(errno));
Chenbo Fengf43bf812017-12-15 18:27:22 -080070 return ret;
71 }
72 return bpfGetUidStatsInternal(uid, stats, uidStatsMap);
73}
74
Chenbo Feng33a4de12018-03-16 18:10:07 -070075int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
76 const base::unique_fd& ifaceStatsMapFd,
77 const base::unique_fd& ifaceNameMapFd) {
78 uint32_t nonExistentKey = NONEXISTENT_IFACE_STATS_KEY;
79 struct StatsValue dummyValue;
80 int64_t unknownIfaceBytesTotal = 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -080081 stats->tcpRxPackets = -1;
82 stats->tcpTxPackets = -1;
Chenbo Feng33a4de12018-03-16 18:10:07 -070083 auto processIfaceStats = [iface, stats, &ifaceNameMapFd, &unknownIfaceBytesTotal](
84 void* key, const base::unique_fd& ifaceStatsMapFd) {
85 char ifname[IFNAMSIZ];
86 int ifIndex = *(int *)key;
87 if (getIfaceNameFromMap(ifaceNameMapFd, ifaceStatsMapFd, ifIndex, ifname, &ifIndex,
88 &unknownIfaceBytesTotal)) {
Chenbo Fengef1cab32018-04-13 19:50:49 -070089 return BPF_CONTINUE;
Chenbo Feng33a4de12018-03-16 18:10:07 -070090 }
91 if (!iface || !strcmp(iface, ifname)) {
92 StatsValue statsEntry;
93 int ret = bpf::findMapEntry(ifaceStatsMapFd, &ifIndex, &statsEntry);
94 if (ret) return -errno;
95 stats->rxPackets += statsEntry.rxPackets;
96 stats->txPackets += statsEntry.txPackets;
97 stats->rxBytes += statsEntry.rxBytes;
98 stats->txBytes += statsEntry.txBytes;
99 }
Chenbo Fengef1cab32018-04-13 19:50:49 -0700100 return BPF_CONTINUE;
Chenbo Feng33a4de12018-03-16 18:10:07 -0700101 };
102 return bpfIterateMap(nonExistentKey, dummyValue, ifaceStatsMapFd, processIfaceStats);
Chenbo Fengf43bf812017-12-15 18:27:22 -0800103}
104
105int bpfGetIfaceStats(const char* iface, Stats* stats) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700106 base::unique_fd ifaceStatsMap(bpf::mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
107 int ret;
108 if (ifaceStatsMap < 0) {
109 ret = -errno;
110 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
111 return ret;
112 }
113 base::unique_fd ifaceIndexNameMap(bpf::mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
114 if (ifaceIndexNameMap < 0) {
115 ret = -errno;
116 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
117 return ret;
118 }
119 return bpfGetIfaceStatsInternal(iface, stats, ifaceStatsMap, ifaceIndexNameMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -0800120}
121
122stats_line populateStatsEntry(const StatsKey& statsKey, const StatsValue& statsEntry,
123 const char* ifname) {
124 stats_line newLine;
125 strlcpy(newLine.iface, ifname, sizeof(newLine.iface));
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700126 newLine.uid = (int32_t)statsKey.uid;
127 newLine.set = (int32_t)statsKey.counterSet;
128 newLine.tag = (int32_t)statsKey.tag;
Chenbo Fengeac6c472018-02-05 15:06:23 -0800129 newLine.rxPackets = statsEntry.rxPackets;
130 newLine.txPackets = statsEntry.txPackets;
131 newLine.rxBytes = statsEntry.rxBytes;
132 newLine.txBytes = statsEntry.txBytes;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800133 return newLine;
134}
135
Chenbo Feng33a4de12018-03-16 18:10:07 -0700136void maybeLogUnknownIface(int ifaceIndex, const base::unique_fd& statsMapFd, void* curKey,
137 int64_t* unknownIfaceBytesTotal) {
138 // Have we already logged an error?
139 if (*unknownIfaceBytesTotal == -1) {
140 return;
141 }
142
143 // Are we undercounting enough data to be worth logging?
144 StatsValue statsEntry;
145 if (bpf::findMapEntry(statsMapFd, curKey, &statsEntry) < 0) {
146 // No data is being undercounted.
147 return;
148 }
149
150 *unknownIfaceBytesTotal += (statsEntry.rxBytes + statsEntry.txBytes);
151 if (*unknownIfaceBytesTotal >= MAX_UNKNOWN_IFACE_BYTES) {
152 ALOGE("Unknown name for ifindex %d with more than %" PRId64 " bytes of traffic",
153 ifaceIndex, *unknownIfaceBytesTotal);
154 *unknownIfaceBytesTotal = -1;
155 }
156}
157
Chenbo Feng7e974052018-02-28 22:57:21 -0800158int getIfaceNameFromMap(const base::unique_fd& ifaceMapFd, const base::unique_fd& statsMapFd,
Chenbo Feng33a4de12018-03-16 18:10:07 -0700159 uint32_t ifaceIndex, char* ifname, void* curKey,
160 int64_t* unknownIfaceBytesTotal) {
161 if (bpf::findMapEntry(ifaceMapFd, &ifaceIndex, ifname) < 0) {
162 maybeLogUnknownIface(ifaceIndex, statsMapFd, curKey, unknownIfaceBytesTotal);
Chenbo Feng7e974052018-02-28 22:57:21 -0800163 return -ENODEV;
164 }
165 return 0;
166}
167
Chenbo Feng16513482018-03-15 17:59:58 -0700168int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
169 const std::vector<std::string>& limitIfaces, int limitTag,
170 int limitUid, const base::unique_fd& statsMapFd,
171 const base::unique_fd& ifaceMapFd) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700172 int64_t unknownIfaceBytesTotal = 0;
Chenbo Feng16513482018-03-15 17:59:58 -0700173 struct StatsKey nonExistentKey = NONEXISTENT_STATSKEY;
174 struct StatsValue dummyValue;
175 auto processDetailUidStats = [lines, &limitIfaces, limitTag, limitUid,
176 &unknownIfaceBytesTotal, &ifaceMapFd]
177 (void* key, const base::unique_fd& statsMapFd) {
178 struct StatsKey curKey = * (struct StatsKey*)key;
Chenbo Feng7e974052018-02-28 22:57:21 -0800179 char ifname[IFNAMSIZ];
Chenbo Feng33a4de12018-03-16 18:10:07 -0700180 if (getIfaceNameFromMap(ifaceMapFd, statsMapFd, curKey.ifaceIndex, ifname, &curKey,
181 &unknownIfaceBytesTotal)) {
Chenbo Fengef1cab32018-04-13 19:50:49 -0700182 return BPF_CONTINUE;
Chenbo Feng7e974052018-02-28 22:57:21 -0800183 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800184 std::string ifnameStr(ifname);
185 if (limitIfaces.size() > 0 &&
186 std::find(limitIfaces.begin(), limitIfaces.end(), ifnameStr) == limitIfaces.end()) {
187 // Nothing matched; skip this line.
Chenbo Fengef1cab32018-04-13 19:50:49 -0700188 return BPF_CONTINUE;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800189 }
Chenbo Feng16513482018-03-15 17:59:58 -0700190 if (limitTag != TAG_ALL && uint32_t(limitTag) != curKey.tag) {
Chenbo Fengef1cab32018-04-13 19:50:49 -0700191 return BPF_CONTINUE;
Chenbo Feng16513482018-03-15 17:59:58 -0700192 }
193 if (limitUid != UID_ALL && uint32_t(limitUid) != curKey.uid) {
Chenbo Fengef1cab32018-04-13 19:50:49 -0700194 return BPF_CONTINUE;
Chenbo Feng16513482018-03-15 17:59:58 -0700195 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800196 StatsValue statsEntry;
Chenbo Feng7e974052018-02-28 22:57:21 -0800197 if (bpf::findMapEntry(statsMapFd, &curKey, &statsEntry) < 0) return -errno;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800198 lines->push_back(populateStatsEntry(curKey, statsEntry, ifname));
Chenbo Fengef1cab32018-04-13 19:50:49 -0700199 return BPF_CONTINUE;
Chenbo Feng16513482018-03-15 17:59:58 -0700200 };
201 return bpfIterateMap(nonExistentKey, dummyValue, statsMapFd, processDetailUidStats);
Chenbo Fengf43bf812017-12-15 18:27:22 -0800202}
203
204int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
205 const std::vector<std::string>& limitIfaces, int limitTag,
206 int limitUid) {
Chenbo Fengf43bf812017-12-15 18:27:22 -0800207 int ret = 0;
Chenbo Feng7e974052018-02-28 22:57:21 -0800208 base::unique_fd ifaceIndexNameMap(bpf::mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
209 if (ifaceIndexNameMap < 0) {
210 ret = -errno;
211 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
212 return ret;
213 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800214
Chenbo Feng16513482018-03-15 17:59:58 -0700215 // If the caller did not pass in TAG_NONE, read tag data.
216 if (limitTag != TAG_NONE) {
217 base::unique_fd tagStatsMap(bpf::mapRetrieve(TAG_STATS_MAP_PATH, BPF_OPEN_FLAGS));
218 if (tagStatsMap < 0) {
219 ret = -errno;
220 ALOGE("get tagStats map fd failed: %s", strerror(errno));
221 return ret;
222 }
223 ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid,
224 tagStatsMap, ifaceIndexNameMap);
225 if (ret) return ret;
226 }
227
228 // If the caller did not pass in a specific tag (i.e., if limitTag is TAG_NONE(0) or
229 // TAG_ALL(-1)) read UID data.
230 if (limitTag == TAG_NONE || limitTag == TAG_ALL) {
Lorenzo Colittif9c654c2018-03-01 18:02:15 +0900231 base::unique_fd uidStatsMap(bpf::mapRetrieve(UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Fengf43bf812017-12-15 18:27:22 -0800232 if (uidStatsMap < 0) {
233 ret = -errno;
Lorenzo Colittif9c654c2018-03-01 18:02:15 +0900234 ALOGE("Opening map fd from %s failed: %s", UID_STATS_MAP_PATH, strerror(errno));
Chenbo Fengf43bf812017-12-15 18:27:22 -0800235 return ret;
236 }
Chenbo Feng16513482018-03-15 17:59:58 -0700237 ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid,
238 uidStatsMap, ifaceIndexNameMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -0800239 }
240 return ret;
241}
242
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700243int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
244 const base::unique_fd& statsMapFd,
245 const base::unique_fd& ifaceMapFd) {
246 int64_t unknownIfaceBytesTotal = 0;
247 uint32_t nonExistentKey = NONEXISTENT_IFACE_STATS_KEY;
248 struct StatsValue dummyValue;
249 auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMapFd](
250 void* key, void* value, const base::unique_fd& statsMapFd) {
251 uint32_t ifIndex = *(uint32_t*)key;
252 char ifname[IFNAMSIZ];
253 if (getIfaceNameFromMap(ifaceMapFd, statsMapFd, ifIndex, ifname, &ifIndex,
254 &unknownIfaceBytesTotal)) {
255 return BPF_CONTINUE;
256 }
257 StatsValue* statsEntry = (StatsValue*)value;
258 StatsKey fakeKey = {
259 .uid = (uint32_t)UID_ALL, .counterSet = (uint32_t)SET_ALL, .tag = (uint32_t)TAG_NONE};
260 lines->push_back(populateStatsEntry(fakeKey, *statsEntry, ifname));
261 return BPF_CONTINUE;
262 };
263 return bpfIterateMapWithValue(nonExistentKey, dummyValue, statsMapFd, processDetailIfaceStats);
264}
265
266int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
267 int ret = 0;
268 base::unique_fd ifaceIndexNameMap(bpf::mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
269 if (ifaceIndexNameMap < 0) {
270 ret = -errno;
271 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
272 return ret;
273 }
274
275 base::unique_fd ifaceStatsMap(bpf::mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
276 if (ifaceStatsMap < 0) {
277 ret = -errno;
278 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
279 return ret;
280 }
281 return parseBpfNetworkStatsDevInternal(lines, ifaceStatsMap, ifaceIndexNameMap);
282}
283
Chenbo Fengf43bf812017-12-15 18:27:22 -0800284uint64_t combineUidTag(const uid_t uid, const uint32_t tag) {
285 return (uint64_t)uid << 32 | tag;
286}
287
288// This function get called when the system_server decided to clean up the
289// tagStatsMap after it gethered the information of taggged socket stats. The
290// function go through all the entry in tagStatsMap and remove all the entry
291// for which the tag no longer exists.
292int cleanStatsMapInternal(const base::unique_fd& cookieTagMap, const base::unique_fd& tagStatsMap) {
293 uint64_t curCookie = 0;
294 uint64_t nextCookie = 0;
295 int res;
296 UidTag tmp_uidtag;
297 std::unordered_set<uint64_t> uidTagSet;
298 StatsKey curKey, nextKey;
299
300 // Find all the uid, tag pair exist in cookieTagMap.
301 while (bpf::getNextMapKey(cookieTagMap, &curCookie, &nextCookie) != -1) {
302 curCookie = nextCookie;
303 res = bpf::findMapEntry(cookieTagMap, &curCookie, &tmp_uidtag);
304 if (res < 0) {
305 // might be a concurrent delete, continue to check other entries.
306 continue;
307 }
308 uint64_t uidTag = combineUidTag(tmp_uidtag.uid, tmp_uidtag.tag);
309 uidTagSet.insert(uidTag);
310 }
311
312 // Find all the entries in tagStatsMap where the key is not in the set of
313 // uid, tag pairs found above.
314 curKey = NONEXISTENT_STATSKEY;
315 std::vector<StatsKey> keyList;
316 while (bpf::getNextMapKey(tagStatsMap, &curKey, &nextKey) != -1) {
317 curKey = nextKey;
318 uint64_t uidTag = combineUidTag(curKey.uid, curKey.tag);
319 if (uidTagSet.find(uidTag) == uidTagSet.end()) {
320 keyList.push_back(curKey);
321 }
322 }
323
324 // Delete the entries
325 int size = keyList.size();
326 while (!keyList.empty()) {
327 StatsKey key = keyList.back();
328 keyList.pop_back();
329 res = bpf::deleteMapEntry(tagStatsMap, &key);
330 if (res < 0 && errno != ENOENT) {
331 res = -errno;
332 ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", key.uid, key.tag, strerror(errno));
333 return res;
334 }
335 }
336 ALOGD("finish clean up, %d stats entry cleaned", size);
337 return 0;
338}
339
340int cleanStatsMap() {
Chenbo Feng89c12f12018-03-21 10:29:18 -0700341 base::unique_fd cookieTagMap(bpf::mapRetrieve(COOKIE_TAG_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Fengf43bf812017-12-15 18:27:22 -0800342 int ret = 0;
343 if (cookieTagMap < 0) {
344 ret = -errno;
345 ALOGE("get cookieTag map fd failed: %s", strerror(errno));
346 return ret;
347 }
348
Lorenzo Colittif9c654c2018-03-01 18:02:15 +0900349 base::unique_fd tagStatsMap(bpf::mapRetrieve(TAG_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Fengf43bf812017-12-15 18:27:22 -0800350 if (tagStatsMap < 0) {
351 ret = -errno;
352 ALOGE("get tagStats map fd failed: %s", strerror(errno));
353 return ret;
354 }
355
356 return cleanStatsMapInternal(cookieTagMap, tagStatsMap);
357}
358
359} // namespace bpf
360} // namespace android