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 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 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 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 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 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 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 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 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 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 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