blob: d75c1860f2e30aa888a5de1ce7237426ec56f946 [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 Feng2c67f262018-04-26 10:37:55 -070028#include "bpf/BpfMap.h"
Chenbo Fengf43bf812017-12-15 18:27:22 -080029#include "bpf/BpfNetworkStats.h"
Chenbo Fengf43bf812017-12-15 18:27:22 -080030
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 Feng2c67f262018-04-26 10:37:55 -070040using netdutils::Status;
Chenbo Feng8cc480c2018-02-28 22:57:21 -080041
Chenbo Feng9355c3f2018-03-05 04:10:38 -080042static constexpr uint32_t BPF_OPEN_FLAGS = BPF_F_RDONLY;
Lorenzo Colittif9c654c2018-03-01 18:02:15 +090043
Chenbo Feng2c67f262018-04-26 10:37:55 -070044int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
Chenbo Feng58a09b52018-05-11 19:15:15 -070045 const BpfMap<uint32_t, StatsValue>& appUidStatsMap) {
46 auto statsEntry = appUidStatsMap.readValue(uid);
47 if (isOk(statsEntry)) {
48 stats->rxPackets = statsEntry.value().rxPackets;
49 stats->txPackets = statsEntry.value().txPackets;
50 stats->rxBytes = statsEntry.value().rxBytes;
51 stats->txBytes = statsEntry.value().txBytes;
52 }
53 return -statsEntry.status().code();
Chenbo Fengf43bf812017-12-15 18:27:22 -080054}
55
56int bpfGetUidStats(uid_t uid, Stats* stats) {
Chenbo Feng58a09b52018-05-11 19:15:15 -070057 BpfMap<uint32_t, StatsValue> appUidStatsMap(
58 mapRetrieve(APP_UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Feng2c67f262018-04-26 10:37:55 -070059
Chenbo Feng58a09b52018-05-11 19:15:15 -070060 if (!appUidStatsMap.isValid()) {
Chenbo Fengf43bf812017-12-15 18:27:22 -080061 int ret = -errno;
Chenbo Feng58a09b52018-05-11 19:15:15 -070062 ALOGE("Opening appUidStatsMap(%s) failed: %s", UID_STATS_MAP_PATH, strerror(errno));
Chenbo Fengf43bf812017-12-15 18:27:22 -080063 return ret;
64 }
Chenbo Feng58a09b52018-05-11 19:15:15 -070065 return bpfGetUidStatsInternal(uid, stats, appUidStatsMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -080066}
67
Chenbo Feng3c69a392018-03-16 18:10:07 -070068int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
Chenbo Feng2c67f262018-04-26 10:37:55 -070069 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
70 const BpfMap<uint32_t, IfaceValue>& ifaceNameMap) {
Chenbo Feng3c69a392018-03-16 18:10:07 -070071 int64_t unknownIfaceBytesTotal = 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -080072 stats->tcpRxPackets = -1;
73 stats->tcpTxPackets = -1;
Chenbo Feng2c67f262018-04-26 10:37:55 -070074 const auto processIfaceStats = [iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal]
75 (const uint32_t& key,
76 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) {
Chenbo Feng3c69a392018-03-16 18:10:07 -070077 char ifname[IFNAMSIZ];
Chenbo Feng2c67f262018-04-26 10:37:55 -070078 if (getIfaceNameFromMap(ifaceNameMap, ifaceStatsMap, key, ifname, key,
Chenbo Feng3c69a392018-03-16 18:10:07 -070079 &unknownIfaceBytesTotal)) {
Chenbo Feng2c67f262018-04-26 10:37:55 -070080 return netdutils::status::ok;
Chenbo Feng3c69a392018-03-16 18:10:07 -070081 }
82 if (!iface || !strcmp(iface, ifname)) {
83 StatsValue statsEntry;
Chenbo Feng2c67f262018-04-26 10:37:55 -070084 ASSIGN_OR_RETURN(statsEntry, ifaceStatsMap.readValue(key));
Chenbo Feng3c69a392018-03-16 18:10:07 -070085 stats->rxPackets += statsEntry.rxPackets;
86 stats->txPackets += statsEntry.txPackets;
87 stats->rxBytes += statsEntry.rxBytes;
88 stats->txBytes += statsEntry.txBytes;
89 }
Chenbo Feng2c67f262018-04-26 10:37:55 -070090 return netdutils::status::ok;
Chenbo Feng3c69a392018-03-16 18:10:07 -070091 };
Chenbo Feng2c67f262018-04-26 10:37:55 -070092 return -ifaceStatsMap.iterate(processIfaceStats).code();
Chenbo Fengf43bf812017-12-15 18:27:22 -080093}
94
95int bpfGetIfaceStats(const char* iface, Stats* stats) {
Chenbo Feng2c67f262018-04-26 10:37:55 -070096 BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Feng3c69a392018-03-16 18:10:07 -070097 int ret;
Chenbo Feng2c67f262018-04-26 10:37:55 -070098 if (!ifaceStatsMap.isValid()) {
Chenbo Feng3c69a392018-03-16 18:10:07 -070099 ret = -errno;
100 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
101 return ret;
102 }
Chenbo Feng2c67f262018-04-26 10:37:55 -0700103 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
104 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
105 if (!ifaceIndexNameMap.isValid()) {
Chenbo Feng3c69a392018-03-16 18:10:07 -0700106 ret = -errno;
107 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
108 return ret;
109 }
110 return bpfGetIfaceStatsInternal(iface, stats, ifaceStatsMap, ifaceIndexNameMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -0800111}
112
113stats_line populateStatsEntry(const StatsKey& statsKey, const StatsValue& statsEntry,
114 const char* ifname) {
115 stats_line newLine;
116 strlcpy(newLine.iface, ifname, sizeof(newLine.iface));
Chenbo Fengb9c3fdb2018-04-18 15:27:19 -0700117 newLine.uid = (int32_t)statsKey.uid;
118 newLine.set = (int32_t)statsKey.counterSet;
119 newLine.tag = (int32_t)statsKey.tag;
Chenbo Fengeac6c472018-02-05 15:06:23 -0800120 newLine.rxPackets = statsEntry.rxPackets;
121 newLine.txPackets = statsEntry.txPackets;
122 newLine.rxBytes = statsEntry.rxBytes;
123 newLine.txBytes = statsEntry.txBytes;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800124 return newLine;
125}
126
Chenbo Feng40508ea2018-03-15 17:59:58 -0700127int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
128 const std::vector<std::string>& limitIfaces, int limitTag,
Chenbo Feng2c67f262018-04-26 10:37:55 -0700129 int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
130 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
Chenbo Feng3c69a392018-03-16 18:10:07 -0700131 int64_t unknownIfaceBytesTotal = 0;
Chenbo Feng2c67f262018-04-26 10:37:55 -0700132 const auto processDetailUidStats = [lines, &limitIfaces, &limitTag, &limitUid,
133 &unknownIfaceBytesTotal,
134 &ifaceMap](const StatsKey& key,
135 const BpfMap<StatsKey, StatsValue>& statsMap) {
Chenbo Feng8cc480c2018-02-28 22:57:21 -0800136 char ifname[IFNAMSIZ];
Chenbo Feng2c67f262018-04-26 10:37:55 -0700137 if (getIfaceNameFromMap(ifaceMap, statsMap, key.ifaceIndex, ifname, key,
Chenbo Feng3c69a392018-03-16 18:10:07 -0700138 &unknownIfaceBytesTotal)) {
Chenbo Feng2c67f262018-04-26 10:37:55 -0700139 return netdutils::status::ok;
Chenbo Feng8cc480c2018-02-28 22:57:21 -0800140 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800141 std::string ifnameStr(ifname);
142 if (limitIfaces.size() > 0 &&
143 std::find(limitIfaces.begin(), limitIfaces.end(), ifnameStr) == limitIfaces.end()) {
144 // Nothing matched; skip this line.
Chenbo Feng2c67f262018-04-26 10:37:55 -0700145 return netdutils::status::ok;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800146 }
Chenbo Feng2c67f262018-04-26 10:37:55 -0700147 if (limitTag != TAG_ALL && uint32_t(limitTag) != key.tag) {
148 return netdutils::status::ok;
Chenbo Feng40508ea2018-03-15 17:59:58 -0700149 }
Chenbo Feng2c67f262018-04-26 10:37:55 -0700150 if (limitUid != UID_ALL && uint32_t(limitUid) != key.uid) {
151 return netdutils::status::ok;
Chenbo Feng40508ea2018-03-15 17:59:58 -0700152 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800153 StatsValue statsEntry;
Chenbo Feng2c67f262018-04-26 10:37:55 -0700154 ASSIGN_OR_RETURN(statsEntry, statsMap.readValue(key));
155 lines->push_back(populateStatsEntry(key, statsEntry, ifname));
156 return netdutils::status::ok;
Chenbo Feng40508ea2018-03-15 17:59:58 -0700157 };
Chenbo Feng2c67f262018-04-26 10:37:55 -0700158 Status res = statsMap.iterate(processDetailUidStats);
159 if (!isOk(res)) {
160 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
161 strerror(res.code()));
162 return -res.code();
163 }
junyulaiaa7a4742018-11-13 10:12:46 +0800164
165 // Since eBPF use hash map to record stats, network stats collected from
166 // eBPF will be out of order. And the performance of findIndexHinted in
167 // NetworkStats will also be impacted.
168 //
169 // Furthermore, since the StatsKey contains iface index, the network stats
170 // reported to framework would create items with the same iface, uid, tag
171 // and set, which causes NetworkStats maps wrong item to subtract.
172 //
173 // Thus, the stats needs to be properly sorted and grouped before reported.
174 groupNetworkStats(lines);
Chenbo Feng2c67f262018-04-26 10:37:55 -0700175 return 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800176}
177
178int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
179 const std::vector<std::string>& limitIfaces, int limitTag,
180 int limitUid) {
Chenbo Fengf43bf812017-12-15 18:27:22 -0800181 int ret = 0;
Chenbo Feng2c67f262018-04-26 10:37:55 -0700182 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
183 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
184 if (!ifaceIndexNameMap.isValid()) {
Chenbo Feng8cc480c2018-02-28 22:57:21 -0800185 ret = -errno;
186 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
187 return ret;
188 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800189
Chenbo Feng40508ea2018-03-15 17:59:58 -0700190 // If the caller did not pass in TAG_NONE, read tag data.
191 if (limitTag != TAG_NONE) {
Chenbo Feng2c67f262018-04-26 10:37:55 -0700192 BpfMap<StatsKey, StatsValue> tagStatsMap(mapRetrieve(TAG_STATS_MAP_PATH, BPF_OPEN_FLAGS));
193 if (!tagStatsMap.isValid()) {
Chenbo Feng40508ea2018-03-15 17:59:58 -0700194 ret = -errno;
195 ALOGE("get tagStats map fd failed: %s", strerror(errno));
196 return ret;
197 }
198 ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid,
199 tagStatsMap, ifaceIndexNameMap);
200 if (ret) return ret;
201 }
202
203 // If the caller did not pass in a specific tag (i.e., if limitTag is TAG_NONE(0) or
204 // TAG_ALL(-1)) read UID data.
205 if (limitTag == TAG_NONE || limitTag == TAG_ALL) {
Chenbo Feng2c67f262018-04-26 10:37:55 -0700206 BpfMap<StatsKey, StatsValue> uidStatsMap(mapRetrieve(UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
207 if (!uidStatsMap.isValid()) {
Chenbo Fengf43bf812017-12-15 18:27:22 -0800208 ret = -errno;
Lorenzo Colittif9c654c2018-03-01 18:02:15 +0900209 ALOGE("Opening map fd from %s failed: %s", UID_STATS_MAP_PATH, strerror(errno));
Chenbo Fengf43bf812017-12-15 18:27:22 -0800210 return ret;
211 }
Chenbo Feng40508ea2018-03-15 17:59:58 -0700212 ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid,
213 uidStatsMap, ifaceIndexNameMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -0800214 }
215 return ret;
216}
217
Chenbo Fengb9c3fdb2018-04-18 15:27:19 -0700218int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
Chenbo Feng2c67f262018-04-26 10:37:55 -0700219 const BpfMap<uint32_t, StatsValue>& statsMap,
220 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
Chenbo Fengb9c3fdb2018-04-18 15:27:19 -0700221 int64_t unknownIfaceBytesTotal = 0;
Chenbo Feng2c67f262018-04-26 10:37:55 -0700222 const auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMap, &statsMap](
223 const uint32_t& key, const StatsValue& value,
224 const BpfMap<uint32_t, StatsValue>&) {
Chenbo Fengb9c3fdb2018-04-18 15:27:19 -0700225 char ifname[IFNAMSIZ];
Chenbo Feng2c67f262018-04-26 10:37:55 -0700226 if (getIfaceNameFromMap(ifaceMap, statsMap, key, ifname, key, &unknownIfaceBytesTotal)) {
227 return netdutils::status::ok;
Chenbo Fengb9c3fdb2018-04-18 15:27:19 -0700228 }
Chenbo Fengb9c3fdb2018-04-18 15:27:19 -0700229 StatsKey fakeKey = {
230 .uid = (uint32_t)UID_ALL, .counterSet = (uint32_t)SET_ALL, .tag = (uint32_t)TAG_NONE};
Chenbo Feng2c67f262018-04-26 10:37:55 -0700231 lines->push_back(populateStatsEntry(fakeKey, value, ifname));
232 return netdutils::status::ok;
Chenbo Fengb9c3fdb2018-04-18 15:27:19 -0700233 };
Chenbo Feng2c67f262018-04-26 10:37:55 -0700234 Status res = statsMap.iterateWithValue(processDetailIfaceStats);
235 if (!isOk(res)) {
236 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
237 strerror(res.code()));
238 return -res.code();
239 }
junyulaiaa7a4742018-11-13 10:12:46 +0800240
241 groupNetworkStats(lines);
Chenbo Feng2c67f262018-04-26 10:37:55 -0700242 return 0;
Chenbo Fengb9c3fdb2018-04-18 15:27:19 -0700243}
244
245int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
246 int ret = 0;
Chenbo Feng2c67f262018-04-26 10:37:55 -0700247 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
248 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
249 if (!ifaceIndexNameMap.isValid()) {
Chenbo Fengb9c3fdb2018-04-18 15:27:19 -0700250 ret = -errno;
251 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
252 return ret;
253 }
254
Chenbo Feng2c67f262018-04-26 10:37:55 -0700255 BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
256 if (!ifaceStatsMap.isValid()) {
Chenbo Fengb9c3fdb2018-04-18 15:27:19 -0700257 ret = -errno;
258 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
259 return ret;
260 }
261 return parseBpfNetworkStatsDevInternal(lines, ifaceStatsMap, ifaceIndexNameMap);
262}
263
Chenbo Fengf43bf812017-12-15 18:27:22 -0800264uint64_t combineUidTag(const uid_t uid, const uint32_t tag) {
265 return (uint64_t)uid << 32 | tag;
266}
267
junyulaiaa7a4742018-11-13 10:12:46 +0800268void groupNetworkStats(std::vector<stats_line>* lines) {
269 if (lines->size() <= 1) return;
270 std::sort(lines->begin(), lines->end());
271
272 // Similar to std::unique(), but aggregates the duplicates rather than discarding them.
273 size_t nextOutput = 0;
274 for (size_t i = 1; i < lines->size(); i++) {
275 if (lines->at(nextOutput) == lines->at(i)) {
276 lines->at(nextOutput) += lines->at(i);
277 } else {
278 nextOutput++;
279 if (nextOutput != i) {
280 lines->at(nextOutput) = lines->at(i);
281 }
282 }
283 }
284
285 if (lines->size() != nextOutput + 1) {
286 lines->resize(nextOutput + 1);
287 }
288}
289
290// True if lhs equals to rhs, only compare iface, uid, tag and set.
291bool operator==(const stats_line& lhs, const stats_line& rhs) {
292 return ((lhs.uid == rhs.uid) && (lhs.tag == rhs.tag) && (lhs.set == rhs.set) &&
293 !strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface)));
294}
295
296// True if lhs is smaller then rhs, only compare iface, uid, tag and set.
297bool operator<(const stats_line& lhs, const stats_line& rhs) {
298 int ret = strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface));
299 if (ret != 0) return ret < 0;
300 if (lhs.uid < rhs.uid) return true;
301 if (lhs.uid > rhs.uid) return false;
302 if (lhs.tag < rhs.tag) return true;
303 if (lhs.tag > rhs.tag) return false;
304 if (lhs.set < rhs.set) return true;
305 if (lhs.set > rhs.set) return false;
306 return false;
307}
308
309stats_line& stats_line::operator=(const stats_line& rhs) {
310 strlcpy(iface, rhs.iface, sizeof(iface));
311 uid = rhs.uid;
312 set = rhs.set;
313 tag = rhs.tag;
314 rxPackets = rhs.rxPackets;
315 txPackets = rhs.txPackets;
316 rxBytes = rhs.rxBytes;
317 txBytes = rhs.txBytes;
318 return *this;
319}
320
321stats_line& stats_line::operator+=(const stats_line& rhs) {
322 rxPackets += rhs.rxPackets;
323 txPackets += rhs.txPackets;
324 rxBytes += rhs.rxBytes;
325 txBytes += rhs.txBytes;
326 return *this;
327}
328
Chenbo Fengf43bf812017-12-15 18:27:22 -0800329} // namespace bpf
330} // namespace android