blob: 907cdea06c2920157cb5d2921c8176b41bcf7059 [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"
Chenbo Feng4f6c2372018-04-26 10:37:55 -070028#include "bpf/BpfMap.h"
Chenbo Fengd6104d12018-10-16 20:29:29 -070029#include "netdbpf/BpfNetworkStats.h"
30#include "netdbpf/bpf_shared.h"
Chenbo Fengf43bf812017-12-15 18:27:22 -080031
Lorenzo Colittif9c654c2018-03-01 18:02:15 +090032#ifdef LOG_TAG
33#undef LOG_TAG
34#endif
35
36#define LOG_TAG "BpfNetworkStats"
37
Chenbo Fengf43bf812017-12-15 18:27:22 -080038namespace android {
39namespace bpf {
40
Chenbo Feng4f6c2372018-04-26 10:37:55 -070041using netdutils::Status;
Chenbo Feng7e974052018-02-28 22:57:21 -080042
Chenbo Feng06c73642018-03-05 04:10:38 -080043static constexpr uint32_t BPF_OPEN_FLAGS = BPF_F_RDONLY;
Lorenzo Colittif9c654c2018-03-01 18:02:15 +090044
Chenbo Feng4f6c2372018-04-26 10:37:55 -070045int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070046 const BpfMap<uint32_t, StatsValue>& appUidStatsMap) {
47 auto statsEntry = appUidStatsMap.readValue(uid);
48 if (isOk(statsEntry)) {
49 stats->rxPackets = statsEntry.value().rxPackets;
50 stats->txPackets = statsEntry.value().txPackets;
51 stats->rxBytes = statsEntry.value().rxBytes;
52 stats->txBytes = statsEntry.value().txBytes;
53 }
54 return -statsEntry.status().code();
Chenbo Fengf43bf812017-12-15 18:27:22 -080055}
56
57int bpfGetUidStats(uid_t uid, Stats* stats) {
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070058 BpfMap<uint32_t, StatsValue> appUidStatsMap(
59 mapRetrieve(APP_UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Feng4f6c2372018-04-26 10:37:55 -070060
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070061 if (!appUidStatsMap.isValid()) {
Chenbo Fengf43bf812017-12-15 18:27:22 -080062 int ret = -errno;
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070063 ALOGE("Opening appUidStatsMap(%s) failed: %s", UID_STATS_MAP_PATH, strerror(errno));
Chenbo Fengf43bf812017-12-15 18:27:22 -080064 return ret;
65 }
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070066 return bpfGetUidStatsInternal(uid, stats, appUidStatsMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -080067}
68
Chenbo Feng33a4de12018-03-16 18:10:07 -070069int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
Chenbo Feng4f6c2372018-04-26 10:37:55 -070070 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
71 const BpfMap<uint32_t, IfaceValue>& ifaceNameMap) {
Chenbo Feng33a4de12018-03-16 18:10:07 -070072 int64_t unknownIfaceBytesTotal = 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -080073 stats->tcpRxPackets = -1;
74 stats->tcpTxPackets = -1;
Chenbo Feng4f6c2372018-04-26 10:37:55 -070075 const auto processIfaceStats = [iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal]
76 (const uint32_t& key,
77 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) {
Chenbo Feng33a4de12018-03-16 18:10:07 -070078 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -070079 if (getIfaceNameFromMap(ifaceNameMap, ifaceStatsMap, key, ifname, key,
Chenbo Feng33a4de12018-03-16 18:10:07 -070080 &unknownIfaceBytesTotal)) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -070081 return netdutils::status::ok;
Chenbo Feng33a4de12018-03-16 18:10:07 -070082 }
83 if (!iface || !strcmp(iface, ifname)) {
84 StatsValue statsEntry;
Chenbo Feng4f6c2372018-04-26 10:37:55 -070085 ASSIGN_OR_RETURN(statsEntry, ifaceStatsMap.readValue(key));
Chenbo Feng33a4de12018-03-16 18:10:07 -070086 stats->rxPackets += statsEntry.rxPackets;
87 stats->txPackets += statsEntry.txPackets;
88 stats->rxBytes += statsEntry.rxBytes;
89 stats->txBytes += statsEntry.txBytes;
90 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -070091 return netdutils::status::ok;
Chenbo Feng33a4de12018-03-16 18:10:07 -070092 };
Chenbo Feng4f6c2372018-04-26 10:37:55 -070093 return -ifaceStatsMap.iterate(processIfaceStats).code();
Chenbo Fengf43bf812017-12-15 18:27:22 -080094}
95
96int bpfGetIfaceStats(const char* iface, Stats* stats) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -070097 BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Feng33a4de12018-03-16 18:10:07 -070098 int ret;
Chenbo Feng4f6c2372018-04-26 10:37:55 -070099 if (!ifaceStatsMap.isValid()) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700100 ret = -errno;
101 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
102 return ret;
103 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700104 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
105 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
106 if (!ifaceIndexNameMap.isValid()) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700107 ret = -errno;
108 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
109 return ret;
110 }
111 return bpfGetIfaceStatsInternal(iface, stats, ifaceStatsMap, ifaceIndexNameMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -0800112}
113
114stats_line populateStatsEntry(const StatsKey& statsKey, const StatsValue& statsEntry,
115 const char* ifname) {
116 stats_line newLine;
117 strlcpy(newLine.iface, ifname, sizeof(newLine.iface));
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700118 newLine.uid = (int32_t)statsKey.uid;
119 newLine.set = (int32_t)statsKey.counterSet;
120 newLine.tag = (int32_t)statsKey.tag;
Chenbo Fengeac6c472018-02-05 15:06:23 -0800121 newLine.rxPackets = statsEntry.rxPackets;
122 newLine.txPackets = statsEntry.txPackets;
123 newLine.rxBytes = statsEntry.rxBytes;
124 newLine.txBytes = statsEntry.txBytes;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800125 return newLine;
126}
127
Chenbo Feng16513482018-03-15 17:59:58 -0700128int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
129 const std::vector<std::string>& limitIfaces, int limitTag,
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700130 int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
131 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700132 int64_t unknownIfaceBytesTotal = 0;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700133 const auto processDetailUidStats = [lines, &limitIfaces, &limitTag, &limitUid,
134 &unknownIfaceBytesTotal,
135 &ifaceMap](const StatsKey& key,
136 const BpfMap<StatsKey, StatsValue>& statsMap) {
Chenbo Feng7e974052018-02-28 22:57:21 -0800137 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700138 if (getIfaceNameFromMap(ifaceMap, statsMap, key.ifaceIndex, ifname, key,
Chenbo Feng33a4de12018-03-16 18:10:07 -0700139 &unknownIfaceBytesTotal)) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700140 return netdutils::status::ok;
Chenbo Feng7e974052018-02-28 22:57:21 -0800141 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800142 std::string ifnameStr(ifname);
143 if (limitIfaces.size() > 0 &&
144 std::find(limitIfaces.begin(), limitIfaces.end(), ifnameStr) == limitIfaces.end()) {
145 // Nothing matched; skip this line.
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700146 return netdutils::status::ok;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800147 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700148 if (limitTag != TAG_ALL && uint32_t(limitTag) != key.tag) {
149 return netdutils::status::ok;
Chenbo Feng16513482018-03-15 17:59:58 -0700150 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700151 if (limitUid != UID_ALL && uint32_t(limitUid) != key.uid) {
152 return netdutils::status::ok;
Chenbo Feng16513482018-03-15 17:59:58 -0700153 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800154 StatsValue statsEntry;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700155 ASSIGN_OR_RETURN(statsEntry, statsMap.readValue(key));
156 lines->push_back(populateStatsEntry(key, statsEntry, ifname));
157 return netdutils::status::ok;
Chenbo Feng16513482018-03-15 17:59:58 -0700158 };
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700159 Status res = statsMap.iterate(processDetailUidStats);
160 if (!isOk(res)) {
161 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
162 strerror(res.code()));
163 return -res.code();
164 }
junyulaia130ac22018-11-09 16:12:29 +0800165
166 // Since eBPF use hash map to record stats, network stats collected from
167 // eBPF will be out of order. And the performance of findIndexHinted in
168 // NetworkStats will also be impacted.
169 //
170 // Furthermore, since the StatsKey contains iface index, the network stats
171 // reported to framework would create items with the same iface, uid, tag
172 // and set, which causes NetworkStats maps wrong item to subtract.
173 //
174 // Thus, the stats needs to be properly sorted and grouped before reported.
175 groupNetworkStats(lines);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700176 return 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800177}
178
179int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
180 const std::vector<std::string>& limitIfaces, int limitTag,
181 int limitUid) {
Chenbo Fengf43bf812017-12-15 18:27:22 -0800182 int ret = 0;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700183 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
184 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
185 if (!ifaceIndexNameMap.isValid()) {
Chenbo Feng7e974052018-02-28 22:57:21 -0800186 ret = -errno;
187 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
188 return ret;
189 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800190
Chenbo Feng16513482018-03-15 17:59:58 -0700191 // If the caller did not pass in TAG_NONE, read tag data.
192 if (limitTag != TAG_NONE) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700193 BpfMap<StatsKey, StatsValue> tagStatsMap(mapRetrieve(TAG_STATS_MAP_PATH, BPF_OPEN_FLAGS));
194 if (!tagStatsMap.isValid()) {
Chenbo Feng16513482018-03-15 17:59:58 -0700195 ret = -errno;
196 ALOGE("get tagStats map fd failed: %s", strerror(errno));
197 return ret;
198 }
199 ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid,
200 tagStatsMap, ifaceIndexNameMap);
201 if (ret) return ret;
202 }
203
204 // If the caller did not pass in a specific tag (i.e., if limitTag is TAG_NONE(0) or
205 // TAG_ALL(-1)) read UID data.
206 if (limitTag == TAG_NONE || limitTag == TAG_ALL) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700207 BpfMap<StatsKey, StatsValue> uidStatsMap(mapRetrieve(UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
208 if (!uidStatsMap.isValid()) {
Chenbo Fengf43bf812017-12-15 18:27:22 -0800209 ret = -errno;
Lorenzo Colittif9c654c2018-03-01 18:02:15 +0900210 ALOGE("Opening map fd from %s failed: %s", UID_STATS_MAP_PATH, strerror(errno));
Chenbo Fengf43bf812017-12-15 18:27:22 -0800211 return ret;
212 }
Chenbo Feng16513482018-03-15 17:59:58 -0700213 ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid,
214 uidStatsMap, ifaceIndexNameMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -0800215 }
216 return ret;
217}
218
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700219int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700220 const BpfMap<uint32_t, StatsValue>& statsMap,
221 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700222 int64_t unknownIfaceBytesTotal = 0;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700223 const auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMap, &statsMap](
224 const uint32_t& key, const StatsValue& value,
225 const BpfMap<uint32_t, StatsValue>&) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700226 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700227 if (getIfaceNameFromMap(ifaceMap, statsMap, key, ifname, key, &unknownIfaceBytesTotal)) {
228 return netdutils::status::ok;
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700229 }
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700230 StatsKey fakeKey = {
231 .uid = (uint32_t)UID_ALL, .counterSet = (uint32_t)SET_ALL, .tag = (uint32_t)TAG_NONE};
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700232 lines->push_back(populateStatsEntry(fakeKey, value, ifname));
233 return netdutils::status::ok;
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700234 };
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700235 Status res = statsMap.iterateWithValue(processDetailIfaceStats);
236 if (!isOk(res)) {
237 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
238 strerror(res.code()));
239 return -res.code();
240 }
junyulaia130ac22018-11-09 16:12:29 +0800241
242 groupNetworkStats(lines);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700243 return 0;
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700244}
245
246int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
247 int ret = 0;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700248 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
249 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
250 if (!ifaceIndexNameMap.isValid()) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700251 ret = -errno;
252 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
253 return ret;
254 }
255
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700256 BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
257 if (!ifaceStatsMap.isValid()) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700258 ret = -errno;
259 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
260 return ret;
261 }
262 return parseBpfNetworkStatsDevInternal(lines, ifaceStatsMap, ifaceIndexNameMap);
263}
264
Chenbo Fengf43bf812017-12-15 18:27:22 -0800265uint64_t combineUidTag(const uid_t uid, const uint32_t tag) {
266 return (uint64_t)uid << 32 | tag;
267}
268
junyulaia130ac22018-11-09 16:12:29 +0800269void groupNetworkStats(std::vector<stats_line>* lines) {
270 if (lines->size() <= 1) return;
271 std::sort(lines->begin(), lines->end());
272
273 // Similar to std::unique(), but aggregates the duplicates rather than discarding them.
274 size_t nextOutput = 0;
275 for (size_t i = 1; i < lines->size(); i++) {
276 if (lines->at(nextOutput) == lines->at(i)) {
277 lines->at(nextOutput) += lines->at(i);
278 } else {
279 nextOutput++;
280 if (nextOutput != i) {
281 lines->at(nextOutput) = lines->at(i);
282 }
283 }
284 }
285
286 if (lines->size() != nextOutput + 1) {
287 lines->resize(nextOutput + 1);
288 }
289}
290
291// True if lhs equals to rhs, only compare iface, uid, tag and set.
292bool operator==(const stats_line& lhs, const stats_line& rhs) {
293 return ((lhs.uid == rhs.uid) && (lhs.tag == rhs.tag) && (lhs.set == rhs.set) &&
294 !strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface)));
295}
296
297// True if lhs is smaller then rhs, only compare iface, uid, tag and set.
298bool operator<(const stats_line& lhs, const stats_line& rhs) {
299 int ret = strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface));
300 if (ret != 0) return ret < 0;
301 if (lhs.uid < rhs.uid) return true;
302 if (lhs.uid > rhs.uid) return false;
303 if (lhs.tag < rhs.tag) return true;
304 if (lhs.tag > rhs.tag) return false;
305 if (lhs.set < rhs.set) return true;
306 if (lhs.set > rhs.set) return false;
307 return false;
308}
309
310stats_line& stats_line::operator=(const stats_line& rhs) {
311 strlcpy(iface, rhs.iface, sizeof(iface));
312 uid = rhs.uid;
313 set = rhs.set;
314 tag = rhs.tag;
315 rxPackets = rhs.rxPackets;
316 txPackets = rhs.txPackets;
317 rxBytes = rhs.rxBytes;
318 txBytes = rhs.txBytes;
319 return *this;
320}
321
322stats_line& stats_line::operator+=(const stats_line& rhs) {
323 rxPackets += rhs.rxPackets;
324 txPackets += rhs.txPackets;
325 rxBytes += rhs.rxBytes;
326 txBytes += rhs.txBytes;
327 return *this;
328}
329
Chenbo Fengf43bf812017-12-15 18:27:22 -0800330} // namespace bpf
331} // namespace android