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 
makeSkDestroyListener()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 
changeOwnerAndMode(const char * path,gid_t group,const char * debugName,bool netdOnly)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 
TrafficController()115 TrafficController::TrafficController() {
116     ebpfSupported = hasBpfSupport();
117 }
118 
initialOwnerMap(BpfMap<uint32_t,uint8_t> & map)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 
initMaps()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 
start()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 
tagSocket(int sockFd,uint32_t tag,uid_t uid)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 
untagSocket(int sockFd)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 
setCounterSet(int counterSetNum,uid_t uid)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 
deleteTagData(uint32_t tag,uid_t uid)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 
addInterface(const char * name,uint32_t ifaceIndex)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 
updateOwnerMapEntry(BpfMap<uint32_t,uint8_t> & map,uid_t uid,FirewallRule rule,FirewallType type)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 
changeUidOwnerRule(ChildChain chain,uid_t uid,FirewallRule rule,FirewallType type)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 
replaceUidsInMap(BpfMap<uint32_t,uint8_t> & map,const std::vector<int32_t> & uids,FirewallRule rule,FirewallType type)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 
replaceUidOwnerMap(const std::string & name,bool isWhitelist,const std::vector<int32_t> & uids)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 
toggleUidOwnerMap(ChildChain chain,bool enable)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 
checkBpfStatsEnable()558 bool TrafficController::checkBpfStatsEnable() {
559     return ebpfSupported;
560 }
561 
getProgramStatus(const char * path)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 
getMapStatus(const base::unique_fd & map_fd,const char * path)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 
dumpBpfMap(std::string mapName,DumpWriter & dw,const std::string & header)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 
dump(DumpWriter & dw,bool verbose)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