blob: a77f6f924e1f16909a4c6ec23dd37012833eeab1 [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 Feng4f6c2372018-04-26 10:37:55 -070047int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070048 const BpfMap<uint32_t, StatsValue>& appUidStatsMap) {
49 auto statsEntry = appUidStatsMap.readValue(uid);
Bernie Innocenti615fd742020-02-06 03:55:09 +090050 if (statsEntry.ok()) {
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070051 stats->rxPackets = statsEntry.value().rxPackets;
52 stats->txPackets = statsEntry.value().txPackets;
53 stats->rxBytes = statsEntry.value().rxBytes;
54 stats->txBytes = statsEntry.value().txBytes;
55 }
Bernie Innocenti615fd742020-02-06 03:55:09 +090056 return (statsEntry.ok() || statsEntry.error().code() == ENOENT) ? 0
57 : -statsEntry.error().code();
Chenbo Fengf43bf812017-12-15 18:27:22 -080058}
59
60int bpfGetUidStats(uid_t uid, Stats* stats) {
Maciej Żenczykowskif879a7f2020-01-20 03:18:06 -080061 BpfMapRO<uint32_t, StatsValue> appUidStatsMap(APP_UID_STATS_MAP_PATH);
Chenbo Feng4f6c2372018-04-26 10:37:55 -070062
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070063 if (!appUidStatsMap.isValid()) {
Chenbo Fengf43bf812017-12-15 18:27:22 -080064 int ret = -errno;
Chenbo Fengf434e862018-06-27 14:08:39 -070065 ALOGE("Opening appUidStatsMap(%s) failed: %s", APP_UID_STATS_MAP_PATH, strerror(errno));
Chenbo Fengf43bf812017-12-15 18:27:22 -080066 return ret;
67 }
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070068 return bpfGetUidStatsInternal(uid, stats, appUidStatsMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -080069}
70
Chenbo Feng33a4de12018-03-16 18:10:07 -070071int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
Chenbo Feng4f6c2372018-04-26 10:37:55 -070072 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
73 const BpfMap<uint32_t, IfaceValue>& ifaceNameMap) {
Chenbo Feng33a4de12018-03-16 18:10:07 -070074 int64_t unknownIfaceBytesTotal = 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -080075 stats->tcpRxPackets = -1;
76 stats->tcpTxPackets = -1;
Steven Morelanda3074542020-01-13 14:13:44 -080077 const auto processIfaceStats =
78 [iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal](
79 const uint32_t& key,
80 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) -> Result<void> {
Chenbo Feng33a4de12018-03-16 18:10:07 -070081 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -070082 if (getIfaceNameFromMap(ifaceNameMap, ifaceStatsMap, key, ifname, key,
Chenbo Feng33a4de12018-03-16 18:10:07 -070083 &unknownIfaceBytesTotal)) {
Steven Morelanda3074542020-01-13 14:13:44 -080084 return Result<void>();
Chenbo Feng33a4de12018-03-16 18:10:07 -070085 }
86 if (!iface || !strcmp(iface, ifname)) {
Steven Morelanda3074542020-01-13 14:13:44 -080087 Result<StatsValue> statsEntry = ifaceStatsMap.readValue(key);
Bernie Innocenti615fd742020-02-06 03:55:09 +090088 if (!statsEntry.ok()) {
Steven Morelanda3074542020-01-13 14:13:44 -080089 return statsEntry.error();
90 }
91 stats->rxPackets += statsEntry.value().rxPackets;
92 stats->txPackets += statsEntry.value().txPackets;
93 stats->rxBytes += statsEntry.value().rxBytes;
94 stats->txBytes += statsEntry.value().txBytes;
Chenbo Feng33a4de12018-03-16 18:10:07 -070095 }
Steven Morelanda3074542020-01-13 14:13:44 -080096 return Result<void>();
Chenbo Feng33a4de12018-03-16 18:10:07 -070097 };
Steven Morelanda3074542020-01-13 14:13:44 -080098 auto res = ifaceStatsMap.iterate(processIfaceStats);
Bernie Innocenti615fd742020-02-06 03:55:09 +090099 return res.ok() ? 0 : -res.error().code();
Chenbo Fengf43bf812017-12-15 18:27:22 -0800100}
101
102int bpfGetIfaceStats(const char* iface, Stats* stats) {
Maciej Żenczykowskif879a7f2020-01-20 03:18:06 -0800103 BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
Chenbo Feng33a4de12018-03-16 18:10:07 -0700104 int ret;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700105 if (!ifaceStatsMap.isValid()) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700106 ret = -errno;
107 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
108 return ret;
109 }
Maciej Żenczykowskif879a7f2020-01-20 03:18:06 -0800110 BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700111 if (!ifaceIndexNameMap.isValid()) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700112 ret = -errno;
113 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
114 return ret;
115 }
116 return bpfGetIfaceStatsInternal(iface, stats, ifaceStatsMap, ifaceIndexNameMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -0800117}
118
119stats_line populateStatsEntry(const StatsKey& statsKey, const StatsValue& statsEntry,
120 const char* ifname) {
121 stats_line newLine;
122 strlcpy(newLine.iface, ifname, sizeof(newLine.iface));
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700123 newLine.uid = (int32_t)statsKey.uid;
124 newLine.set = (int32_t)statsKey.counterSet;
125 newLine.tag = (int32_t)statsKey.tag;
Chenbo Fengeac6c472018-02-05 15:06:23 -0800126 newLine.rxPackets = statsEntry.rxPackets;
127 newLine.txPackets = statsEntry.txPackets;
128 newLine.rxBytes = statsEntry.rxBytes;
129 newLine.txBytes = statsEntry.txBytes;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800130 return newLine;
131}
132
Chenbo Feng16513482018-03-15 17:59:58 -0700133int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
134 const std::vector<std::string>& limitIfaces, int limitTag,
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700135 int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
136 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700137 int64_t unknownIfaceBytesTotal = 0;
Steven Morelanda3074542020-01-13 14:13:44 -0800138 const auto processDetailUidStats =
139 [lines, &limitIfaces, &limitTag, &limitUid, &unknownIfaceBytesTotal, &ifaceMap](
140 const StatsKey& key,
141 const BpfMap<StatsKey, StatsValue>& statsMap) -> Result<void> {
Chenbo Feng7e974052018-02-28 22:57:21 -0800142 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700143 if (getIfaceNameFromMap(ifaceMap, statsMap, key.ifaceIndex, ifname, key,
Chenbo Feng33a4de12018-03-16 18:10:07 -0700144 &unknownIfaceBytesTotal)) {
Steven Morelanda3074542020-01-13 14:13:44 -0800145 return Result<void>();
Chenbo Feng7e974052018-02-28 22:57:21 -0800146 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800147 std::string ifnameStr(ifname);
148 if (limitIfaces.size() > 0 &&
149 std::find(limitIfaces.begin(), limitIfaces.end(), ifnameStr) == limitIfaces.end()) {
150 // Nothing matched; skip this line.
Steven Morelanda3074542020-01-13 14:13:44 -0800151 return Result<void>();
Chenbo Fengf43bf812017-12-15 18:27:22 -0800152 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700153 if (limitTag != TAG_ALL && uint32_t(limitTag) != key.tag) {
Steven Morelanda3074542020-01-13 14:13:44 -0800154 return Result<void>();
Chenbo Feng16513482018-03-15 17:59:58 -0700155 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700156 if (limitUid != UID_ALL && uint32_t(limitUid) != key.uid) {
Steven Morelanda3074542020-01-13 14:13:44 -0800157 return Result<void>();
Chenbo Feng16513482018-03-15 17:59:58 -0700158 }
Steven Morelanda3074542020-01-13 14:13:44 -0800159 Result<StatsValue> statsEntry = statsMap.readValue(key);
Bernie Innocenti615fd742020-02-06 03:55:09 +0900160 if (!statsEntry.ok()) {
Steven Morelanda3074542020-01-13 14:13:44 -0800161 return base::ResultError(statsEntry.error().message(), statsEntry.error().code());
162 }
163 lines->push_back(populateStatsEntry(key, statsEntry.value(), ifname));
164 return Result<void>();
Chenbo Feng16513482018-03-15 17:59:58 -0700165 };
Steven Morelanda3074542020-01-13 14:13:44 -0800166 Result<void> res = statsMap.iterate(processDetailUidStats);
Bernie Innocenti615fd742020-02-06 03:55:09 +0900167 if (!res.ok()) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700168 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
Steven Morelanda3074542020-01-13 14:13:44 -0800169 strerror(res.error().code()));
170 return -res.error().code();
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700171 }
junyulaia130ac22018-11-09 16:12:29 +0800172
173 // Since eBPF use hash map to record stats, network stats collected from
174 // eBPF will be out of order. And the performance of findIndexHinted in
175 // NetworkStats will also be impacted.
176 //
177 // Furthermore, since the StatsKey contains iface index, the network stats
178 // reported to framework would create items with the same iface, uid, tag
179 // and set, which causes NetworkStats maps wrong item to subtract.
180 //
181 // Thus, the stats needs to be properly sorted and grouped before reported.
182 groupNetworkStats(lines);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700183 return 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800184}
185
186int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
187 const std::vector<std::string>& limitIfaces, int limitTag,
188 int limitUid) {
Maciej Żenczykowskif879a7f2020-01-20 03:18:06 -0800189 BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700190 if (!ifaceIndexNameMap.isValid()) {
Chenbo Fengf434e862018-06-27 14:08:39 -0700191 int ret = -errno;
Chenbo Feng7e974052018-02-28 22:57:21 -0800192 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
193 return ret;
194 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800195
Maciej Żenczykowskif879a7f2020-01-20 03:18:06 -0800196 BpfMapRO<uint32_t, uint8_t> configurationMap(CONFIGURATION_MAP_PATH);
Chenbo Fengf434e862018-06-27 14:08:39 -0700197 if (!configurationMap.isValid()) {
198 int ret = -errno;
199 ALOGE("get configuration map fd failed: %s", strerror(errno));
200 return ret;
201 }
202 auto configuration = configurationMap.readValue(CURRENT_STATS_MAP_CONFIGURATION_KEY);
Bernie Innocenti615fd742020-02-06 03:55:09 +0900203 if (!configuration.ok()) {
Chenbo Fengf434e862018-06-27 14:08:39 -0700204 ALOGE("Cannot read the old configuration from map: %s",
Steven Morelanda3074542020-01-13 14:13:44 -0800205 configuration.error().message().c_str());
206 return -configuration.error().code();
Chenbo Fengf434e862018-06-27 14:08:39 -0700207 }
208 const char* statsMapPath = STATS_MAP_PATH[configuration.value()];
Maciej Żenczykowskif879a7f2020-01-20 03:18:06 -0800209 BpfMap<StatsKey, StatsValue> statsMap(statsMapPath);
Chenbo Fengf434e862018-06-27 14:08:39 -0700210 if (!statsMap.isValid()) {
211 int ret = -errno;
212 ALOGE("get stats map fd failed: %s, path: %s", strerror(errno), statsMapPath);
213 return ret;
Chenbo Feng16513482018-03-15 17:59:58 -0700214 }
215
Chenbo Feng873ae142019-04-10 12:26:06 -0700216 // It is safe to read and clear the old map now since the
217 // networkStatsFactory should call netd to swap the map in advance already.
218 int ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid, statsMap,
219 ifaceIndexNameMap);
Chenbo Fengf434e862018-06-27 14:08:39 -0700220 if (ret) {
221 ALOGE("parse detail network stats failed: %s", strerror(errno));
222 return ret;
223 }
224
Steven Morelanda3074542020-01-13 14:13:44 -0800225 Result<void> res = statsMap.clear();
Bernie Innocenti615fd742020-02-06 03:55:09 +0900226 if (!res.ok()) {
Steven Morelanda3074542020-01-13 14:13:44 -0800227 ALOGE("Clean up current stats map failed: %s", strerror(res.error().code()));
228 return -res.error().code();
Chenbo Fengf434e862018-06-27 14:08:39 -0700229 }
230
231 return 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800232}
233
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700234int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700235 const BpfMap<uint32_t, StatsValue>& statsMap,
236 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700237 int64_t unknownIfaceBytesTotal = 0;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700238 const auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMap, &statsMap](
239 const uint32_t& key, const StatsValue& value,
240 const BpfMap<uint32_t, StatsValue>&) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700241 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700242 if (getIfaceNameFromMap(ifaceMap, statsMap, key, ifname, key, &unknownIfaceBytesTotal)) {
Steven Morelanda3074542020-01-13 14:13:44 -0800243 return Result<void>();
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700244 }
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700245 StatsKey fakeKey = {
Nick Desaulniersa1aae6c2019-10-07 17:43:50 -0700246 .uid = (uint32_t)UID_ALL,
247 .tag = (uint32_t)TAG_NONE,
248 .counterSet = (uint32_t)SET_ALL,
249 };
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700250 lines->push_back(populateStatsEntry(fakeKey, value, ifname));
Steven Morelanda3074542020-01-13 14:13:44 -0800251 return Result<void>();
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700252 };
Steven Morelanda3074542020-01-13 14:13:44 -0800253 Result<void> res = statsMap.iterateWithValue(processDetailIfaceStats);
Bernie Innocenti615fd742020-02-06 03:55:09 +0900254 if (!res.ok()) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700255 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
Steven Morelanda3074542020-01-13 14:13:44 -0800256 strerror(res.error().code()));
257 return -res.error().code();
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700258 }
junyulaia130ac22018-11-09 16:12:29 +0800259
260 groupNetworkStats(lines);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700261 return 0;
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700262}
263
264int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
265 int ret = 0;
Maciej Żenczykowskif879a7f2020-01-20 03:18:06 -0800266 BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700267 if (!ifaceIndexNameMap.isValid()) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700268 ret = -errno;
269 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
270 return ret;
271 }
272
Maciej Żenczykowskif879a7f2020-01-20 03:18:06 -0800273 BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700274 if (!ifaceStatsMap.isValid()) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700275 ret = -errno;
276 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
277 return ret;
278 }
279 return parseBpfNetworkStatsDevInternal(lines, ifaceStatsMap, ifaceIndexNameMap);
280}
281
Chenbo Fengf43bf812017-12-15 18:27:22 -0800282uint64_t combineUidTag(const uid_t uid, const uint32_t tag) {
283 return (uint64_t)uid << 32 | tag;
284}
285
junyulaia130ac22018-11-09 16:12:29 +0800286void groupNetworkStats(std::vector<stats_line>* lines) {
287 if (lines->size() <= 1) return;
288 std::sort(lines->begin(), lines->end());
289
290 // Similar to std::unique(), but aggregates the duplicates rather than discarding them.
291 size_t nextOutput = 0;
292 for (size_t i = 1; i < lines->size(); i++) {
293 if (lines->at(nextOutput) == lines->at(i)) {
294 lines->at(nextOutput) += lines->at(i);
295 } else {
296 nextOutput++;
297 if (nextOutput != i) {
298 lines->at(nextOutput) = lines->at(i);
299 }
300 }
301 }
302
303 if (lines->size() != nextOutput + 1) {
304 lines->resize(nextOutput + 1);
305 }
306}
307
308// True if lhs equals to rhs, only compare iface, uid, tag and set.
309bool operator==(const stats_line& lhs, const stats_line& rhs) {
310 return ((lhs.uid == rhs.uid) && (lhs.tag == rhs.tag) && (lhs.set == rhs.set) &&
311 !strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface)));
312}
313
314// True if lhs is smaller then rhs, only compare iface, uid, tag and set.
315bool operator<(const stats_line& lhs, const stats_line& rhs) {
316 int ret = strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface));
317 if (ret != 0) return ret < 0;
318 if (lhs.uid < rhs.uid) return true;
319 if (lhs.uid > rhs.uid) return false;
320 if (lhs.tag < rhs.tag) return true;
321 if (lhs.tag > rhs.tag) return false;
322 if (lhs.set < rhs.set) return true;
323 if (lhs.set > rhs.set) return false;
324 return false;
325}
326
327stats_line& stats_line::operator=(const stats_line& rhs) {
Luca Stefani28e84722019-09-01 21:11:02 +0200328 if (this == &rhs) return *this;
329
junyulaia130ac22018-11-09 16:12:29 +0800330 strlcpy(iface, rhs.iface, sizeof(iface));
331 uid = rhs.uid;
332 set = rhs.set;
333 tag = rhs.tag;
334 rxPackets = rhs.rxPackets;
335 txPackets = rhs.txPackets;
336 rxBytes = rhs.rxBytes;
337 txBytes = rhs.txBytes;
338 return *this;
339}
340
341stats_line& stats_line::operator+=(const stats_line& rhs) {
342 rxPackets += rhs.rxPackets;
343 txPackets += rhs.txPackets;
344 rxBytes += rhs.rxBytes;
345 txBytes += rhs.txBytes;
346 return *this;
347}
348
Chenbo Fengf43bf812017-12-15 18:27:22 -0800349} // namespace bpf
350} // namespace android