blob: bc9a932f5a52176115cedd7a2d91b9696d15f9ee [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));
126 newLine.uid = statsKey.uid;
127 newLine.set = statsKey.counterSet;
128 newLine.tag = 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
243uint64_t combineUidTag(const uid_t uid, const uint32_t tag) {
244 return (uint64_t)uid << 32 | tag;
245}
246
247// This function get called when the system_server decided to clean up the
248// tagStatsMap after it gethered the information of taggged socket stats. The
249// function go through all the entry in tagStatsMap and remove all the entry
250// for which the tag no longer exists.
251int cleanStatsMapInternal(const base::unique_fd& cookieTagMap, const base::unique_fd& tagStatsMap) {
252 uint64_t curCookie = 0;
253 uint64_t nextCookie = 0;
254 int res;
255 UidTag tmp_uidtag;
256 std::unordered_set<uint64_t> uidTagSet;
257 StatsKey curKey, nextKey;
258
259 // Find all the uid, tag pair exist in cookieTagMap.
260 while (bpf::getNextMapKey(cookieTagMap, &curCookie, &nextCookie) != -1) {
261 curCookie = nextCookie;
262 res = bpf::findMapEntry(cookieTagMap, &curCookie, &tmp_uidtag);
263 if (res < 0) {
264 // might be a concurrent delete, continue to check other entries.
265 continue;
266 }
267 uint64_t uidTag = combineUidTag(tmp_uidtag.uid, tmp_uidtag.tag);
268 uidTagSet.insert(uidTag);
269 }
270
271 // Find all the entries in tagStatsMap where the key is not in the set of
272 // uid, tag pairs found above.
273 curKey = NONEXISTENT_STATSKEY;
274 std::vector<StatsKey> keyList;
275 while (bpf::getNextMapKey(tagStatsMap, &curKey, &nextKey) != -1) {
276 curKey = nextKey;
277 uint64_t uidTag = combineUidTag(curKey.uid, curKey.tag);
278 if (uidTagSet.find(uidTag) == uidTagSet.end()) {
279 keyList.push_back(curKey);
280 }
281 }
282
283 // Delete the entries
284 int size = keyList.size();
285 while (!keyList.empty()) {
286 StatsKey key = keyList.back();
287 keyList.pop_back();
288 res = bpf::deleteMapEntry(tagStatsMap, &key);
289 if (res < 0 && errno != ENOENT) {
290 res = -errno;
291 ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", key.uid, key.tag, strerror(errno));
292 return res;
293 }
294 }
295 ALOGD("finish clean up, %d stats entry cleaned", size);
296 return 0;
297}
298
299int cleanStatsMap() {
Chenbo Feng89c12f12018-03-21 10:29:18 -0700300 base::unique_fd cookieTagMap(bpf::mapRetrieve(COOKIE_TAG_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Fengf43bf812017-12-15 18:27:22 -0800301 int ret = 0;
302 if (cookieTagMap < 0) {
303 ret = -errno;
304 ALOGE("get cookieTag map fd failed: %s", strerror(errno));
305 return ret;
306 }
307
Lorenzo Colittif9c654c2018-03-01 18:02:15 +0900308 base::unique_fd tagStatsMap(bpf::mapRetrieve(TAG_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Fengf43bf812017-12-15 18:27:22 -0800309 if (tagStatsMap < 0) {
310 ret = -errno;
311 ALOGE("get tagStats map fd failed: %s", strerror(errno));
312 return ret;
313 }
314
315 return cleanStatsMapInternal(cookieTagMap, tagStatsMap);
316}
317
318} // namespace bpf
319} // namespace android