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