blob: ccde15738f159a10f7e436328a3a02bde519619b [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 Fengf434e862018-06-27 14:08:39 -070043static constexpr char const* STATS_MAP_PATH[] = {STATS_MAP_A_PATH, STATS_MAP_B_PATH};
44
Chenbo Feng06c73642018-03-05 04:10:38 -080045static constexpr uint32_t BPF_OPEN_FLAGS = BPF_F_RDONLY;
Lorenzo Colittif9c654c2018-03-01 18:02:15 +090046
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);
50 if (isOk(statsEntry)) {
51 stats->rxPackets = statsEntry.value().rxPackets;
52 stats->txPackets = statsEntry.value().txPackets;
53 stats->rxBytes = statsEntry.value().rxBytes;
54 stats->txBytes = statsEntry.value().txBytes;
55 }
Chenbo Feng1002c2c2019-04-04 15:53:59 -070056 return statsEntry.status().code() == ENOENT ? 0 : -statsEntry.status().code();
Chenbo Fengf43bf812017-12-15 18:27:22 -080057}
58
59int bpfGetUidStats(uid_t uid, Stats* stats) {
Chenbo Fengbc4a15f2018-05-11 19:15:15 -070060 BpfMap<uint32_t, StatsValue> appUidStatsMap(
61 mapRetrieve(APP_UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
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;
Chenbo Feng4f6c2372018-04-26 10:37:55 -070077 const auto processIfaceStats = [iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal]
78 (const uint32_t& key,
79 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) {
Chenbo Feng33a4de12018-03-16 18:10:07 -070080 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -070081 if (getIfaceNameFromMap(ifaceNameMap, ifaceStatsMap, key, ifname, key,
Chenbo Feng33a4de12018-03-16 18:10:07 -070082 &unknownIfaceBytesTotal)) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -070083 return netdutils::status::ok;
Chenbo Feng33a4de12018-03-16 18:10:07 -070084 }
85 if (!iface || !strcmp(iface, ifname)) {
86 StatsValue statsEntry;
Chenbo Feng4f6c2372018-04-26 10:37:55 -070087 ASSIGN_OR_RETURN(statsEntry, ifaceStatsMap.readValue(key));
Chenbo Feng33a4de12018-03-16 18:10:07 -070088 stats->rxPackets += statsEntry.rxPackets;
89 stats->txPackets += statsEntry.txPackets;
90 stats->rxBytes += statsEntry.rxBytes;
91 stats->txBytes += statsEntry.txBytes;
92 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -070093 return netdutils::status::ok;
Chenbo Feng33a4de12018-03-16 18:10:07 -070094 };
Chenbo Feng4f6c2372018-04-26 10:37:55 -070095 return -ifaceStatsMap.iterate(processIfaceStats).code();
Chenbo Fengf43bf812017-12-15 18:27:22 -080096}
97
98int bpfGetIfaceStats(const char* iface, Stats* stats) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -070099 BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
Chenbo Feng33a4de12018-03-16 18:10:07 -0700100 int ret;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700101 if (!ifaceStatsMap.isValid()) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700102 ret = -errno;
103 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
104 return ret;
105 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700106 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
107 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
108 if (!ifaceIndexNameMap.isValid()) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700109 ret = -errno;
110 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
111 return ret;
112 }
113 return bpfGetIfaceStatsInternal(iface, stats, ifaceStatsMap, ifaceIndexNameMap);
Chenbo Fengf43bf812017-12-15 18:27:22 -0800114}
115
116stats_line populateStatsEntry(const StatsKey& statsKey, const StatsValue& statsEntry,
117 const char* ifname) {
118 stats_line newLine;
119 strlcpy(newLine.iface, ifname, sizeof(newLine.iface));
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700120 newLine.uid = (int32_t)statsKey.uid;
121 newLine.set = (int32_t)statsKey.counterSet;
122 newLine.tag = (int32_t)statsKey.tag;
Chenbo Fengeac6c472018-02-05 15:06:23 -0800123 newLine.rxPackets = statsEntry.rxPackets;
124 newLine.txPackets = statsEntry.txPackets;
125 newLine.rxBytes = statsEntry.rxBytes;
126 newLine.txBytes = statsEntry.txBytes;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800127 return newLine;
128}
129
Chenbo Feng16513482018-03-15 17:59:58 -0700130int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
131 const std::vector<std::string>& limitIfaces, int limitTag,
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700132 int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
133 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700134 int64_t unknownIfaceBytesTotal = 0;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700135 const auto processDetailUidStats = [lines, &limitIfaces, &limitTag, &limitUid,
136 &unknownIfaceBytesTotal,
137 &ifaceMap](const StatsKey& key,
138 const BpfMap<StatsKey, StatsValue>& statsMap) {
Chenbo Feng7e974052018-02-28 22:57:21 -0800139 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700140 if (getIfaceNameFromMap(ifaceMap, statsMap, key.ifaceIndex, ifname, key,
Chenbo Feng33a4de12018-03-16 18:10:07 -0700141 &unknownIfaceBytesTotal)) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700142 return netdutils::status::ok;
Chenbo Feng7e974052018-02-28 22:57:21 -0800143 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800144 std::string ifnameStr(ifname);
145 if (limitIfaces.size() > 0 &&
146 std::find(limitIfaces.begin(), limitIfaces.end(), ifnameStr) == limitIfaces.end()) {
147 // Nothing matched; skip this line.
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700148 return netdutils::status::ok;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800149 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700150 if (limitTag != TAG_ALL && uint32_t(limitTag) != key.tag) {
151 return netdutils::status::ok;
Chenbo Feng16513482018-03-15 17:59:58 -0700152 }
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700153 if (limitUid != UID_ALL && uint32_t(limitUid) != key.uid) {
154 return netdutils::status::ok;
Chenbo Feng16513482018-03-15 17:59:58 -0700155 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800156 StatsValue statsEntry;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700157 ASSIGN_OR_RETURN(statsEntry, statsMap.readValue(key));
158 lines->push_back(populateStatsEntry(key, statsEntry, ifname));
159 return netdutils::status::ok;
Chenbo Feng16513482018-03-15 17:59:58 -0700160 };
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700161 Status res = statsMap.iterate(processDetailUidStats);
162 if (!isOk(res)) {
163 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
164 strerror(res.code()));
165 return -res.code();
166 }
junyulaia130ac22018-11-09 16:12:29 +0800167
168 // Since eBPF use hash map to record stats, network stats collected from
169 // eBPF will be out of order. And the performance of findIndexHinted in
170 // NetworkStats will also be impacted.
171 //
172 // Furthermore, since the StatsKey contains iface index, the network stats
173 // reported to framework would create items with the same iface, uid, tag
174 // and set, which causes NetworkStats maps wrong item to subtract.
175 //
176 // Thus, the stats needs to be properly sorted and grouped before reported.
177 groupNetworkStats(lines);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700178 return 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800179}
180
181int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
182 const std::vector<std::string>& limitIfaces, int limitTag,
183 int limitUid) {
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700184 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
185 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
186 if (!ifaceIndexNameMap.isValid()) {
Chenbo Fengf434e862018-06-27 14:08:39 -0700187 int ret = -errno;
Chenbo Feng7e974052018-02-28 22:57:21 -0800188 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
189 return ret;
190 }
Chenbo Fengf43bf812017-12-15 18:27:22 -0800191
Chenbo Fengf434e862018-06-27 14:08:39 -0700192 BpfMap<uint32_t, uint8_t> configurationMap(mapRetrieve(CONFIGURATION_MAP_PATH, 0));
193 if (!configurationMap.isValid()) {
194 int ret = -errno;
195 ALOGE("get configuration map fd failed: %s", strerror(errno));
196 return ret;
197 }
198 auto configuration = configurationMap.readValue(CURRENT_STATS_MAP_CONFIGURATION_KEY);
199 if (!isOk(configuration)) {
200 ALOGE("Cannot read the old configuration from map: %s",
201 configuration.status().msg().c_str());
202 return -configuration.status().code();
203 }
204 const char* statsMapPath = STATS_MAP_PATH[configuration.value()];
205 BpfMap<StatsKey, StatsValue> statsMap(mapRetrieve(statsMapPath, 0));
206 if (!statsMap.isValid()) {
207 int ret = -errno;
208 ALOGE("get stats map fd failed: %s, path: %s", strerror(errno), statsMapPath);
209 return ret;
Chenbo Feng16513482018-03-15 17:59:58 -0700210 }
211
Chenbo Fengf434e862018-06-27 14:08:39 -0700212 // Write to the configuration map to inform the kernel eBPF program to switch
213 // from using one map to the other.
214 uint8_t newConfigure = (configuration.value() == SELECT_MAP_A) ? SELECT_MAP_B : SELECT_MAP_A;
215 Status res = configurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, newConfigure,
216 BPF_EXIST);
217 if (!isOk(res)) {
218 ALOGE("Failed to toggle the stats map: %s", strerror(res.code()));
219 return -res.code();
Chenbo Fengf43bf812017-12-15 18:27:22 -0800220 }
Chenbo Fengf434e862018-06-27 14:08:39 -0700221
222 // After changing the config, we need to make sure all the current running
223 // eBPF programs are finished and all the CPUs are aware of this config change
224 // before we modify the old map. So we do a special hack here to wait for
225 // the kernel to do a synchronize_rcu(). Once the kernel called
226 // synchronize_rcu(), the config we just updated will be available to all cores
227 // and the next eBPF programs triggered inside the kernel will use the new
228 // map configuration. So once this function returns we can safely modify the
229 // old stats map without concerning about race between the kernel and
230 // userspace.
231 int ret = synchronizeKernelRCU();
232 if (ret) {
233 ALOGE("map swap synchronize_rcu() ended with failure: %s", strerror(-ret));
234 return ret;
235 }
236
237 // It is safe to read and clear the old map now.
238 ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid, statsMap,
239 ifaceIndexNameMap);
240 if (ret) {
241 ALOGE("parse detail network stats failed: %s", strerror(errno));
242 return ret;
243 }
244
245 res = statsMap.clear();
246 if (!isOk(res)) {
247 ALOGE("Clean up current stats map failed: %s", strerror(res.code()));
248 return -res.code();
249 }
250
251 return 0;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800252}
253
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700254int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700255 const BpfMap<uint32_t, StatsValue>& statsMap,
256 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700257 int64_t unknownIfaceBytesTotal = 0;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700258 const auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMap, &statsMap](
259 const uint32_t& key, const StatsValue& value,
260 const BpfMap<uint32_t, StatsValue>&) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700261 char ifname[IFNAMSIZ];
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700262 if (getIfaceNameFromMap(ifaceMap, statsMap, key, ifname, key, &unknownIfaceBytesTotal)) {
263 return netdutils::status::ok;
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700264 }
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700265 StatsKey fakeKey = {
266 .uid = (uint32_t)UID_ALL, .counterSet = (uint32_t)SET_ALL, .tag = (uint32_t)TAG_NONE};
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700267 lines->push_back(populateStatsEntry(fakeKey, value, ifname));
268 return netdutils::status::ok;
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700269 };
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700270 Status res = statsMap.iterateWithValue(processDetailIfaceStats);
271 if (!isOk(res)) {
272 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
273 strerror(res.code()));
274 return -res.code();
275 }
junyulaia130ac22018-11-09 16:12:29 +0800276
277 groupNetworkStats(lines);
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700278 return 0;
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700279}
280
281int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
282 int ret = 0;
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700283 BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
284 mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
285 if (!ifaceIndexNameMap.isValid()) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700286 ret = -errno;
287 ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
288 return ret;
289 }
290
Chenbo Feng4f6c2372018-04-26 10:37:55 -0700291 BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
292 if (!ifaceStatsMap.isValid()) {
Chenbo Fengf4b812d2018-04-18 15:27:19 -0700293 ret = -errno;
294 ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
295 return ret;
296 }
297 return parseBpfNetworkStatsDevInternal(lines, ifaceStatsMap, ifaceIndexNameMap);
298}
299
Chenbo Fengf43bf812017-12-15 18:27:22 -0800300uint64_t combineUidTag(const uid_t uid, const uint32_t tag) {
301 return (uint64_t)uid << 32 | tag;
302}
303
junyulaia130ac22018-11-09 16:12:29 +0800304void groupNetworkStats(std::vector<stats_line>* lines) {
305 if (lines->size() <= 1) return;
306 std::sort(lines->begin(), lines->end());
307
308 // Similar to std::unique(), but aggregates the duplicates rather than discarding them.
309 size_t nextOutput = 0;
310 for (size_t i = 1; i < lines->size(); i++) {
311 if (lines->at(nextOutput) == lines->at(i)) {
312 lines->at(nextOutput) += lines->at(i);
313 } else {
314 nextOutput++;
315 if (nextOutput != i) {
316 lines->at(nextOutput) = lines->at(i);
317 }
318 }
319 }
320
321 if (lines->size() != nextOutput + 1) {
322 lines->resize(nextOutput + 1);
323 }
324}
325
326// True if lhs equals to rhs, only compare iface, uid, tag and set.
327bool operator==(const stats_line& lhs, const stats_line& rhs) {
328 return ((lhs.uid == rhs.uid) && (lhs.tag == rhs.tag) && (lhs.set == rhs.set) &&
329 !strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface)));
330}
331
332// True if lhs is smaller then rhs, only compare iface, uid, tag and set.
333bool operator<(const stats_line& lhs, const stats_line& rhs) {
334 int ret = strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface));
335 if (ret != 0) return ret < 0;
336 if (lhs.uid < rhs.uid) return true;
337 if (lhs.uid > rhs.uid) return false;
338 if (lhs.tag < rhs.tag) return true;
339 if (lhs.tag > rhs.tag) return false;
340 if (lhs.set < rhs.set) return true;
341 if (lhs.set > rhs.set) return false;
342 return false;
343}
344
345stats_line& stats_line::operator=(const stats_line& rhs) {
346 strlcpy(iface, rhs.iface, sizeof(iface));
347 uid = rhs.uid;
348 set = rhs.set;
349 tag = rhs.tag;
350 rxPackets = rhs.rxPackets;
351 txPackets = rhs.txPackets;
352 rxBytes = rhs.rxBytes;
353 txBytes = rhs.txBytes;
354 return *this;
355}
356
357stats_line& stats_line::operator+=(const stats_line& rhs) {
358 rxPackets += rhs.rxPackets;
359 txPackets += rhs.txPackets;
360 rxBytes += rhs.rxBytes;
361 txBytes += rhs.txBytes;
362 return *this;
363}
364
Chenbo Fengf43bf812017-12-15 18:27:22 -0800365} // namespace bpf
366} // namespace android