blob: d2222fb84cabc84fdd1d674dfbc17fcc56ed37a6 [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
Steven Morelanda3074542020-01-13 14:13:44 -080041using base::Result;
Chenbo Feng7e974052018-02-28 22:57:21 -080042
Chenbo Feng873ae142019-04-10 12:26:06 -070043// The target map for stats reading should be the inactive map, which is oppsite
44// from the config value.
45static constexpr char const* STATS_MAP_PATH[] = {STATS_MAP_B_PATH, STATS_MAP_A_PATH};
Chenbo Fengf434e862018-06-27 14:08:39 -070046
Chenbo Feng06c73642018-03-05 04:10:38 -080047static constexpr uint32_t BPF_OPEN_FLAGS = BPF_F_RDONLY;
Lorenzo Colittif9c654c2018-03-01 18:02:15 +090048
Chenbo Feng4f6c2372018-04-26 10:37:55 -070049int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070050 const BpfMap<uint32_t, StatsValue>& appUidStatsMap) {
51 auto statsEntry = appUidStatsMap.readValue(uid);
Steven Morelanda3074542020-01-13 14:13:44 -080052 if (statsEntry) {
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070053 stats->rxPackets = statsEntry.value().rxPackets;
54 stats->txPackets = statsEntry.value().txPackets;
55 stats->rxBytes = statsEntry.value().rxBytes;
56 stats->txBytes = statsEntry.value().txBytes;
57 }
Steven Morelanda3074542020-01-13 14:13:44 -080058 return (statsEntry || statsEntry.error().code() == ENOENT) ? 0 : -statsEntry.error().code();
Chenbo Fengf43bf812017-12-15 18:27:22 -080059}
60
61int bpfGetUidStats(uid_t uid, Stats* stats) {
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070062 BpfMap<uint32_t, StatsValue> appUidStatsMap(
63 mapRetrieve(APP_UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Feng4f6c2372018-04-26 10:37:55 -070064
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070065 if (!appUidStatsMap.isValid()) {
Chenbo Fengf43bf812017-12-15 18:27:22 -080066 int ret = -errno;
Chenbo Fengf434e862018-06-27 14:08:39 -070067 ALOGE("Opening appUidStatsMap(%s) failed: %s", APP_UID_STATS_MAP_PATH, strerror(errno));
Chenbo Fengf43bf812017-12-15 18:27:22 -080068 return ret;
69 }
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070070 return bpfGetUidStatsInternal(uid, stats, appUidStatsMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -080071}
72
Chenbo Feng33a4de12018-03-16 18:10:07 -070073int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
Chenbo Feng4f6c2372018-04-26 10:37:55 -070074 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
75 const BpfMap<uint32_t, IfaceValue>& ifaceNameMap) {
Chenbo Feng33a4de12018-03-16 18:10:07 -070076 int64_t unknownIfaceBytesTotal = 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -080077 stats->tcpRxPackets = -1;
78 stats->tcpTxPackets = -1;
Steven Morelanda3074542020-01-13 14:13:44 -080079 const auto processIfaceStats =
80 [iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal](
81 const uint32_t& key,
82 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) -> Result<void> {
Chenbo Feng33a4de12018-03-16 18:10:07 -070083 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -070084 if (getIfaceNameFromMap(ifaceNameMap, ifaceStatsMap, key, ifname, key,
Chenbo Feng33a4de12018-03-16 18:10:07 -070085 &unknownIfaceBytesTotal)) {
Steven Morelanda3074542020-01-13 14:13:44 -080086 return Result<void>();
Chenbo Feng33a4de12018-03-16 18:10:07 -070087 }
88 if (!iface || !strcmp(iface, ifname)) {
Steven Morelanda3074542020-01-13 14:13:44 -080089 Result<StatsValue> statsEntry = ifaceStatsMap.readValue(key);
90 if (!statsEntry) {
91 return statsEntry.error();
92 }
93 stats->rxPackets += statsEntry.value().rxPackets;
94 stats->txPackets += statsEntry.value().txPackets;
95 stats->rxBytes += statsEntry.value().rxBytes;
96 stats->txBytes += statsEntry.value().txBytes;
Chenbo Feng33a4de12018-03-16 18:10:07 -070097 }
Steven Morelanda3074542020-01-13 14:13:44 -080098 return Result<void>();
Chenbo Feng33a4de12018-03-16 18:10:07 -070099 };
Steven Morelanda3074542020-01-13 14:13:44 -0800100 auto res = ifaceStatsMap.iterate(processIfaceStats);
101 return res ? 0 : -res.error().code();
Chenbo Fengf43bf812017-12-15 18:27:22 -0800102}
103
104int bpfGetIfaceStats(const char* iface, Stats* stats) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700105 BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Feng33a4de12018-03-16 18:10:07 -0700106 int ret;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700107 if (!ifaceStatsMap.isValid()) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700108 ret = -errno;
109 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
110 return ret;
111 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700112 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
113 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
114 if (!ifaceIndexNameMap.isValid()) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700115 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 Feng16513482018-03-15 17:59:58 -0700136int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
137 const std::vector<std::string>& limitIfaces, int limitTag,
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700138 int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
139 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700140 int64_t unknownIfaceBytesTotal = 0;
Steven Morelanda3074542020-01-13 14:13:44 -0800141 const auto processDetailUidStats =
142 [lines, &limitIfaces, &limitTag, &limitUid, &unknownIfaceBytesTotal, &ifaceMap](
143 const StatsKey& key,
144 const BpfMap<StatsKey, StatsValue>& statsMap) -> Result<void> {
Chenbo Feng7e974052018-02-28 22:57:21 -0800145 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700146 if (getIfaceNameFromMap(ifaceMap, statsMap, key.ifaceIndex, ifname, key,
Chenbo Feng33a4de12018-03-16 18:10:07 -0700147 &unknownIfaceBytesTotal)) {
Steven Morelanda3074542020-01-13 14:13:44 -0800148 return Result<void>();
Chenbo Feng7e974052018-02-28 22:57:21 -0800149 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800150 std::string ifnameStr(ifname);
151 if (limitIfaces.size() > 0 &&
152 std::find(limitIfaces.begin(), limitIfaces.end(), ifnameStr) == limitIfaces.end()) {
153 // Nothing matched; skip this line.
Steven Morelanda3074542020-01-13 14:13:44 -0800154 return Result<void>();
Chenbo Fengf43bf812017-12-15 18:27:22 -0800155 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700156 if (limitTag != TAG_ALL && uint32_t(limitTag) != key.tag) {
Steven Morelanda3074542020-01-13 14:13:44 -0800157 return Result<void>();
Chenbo Feng16513482018-03-15 17:59:58 -0700158 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700159 if (limitUid != UID_ALL && uint32_t(limitUid) != key.uid) {
Steven Morelanda3074542020-01-13 14:13:44 -0800160 return Result<void>();
Chenbo Feng16513482018-03-15 17:59:58 -0700161 }
Steven Morelanda3074542020-01-13 14:13:44 -0800162 Result<StatsValue> statsEntry = statsMap.readValue(key);
163 if (!statsEntry) {
164 return base::ResultError(statsEntry.error().message(), statsEntry.error().code());
165 }
166 lines->push_back(populateStatsEntry(key, statsEntry.value(), ifname));
167 return Result<void>();
Chenbo Feng16513482018-03-15 17:59:58 -0700168 };
Steven Morelanda3074542020-01-13 14:13:44 -0800169 Result<void> res = statsMap.iterate(processDetailUidStats);
170 if (!res) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700171 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
Steven Morelanda3074542020-01-13 14:13:44 -0800172 strerror(res.error().code()));
173 return -res.error().code();
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700174 }
junyulaia130ac22018-11-09 16:12:29 +0800175
176 // Since eBPF use hash map to record stats, network stats collected from
177 // eBPF will be out of order. And the performance of findIndexHinted in
178 // NetworkStats will also be impacted.
179 //
180 // Furthermore, since the StatsKey contains iface index, the network stats
181 // reported to framework would create items with the same iface, uid, tag
182 // and set, which causes NetworkStats maps wrong item to subtract.
183 //
184 // Thus, the stats needs to be properly sorted and grouped before reported.
185 groupNetworkStats(lines);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700186 return 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800187}
188
189int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
190 const std::vector<std::string>& limitIfaces, int limitTag,
191 int limitUid) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700192 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
193 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
194 if (!ifaceIndexNameMap.isValid()) {
Chenbo Fengf434e862018-06-27 14:08:39 -0700195 int ret = -errno;
Chenbo Feng7e974052018-02-28 22:57:21 -0800196 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
197 return ret;
198 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800199
mtk13799ae0704e2019-03-28 15:07:14 +0800200 BpfMap<uint32_t, uint8_t> configurationMap(mapRetrieve(CONFIGURATION_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Fengf434e862018-06-27 14:08:39 -0700201 if (!configurationMap.isValid()) {
202 int ret = -errno;
203 ALOGE("get configuration map fd failed: %s", strerror(errno));
204 return ret;
205 }
206 auto configuration = configurationMap.readValue(CURRENT_STATS_MAP_CONFIGURATION_KEY);
Steven Morelanda3074542020-01-13 14:13:44 -0800207 if (!configuration) {
Chenbo Fengf434e862018-06-27 14:08:39 -0700208 ALOGE("Cannot read the old configuration from map: %s",
Steven Morelanda3074542020-01-13 14:13:44 -0800209 configuration.error().message().c_str());
210 return -configuration.error().code();
Chenbo Fengf434e862018-06-27 14:08:39 -0700211 }
212 const char* statsMapPath = STATS_MAP_PATH[configuration.value()];
213 BpfMap<StatsKey, StatsValue> statsMap(mapRetrieve(statsMapPath, 0));
214 if (!statsMap.isValid()) {
215 int ret = -errno;
216 ALOGE("get stats map fd failed: %s, path: %s", strerror(errno), statsMapPath);
217 return ret;
Chenbo Feng16513482018-03-15 17:59:58 -0700218 }
219
Chenbo Feng873ae142019-04-10 12:26:06 -0700220 // It is safe to read and clear the old map now since the
221 // networkStatsFactory should call netd to swap the map in advance already.
222 int ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid, statsMap,
223 ifaceIndexNameMap);
Chenbo Fengf434e862018-06-27 14:08:39 -0700224 if (ret) {
225 ALOGE("parse detail network stats failed: %s", strerror(errno));
226 return ret;
227 }
228
Steven Morelanda3074542020-01-13 14:13:44 -0800229 Result<void> res = statsMap.clear();
230 if (!res) {
231 ALOGE("Clean up current stats map failed: %s", strerror(res.error().code()));
232 return -res.error().code();
Chenbo Fengf434e862018-06-27 14:08:39 -0700233 }
234
235 return 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800236}
237
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700238int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700239 const BpfMap<uint32_t, StatsValue>& statsMap,
240 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700241 int64_t unknownIfaceBytesTotal = 0;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700242 const auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMap, &statsMap](
243 const uint32_t& key, const StatsValue& value,
244 const BpfMap<uint32_t, StatsValue>&) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700245 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700246 if (getIfaceNameFromMap(ifaceMap, statsMap, key, ifname, key, &unknownIfaceBytesTotal)) {
Steven Morelanda3074542020-01-13 14:13:44 -0800247 return Result<void>();
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700248 }
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700249 StatsKey fakeKey = {
Nick Desaulniersa1aae6c2019-10-07 17:43:50 -0700250 .uid = (uint32_t)UID_ALL,
251 .tag = (uint32_t)TAG_NONE,
252 .counterSet = (uint32_t)SET_ALL,
253 };
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700254 lines->push_back(populateStatsEntry(fakeKey, value, ifname));
Steven Morelanda3074542020-01-13 14:13:44 -0800255 return Result<void>();
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700256 };
Steven Morelanda3074542020-01-13 14:13:44 -0800257 Result<void> res = statsMap.iterateWithValue(processDetailIfaceStats);
258 if (!res) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700259 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
Steven Morelanda3074542020-01-13 14:13:44 -0800260 strerror(res.error().code()));
261 return -res.error().code();
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700262 }
junyulaia130ac22018-11-09 16:12:29 +0800263
264 groupNetworkStats(lines);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700265 return 0;
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700266}
267
268int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
269 int ret = 0;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700270 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
271 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
272 if (!ifaceIndexNameMap.isValid()) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700273 ret = -errno;
274 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
275 return ret;
276 }
277
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700278 BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
279 if (!ifaceStatsMap.isValid()) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700280 ret = -errno;
281 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
282 return ret;
283 }
284 return parseBpfNetworkStatsDevInternal(lines, ifaceStatsMap, ifaceIndexNameMap);
285}
286
Chenbo Fengf43bf812017-12-15 18:27:22 -0800287uint64_t combineUidTag(const uid_t uid, const uint32_t tag) {
288 return (uint64_t)uid << 32 | tag;
289}
290
junyulaia130ac22018-11-09 16:12:29 +0800291void groupNetworkStats(std::vector<stats_line>* lines) {
292 if (lines->size() <= 1) return;
293 std::sort(lines->begin(), lines->end());
294
295 // Similar to std::unique(), but aggregates the duplicates rather than discarding them.
296 size_t nextOutput = 0;
297 for (size_t i = 1; i < lines->size(); i++) {
298 if (lines->at(nextOutput) == lines->at(i)) {
299 lines->at(nextOutput) += lines->at(i);
300 } else {
301 nextOutput++;
302 if (nextOutput != i) {
303 lines->at(nextOutput) = lines->at(i);
304 }
305 }
306 }
307
308 if (lines->size() != nextOutput + 1) {
309 lines->resize(nextOutput + 1);
310 }
311}
312
313// True if lhs equals to rhs, only compare iface, uid, tag and set.
314bool operator==(const stats_line& lhs, const stats_line& rhs) {
315 return ((lhs.uid == rhs.uid) && (lhs.tag == rhs.tag) && (lhs.set == rhs.set) &&
316 !strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface)));
317}
318
319// True if lhs is smaller then rhs, only compare iface, uid, tag and set.
320bool operator<(const stats_line& lhs, const stats_line& rhs) {
321 int ret = strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface));
322 if (ret != 0) return ret < 0;
323 if (lhs.uid < rhs.uid) return true;
324 if (lhs.uid > rhs.uid) return false;
325 if (lhs.tag < rhs.tag) return true;
326 if (lhs.tag > rhs.tag) return false;
327 if (lhs.set < rhs.set) return true;
328 if (lhs.set > rhs.set) return false;
329 return false;
330}
331
332stats_line& stats_line::operator=(const stats_line& rhs) {
Luca Stefani28e84722019-09-01 21:11:02 +0200333 if (this == &rhs) return *this;
334
junyulaia130ac22018-11-09 16:12:29 +0800335 strlcpy(iface, rhs.iface, sizeof(iface));
336 uid = rhs.uid;
337 set = rhs.set;
338 tag = rhs.tag;
339 rxPackets = rhs.rxPackets;
340 txPackets = rhs.txPackets;
341 rxBytes = rhs.rxBytes;
342 txBytes = rhs.txBytes;
343 return *this;
344}
345
346stats_line& stats_line::operator+=(const stats_line& rhs) {
347 rxPackets += rhs.rxPackets;
348 txPackets += rhs.txPackets;
349 rxBytes += rhs.rxBytes;
350 txBytes += rhs.txBytes;
351 return *this;
352}
353
Chenbo Fengf43bf812017-12-15 18:27:22 -0800354} // namespace bpf
355} // namespace android