Home | History | Annotate | Download | only in server
      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 #define LOG_TAG "TrafficController"
     18 #include <inttypes.h>
     19 #include <linux/bpf.h>
     20 #include <linux/if_ether.h>
     21 #include <linux/in.h>
     22 #include <linux/inet_diag.h>
     23 #include <linux/netlink.h>
     24 #include <linux/sock_diag.h>
     25 #include <linux/unistd.h>
     26 #include <net/if.h>
     27 #include <stdlib.h>
     28 #include <string.h>
     29 #include <sys/socket.h>
     30 #include <sys/stat.h>
     31 #include <sys/types.h>
     32 #include <sys/utsname.h>
     33 #include <sys/wait.h>
     34 #include <mutex>
     35 #include <unordered_set>
     36 #include <vector>
     37 
     38 #include <android-base/stringprintf.h>
     39 #include <android-base/strings.h>
     40 #include <android-base/unique_fd.h>
     41 #include <logwrap/logwrap.h>
     42 #include <netdutils/StatusOr.h>
     43 
     44 #include <netdutils/Misc.h>
     45 #include <netdutils/Syscalls.h>
     46 #include "TrafficController.h"
     47 #include "bpf/BpfMap.h"
     48 #include "bpf/bpf_shared.h"
     49 
     50 #include "DumpWriter.h"
     51 #include "FirewallController.h"
     52 #include "InterfaceController.h"
     53 #include "NetlinkListener.h"
     54 #include "qtaguid/qtaguid.h"
     55 
     56 using namespace android::bpf;
     57 
     58 namespace android {
     59 namespace net {
     60 
     61 using base::StringPrintf;
     62 using base::unique_fd;
     63 using base::Join;
     64 using netdutils::extract;
     65 using netdutils::Slice;
     66 using netdutils::sSyscalls;
     67 using netdutils::Status;
     68 using netdutils::statusFromErrno;
     69 using netdutils::StatusOr;
     70 using netdutils::status::ok;
     71 
     72 constexpr int kSockDiagMsgType = SOCK_DIAG_BY_FAMILY;
     73 constexpr int kSockDiagDoneMsgType = NLMSG_DONE;
     74 
     75 StatusOr<std::unique_ptr<NetlinkListenerInterface>> makeSkDestroyListener() {
     76     const auto& sys = sSyscalls.get();
     77     ASSIGN_OR_RETURN(auto event, sys.eventfd(0, EFD_CLOEXEC));
     78     const int domain = AF_NETLINK;
     79     const int type = SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK;
     80     const int protocol = NETLINK_INET_DIAG;
     81     ASSIGN_OR_RETURN(auto sock, sys.socket(domain, type, protocol));
     82 
     83     sockaddr_nl addr = {
     84         .nl_family = AF_NETLINK,
     85         .nl_groups = 1 << (SKNLGRP_INET_TCP_DESTROY - 1) | 1 << (SKNLGRP_INET_UDP_DESTROY - 1) |
     86                      1 << (SKNLGRP_INET6_TCP_DESTROY - 1) | 1 << (SKNLGRP_INET6_UDP_DESTROY - 1)};
     87     RETURN_IF_NOT_OK(sys.bind(sock, addr));
     88 
     89     const sockaddr_nl kernel = {.nl_family = AF_NETLINK};
     90     RETURN_IF_NOT_OK(sys.connect(sock, kernel));
     91 
     92     std::unique_ptr<NetlinkListenerInterface> listener =
     93         std::make_unique<NetlinkListener>(std::move(event), std::move(sock));
     94 
     95     return listener;
     96 }
     97 
     98 Status changeOwnerAndMode(const char* path, gid_t group, const char* debugName, bool netdOnly) {
     99     int ret = chown(path, AID_ROOT, group);
    100     if (ret != 0) return statusFromErrno(errno, StringPrintf("change %s group failed", debugName));
    101 
    102     if (netdOnly) {
    103         ret = chmod(path, S_IRWXU);
    104     } else {
    105         // Allow both netd and system server to obtain map fd from the path.
    106         // chmod doesn't grant permission to all processes in that group to
    107         // read/write the bpf map. They still need correct sepolicy to
    108         // read/write the map.
    109         ret = chmod(path, S_IRWXU | S_IRGRP);
    110     }
    111     if (ret != 0) return statusFromErrno(errno, StringPrintf("change %s mode failed", debugName));
    112     return netdutils::status::ok;
    113 }
    114 
    115 TrafficController::TrafficController() {
    116     ebpfSupported = hasBpfSupport();
    117 }
    118 
    119 Status initialOwnerMap(BpfMap<uint32_t, uint8_t>& map) {
    120     // Check and delete all the entries from the map in case it is a runtime
    121     // restart
    122     const auto deleteAllEntries = [](const uint32_t& key, BpfMap<uint32_t, uint8_t>& map) {
    123         Status res = map.deleteValue(key);
    124         if (!isOk(res) && (res.code() == ENOENT)) {
    125             ALOGE("Failed to delete data(uid=%u): %s\n", key, strerror(res.code()));
    126         }
    127         return netdutils::status::ok;
    128     };
    129     // It is safe to delete from this map because nothing will concurrently iterate over it:
    130     // - Nothing in netd will iterate over it because we're holding mOwnerMatchMutex.
    131     // - Nothing outside netd iterates over it.
    132     map.iterate(deleteAllEntries);
    133     uint32_t mapSettingKey = UID_MAP_ENABLED;
    134     uint8_t defaultMapState = 0;
    135     return map.writeValue(mapSettingKey, defaultMapState, BPF_NOEXIST);
    136 }
    137 
    138 Status TrafficController::initMaps() {
    139     std::lock_guard<std::mutex> ownerMapGuard(mOwnerMatchMutex);
    140     RETURN_IF_NOT_OK(
    141         mCookieTagMap.getOrCreate(COOKIE_UID_MAP_SIZE, COOKIE_TAG_MAP_PATH, BPF_MAP_TYPE_HASH));
    142 
    143     RETURN_IF_NOT_OK(changeOwnerAndMode(COOKIE_TAG_MAP_PATH, AID_NET_BW_ACCT, "CookieTagMap",
    144                                         false));
    145 
    146     RETURN_IF_NOT_OK(mUidCounterSetMap.getOrCreate(UID_COUNTERSET_MAP_SIZE, UID_COUNTERSET_MAP_PATH,
    147                                                    BPF_MAP_TYPE_HASH));
    148     RETURN_IF_NOT_OK(changeOwnerAndMode(UID_COUNTERSET_MAP_PATH, AID_NET_BW_ACCT,
    149                                         "UidCounterSetMap", false));
    150 
    151     RETURN_IF_NOT_OK(
    152         mAppUidStatsMap.getOrCreate(UID_STATS_MAP_SIZE, APP_UID_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
    153     RETURN_IF_NOT_OK(
    154         changeOwnerAndMode(APP_UID_STATS_MAP_PATH, AID_NET_BW_STATS, "AppUidStatsMap", false));
    155 
    156     RETURN_IF_NOT_OK(
    157         mUidStatsMap.getOrCreate(UID_STATS_MAP_SIZE, UID_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
    158     RETURN_IF_NOT_OK(changeOwnerAndMode(UID_STATS_MAP_PATH, AID_NET_BW_STATS, "UidStatsMap",
    159                                         false));
    160 
    161     RETURN_IF_NOT_OK(
    162         mTagStatsMap.getOrCreate(TAG_STATS_MAP_SIZE, TAG_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
    163     RETURN_IF_NOT_OK(changeOwnerAndMode(TAG_STATS_MAP_PATH, AID_NET_BW_STATS, "TagStatsMap",
    164                                         false));
    165 
    166     RETURN_IF_NOT_OK(mIfaceIndexNameMap.getOrCreate(IFACE_INDEX_NAME_MAP_SIZE,
    167                                                     IFACE_INDEX_NAME_MAP_PATH, BPF_MAP_TYPE_HASH));
    168     RETURN_IF_NOT_OK(changeOwnerAndMode(IFACE_INDEX_NAME_MAP_PATH, AID_NET_BW_STATS,
    169                                         "IfaceIndexNameMap", false));
    170 
    171     RETURN_IF_NOT_OK(
    172         mDozableUidMap.getOrCreate(UID_OWNER_MAP_SIZE, DOZABLE_UID_MAP_PATH, BPF_MAP_TYPE_HASH));
    173     RETURN_IF_NOT_OK(changeOwnerAndMode(DOZABLE_UID_MAP_PATH, AID_ROOT, "DozableUidMap", true));
    174     RETURN_IF_NOT_OK(initialOwnerMap(mDozableUidMap));
    175 
    176     RETURN_IF_NOT_OK(
    177         mStandbyUidMap.getOrCreate(UID_OWNER_MAP_SIZE, STANDBY_UID_MAP_PATH, BPF_MAP_TYPE_HASH));
    178     RETURN_IF_NOT_OK(changeOwnerAndMode(STANDBY_UID_MAP_PATH, AID_ROOT, "StandbyUidMap", true));
    179     RETURN_IF_NOT_OK(initialOwnerMap(mStandbyUidMap));
    180 
    181     RETURN_IF_NOT_OK(mPowerSaveUidMap.getOrCreate(UID_OWNER_MAP_SIZE, POWERSAVE_UID_MAP_PATH,
    182                                                   BPF_MAP_TYPE_HASH));
    183     RETURN_IF_NOT_OK(changeOwnerAndMode(POWERSAVE_UID_MAP_PATH, AID_ROOT, "PowerSaveUidMap", true));
    184     RETURN_IF_NOT_OK(initialOwnerMap(mPowerSaveUidMap));
    185 
    186     RETURN_IF_NOT_OK(
    187         mIfaceStatsMap.getOrCreate(IFACE_STATS_MAP_SIZE, IFACE_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
    188     RETURN_IF_NOT_OK(changeOwnerAndMode(IFACE_STATS_MAP_PATH, AID_NET_BW_STATS, "IfaceStatsMap",
    189                                         false));
    190     return netdutils::status::ok;
    191 }
    192 
    193 Status TrafficController::start() {
    194 
    195     if (!ebpfSupported) {
    196         return netdutils::status::ok;
    197     }
    198 
    199     /* When netd restart from a crash without total system reboot, the program
    200      * is still attached to the cgroup, detach it so the program can be freed
    201      * and we can load and attach new program into the target cgroup.
    202      *
    203      * TODO: Scrape existing socket when run-time restart and clean up the map
    204      * if the socket no longer exist
    205      */
    206 
    207     RETURN_IF_NOT_OK(initMaps());
    208 
    209     // Fetch the list of currently-existing interfaces. At this point NetlinkHandler is
    210     // already running, so it will call addInterface() when any new interface appears.
    211     std::map<std::string, uint32_t> ifacePairs;
    212     ASSIGN_OR_RETURN(ifacePairs, InterfaceController::getIfaceList());
    213     for (const auto& ifacePair:ifacePairs) {
    214         addInterface(ifacePair.first.c_str(), ifacePair.second);
    215     }
    216 
    217 
    218     auto result = makeSkDestroyListener();
    219     if (!isOk(result)) {
    220         ALOGE("Unable to create SkDestroyListener: %s", toString(result).c_str());
    221     } else {
    222         mSkDestroyListener = std::move(result.value());
    223     }
    224     // Rx handler extracts nfgenmsg looks up and invokes registered dispatch function.
    225     const auto rxHandler = [this](const nlmsghdr&, const Slice msg) {
    226         inet_diag_msg diagmsg = {};
    227         if (extract(msg, diagmsg) < sizeof(inet_diag_msg)) {
    228             ALOGE("unrecognized netlink message: %s", toString(msg).c_str());
    229             return;
    230         }
    231         uint64_t sock_cookie = static_cast<uint64_t>(diagmsg.id.idiag_cookie[0]) |
    232                                (static_cast<uint64_t>(diagmsg.id.idiag_cookie[1]) << 32);
    233 
    234         mCookieTagMap.deleteValue(sock_cookie);
    235     };
    236     expectOk(mSkDestroyListener->subscribe(kSockDiagMsgType, rxHandler));
    237 
    238     // In case multiple netlink message comes in as a stream, we need to handle the rxDone message
    239     // properly.
    240     const auto rxDoneHandler = [](const nlmsghdr&, const Slice msg) {
    241         // Ignore NLMSG_DONE  messages
    242         inet_diag_msg diagmsg = {};
    243         extract(msg, diagmsg);
    244     };
    245     expectOk(mSkDestroyListener->subscribe(kSockDiagDoneMsgType, rxDoneHandler));
    246 
    247     int* status = nullptr;
    248 
    249     std::vector<const char*> prog_args{
    250         "/system/bin/bpfloader",
    251     };
    252     int ret = access(BPF_INGRESS_PROG_PATH, R_OK);
    253     if (ret != 0 && errno == ENOENT) {
    254         prog_args.push_back((char*)"-i");
    255     }
    256     ret = access(BPF_EGRESS_PROG_PATH, R_OK);
    257     if (ret != 0 && errno == ENOENT) {
    258         prog_args.push_back((char*)"-e");
    259     }
    260     ret = access(XT_BPF_INGRESS_PROG_PATH, R_OK);
    261     if (ret != 0 && errno == ENOENT) {
    262         prog_args.push_back((char*)"-p");
    263     }
    264     ret = access(XT_BPF_EGRESS_PROG_PATH, R_OK);
    265     if (ret != 0 && errno == ENOENT) {
    266         prog_args.push_back((char*)"-m");
    267     }
    268 
    269     if (prog_args.size() == 1) {
    270         // all program are loaded already.
    271         return netdutils::status::ok;
    272     }
    273 
    274     prog_args.push_back(nullptr);
    275     ret = android_fork_execvp(prog_args.size(), (char**)prog_args.data(), status, false, true);
    276     if (ret) {
    277         ret = errno;
    278         ALOGE("failed to execute %s: %s", prog_args[0], strerror(errno));
    279         return statusFromErrno(ret, "run bpf loader failed");
    280     }
    281     return netdutils::status::ok;
    282 }
    283 
    284 int TrafficController::tagSocket(int sockFd, uint32_t tag, uid_t uid) {
    285     if (!ebpfSupported) {
    286         if (legacy_tagSocket(sockFd, tag, uid)) return -errno;
    287         return 0;
    288     }
    289 
    290     uint64_t sock_cookie = getSocketCookie(sockFd);
    291     if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
    292     UidTag newKey = {.uid = (uint32_t)uid, .tag = tag};
    293 
    294     // Update the tag information of a socket to the cookieUidMap. Use BPF_ANY
    295     // flag so it will insert a new entry to the map if that value doesn't exist
    296     // yet. And update the tag if there is already a tag stored. Since the eBPF
    297     // program in kernel only read this map, and is protected by rcu read lock. It
    298     // should be fine to cocurrently update the map while eBPF program is running.
    299     Status res = mCookieTagMap.writeValue(sock_cookie, newKey, BPF_ANY);
    300     if (!isOk(res)) {
    301         ALOGE("Failed to tag the socket: %s, fd: %d", strerror(res.code()),
    302               mCookieTagMap.getMap().get());
    303     }
    304     return -res.code();
    305 }
    306 
    307 int TrafficController::untagSocket(int sockFd) {
    308     if (!ebpfSupported) {
    309         if (legacy_untagSocket(sockFd)) return -errno;
    310         return 0;
    311     }
    312     uint64_t sock_cookie = getSocketCookie(sockFd);
    313 
    314     if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
    315     Status res = mCookieTagMap.deleteValue(sock_cookie);
    316     if (!isOk(res)) {
    317         ALOGE("Failed to untag socket: %s\n", strerror(res.code()));
    318     }
    319     return -res.code();
    320 }
    321 
    322 int TrafficController::setCounterSet(int counterSetNum, uid_t uid) {
    323     if (counterSetNum < 0 || counterSetNum >= OVERFLOW_COUNTERSET) return -EINVAL;
    324     Status res;
    325     if (!ebpfSupported) {
    326         if (legacy_setCounterSet(counterSetNum, uid)) return -errno;
    327         return 0;
    328     }
    329 
    330     // The default counter set for all uid is 0, so deleting the current counterset for that uid
    331     // will automatically set it to 0.
    332     if (counterSetNum == 0) {
    333         Status res = mUidCounterSetMap.deleteValue(uid);
    334         if (isOk(res) || (!isOk(res) && res.code() == ENOENT)) {
    335             return 0;
    336         } else {
    337             ALOGE("Failed to delete the counterSet: %s\n", strerror(res.code()));
    338             return -res.code();
    339         }
    340     }
    341     uint8_t tmpCounterSetNum = (uint8_t)counterSetNum;
    342     res = mUidCounterSetMap.writeValue(uid, tmpCounterSetNum, BPF_ANY);
    343     if (!isOk(res)) {
    344         ALOGE("Failed to set the counterSet: %s, fd: %d", strerror(res.code()),
    345               mUidCounterSetMap.getMap().get());
    346         return -res.code();
    347     }
    348     return 0;
    349 }
    350 
    351 int TrafficController::deleteTagData(uint32_t tag, uid_t uid) {
    352 
    353     if (!ebpfSupported) {
    354         if (legacy_deleteTagData(tag, uid)) return -errno;
    355         return 0;
    356     }
    357 
    358     // First we go through the cookieTagMap to delete the target uid tag combination. Or delete all
    359     // the tags related to the uid if the tag is 0.
    360     const auto deleteMatchedCookieEntries = [uid, tag](const uint64_t& key, const UidTag& value,
    361                                                        BpfMap<uint64_t, UidTag>& map) {
    362         if (value.uid == uid && (value.tag == tag || tag == 0)) {
    363             Status res = map.deleteValue(key);
    364             if (isOk(res) || (res.code() == ENOENT)) {
    365                 return netdutils::status::ok;
    366             }
    367             ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s\n", key, strerror(res.code()));
    368         }
    369         // Move forward to next cookie in the map.
    370         return netdutils::status::ok;
    371     };
    372     mCookieTagMap.iterateWithValue(deleteMatchedCookieEntries);
    373     // Now we go through the Tag stats map and delete the data entry with correct uid and tag
    374     // combination. Or all tag stats under that uid if the target tag is 0.
    375     const auto deleteMatchedUidTagEntries = [uid, tag](const StatsKey& key,
    376                                                        BpfMap<StatsKey, StatsValue>& map) {
    377         if (key.uid == uid && (key.tag == tag || tag == 0)) {
    378             Status res = map.deleteValue(key);
    379             if (isOk(res) || (res.code() == ENOENT)) {
    380                 //Entry is deleted, use the current key to get a new nextKey;
    381                 return netdutils::status::ok;
    382             }
    383             ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", key.uid, key.tag,
    384                   strerror(res.code()));
    385         }
    386         return netdutils::status::ok;
    387     };
    388     mTagStatsMap.iterate(deleteMatchedUidTagEntries);
    389     // If the tag is not zero, we already deleted all the data entry required. If tag is 0, we also
    390     // need to delete the stats stored in uidStatsMap and counterSet map.
    391     if (tag != 0) return 0;
    392 
    393     Status res = mUidCounterSetMap.deleteValue(uid);
    394     if (!isOk(res) && res.code() != ENOENT) {
    395         ALOGE("Failed to delete counterSet data(uid=%u, tag=%u): %s\n", uid, tag,
    396               strerror(res.code()));
    397     }
    398     mUidStatsMap.iterate(deleteMatchedUidTagEntries);
    399 
    400     auto deleteAppUidStatsEntry = [uid](const uint32_t& key, BpfMap<uint32_t, StatsValue>& map) {
    401         if (key == uid) {
    402             Status res = map.deleteValue(key);
    403             if (isOk(res) || (res.code() == ENOENT)) {
    404                 return netdutils::status::ok;
    405             }
    406             ALOGE("Failed to delete data(uid=%u): %s", key, strerror(res.code()));
    407         }
    408         return netdutils::status::ok;
    409     };
    410     mAppUidStatsMap.iterate(deleteAppUidStatsEntry);
    411     return 0;
    412 }
    413 
    414 int TrafficController::addInterface(const char* name, uint32_t ifaceIndex) {
    415     if (!ebpfSupported) return 0;
    416 
    417     IfaceValue iface;
    418     if (ifaceIndex == 0) {
    419         ALOGE("Unknown interface %s(%d)", name, ifaceIndex);
    420         return -1;
    421     }
    422 
    423     strlcpy(iface.name, name, sizeof(IfaceValue));
    424     Status res = mIfaceIndexNameMap.writeValue(ifaceIndex, iface, BPF_ANY);
    425     if (!isOk(res)) {
    426         ALOGE("Failed to add iface %s(%d): %s", name, ifaceIndex, strerror(res.code()));
    427         return -res.code();
    428     }
    429     return 0;
    430 }
    431 
    432 Status TrafficController::updateOwnerMapEntry(BpfMap<uint32_t, uint8_t>& map, uid_t uid,
    433                                               FirewallRule rule, FirewallType type) {
    434     if (uid == UID_MAP_ENABLED) {
    435         return statusFromErrno(-EINVAL, "This uid is reserved for map state");
    436     }
    437 
    438     if ((rule == ALLOW && type == WHITELIST) || (rule == DENY && type == BLACKLIST)) {
    439         uint8_t flag = (type == WHITELIST) ? BPF_PASS : BPF_DROP;
    440         RETURN_IF_NOT_OK(map.writeValue(uid, flag, BPF_ANY));
    441     } else if ((rule == ALLOW && type == BLACKLIST) || (rule == DENY && type == WHITELIST)) {
    442         RETURN_IF_NOT_OK(map.deleteValue(uid));
    443     } else {
    444         //Cannot happen.
    445         return statusFromErrno(-EINVAL, "");
    446     }
    447     return netdutils::status::ok;
    448 }
    449 
    450 int TrafficController::changeUidOwnerRule(ChildChain chain, uid_t uid, FirewallRule rule,
    451                                           FirewallType type) {
    452     std::lock_guard<std::mutex> guard(mOwnerMatchMutex);
    453     if (!ebpfSupported) {
    454         ALOGE("bpf is not set up, should use iptables rule");
    455         return -ENOSYS;
    456     }
    457     Status res;
    458     switch (chain) {
    459         case DOZABLE:
    460             res = updateOwnerMapEntry(mDozableUidMap, uid, rule, type);
    461             break;
    462         case STANDBY:
    463             res = updateOwnerMapEntry(mStandbyUidMap, uid, rule, type);
    464             break;
    465         case POWERSAVE:
    466             res = updateOwnerMapEntry(mPowerSaveUidMap, uid, rule, type);
    467             break;
    468         case NONE:
    469         default:
    470             return -EINVAL;
    471     }
    472     if (!isOk(res)) {
    473         ALOGE("change uid(%u) rule of %d failed: %s, rule: %d, type: %d", uid, chain,
    474               res.msg().c_str(), rule, type);
    475         return -res.code();
    476     }
    477     return 0;
    478 }
    479 
    480 Status TrafficController::replaceUidsInMap(BpfMap<uint32_t, uint8_t>& map,
    481                                            const std::vector<int32_t>& uids, FirewallRule rule,
    482                                            FirewallType type) {
    483     std::set<int32_t> uidSet(uids.begin(), uids.end());
    484     std::vector<uint32_t> uidsToDelete;
    485     auto getUidsToDelete = [&uidsToDelete, &uidSet](const uint32_t& key,
    486                                                     const BpfMap<uint32_t, uint8_t>&) {
    487         if (key != UID_MAP_ENABLED && uidSet.find((int32_t)key) == uidSet.end()) {
    488             uidsToDelete.push_back(key);
    489         }
    490         return netdutils::status::ok;
    491     };
    492     RETURN_IF_NOT_OK(map.iterate(getUidsToDelete));
    493 
    494     for(auto uid : uidsToDelete) {
    495         RETURN_IF_NOT_OK(map.deleteValue(uid));
    496     }
    497 
    498     for (auto uid : uids) {
    499         RETURN_IF_NOT_OK(updateOwnerMapEntry(map, uid, rule, type));
    500     }
    501     return netdutils::status::ok;
    502 }
    503 
    504 int TrafficController::replaceUidOwnerMap(const std::string& name, bool isWhitelist,
    505                                           const std::vector<int32_t>& uids) {
    506     std::lock_guard<std::mutex> guard(mOwnerMatchMutex);
    507     FirewallRule rule;
    508     FirewallType type;
    509     if (isWhitelist) {
    510         type = WHITELIST;
    511         rule = ALLOW;
    512     } else {
    513         type = BLACKLIST;
    514         rule = DENY;
    515     }
    516     Status res;
    517     if (!name.compare(FirewallController::LOCAL_DOZABLE)) {
    518         res = replaceUidsInMap(mDozableUidMap, uids, rule, type);
    519     } else if (!name.compare(FirewallController::LOCAL_STANDBY)) {
    520         res = replaceUidsInMap(mStandbyUidMap, uids, rule, type);
    521     } else if (!name.compare(FirewallController::LOCAL_POWERSAVE)) {
    522         res = replaceUidsInMap(mPowerSaveUidMap, uids, rule, type);
    523     } else {
    524         ALOGE("unknown chain name: %s", name.c_str());
    525         return -EINVAL;
    526     }
    527     if (!isOk(res)) {
    528         ALOGE("Failed to clean up chain: %s: %s", name.c_str(), res.msg().c_str());
    529         return -res.code();
    530     }
    531     return 0;
    532 }
    533 
    534 int TrafficController::toggleUidOwnerMap(ChildChain chain, bool enable) {
    535     std::lock_guard<std::mutex> guard(mOwnerMatchMutex);
    536     uint32_t keyUid = UID_MAP_ENABLED;
    537     uint8_t mapState = enable ? 1 : 0;
    538     Status res;
    539     switch (chain) {
    540         case DOZABLE:
    541             res = mDozableUidMap.writeValue(keyUid, mapState, BPF_EXIST);
    542             break;
    543         case STANDBY:
    544             res = mStandbyUidMap.writeValue(keyUid, mapState, BPF_EXIST);
    545             break;
    546         case POWERSAVE:
    547             res = mPowerSaveUidMap.writeValue(keyUid, mapState, BPF_EXIST);
    548             break;
    549         default:
    550             return -EINVAL;
    551     }
    552     if (!isOk(res)) {
    553         ALOGE("Failed to toggleUidOwnerMap(%d): %s", chain, res.msg().c_str());
    554     }
    555     return -res.code();
    556 }
    557 
    558 bool TrafficController::checkBpfStatsEnable() {
    559     return ebpfSupported;
    560 }
    561 
    562 std::string getProgramStatus(const char *path) {
    563     int ret = access(path, R_OK);
    564     if (ret == 0) {
    565         return StringPrintf("OK");
    566     }
    567     if (ret != 0 && errno == ENOENT) {
    568         return StringPrintf("program is missing at: %s", path);
    569     }
    570     return StringPrintf("check Program %s error: %s", path, strerror(errno));
    571 }
    572 
    573 std::string getMapStatus(const base::unique_fd& map_fd, const char* path) {
    574     if (map_fd.get() < 0) {
    575         return StringPrintf("map fd lost");
    576     }
    577     if (access(path, F_OK) != 0) {
    578         return StringPrintf("map not pinned to location: %s", path);
    579     }
    580     return StringPrintf("OK");
    581 }
    582 
    583 void dumpBpfMap(std::string mapName, DumpWriter& dw, const std::string& header) {
    584     dw.blankline();
    585     dw.println("%s:", mapName.c_str());
    586     if(!header.empty()) {
    587         dw.println(header.c_str());
    588     }
    589 }
    590 
    591 const String16 TrafficController::DUMP_KEYWORD = String16("trafficcontroller");
    592 
    593 void TrafficController::dump(DumpWriter& dw, bool verbose) {
    594     std::lock_guard<std::mutex> ownerMapGuard(mOwnerMatchMutex);
    595     dw.incIndent();
    596     dw.println("TrafficController");
    597 
    598     dw.incIndent();
    599     dw.println("BPF module status: %s", ebpfSupported? "ON" : "OFF");
    600 
    601     if (!ebpfSupported)
    602         return;
    603 
    604     dw.blankline();
    605     dw.println("mCookieTagMap status: %s",
    606                getMapStatus(mCookieTagMap.getMap(), COOKIE_TAG_MAP_PATH).c_str());
    607     dw.println("mUidCounterSetMap status: %s",
    608                getMapStatus(mUidCounterSetMap.getMap(), UID_COUNTERSET_MAP_PATH).c_str());
    609     dw.println("mAppUidStatsMap status: %s",
    610                getMapStatus(mAppUidStatsMap.getMap(), APP_UID_STATS_MAP_PATH).c_str());
    611     dw.println("mUidStatsMap status: %s",
    612                getMapStatus(mUidStatsMap.getMap(), UID_STATS_MAP_PATH).c_str());
    613     dw.println("mTagStatsMap status: %s",
    614                getMapStatus(mTagStatsMap.getMap(), TAG_STATS_MAP_PATH).c_str());
    615     dw.println("mIfaceIndexNameMap status: %s",
    616                getMapStatus(mIfaceIndexNameMap.getMap(), IFACE_INDEX_NAME_MAP_PATH).c_str());
    617     dw.println("mIfaceStatsMap status: %s",
    618                getMapStatus(mIfaceStatsMap.getMap(), IFACE_STATS_MAP_PATH).c_str());
    619     dw.println("mDozableUidMap status: %s",
    620                getMapStatus(mDozableUidMap.getMap(), DOZABLE_UID_MAP_PATH).c_str());
    621     dw.println("mStandbyUidMap status: %s",
    622                getMapStatus(mStandbyUidMap.getMap(), STANDBY_UID_MAP_PATH).c_str());
    623     dw.println("mPowerSaveUidMap status: %s",
    624                getMapStatus(mPowerSaveUidMap.getMap(), POWERSAVE_UID_MAP_PATH).c_str());
    625 
    626     dw.blankline();
    627     dw.println("Cgroup ingress program status: %s",
    628                getProgramStatus(BPF_INGRESS_PROG_PATH).c_str());
    629     dw.println("Cgroup egress program status: %s", getProgramStatus(BPF_EGRESS_PROG_PATH).c_str());
    630     dw.println("xt_bpf ingress program status: %s",
    631                getProgramStatus(XT_BPF_INGRESS_PROG_PATH).c_str());
    632     dw.println("xt_bpf egress program status: %s",
    633                getProgramStatus(XT_BPF_EGRESS_PROG_PATH).c_str());
    634 
    635     if(!verbose) return;
    636 
    637     dw.blankline();
    638     dw.println("BPF map content:");
    639 
    640     dw.incIndent();
    641 
    642     // Print CookieTagMap content.
    643     dumpBpfMap("mCookieTagMap", dw, "");
    644     const auto printCookieTagInfo = [&dw](const uint64_t& key, const UidTag& value,
    645                                           const BpfMap<uint64_t, UidTag>&) {
    646         dw.println("cookie=%" PRIu64 " tag=0x%x uid=%u", key, value.tag, value.uid);
    647         return netdutils::status::ok;
    648     };
    649     Status res = mCookieTagMap.iterateWithValue(printCookieTagInfo);
    650     if (!isOk(res)) {
    651         dw.println("mCookieTagMap print end with error: %s", res.msg().c_str());
    652     }
    653 
    654     // Print UidCounterSetMap Content
    655     dumpBpfMap("mUidCounterSetMap", dw, "");
    656     const auto printUidInfo = [&dw](const uint32_t& key, const uint8_t& value,
    657                                     const BpfMap<uint32_t, uint8_t>&) {
    658         dw.println("%u %u", key, value);
    659         return netdutils::status::ok;
    660     };
    661     res = mUidCounterSetMap.iterateWithValue(printUidInfo);
    662     if (!isOk(res)) {
    663         dw.println("mUidCounterSetMap print end with error: %s", res.msg().c_str());
    664     }
    665 
    666     // Print AppUidStatsMap content
    667     std::string appUidStatsHeader = StringPrintf("uid rxBytes rxPackets txBytes txPackets");
    668     dumpBpfMap("mAppUidStatsMap:", dw, appUidStatsHeader);
    669     auto printAppUidStatsInfo = [&dw](const uint32_t& key, const StatsValue& value,
    670                                       const BpfMap<uint32_t, StatsValue>&) {
    671         dw.println("%u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, value.rxBytes,
    672                    value.rxPackets, value.txBytes, value.txPackets);
    673         return netdutils::status::ok;
    674     };
    675     res = mAppUidStatsMap.iterateWithValue(printAppUidStatsInfo);
    676     if (!res.ok()) {
    677         dw.println("mAppUidStatsMap print end with error: %s", res.msg().c_str());
    678     }
    679 
    680     // Print uidStatsMap content
    681     std::string statsHeader = StringPrintf("ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes"
    682                                            " rxPackets txBytes txPackets");
    683     dumpBpfMap("mUidStatsMap", dw, statsHeader);
    684     const auto printStatsInfo = [&dw, this](const StatsKey& key, const StatsValue& value,
    685                                             const BpfMap<StatsKey, StatsValue>&) {
    686         uint32_t ifIndex = key.ifaceIndex;
    687         auto ifname = mIfaceIndexNameMap.readValue(ifIndex);
    688         if (!isOk(ifname)) {
    689             strlcpy(ifname.value().name, "unknown", sizeof(IfaceValue));
    690         }
    691         dw.println("%u %s 0x%x %u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, ifIndex,
    692                    ifname.value().name, key.tag, key.uid, key.counterSet, value.rxBytes,
    693                    value.rxPackets, value.txBytes, value.txPackets);
    694         return netdutils::status::ok;
    695     };
    696     res = mUidStatsMap.iterateWithValue(printStatsInfo);
    697     if (!isOk(res)) {
    698         dw.println("mUidStatsMap print end with error: %s", res.msg().c_str());
    699     }
    700 
    701     // Print TagStatsMap content.
    702     dumpBpfMap("mTagStatsMap", dw, statsHeader);
    703     res = mTagStatsMap.iterateWithValue(printStatsInfo);
    704     if (!isOk(res)) {
    705         dw.println("mTagStatsMap print end with error: %s", res.msg().c_str());
    706     }
    707 
    708     // Print ifaceIndexToNameMap content.
    709     dumpBpfMap("mIfaceIndexNameMap", dw, "");
    710     const auto printIfaceNameInfo = [&dw](const uint32_t& key, const IfaceValue& value,
    711                                           const BpfMap<uint32_t, IfaceValue>&) {
    712         const char* ifname = value.name;
    713         dw.println("ifaceIndex=%u ifaceName=%s", key, ifname);
    714         return netdutils::status::ok;
    715     };
    716     res = mIfaceIndexNameMap.iterateWithValue(printIfaceNameInfo);
    717     if (!isOk(res)) {
    718         dw.println("mIfaceIndexNameMap print end with error: %s", res.msg().c_str());
    719     }
    720 
    721     // Print ifaceStatsMap content
    722     std::string ifaceStatsHeader = StringPrintf("ifaceIndex ifaceName rxBytes rxPackets txBytes"
    723                                                 " txPackets");
    724     dumpBpfMap("mIfaceStatsMap:", dw, ifaceStatsHeader);
    725     const auto printIfaceStatsInfo = [&dw, this](const uint32_t& key, const StatsValue& value,
    726                                                  const BpfMap<uint32_t, StatsValue>&) {
    727         auto ifname = mIfaceIndexNameMap.readValue(key);
    728         if (!isOk(ifname)) {
    729             strlcpy(ifname.value().name, "unknown", sizeof(IfaceValue));
    730         }
    731         dw.println("%u %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, ifname.value().name,
    732                    value.rxBytes, value.rxPackets, value.txBytes, value.txPackets);
    733         return netdutils::status::ok;
    734     };
    735     res = mIfaceStatsMap.iterateWithValue(printIfaceStatsInfo);
    736     if (!isOk(res)) {
    737         dw.println("mIfaceStatsMap print end with error: %s", res.msg().c_str());
    738     }
    739 
    740     // Print owner match uid maps
    741     dumpBpfMap("mDozableUidMap", dw, "");
    742     res = mDozableUidMap.iterateWithValue(printUidInfo);
    743     if (!isOk(res)) {
    744         dw.println("mDozableUidMap print end with error: %s", res.msg().c_str());
    745     }
    746 
    747     dumpBpfMap("mStandbyUidMap", dw, "");
    748     res = mStandbyUidMap.iterateWithValue(printUidInfo);
    749     if (!isOk(res)) {
    750         dw.println("mDozableUidMap print end with error: %s", res.msg().c_str());
    751     }
    752 
    753     dumpBpfMap("mPowerSaveUidMap", dw, "");
    754     res = mPowerSaveUidMap.iterateWithValue(printUidInfo);
    755     if (!isOk(res)) {
    756         dw.println("mDozableUidMap print end with error: %s", res.msg().c_str());
    757     }
    758 
    759     dw.decIndent();
    760 
    761     dw.decIndent();
    762 
    763     dw.decIndent();
    764 
    765 }
    766 
    767 }  // namespace net
    768 }  // namespace android
    769