1 /*
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"
28 #include "bpf/BpfMap.h"
29 #include "netd.h"
30 #include "netdbpf/BpfNetworkStats.h"
31
32 #ifdef LOG_TAG
33 #undef LOG_TAG
34 #endif
35
36 #define LOG_TAG "BpfNetworkStats"
37
38 namespace android {
39 namespace bpf {
40
41 using base::Result;
42
bpfGetUidStatsInternal(uid_t uid,Stats * stats,const BpfMap<uint32_t,StatsValue> & appUidStatsMap)43 int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
44 const BpfMap<uint32_t, StatsValue>& appUidStatsMap) {
45 auto statsEntry = appUidStatsMap.readValue(uid);
46 if (statsEntry.ok()) {
47 stats->rxPackets = statsEntry.value().rxPackets;
48 stats->txPackets = statsEntry.value().txPackets;
49 stats->rxBytes = statsEntry.value().rxBytes;
50 stats->txBytes = statsEntry.value().txBytes;
51 }
52 return (statsEntry.ok() || statsEntry.error().code() == ENOENT) ? 0
53 : -statsEntry.error().code();
54 }
55
bpfGetUidStats(uid_t uid,Stats * stats)56 int bpfGetUidStats(uid_t uid, Stats* stats) {
57 static BpfMapRO<uint32_t, StatsValue> appUidStatsMap(APP_UID_STATS_MAP_PATH);
58 return bpfGetUidStatsInternal(uid, stats, appUidStatsMap);
59 }
60
bpfGetIfaceStatsInternal(const char * iface,Stats * stats,const BpfMap<uint32_t,StatsValue> & ifaceStatsMap,const BpfMap<uint32_t,IfaceValue> & ifaceNameMap)61 int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
62 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
63 const BpfMap<uint32_t, IfaceValue>& ifaceNameMap) {
64 int64_t unknownIfaceBytesTotal = 0;
65 stats->tcpRxPackets = -1;
66 stats->tcpTxPackets = -1;
67 const auto processIfaceStats =
68 [iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal](
69 const uint32_t& key,
70 const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) -> Result<void> {
71 char ifname[IFNAMSIZ];
72 if (getIfaceNameFromMap(ifaceNameMap, ifaceStatsMap, key, ifname, key,
73 &unknownIfaceBytesTotal)) {
74 return Result<void>();
75 }
76 if (!iface || !strcmp(iface, ifname)) {
77 Result<StatsValue> statsEntry = ifaceStatsMap.readValue(key);
78 if (!statsEntry.ok()) {
79 return statsEntry.error();
80 }
81 stats->rxPackets += statsEntry.value().rxPackets;
82 stats->txPackets += statsEntry.value().txPackets;
83 stats->rxBytes += statsEntry.value().rxBytes;
84 stats->txBytes += statsEntry.value().txBytes;
85 }
86 return Result<void>();
87 };
88 auto res = ifaceStatsMap.iterate(processIfaceStats);
89 return res.ok() ? 0 : -res.error().code();
90 }
91
bpfGetIfaceStats(const char * iface,Stats * stats)92 int bpfGetIfaceStats(const char* iface, Stats* stats) {
93 static BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
94 static BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
95 return bpfGetIfaceStatsInternal(iface, stats, ifaceStatsMap, ifaceIndexNameMap);
96 }
97
populateStatsEntry(const StatsKey & statsKey,const StatsValue & statsEntry,const char * ifname)98 stats_line populateStatsEntry(const StatsKey& statsKey, const StatsValue& statsEntry,
99 const char* ifname) {
100 stats_line newLine;
101 strlcpy(newLine.iface, ifname, sizeof(newLine.iface));
102 newLine.uid = (int32_t)statsKey.uid;
103 newLine.set = (int32_t)statsKey.counterSet;
104 newLine.tag = (int32_t)statsKey.tag;
105 newLine.rxPackets = statsEntry.rxPackets;
106 newLine.txPackets = statsEntry.txPackets;
107 newLine.rxBytes = statsEntry.rxBytes;
108 newLine.txBytes = statsEntry.txBytes;
109 return newLine;
110 }
111
parseBpfNetworkStatsDetailInternal(std::vector<stats_line> & lines,const BpfMap<StatsKey,StatsValue> & statsMap,const BpfMap<uint32_t,IfaceValue> & ifaceMap)112 int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>& lines,
113 const BpfMap<StatsKey, StatsValue>& statsMap,
114 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
115 int64_t unknownIfaceBytesTotal = 0;
116 const auto processDetailUidStats =
117 [&lines, &unknownIfaceBytesTotal, &ifaceMap](
118 const StatsKey& key,
119 const BpfMap<StatsKey, StatsValue>& statsMap) -> Result<void> {
120 char ifname[IFNAMSIZ];
121 if (getIfaceNameFromMap(ifaceMap, statsMap, key.ifaceIndex, ifname, key,
122 &unknownIfaceBytesTotal)) {
123 return Result<void>();
124 }
125 Result<StatsValue> statsEntry = statsMap.readValue(key);
126 if (!statsEntry.ok()) {
127 return base::ResultError(statsEntry.error().message(), statsEntry.error().code());
128 }
129 stats_line newLine = populateStatsEntry(key, statsEntry.value(), ifname);
130 lines.push_back(newLine);
131 if (newLine.tag) {
132 // account tagged traffic in the untagged stats (for historical reasons?)
133 newLine.tag = 0;
134 lines.push_back(newLine);
135 }
136 return Result<void>();
137 };
138 Result<void> res = statsMap.iterate(processDetailUidStats);
139 if (!res.ok()) {
140 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
141 strerror(res.error().code()));
142 return -res.error().code();
143 }
144
145 // Since eBPF use hash map to record stats, network stats collected from
146 // eBPF will be out of order. And the performance of findIndexHinted in
147 // NetworkStats will also be impacted.
148 //
149 // Furthermore, since the StatsKey contains iface index, the network stats
150 // reported to framework would create items with the same iface, uid, tag
151 // and set, which causes NetworkStats maps wrong item to subtract.
152 //
153 // Thus, the stats needs to be properly sorted and grouped before reported.
154 groupNetworkStats(lines);
155 return 0;
156 }
157
parseBpfNetworkStatsDetail(std::vector<stats_line> * lines)158 int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines) {
159 static BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
160 static BpfMapRO<uint32_t, uint32_t> configurationMap(CONFIGURATION_MAP_PATH);
161 static BpfMap<StatsKey, StatsValue> statsMapA(STATS_MAP_A_PATH);
162 static BpfMap<StatsKey, StatsValue> statsMapB(STATS_MAP_B_PATH);
163 auto configuration = configurationMap.readValue(CURRENT_STATS_MAP_CONFIGURATION_KEY);
164 if (!configuration.ok()) {
165 ALOGE("Cannot read the old configuration from map: %s",
166 configuration.error().message().c_str());
167 return -configuration.error().code();
168 }
169 // The target map for stats reading should be the inactive map, which is opposite
170 // from the config value.
171 BpfMap<StatsKey, StatsValue> *inactiveStatsMap;
172 switch (configuration.value()) {
173 case SELECT_MAP_A:
174 inactiveStatsMap = &statsMapB;
175 break;
176 case SELECT_MAP_B:
177 inactiveStatsMap = &statsMapA;
178 break;
179 default:
180 ALOGE("%s unknown configuration value: %d", __func__, configuration.value());
181 return -EINVAL;
182 }
183
184 // It is safe to read and clear the old map now since the
185 // networkStatsFactory should call netd to swap the map in advance already.
186 // TODO: the above comment feels like it may be obsolete / out of date,
187 // since we no longer swap the map via netd binder rpc - though we do
188 // still swap it.
189 int ret = parseBpfNetworkStatsDetailInternal(*lines, *inactiveStatsMap, ifaceIndexNameMap);
190 if (ret) {
191 ALOGE("parse detail network stats failed: %s", strerror(errno));
192 return ret;
193 }
194
195 Result<void> res = inactiveStatsMap->clear();
196 if (!res.ok()) {
197 ALOGE("Clean up current stats map failed: %s", strerror(res.error().code()));
198 return -res.error().code();
199 }
200
201 return 0;
202 }
203
parseBpfNetworkStatsDevInternal(std::vector<stats_line> & lines,const BpfMap<uint32_t,StatsValue> & statsMap,const BpfMap<uint32_t,IfaceValue> & ifaceMap)204 int parseBpfNetworkStatsDevInternal(std::vector<stats_line>& lines,
205 const BpfMap<uint32_t, StatsValue>& statsMap,
206 const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
207 int64_t unknownIfaceBytesTotal = 0;
208 const auto processDetailIfaceStats = [&lines, &unknownIfaceBytesTotal, &ifaceMap, &statsMap](
209 const uint32_t& key, const StatsValue& value,
210 const BpfMap<uint32_t, StatsValue>&) {
211 char ifname[IFNAMSIZ];
212 if (getIfaceNameFromMap(ifaceMap, statsMap, key, ifname, key, &unknownIfaceBytesTotal)) {
213 return Result<void>();
214 }
215 StatsKey fakeKey = {
216 .uid = (uint32_t)UID_ALL,
217 .tag = (uint32_t)TAG_NONE,
218 .counterSet = (uint32_t)SET_ALL,
219 };
220 lines.push_back(populateStatsEntry(fakeKey, value, ifname));
221 return Result<void>();
222 };
223 Result<void> res = statsMap.iterateWithValue(processDetailIfaceStats);
224 if (!res.ok()) {
225 ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
226 strerror(res.error().code()));
227 return -res.error().code();
228 }
229
230 groupNetworkStats(lines);
231 return 0;
232 }
233
parseBpfNetworkStatsDev(std::vector<stats_line> * lines)234 int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
235 static BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
236 static BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
237 return parseBpfNetworkStatsDevInternal(*lines, ifaceStatsMap, ifaceIndexNameMap);
238 }
239
groupNetworkStats(std::vector<stats_line> & lines)240 void groupNetworkStats(std::vector<stats_line>& lines) {
241 if (lines.size() <= 1) return;
242 std::sort(lines.begin(), lines.end());
243
244 // Similar to std::unique(), but aggregates the duplicates rather than discarding them.
245 size_t currentOutput = 0;
246 for (size_t i = 1; i < lines.size(); i++) {
247 // note that == operator only compares the 'key' portion: iface/uid/tag/set
248 if (lines[currentOutput] == lines[i]) {
249 // while += operator only affects the 'data' portion: {rx,tx}{Bytes,Packets}
250 lines[currentOutput] += lines[i];
251 } else {
252 // okay, we're done aggregating the current line, move to the next one
253 lines[++currentOutput] = lines[i];
254 }
255 }
256
257 // possibly shrink the vector - currentOutput is the last line with valid data
258 lines.resize(currentOutput + 1);
259 }
260
261 // True if lhs equals to rhs, only compare iface, uid, tag and set.
operator ==(const stats_line & lhs,const stats_line & rhs)262 bool operator==(const stats_line& lhs, const stats_line& rhs) {
263 return ((lhs.uid == rhs.uid) && (lhs.tag == rhs.tag) && (lhs.set == rhs.set) &&
264 !strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface)));
265 }
266
267 // True if lhs is smaller than rhs, only compare iface, uid, tag and set.
operator <(const stats_line & lhs,const stats_line & rhs)268 bool operator<(const stats_line& lhs, const stats_line& rhs) {
269 int ret = strncmp(lhs.iface, rhs.iface, sizeof(lhs.iface));
270 if (ret != 0) return ret < 0;
271 if (lhs.uid < rhs.uid) return true;
272 if (lhs.uid > rhs.uid) return false;
273 if (lhs.tag < rhs.tag) return true;
274 if (lhs.tag > rhs.tag) return false;
275 if (lhs.set < rhs.set) return true;
276 if (lhs.set > rhs.set) return false;
277 return false;
278 }
279
operator =(const stats_line & rhs)280 stats_line& stats_line::operator=(const stats_line& rhs) {
281 if (this == &rhs) return *this;
282
283 strlcpy(iface, rhs.iface, sizeof(iface));
284 uid = rhs.uid;
285 set = rhs.set;
286 tag = rhs.tag;
287 rxPackets = rhs.rxPackets;
288 txPackets = rhs.txPackets;
289 rxBytes = rhs.rxBytes;
290 txBytes = rhs.txBytes;
291 return *this;
292 }
293
operator +=(const stats_line & rhs)294 stats_line& stats_line::operator+=(const stats_line& rhs) {
295 rxPackets += rhs.rxPackets;
296 txPackets += rhs.txPackets;
297 rxBytes += rhs.rxBytes;
298 txBytes += rhs.txBytes;
299 return *this;
300 }
301
302 } // namespace bpf
303 } // namespace android
304