Home | History | Annotate | Download | only in libbpf
      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