• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "bpf/BpfNetworkStats.h"
30 
31 #ifdef LOG_TAG
32 #undef LOG_TAG
33 #endif
34 
35 #define LOG_TAG "BpfNetworkStats"
36 
37 namespace android {
38 namespace bpf {
39 
40 using netdutils::Status;
41 
42 static constexpr uint32_t BPF_OPEN_FLAGS = BPF_F_RDONLY;
43 
bpfGetUidStatsInternal(uid_t uid,Stats * stats,const BpfMap<uint32_t,StatsValue> & appUidStatsMap)44 int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
45                            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();
54 }
55 
bpfGetUidStats(uid_t uid,Stats * stats)56 int bpfGetUidStats(uid_t uid, Stats* stats) {
57     BpfMap<uint32_t, StatsValue> appUidStatsMap(
58         mapRetrieve(APP_UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
59 
60     if (!appUidStatsMap.isValid()) {
61         int ret = -errno;
62         ALOGE("Opening appUidStatsMap(%s) failed: %s", UID_STATS_MAP_PATH, strerror(errno));
63         return ret;
64     }
65     return bpfGetUidStatsInternal(uid, stats, appUidStatsMap);
66 }
67 
bpfGetIfaceStatsInternal(const char * iface,Stats * stats,const BpfMap<uint32_t,StatsValue> & ifaceStatsMap,const BpfMap<uint32_t,IfaceValue> & ifaceNameMap)68 int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
69                              const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
70                              const BpfMap<uint32_t, IfaceValue>& ifaceNameMap) {
71     int64_t unknownIfaceBytesTotal = 0;
72     stats->tcpRxPackets = -1;
73     stats->tcpTxPackets = -1;
74     const auto processIfaceStats = [iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal]
75                                    (const uint32_t& key,
76                                     const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) {
77         char ifname[IFNAMSIZ];
78         if (getIfaceNameFromMap(ifaceNameMap, ifaceStatsMap, key, ifname, key,
79                                 &unknownIfaceBytesTotal)) {
80             return netdutils::status::ok;
81         }
82         if (!iface || !strcmp(iface, ifname)) {
83             StatsValue statsEntry;
84             ASSIGN_OR_RETURN(statsEntry, ifaceStatsMap.readValue(key));
85             stats->rxPackets += statsEntry.rxPackets;
86             stats->txPackets += statsEntry.txPackets;
87             stats->rxBytes += statsEntry.rxBytes;
88             stats->txBytes += statsEntry.txBytes;
89         }
90         return netdutils::status::ok;
91     };
92     return -ifaceStatsMap.iterate(processIfaceStats).code();
93 }
94 
bpfGetIfaceStats(const char * iface,Stats * stats)95 int bpfGetIfaceStats(const char* iface, Stats* stats) {
96     BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
97     int ret;
98     if (!ifaceStatsMap.isValid()) {
99         ret = -errno;
100         ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
101         return ret;
102     }
103     BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
104         mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
105     if (!ifaceIndexNameMap.isValid()) {
106         ret = -errno;
107         ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
108         return ret;
109     }
110     return bpfGetIfaceStatsInternal(iface, stats, ifaceStatsMap, ifaceIndexNameMap);
111 }
112 
populateStatsEntry(const StatsKey & statsKey,const StatsValue & statsEntry,const char * ifname)113 stats_line populateStatsEntry(const StatsKey& statsKey, const StatsValue& statsEntry,
114                               const char* ifname) {
115     stats_line newLine;
116     strlcpy(newLine.iface, ifname, sizeof(newLine.iface));
117     newLine.uid = (int32_t)statsKey.uid;
118     newLine.set = (int32_t)statsKey.counterSet;
119     newLine.tag = (int32_t)statsKey.tag;
120     newLine.rxPackets = statsEntry.rxPackets;
121     newLine.txPackets = statsEntry.txPackets;
122     newLine.rxBytes = statsEntry.rxBytes;
123     newLine.txBytes = statsEntry.txBytes;
124     return newLine;
125 }
126 
parseBpfNetworkStatsDetailInternal(std::vector<stats_line> * lines,const std::vector<std::string> & limitIfaces,int limitTag,int limitUid,const BpfMap<StatsKey,StatsValue> & statsMap,const BpfMap<uint32_t,IfaceValue> & ifaceMap)127 int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
128                                        const std::vector<std::string>& limitIfaces, int limitTag,
129                                        int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
130                                        const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
131     int64_t unknownIfaceBytesTotal = 0;
132     const auto processDetailUidStats = [lines, &limitIfaces, &limitTag, &limitUid,
133                                         &unknownIfaceBytesTotal,
134                                         &ifaceMap](const StatsKey& key,
135                                                    const BpfMap<StatsKey, StatsValue>& statsMap) {
136         char ifname[IFNAMSIZ];
137         if (getIfaceNameFromMap(ifaceMap, statsMap, key.ifaceIndex, ifname, key,
138                                 &unknownIfaceBytesTotal)) {
139             return netdutils::status::ok;
140         }
141         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.
145             return netdutils::status::ok;
146         }
147         if (limitTag != TAG_ALL && uint32_t(limitTag) != key.tag) {
148             return netdutils::status::ok;
149         }
150         if (limitUid != UID_ALL && uint32_t(limitUid) != key.uid) {
151             return netdutils::status::ok;
152         }
153         StatsValue statsEntry;
154         ASSIGN_OR_RETURN(statsEntry, statsMap.readValue(key));
155         lines->push_back(populateStatsEntry(key, statsEntry, ifname));
156         return netdutils::status::ok;
157     };
158     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     }
164     return 0;
165 }
166 
parseBpfNetworkStatsDetail(std::vector<stats_line> * lines,const std::vector<std::string> & limitIfaces,int limitTag,int limitUid)167 int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
168                                const std::vector<std::string>& limitIfaces, int limitTag,
169                                int limitUid) {
170     int ret = 0;
171     BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
172         mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
173     if (!ifaceIndexNameMap.isValid()) {
174         ret = -errno;
175         ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
176         return ret;
177     }
178 
179     // If the caller did not pass in TAG_NONE, read tag data.
180     if (limitTag != TAG_NONE) {
181         BpfMap<StatsKey, StatsValue> tagStatsMap(mapRetrieve(TAG_STATS_MAP_PATH, BPF_OPEN_FLAGS));
182         if (!tagStatsMap.isValid()) {
183             ret = -errno;
184             ALOGE("get tagStats map fd failed: %s", strerror(errno));
185             return ret;
186         }
187         ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid,
188                                                  tagStatsMap, ifaceIndexNameMap);
189         if (ret) return ret;
190     }
191 
192     // If the caller did not pass in a specific tag (i.e., if limitTag is TAG_NONE(0) or
193     // TAG_ALL(-1)) read UID data.
194     if (limitTag == TAG_NONE || limitTag == TAG_ALL) {
195         BpfMap<StatsKey, StatsValue> uidStatsMap(mapRetrieve(UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
196         if (!uidStatsMap.isValid()) {
197             ret = -errno;
198             ALOGE("Opening map fd from %s failed: %s", UID_STATS_MAP_PATH, strerror(errno));
199             return ret;
200         }
201         ret = parseBpfNetworkStatsDetailInternal(lines, limitIfaces, limitTag, limitUid,
202                                                  uidStatsMap, ifaceIndexNameMap);
203     }
204     return ret;
205 }
206 
parseBpfNetworkStatsDevInternal(std::vector<stats_line> * lines,const BpfMap<uint32_t,StatsValue> & statsMap,const BpfMap<uint32_t,IfaceValue> & ifaceMap)207 int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
208                                     const BpfMap<uint32_t, StatsValue>& statsMap,
209                                     const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
210     int64_t unknownIfaceBytesTotal = 0;
211     const auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMap, &statsMap](
212                                              const uint32_t& key, const StatsValue& value,
213                                              const BpfMap<uint32_t, StatsValue>&) {
214         char ifname[IFNAMSIZ];
215         if (getIfaceNameFromMap(ifaceMap, statsMap, key, ifname, key, &unknownIfaceBytesTotal)) {
216             return netdutils::status::ok;
217         }
218         StatsKey fakeKey = {
219             .uid = (uint32_t)UID_ALL, .counterSet = (uint32_t)SET_ALL, .tag = (uint32_t)TAG_NONE};
220         lines->push_back(populateStatsEntry(fakeKey, value, ifname));
221         return netdutils::status::ok;
222     };
223     Status res = statsMap.iterateWithValue(processDetailIfaceStats);
224     if (!isOk(res)) {
225         ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
226               strerror(res.code()));
227         return -res.code();
228     }
229     return 0;
230 }
231 
parseBpfNetworkStatsDev(std::vector<stats_line> * lines)232 int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
233     int ret = 0;
234     BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
235         mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
236     if (!ifaceIndexNameMap.isValid()) {
237         ret = -errno;
238         ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
239         return ret;
240     }
241 
242     BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
243     if (!ifaceStatsMap.isValid()) {
244         ret = -errno;
245         ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
246         return ret;
247     }
248     return parseBpfNetworkStatsDevInternal(lines, ifaceStatsMap, ifaceIndexNameMap);
249 }
250 
combineUidTag(const uid_t uid,const uint32_t tag)251 uint64_t combineUidTag(const uid_t uid, const uint32_t tag) {
252     return (uint64_t)uid << 32 | tag;
253 }
254 
255 }  // namespace bpf
256 }  // namespace android
257