diff --git a/TEST_MAPPING b/TEST_MAPPING index 520124dc3cf051edd8bbcbb33d53d9b90cf48d54..ab3ed66bfcaaa4bb730580bb5f6737b352827c7b 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -99,9 +99,6 @@ { "name": "TetheringIntegrationTests" }, - { - "name": "traffic_controller_unit_test" - }, { "name": "libnetworkstats_test" }, @@ -133,10 +130,6 @@ { "name": "dns_helper_unit_test" }, - { - "name": "traffic_controller_unit_test", - "keywords": ["netd-device-kernel-4.9", "netd-device-kernel-4.14"] - }, { "name": "FrameworksNetDeflakeTest" }, @@ -273,9 +266,6 @@ } ] }, - { - "name": "traffic_controller_unit_test[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]" - }, { "name": "libnetworkstats_test[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]" }, diff --git a/service/Android.bp b/service/Android.bp index 7def20053f3b3d6de0a42f4fdf9048a8a5e61c5c..15ae501564aa492ba99a17ec8c0ef0695bb6446e 100644 --- a/service/Android.bp +++ b/service/Android.bp @@ -124,7 +124,6 @@ cc_library_shared { "libmodules-utils-build", "libnetjniutils", "libnet_utils_device_common_bpfjni", - "libtraffic_controller", "netd_aidl_interface-lateststable-ndk", ], shared_libs: [ diff --git a/service/jni/com_android_server_BpfNetMaps.cpp b/service/jni/com_android_server_BpfNetMaps.cpp index 50a063507033bc6c9d3502011e3b3341c80e5d21..29f6a606ff35fc00d25d12af73b0af9c1df7578c 100644 --- a/service/jni/com_android_server_BpfNetMaps.cpp +++ b/service/jni/com_android_server_BpfNetMaps.cpp @@ -14,179 +14,13 @@ * limitations under the License. */ -#define LOG_TAG "TrafficControllerJni" - -#include "TrafficController.h" - -#include "netd.h" +#include "bpf/BpfUtils.h" #include <jni.h> -#include <log/log.h> #include <nativehelper/JNIHelp.h> -#include <nativehelper/ScopedUtfChars.h> -#include <nativehelper/ScopedPrimitiveArray.h> -#include <netjniutils/netjniutils.h> -#include <net/if.h> -#include <private/android_filesystem_config.h> -#include <unistd.h> -#include <vector> - - -using android::net::TrafficController; -using android::netdutils::Status; - -using UidOwnerMatchType::PENALTY_BOX_MATCH; -using UidOwnerMatchType::HAPPY_BOX_MATCH; - -static android::net::TrafficController mTc; namespace android { -#define CHECK_LOG(status) \ - do { \ - if (!isOk(status)) \ - ALOGE("%s failed, error code = %d", __func__, status.code()); \ - } while (0) - -static void native_init(JNIEnv* env, jclass clazz, jboolean startSkDestroyListener) { - Status status = mTc.start(startSkDestroyListener); - CHECK_LOG(status); - if (!isOk(status)) { - uid_t uid = getuid(); - ALOGE("BpfNetMaps jni init failure as uid=%d", uid); - // We probably only ever get called from system_server (ie. AID_SYSTEM) - // or from tests, and never from network_stack (ie. AID_NETWORK_STACK). - // However, if we ever do add calls from production network_stack code - // we do want to make sure this initializes correctly. - // TODO: Fix tests to not use this jni lib, so we can unconditionally abort() - if (uid == AID_SYSTEM || uid == AID_NETWORK_STACK) abort(); - } -} - -static jint native_addNaughtyApp(JNIEnv* env, jobject self, jint uid) { - const uint32_t appUids = static_cast<uint32_t>(abs(uid)); - Status status = mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, - TrafficController::IptOp::IptOpInsert); - CHECK_LOG(status); - return (jint)status.code(); -} - -static jint native_removeNaughtyApp(JNIEnv* env, jobject self, jint uid) { - const uint32_t appUids = static_cast<uint32_t>(abs(uid)); - Status status = mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, - TrafficController::IptOp::IptOpDelete); - CHECK_LOG(status); - return (jint)status.code(); -} - -static jint native_addNiceApp(JNIEnv* env, jobject self, jint uid) { - const uint32_t appUids = static_cast<uint32_t>(abs(uid)); - Status status = mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, - TrafficController::IptOp::IptOpInsert); - CHECK_LOG(status); - return (jint)status.code(); -} - -static jint native_removeNiceApp(JNIEnv* env, jobject self, jint uid) { - const uint32_t appUids = static_cast<uint32_t>(abs(uid)); - Status status = mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, - TrafficController::IptOp::IptOpDelete); - CHECK_LOG(status); - return (jint)status.code(); -} - -static jint native_setChildChain(JNIEnv* env, jobject self, jint childChain, jboolean enable) { - auto chain = static_cast<ChildChain>(childChain); - int res = mTc.toggleUidOwnerMap(chain, enable); - if (res) ALOGE("%s failed, error code = %d", __func__, res); - return (jint)res; -} - -static jint native_replaceUidChain(JNIEnv* env, jobject self, jstring name, jboolean isAllowlist, - jintArray jUids) { - const ScopedUtfChars chainNameUtf8(env, name); - if (chainNameUtf8.c_str() == nullptr) return -EINVAL; - const std::string chainName(chainNameUtf8.c_str()); - - ScopedIntArrayRO uids(env, jUids); - if (uids.get() == nullptr) return -EINVAL; - - size_t size = uids.size(); - static_assert(sizeof(*(uids.get())) == sizeof(int32_t)); - std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]); - int res = mTc.replaceUidOwnerMap(chainName, isAllowlist, data); - if (res) ALOGE("%s failed, error code = %d", __func__, res); - return (jint)res; -} - -static jint native_setUidRule(JNIEnv* env, jobject self, jint childChain, jint uid, - jint firewallRule) { - auto chain = static_cast<ChildChain>(childChain); - auto rule = static_cast<FirewallRule>(firewallRule); - FirewallType fType = mTc.getFirewallType(chain); - - int res = mTc.changeUidOwnerRule(chain, uid, rule, fType); - if (res) ALOGE("%s failed, error code = %d", __func__, res); - return (jint)res; -} - -static jint native_addUidInterfaceRules(JNIEnv* env, jobject self, jstring ifName, - jintArray jUids) { - // Null ifName is a wildcard to allow apps to receive packets on all interfaces and ifIndex is - // set to 0. - int ifIndex = 0; - if (ifName != nullptr) { - const ScopedUtfChars ifNameUtf8(env, ifName); - const std::string interfaceName(ifNameUtf8.c_str()); - ifIndex = if_nametoindex(interfaceName.c_str()); - } - - ScopedIntArrayRO uids(env, jUids); - if (uids.get() == nullptr) return -EINVAL; - - size_t size = uids.size(); - static_assert(sizeof(*(uids.get())) == sizeof(int32_t)); - std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]); - Status status = mTc.addUidInterfaceRules(ifIndex, data); - CHECK_LOG(status); - return (jint)status.code(); -} - -static jint native_removeUidInterfaceRules(JNIEnv* env, jobject self, jintArray jUids) { - ScopedIntArrayRO uids(env, jUids); - if (uids.get() == nullptr) return -EINVAL; - - size_t size = uids.size(); - static_assert(sizeof(*(uids.get())) == sizeof(int32_t)); - std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]); - Status status = mTc.removeUidInterfaceRules(data); - CHECK_LOG(status); - return (jint)status.code(); -} - -static jint native_updateUidLockdownRule(JNIEnv* env, jobject self, jint uid, jboolean add) { - Status status = mTc.updateUidLockdownRule(uid, add); - CHECK_LOG(status); - return (jint)status.code(); -} - -static jint native_swapActiveStatsMap(JNIEnv* env, jobject self) { - Status status = mTc.swapActiveStatsMap(); - CHECK_LOG(status); - return (jint)status.code(); -} - -static void native_setPermissionForUids(JNIEnv* env, jobject self, jint permission, - jintArray jUids) { - ScopedIntArrayRO uids(env, jUids); - if (uids.get() == nullptr) return; - - size_t size = uids.size(); - static_assert(sizeof(*(uids.get())) == sizeof(uid_t)); - std::vector<uid_t> data ((uid_t *)&uids[0], (uid_t*)&uids[size]); - mTc.setPermissionForUids(permission, data); -} - static jint native_synchronizeKernelRCU(JNIEnv* env, jobject self) { return -bpf::synchronizeKernelRCU(); } @@ -197,32 +31,6 @@ static jint native_synchronizeKernelRCU(JNIEnv* env, jobject self) { // clang-format off static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ - {"native_init", "(Z)V", - (void*)native_init}, - {"native_addNaughtyApp", "(I)I", - (void*)native_addNaughtyApp}, - {"native_removeNaughtyApp", "(I)I", - (void*)native_removeNaughtyApp}, - {"native_addNiceApp", "(I)I", - (void*)native_addNiceApp}, - {"native_removeNiceApp", "(I)I", - (void*)native_removeNiceApp}, - {"native_setChildChain", "(IZ)I", - (void*)native_setChildChain}, - {"native_replaceUidChain", "(Ljava/lang/String;Z[I)I", - (void*)native_replaceUidChain}, - {"native_setUidRule", "(III)I", - (void*)native_setUidRule}, - {"native_addUidInterfaceRules", "(Ljava/lang/String;[I)I", - (void*)native_addUidInterfaceRules}, - {"native_removeUidInterfaceRules", "([I)I", - (void*)native_removeUidInterfaceRules}, - {"native_updateUidLockdownRule", "(IZ)I", - (void*)native_updateUidLockdownRule}, - {"native_swapActiveStatsMap", "()I", - (void*)native_swapActiveStatsMap}, - {"native_setPermissionForUids", "(I[I)V", - (void*)native_setPermissionForUids}, {"native_synchronizeKernelRCU", "()I", (void*)native_synchronizeKernelRCU}, }; diff --git a/service/native/Android.bp b/service/native/Android.bp deleted file mode 100644 index 697fcbd8988ffc527c9beafde10f249f628937f7..0000000000000000000000000000000000000000 --- a/service/native/Android.bp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -cc_library { - name: "libtraffic_controller", - defaults: ["netd_defaults"], - srcs: [ - "TrafficController.cpp", - ], - header_libs: [ - "bpf_connectivity_headers", - ], - static_libs: [ - // TrafficController would use the constants of INetd so that add - // netd_aidl_interface-lateststable-ndk. - "netd_aidl_interface-lateststable-ndk", - ], - shared_libs: [ - // TODO: Find a good way to remove libbase. - "libbase", - "libcutils", - "libnetdutils", - "libutils", - "liblog", - ], - export_include_dirs: ["include"], - sanitize: { - cfi: true, - }, - apex_available: [ - "com.android.tethering", - ], - min_sdk_version: "30", -} - -cc_test { - name: "traffic_controller_unit_test", - test_suites: ["general-tests", "mts-tethering"], - test_config_template: ":net_native_test_config_template", - require_root: true, - local_include_dirs: ["include"], - header_libs: [ - "bpf_connectivity_headers", - ], - srcs: [ - "TrafficControllerTest.cpp", - ], - static_libs: [ - "libbase", - "libgmock", - "liblog", - "libnetdutils", - "libtraffic_controller", - "libutils", - "libnetd_updatable", - "netd_aidl_interface-lateststable-ndk", - ], - compile_multilib: "both", - multilib: { - lib32: { - suffix: "32", - }, - lib64: { - suffix: "64", - }, - }, -} diff --git a/service/native/TrafficController.cpp b/service/native/TrafficController.cpp deleted file mode 100644 index 8cd698e47fc6709e259a93ca94368906c9cc3a0a..0000000000000000000000000000000000000000 --- a/service/native/TrafficController.cpp +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "TrafficController" -#include <inttypes.h> -#include <linux/if_ether.h> -#include <linux/in.h> -#include <linux/inet_diag.h> -#include <linux/netlink.h> -#include <linux/sock_diag.h> -#include <linux/unistd.h> -#include <net/if.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/utsname.h> -#include <sys/wait.h> -#include <map> -#include <mutex> -#include <unordered_set> -#include <vector> - -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <android-base/unique_fd.h> -#include <netdutils/StatusOr.h> -#include <netdutils/Syscalls.h> -#include <netdutils/UidConstants.h> -#include <netdutils/Utils.h> -#include <private/android_filesystem_config.h> - -#include "TrafficController.h" -#include "bpf/BpfMap.h" - -namespace android { -namespace net { - -using base::StringPrintf; -using base::unique_fd; -using bpf::BpfMap; -using bpf::synchronizeKernelRCU; -using netdutils::NetlinkListener; -using netdutils::NetlinkListenerInterface; -using netdutils::Slice; -using netdutils::sSyscalls; -using netdutils::Status; -using netdutils::statusFromErrno; -using netdutils::StatusOr; - -constexpr int kSockDiagMsgType = SOCK_DIAG_BY_FAMILY; -constexpr int kSockDiagDoneMsgType = NLMSG_DONE; - -const char* TrafficController::LOCAL_DOZABLE = "fw_dozable"; -const char* TrafficController::LOCAL_STANDBY = "fw_standby"; -const char* TrafficController::LOCAL_POWERSAVE = "fw_powersave"; -const char* TrafficController::LOCAL_RESTRICTED = "fw_restricted"; -const char* TrafficController::LOCAL_LOW_POWER_STANDBY = "fw_low_power_standby"; -const char* TrafficController::LOCAL_OEM_DENY_1 = "fw_oem_deny_1"; -const char* TrafficController::LOCAL_OEM_DENY_2 = "fw_oem_deny_2"; -const char* TrafficController::LOCAL_OEM_DENY_3 = "fw_oem_deny_3"; - -static_assert(BPF_PERMISSION_INTERNET == INetd::PERMISSION_INTERNET, - "Mismatch between BPF and AIDL permissions: PERMISSION_INTERNET"); -static_assert(BPF_PERMISSION_UPDATE_DEVICE_STATS == INetd::PERMISSION_UPDATE_DEVICE_STATS, - "Mismatch between BPF and AIDL permissions: PERMISSION_UPDATE_DEVICE_STATS"); - -#define FLAG_MSG_TRANS(result, flag, value) \ - do { \ - if ((value) & (flag)) { \ - (result).append(" " #flag); \ - (value) &= ~(flag); \ - } \ - } while (0) - -const std::string uidMatchTypeToString(uint32_t match) { - std::string matchType; - FLAG_MSG_TRANS(matchType, HAPPY_BOX_MATCH, match); - FLAG_MSG_TRANS(matchType, PENALTY_BOX_MATCH, match); - FLAG_MSG_TRANS(matchType, DOZABLE_MATCH, match); - FLAG_MSG_TRANS(matchType, STANDBY_MATCH, match); - FLAG_MSG_TRANS(matchType, POWERSAVE_MATCH, match); - FLAG_MSG_TRANS(matchType, RESTRICTED_MATCH, match); - FLAG_MSG_TRANS(matchType, LOW_POWER_STANDBY_MATCH, match); - FLAG_MSG_TRANS(matchType, IIF_MATCH, match); - FLAG_MSG_TRANS(matchType, LOCKDOWN_VPN_MATCH, match); - FLAG_MSG_TRANS(matchType, OEM_DENY_1_MATCH, match); - FLAG_MSG_TRANS(matchType, OEM_DENY_2_MATCH, match); - FLAG_MSG_TRANS(matchType, OEM_DENY_3_MATCH, match); - if (match) { - return StringPrintf("Unknown match: %u", match); - } - return matchType; -} - -const std::string UidPermissionTypeToString(int permission) { - if (permission == INetd::PERMISSION_NONE) { - return "PERMISSION_NONE"; - } - if (permission == INetd::PERMISSION_UNINSTALLED) { - // This should never appear in the map, complain loudly if it does. - return "PERMISSION_UNINSTALLED error!"; - } - std::string permissionType; - FLAG_MSG_TRANS(permissionType, BPF_PERMISSION_INTERNET, permission); - FLAG_MSG_TRANS(permissionType, BPF_PERMISSION_UPDATE_DEVICE_STATS, permission); - if (permission) { - return StringPrintf("Unknown permission: %u", permission); - } - return permissionType; -} - -StatusOr<std::unique_ptr<NetlinkListenerInterface>> TrafficController::makeSkDestroyListener() { - const auto& sys = sSyscalls.get(); - ASSIGN_OR_RETURN(auto event, sys.eventfd(0, EFD_CLOEXEC)); - const int domain = AF_NETLINK; - const int type = SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK; - const int protocol = NETLINK_INET_DIAG; - ASSIGN_OR_RETURN(auto sock, sys.socket(domain, type, protocol)); - - // TODO: if too many sockets are closed too quickly, we can overflow the socket buffer, and - // some entries in mCookieTagMap will not be freed. In order to fix this we would need to - // periodically dump all sockets and remove the tag entries for sockets that have been closed. - // For now, set a large-enough buffer that we can close hundreds of sockets without getting - // ENOBUFS and leaking mCookieTagMap entries. - int rcvbuf = 512 * 1024; - auto ret = sys.setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); - if (!ret.ok()) { - ALOGW("Failed to set SkDestroyListener buffer size to %d: %s", rcvbuf, ret.msg().c_str()); - } - - sockaddr_nl addr = { - .nl_family = AF_NETLINK, - .nl_groups = 1 << (SKNLGRP_INET_TCP_DESTROY - 1) | 1 << (SKNLGRP_INET_UDP_DESTROY - 1) | - 1 << (SKNLGRP_INET6_TCP_DESTROY - 1) | 1 << (SKNLGRP_INET6_UDP_DESTROY - 1)}; - RETURN_IF_NOT_OK(sys.bind(sock, addr)); - - const sockaddr_nl kernel = {.nl_family = AF_NETLINK}; - RETURN_IF_NOT_OK(sys.connect(sock, kernel)); - - std::unique_ptr<NetlinkListenerInterface> listener = - std::make_unique<NetlinkListener>(std::move(event), std::move(sock), "SkDestroyListen"); - - return listener; -} - -Status TrafficController::initMaps() { - std::lock_guard guard(mMutex); - - RETURN_IF_NOT_OK(mCookieTagMap.init(COOKIE_TAG_MAP_PATH)); - RETURN_IF_NOT_OK(mUidCounterSetMap.init(UID_COUNTERSET_MAP_PATH)); - RETURN_IF_NOT_OK(mAppUidStatsMap.init(APP_UID_STATS_MAP_PATH)); - RETURN_IF_NOT_OK(mStatsMapA.init(STATS_MAP_A_PATH)); - RETURN_IF_NOT_OK(mStatsMapB.init(STATS_MAP_B_PATH)); - RETURN_IF_NOT_OK(mIfaceIndexNameMap.init(IFACE_INDEX_NAME_MAP_PATH)); - RETURN_IF_NOT_OK(mIfaceStatsMap.init(IFACE_STATS_MAP_PATH)); - - RETURN_IF_NOT_OK(mConfigurationMap.init(CONFIGURATION_MAP_PATH)); - - RETURN_IF_NOT_OK(mUidOwnerMap.init(UID_OWNER_MAP_PATH)); - RETURN_IF_NOT_OK(mUidPermissionMap.init(UID_PERMISSION_MAP_PATH)); - ALOGI("%s successfully", __func__); - - return netdutils::status::ok; -} - -Status TrafficController::start(bool startSkDestroyListener) { - RETURN_IF_NOT_OK(initMaps()); - - if (!startSkDestroyListener) { - return netdutils::status::ok; - } - - auto result = makeSkDestroyListener(); - if (!isOk(result)) { - ALOGE("Unable to create SkDestroyListener: %s", toString(result).c_str()); - } else { - mSkDestroyListener = std::move(result.value()); - } - // Rx handler extracts nfgenmsg looks up and invokes registered dispatch function. - const auto rxHandler = [this](const nlmsghdr&, const Slice msg) { - std::lock_guard guard(mMutex); - inet_diag_msg diagmsg = {}; - if (extract(msg, diagmsg) < sizeof(inet_diag_msg)) { - ALOGE("Unrecognized netlink message: %s", toString(msg).c_str()); - return; - } - uint64_t sock_cookie = static_cast<uint64_t>(diagmsg.id.idiag_cookie[0]) | - (static_cast<uint64_t>(diagmsg.id.idiag_cookie[1]) << 32); - - Status s = mCookieTagMap.deleteValue(sock_cookie); - if (!isOk(s) && s.code() != ENOENT) { - ALOGE("Failed to delete cookie %" PRIx64 ": %s", sock_cookie, toString(s).c_str()); - return; - } - }; - expectOk(mSkDestroyListener->subscribe(kSockDiagMsgType, rxHandler)); - - // In case multiple netlink message comes in as a stream, we need to handle the rxDone message - // properly. - const auto rxDoneHandler = [](const nlmsghdr&, const Slice msg) { - // Ignore NLMSG_DONE messages - inet_diag_msg diagmsg = {}; - extract(msg, diagmsg); - }; - expectOk(mSkDestroyListener->subscribe(kSockDiagDoneMsgType, rxDoneHandler)); - - return netdutils::status::ok; -} - -Status TrafficController::updateOwnerMapEntry(UidOwnerMatchType match, uid_t uid, FirewallRule rule, - FirewallType type) { - std::lock_guard guard(mMutex); - if ((rule == ALLOW && type == ALLOWLIST) || (rule == DENY && type == DENYLIST)) { - RETURN_IF_NOT_OK(addRule(uid, match)); - } else if ((rule == ALLOW && type == DENYLIST) || (rule == DENY && type == ALLOWLIST)) { - RETURN_IF_NOT_OK(removeRule(uid, match)); - } else { - //Cannot happen. - return statusFromErrno(EINVAL, ""); - } - return netdutils::status::ok; -} - -Status TrafficController::removeRule(uint32_t uid, UidOwnerMatchType match) { - auto oldMatch = mUidOwnerMap.readValue(uid); - if (oldMatch.ok()) { - UidOwnerValue newMatch = { - .iif = (match == IIF_MATCH) ? 0 : oldMatch.value().iif, - .rule = oldMatch.value().rule & ~match, - }; - if (newMatch.rule == 0) { - RETURN_IF_NOT_OK(mUidOwnerMap.deleteValue(uid)); - } else { - RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY)); - } - } else { - return statusFromErrno(ENOENT, StringPrintf("uid: %u does not exist in map", uid)); - } - return netdutils::status::ok; -} - -Status TrafficController::addRule(uint32_t uid, UidOwnerMatchType match, uint32_t iif) { - if (match != IIF_MATCH && iif != 0) { - return statusFromErrno(EINVAL, "Non-interface match must have zero interface index"); - } - auto oldMatch = mUidOwnerMap.readValue(uid); - if (oldMatch.ok()) { - UidOwnerValue newMatch = { - .iif = (match == IIF_MATCH) ? iif : oldMatch.value().iif, - .rule = oldMatch.value().rule | match, - }; - RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY)); - } else { - UidOwnerValue newMatch = { - .iif = iif, - .rule = match, - }; - RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY)); - } - return netdutils::status::ok; -} - -Status TrafficController::updateUidOwnerMap(const uint32_t uid, - UidOwnerMatchType matchType, IptOp op) { - std::lock_guard guard(mMutex); - if (op == IptOpDelete) { - RETURN_IF_NOT_OK(removeRule(uid, matchType)); - } else if (op == IptOpInsert) { - RETURN_IF_NOT_OK(addRule(uid, matchType)); - } else { - // Cannot happen. - return statusFromErrno(EINVAL, StringPrintf("invalid IptOp: %d, %d", op, matchType)); - } - return netdutils::status::ok; -} - -FirewallType TrafficController::getFirewallType(ChildChain chain) { - switch (chain) { - case DOZABLE: - return ALLOWLIST; - case STANDBY: - return DENYLIST; - case POWERSAVE: - return ALLOWLIST; - case RESTRICTED: - return ALLOWLIST; - case LOW_POWER_STANDBY: - return ALLOWLIST; - case OEM_DENY_1: - return DENYLIST; - case OEM_DENY_2: - return DENYLIST; - case OEM_DENY_3: - return DENYLIST; - case NONE: - default: - return DENYLIST; - } -} - -int TrafficController::changeUidOwnerRule(ChildChain chain, uid_t uid, FirewallRule rule, - FirewallType type) { - Status res; - switch (chain) { - case DOZABLE: - res = updateOwnerMapEntry(DOZABLE_MATCH, uid, rule, type); - break; - case STANDBY: - res = updateOwnerMapEntry(STANDBY_MATCH, uid, rule, type); - break; - case POWERSAVE: - res = updateOwnerMapEntry(POWERSAVE_MATCH, uid, rule, type); - break; - case RESTRICTED: - res = updateOwnerMapEntry(RESTRICTED_MATCH, uid, rule, type); - break; - case LOW_POWER_STANDBY: - res = updateOwnerMapEntry(LOW_POWER_STANDBY_MATCH, uid, rule, type); - break; - case OEM_DENY_1: - res = updateOwnerMapEntry(OEM_DENY_1_MATCH, uid, rule, type); - break; - case OEM_DENY_2: - res = updateOwnerMapEntry(OEM_DENY_2_MATCH, uid, rule, type); - break; - case OEM_DENY_3: - res = updateOwnerMapEntry(OEM_DENY_3_MATCH, uid, rule, type); - break; - case NONE: - default: - ALOGW("Unknown child chain: %d", chain); - return -EINVAL; - } - if (!isOk(res)) { - ALOGE("change uid(%u) rule of %d failed: %s, rule: %d, type: %d", uid, chain, - res.msg().c_str(), rule, type); - return -res.code(); - } - return 0; -} - -Status TrafficController::replaceRulesInMap(const UidOwnerMatchType match, - const std::vector<int32_t>& uids) { - std::lock_guard guard(mMutex); - std::set<int32_t> uidSet(uids.begin(), uids.end()); - std::vector<uint32_t> uidsToDelete; - auto getUidsToDelete = [&uidsToDelete, &uidSet](const uint32_t& key, - const BpfMap<uint32_t, UidOwnerValue>&) { - if (uidSet.find((int32_t) key) == uidSet.end()) { - uidsToDelete.push_back(key); - } - return base::Result<void>(); - }; - RETURN_IF_NOT_OK(mUidOwnerMap.iterate(getUidsToDelete)); - - for(auto uid : uidsToDelete) { - RETURN_IF_NOT_OK(removeRule(uid, match)); - } - - for (auto uid : uids) { - RETURN_IF_NOT_OK(addRule(uid, match)); - } - return netdutils::status::ok; -} - -Status TrafficController::addUidInterfaceRules(const int iif, - const std::vector<int32_t>& uidsToAdd) { - std::lock_guard guard(mMutex); - - for (auto uid : uidsToAdd) { - netdutils::Status result = addRule(uid, IIF_MATCH, iif); - if (!isOk(result)) { - ALOGW("addRule failed(%d): uid=%d iif=%d", result.code(), uid, iif); - } - } - return netdutils::status::ok; -} - -Status TrafficController::removeUidInterfaceRules(const std::vector<int32_t>& uidsToDelete) { - std::lock_guard guard(mMutex); - - for (auto uid : uidsToDelete) { - netdutils::Status result = removeRule(uid, IIF_MATCH); - if (!isOk(result)) { - ALOGW("removeRule failed(%d): uid=%d", result.code(), uid); - } - } - return netdutils::status::ok; -} - -Status TrafficController::updateUidLockdownRule(const uid_t uid, const bool add) { - std::lock_guard guard(mMutex); - - netdutils::Status result = add ? addRule(uid, LOCKDOWN_VPN_MATCH) - : removeRule(uid, LOCKDOWN_VPN_MATCH); - if (!isOk(result)) { - ALOGW("%s Lockdown rule failed(%d): uid=%d", - (add ? "add": "remove"), result.code(), uid); - } - return result; -} - -int TrafficController::replaceUidOwnerMap(const std::string& name, bool isAllowlist __unused, - const std::vector<int32_t>& uids) { - // FirewallRule rule = isAllowlist ? ALLOW : DENY; - // FirewallType type = isAllowlist ? ALLOWLIST : DENYLIST; - Status res; - if (!name.compare(LOCAL_DOZABLE)) { - res = replaceRulesInMap(DOZABLE_MATCH, uids); - } else if (!name.compare(LOCAL_STANDBY)) { - res = replaceRulesInMap(STANDBY_MATCH, uids); - } else if (!name.compare(LOCAL_POWERSAVE)) { - res = replaceRulesInMap(POWERSAVE_MATCH, uids); - } else if (!name.compare(LOCAL_RESTRICTED)) { - res = replaceRulesInMap(RESTRICTED_MATCH, uids); - } else if (!name.compare(LOCAL_LOW_POWER_STANDBY)) { - res = replaceRulesInMap(LOW_POWER_STANDBY_MATCH, uids); - } else if (!name.compare(LOCAL_OEM_DENY_1)) { - res = replaceRulesInMap(OEM_DENY_1_MATCH, uids); - } else if (!name.compare(LOCAL_OEM_DENY_2)) { - res = replaceRulesInMap(OEM_DENY_2_MATCH, uids); - } else if (!name.compare(LOCAL_OEM_DENY_3)) { - res = replaceRulesInMap(OEM_DENY_3_MATCH, uids); - } else { - ALOGE("unknown chain name: %s", name.c_str()); - return -EINVAL; - } - if (!isOk(res)) { - ALOGE("Failed to clean up chain: %s: %s", name.c_str(), res.msg().c_str()); - return -res.code(); - } - return 0; -} - -int TrafficController::toggleUidOwnerMap(ChildChain chain, bool enable) { - std::lock_guard guard(mMutex); - uint32_t key = UID_RULES_CONFIGURATION_KEY; - auto oldConfigure = mConfigurationMap.readValue(key); - if (!oldConfigure.ok()) { - ALOGE("Cannot read the old configuration from map: %s", - oldConfigure.error().message().c_str()); - return -oldConfigure.error().code(); - } - uint32_t match; - switch (chain) { - case DOZABLE: - match = DOZABLE_MATCH; - break; - case STANDBY: - match = STANDBY_MATCH; - break; - case POWERSAVE: - match = POWERSAVE_MATCH; - break; - case RESTRICTED: - match = RESTRICTED_MATCH; - break; - case LOW_POWER_STANDBY: - match = LOW_POWER_STANDBY_MATCH; - break; - case OEM_DENY_1: - match = OEM_DENY_1_MATCH; - break; - case OEM_DENY_2: - match = OEM_DENY_2_MATCH; - break; - case OEM_DENY_3: - match = OEM_DENY_3_MATCH; - break; - default: - return -EINVAL; - } - BpfConfig newConfiguration = - enable ? (oldConfigure.value() | match) : (oldConfigure.value() & ~match); - Status res = mConfigurationMap.writeValue(key, newConfiguration, BPF_EXIST); - if (!isOk(res)) { - ALOGE("Failed to toggleUidOwnerMap(%d): %s", chain, res.msg().c_str()); - } - return -res.code(); -} - -Status TrafficController::swapActiveStatsMap() { - std::lock_guard guard(mMutex); - - uint32_t key = CURRENT_STATS_MAP_CONFIGURATION_KEY; - auto oldConfigure = mConfigurationMap.readValue(key); - if (!oldConfigure.ok()) { - ALOGE("Cannot read the old configuration from map: %s", - oldConfigure.error().message().c_str()); - return Status(oldConfigure.error().code(), oldConfigure.error().message()); - } - - // Write to the configuration map to inform the kernel eBPF program to switch - // from using one map to the other. Use flag BPF_EXIST here since the map should - // be already populated in initMaps. - uint32_t newConfigure = (oldConfigure.value() == SELECT_MAP_A) ? SELECT_MAP_B : SELECT_MAP_A; - auto res = mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, newConfigure, - BPF_EXIST); - if (!res.ok()) { - ALOGE("Failed to toggle the stats map: %s", strerror(res.error().code())); - return res; - } - // After changing the config, we need to make sure all the current running - // eBPF programs are finished and all the CPUs are aware of this config change - // before we modify the old map. So we do a special hack here to wait for - // the kernel to do a synchronize_rcu(). Once the kernel called - // synchronize_rcu(), the config we just updated will be available to all cores - // and the next eBPF programs triggered inside the kernel will use the new - // map configuration. So once this function returns we can safely modify the - // old stats map without concerning about race between the kernel and - // userspace. - int ret = synchronizeKernelRCU(); - if (ret) { - ALOGE("map swap synchronize_rcu() ended with failure: %s", strerror(-ret)); - return statusFromErrno(-ret, "map swap synchronize_rcu() failed"); - } - return netdutils::status::ok; -} - -void TrafficController::setPermissionForUids(int permission, const std::vector<uid_t>& uids) { - std::lock_guard guard(mMutex); - if (permission == INetd::PERMISSION_UNINSTALLED) { - for (uid_t uid : uids) { - // Clean up all permission information for the related uid if all the - // packages related to it are uninstalled. - mPrivilegedUser.erase(uid); - Status ret = mUidPermissionMap.deleteValue(uid); - if (!isOk(ret) && ret.code() != ENOENT) { - ALOGE("Failed to clean up the permission for %u: %s", uid, strerror(ret.code())); - } - } - return; - } - - bool privileged = (permission & INetd::PERMISSION_UPDATE_DEVICE_STATS); - - for (uid_t uid : uids) { - if (privileged) { - mPrivilegedUser.insert(uid); - } else { - mPrivilegedUser.erase(uid); - } - - // The map stores all the permissions that the UID has, except if the only permission - // the UID has is the INTERNET permission, then the UID should not appear in the map. - if (permission != INetd::PERMISSION_INTERNET) { - Status ret = mUidPermissionMap.writeValue(uid, permission, BPF_ANY); - if (!isOk(ret)) { - ALOGE("Failed to set permission: %s of uid(%u) to permission map: %s", - UidPermissionTypeToString(permission).c_str(), uid, strerror(ret.code())); - } - } else { - Status ret = mUidPermissionMap.deleteValue(uid); - if (!isOk(ret) && ret.code() != ENOENT) { - ALOGE("Failed to remove uid %u from permission map: %s", uid, strerror(ret.code())); - } - } - } -} - -} // namespace net -} // namespace android diff --git a/service/native/TrafficControllerTest.cpp b/service/native/TrafficControllerTest.cpp deleted file mode 100644 index 99e98314997d2a4d60df504b85bc27e34ff60c65..0000000000000000000000000000000000000000 --- a/service/native/TrafficControllerTest.cpp +++ /dev/null @@ -1,822 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * TrafficControllerTest.cpp - unit tests for TrafficController.cpp - */ - -#include <cstdint> -#include <string> -#include <vector> - -#include <fcntl.h> -#include <inttypes.h> -#include <linux/inet_diag.h> -#include <linux/sock_diag.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include <gtest/gtest.h> - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <binder/Status.h> - -#include <netdutils/MockSyscalls.h> - -#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING -#include "TrafficController.h" -#include "bpf/BpfUtils.h" -#include "NetdUpdatablePublic.h" - -using namespace android::bpf; // NOLINT(google-build-using-namespace): grandfathered - -namespace android { -namespace net { - -using android::netdutils::Status; -using base::Result; -using netdutils::isOk; -using netdutils::statusFromErrno; - -constexpr int TEST_MAP_SIZE = 10; -constexpr uid_t TEST_UID = 10086; -constexpr uid_t TEST_UID2 = 54321; -constexpr uid_t TEST_UID3 = 98765; -constexpr uint32_t TEST_TAG = 42; -constexpr uint32_t TEST_COUNTERSET = 1; -constexpr int TEST_IFINDEX = 999; -constexpr int RXPACKETS = 1; -constexpr int RXBYTES = 100; -constexpr int TXPACKETS = 0; -constexpr int TXBYTES = 0; - -#define ASSERT_VALID(x) ASSERT_TRUE((x).isValid()) -#define ASSERT_INVALID(x) ASSERT_FALSE((x).isValid()) - -class TrafficControllerTest : public ::testing::Test { - protected: - TrafficControllerTest() {} - TrafficController mTc; - BpfMap<uint64_t, UidTagValue> mFakeCookieTagMap; - BpfMap<uint32_t, StatsValue> mFakeAppUidStatsMap; - BpfMap<StatsKey, StatsValue> mFakeStatsMapA; - BpfMap<StatsKey, StatsValue> mFakeStatsMapB; // makeTrafficControllerMapsInvalid only - BpfMap<uint32_t, StatsValue> mFakeIfaceStatsMap; ; // makeTrafficControllerMapsInvalid only - BpfMap<uint32_t, uint32_t> mFakeConfigurationMap; - BpfMap<uint32_t, UidOwnerValue> mFakeUidOwnerMap; - BpfMap<uint32_t, uint8_t> mFakeUidPermissionMap; - BpfMap<uint32_t, uint8_t> mFakeUidCounterSetMap; - BpfMap<uint32_t, IfaceValue> mFakeIfaceIndexNameMap; - - void SetUp() { - std::lock_guard guard(mTc.mMutex); - ASSERT_EQ(0, setrlimitForTest()); - - mFakeCookieTagMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE); - ASSERT_VALID(mFakeCookieTagMap); - - mFakeAppUidStatsMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE); - ASSERT_VALID(mFakeAppUidStatsMap); - - mFakeStatsMapA.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE); - ASSERT_VALID(mFakeStatsMapA); - - mFakeConfigurationMap.resetMap(BPF_MAP_TYPE_ARRAY, CONFIGURATION_MAP_SIZE); - ASSERT_VALID(mFakeConfigurationMap); - - mFakeUidOwnerMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE); - ASSERT_VALID(mFakeUidOwnerMap); - mFakeUidPermissionMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE); - ASSERT_VALID(mFakeUidPermissionMap); - - mFakeUidCounterSetMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE); - ASSERT_VALID(mFakeUidCounterSetMap); - - mFakeIfaceIndexNameMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE); - ASSERT_VALID(mFakeIfaceIndexNameMap); - - mTc.mCookieTagMap = mFakeCookieTagMap; - ASSERT_VALID(mTc.mCookieTagMap); - mTc.mAppUidStatsMap = mFakeAppUidStatsMap; - ASSERT_VALID(mTc.mAppUidStatsMap); - mTc.mStatsMapA = mFakeStatsMapA; - ASSERT_VALID(mTc.mStatsMapA); - mTc.mConfigurationMap = mFakeConfigurationMap; - ASSERT_VALID(mTc.mConfigurationMap); - - // Always write to stats map A by default. - static_assert(SELECT_MAP_A == 0); - - mTc.mUidOwnerMap = mFakeUidOwnerMap; - ASSERT_VALID(mTc.mUidOwnerMap); - mTc.mUidPermissionMap = mFakeUidPermissionMap; - ASSERT_VALID(mTc.mUidPermissionMap); - mTc.mPrivilegedUser.clear(); - - mTc.mUidCounterSetMap = mFakeUidCounterSetMap; - ASSERT_VALID(mTc.mUidCounterSetMap); - - mTc.mIfaceIndexNameMap = mFakeIfaceIndexNameMap; - ASSERT_VALID(mTc.mIfaceIndexNameMap); - } - - void populateFakeStats(uint64_t cookie, uint32_t uid, uint32_t tag, StatsKey* key) { - UidTagValue cookieMapkey = {.uid = (uint32_t)uid, .tag = tag}; - EXPECT_RESULT_OK(mFakeCookieTagMap.writeValue(cookie, cookieMapkey, BPF_ANY)); - *key = {.uid = uid, .tag = tag, .counterSet = TEST_COUNTERSET, .ifaceIndex = TEST_IFINDEX}; - StatsValue statsMapValue = {.rxPackets = RXPACKETS, .rxBytes = RXBYTES, - .txPackets = TXPACKETS, .txBytes = TXBYTES}; - EXPECT_RESULT_OK(mFakeStatsMapA.writeValue(*key, statsMapValue, BPF_ANY)); - EXPECT_RESULT_OK(mFakeAppUidStatsMap.writeValue(uid, statsMapValue, BPF_ANY)); - // put tag information back to statsKey - key->tag = tag; - } - - void populateFakeCounterSet(uint32_t uid, uint32_t counterSet) { - EXPECT_RESULT_OK(mFakeUidCounterSetMap.writeValue(uid, counterSet, BPF_ANY)); - } - - void populateFakeIfaceIndexName(const char* name, uint32_t ifaceIndex) { - if (name == nullptr || ifaceIndex <= 0) return; - - IfaceValue iface; - strlcpy(iface.name, name, sizeof(IfaceValue)); - EXPECT_RESULT_OK(mFakeIfaceIndexNameMap.writeValue(ifaceIndex, iface, BPF_ANY)); - } - - void checkUidOwnerRuleForChain(ChildChain chain, UidOwnerMatchType match) { - uint32_t uid = TEST_UID; - EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, DENYLIST)); - Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid); - EXPECT_RESULT_OK(value); - EXPECT_TRUE(value.value().rule & match); - - uid = TEST_UID2; - EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, ALLOWLIST)); - value = mFakeUidOwnerMap.readValue(uid); - EXPECT_RESULT_OK(value); - EXPECT_TRUE(value.value().rule & match); - - EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, ALLOWLIST)); - value = mFakeUidOwnerMap.readValue(uid); - EXPECT_FALSE(value.ok()); - EXPECT_EQ(ENOENT, value.error().code()); - - uid = TEST_UID; - EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, DENYLIST)); - value = mFakeUidOwnerMap.readValue(uid); - EXPECT_FALSE(value.ok()); - EXPECT_EQ(ENOENT, value.error().code()); - - uid = TEST_UID3; - EXPECT_EQ(-ENOENT, mTc.changeUidOwnerRule(chain, uid, ALLOW, DENYLIST)); - value = mFakeUidOwnerMap.readValue(uid); - EXPECT_FALSE(value.ok()); - EXPECT_EQ(ENOENT, value.error().code()); - } - - void checkEachUidValue(const std::vector<int32_t>& uids, UidOwnerMatchType match) { - for (uint32_t uid : uids) { - Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid); - EXPECT_RESULT_OK(value); - EXPECT_TRUE(value.value().rule & match); - } - std::set<uint32_t> uidSet(uids.begin(), uids.end()); - const auto checkNoOtherUid = [&uidSet](const int32_t& key, - const BpfMap<uint32_t, UidOwnerValue>&) { - EXPECT_NE(uidSet.end(), uidSet.find(key)); - return Result<void>(); - }; - EXPECT_RESULT_OK(mFakeUidOwnerMap.iterate(checkNoOtherUid)); - } - - void checkUidMapReplace(const std::string& name, const std::vector<int32_t>& uids, - UidOwnerMatchType match) { - bool isAllowlist = true; - EXPECT_EQ(0, mTc.replaceUidOwnerMap(name, isAllowlist, uids)); - checkEachUidValue(uids, match); - - isAllowlist = false; - EXPECT_EQ(0, mTc.replaceUidOwnerMap(name, isAllowlist, uids)); - checkEachUidValue(uids, match); - } - - void expectUidOwnerMapValues(const std::vector<uint32_t>& appUids, uint32_t expectedRule, - uint32_t expectedIif) { - for (uint32_t uid : appUids) { - Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid); - EXPECT_RESULT_OK(value); - EXPECT_EQ(expectedRule, value.value().rule) - << "Expected rule for UID " << uid << " to be " << expectedRule << ", but was " - << value.value().rule; - EXPECT_EQ(expectedIif, value.value().iif) - << "Expected iif for UID " << uid << " to be " << expectedIif << ", but was " - << value.value().iif; - } - } - - template <class Key, class Value> - void expectMapEmpty(BpfMap<Key, Value>& map) { - auto isEmpty = map.isEmpty(); - EXPECT_RESULT_OK(isEmpty); - EXPECT_TRUE(isEmpty.value()); - } - - void expectUidPermissionMapValues(const std::vector<uid_t>& appUids, uint8_t expectedValue) { - for (uid_t uid : appUids) { - Result<uint8_t> value = mFakeUidPermissionMap.readValue(uid); - EXPECT_RESULT_OK(value); - EXPECT_EQ(expectedValue, value.value()) - << "Expected value for UID " << uid << " to be " << expectedValue - << ", but was " << value.value(); - } - } - - void expectPrivilegedUserSet(const std::vector<uid_t>& appUids) { - std::lock_guard guard(mTc.mMutex); - EXPECT_EQ(appUids.size(), mTc.mPrivilegedUser.size()); - for (uid_t uid : appUids) { - EXPECT_NE(mTc.mPrivilegedUser.end(), mTc.mPrivilegedUser.find(uid)); - } - } - - void expectPrivilegedUserSetEmpty() { - std::lock_guard guard(mTc.mMutex); - EXPECT_TRUE(mTc.mPrivilegedUser.empty()); - } - - Status updateUidOwnerMaps(const std::vector<uint32_t>& appUids, - UidOwnerMatchType matchType, TrafficController::IptOp op) { - Status ret(0); - for (auto uid : appUids) { - ret = mTc.updateUidOwnerMap(uid, matchType, op); - if(!isOk(ret)) break; - } - return ret; - } -}; - -TEST_F(TrafficControllerTest, TestUpdateOwnerMapEntry) { - uint32_t uid = TEST_UID; - ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, DENY, DENYLIST))); - Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid); - ASSERT_RESULT_OK(value); - ASSERT_TRUE(value.value().rule & STANDBY_MATCH); - - ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(DOZABLE_MATCH, uid, ALLOW, ALLOWLIST))); - value = mFakeUidOwnerMap.readValue(uid); - ASSERT_RESULT_OK(value); - ASSERT_TRUE(value.value().rule & DOZABLE_MATCH); - - ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(DOZABLE_MATCH, uid, DENY, ALLOWLIST))); - value = mFakeUidOwnerMap.readValue(uid); - ASSERT_RESULT_OK(value); - ASSERT_FALSE(value.value().rule & DOZABLE_MATCH); - - ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, ALLOW, DENYLIST))); - ASSERT_FALSE(mFakeUidOwnerMap.readValue(uid).ok()); - - uid = TEST_UID2; - ASSERT_FALSE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, ALLOW, DENYLIST))); - ASSERT_FALSE(mFakeUidOwnerMap.readValue(uid).ok()); -} - -TEST_F(TrafficControllerTest, TestChangeUidOwnerRule) { - checkUidOwnerRuleForChain(DOZABLE, DOZABLE_MATCH); - checkUidOwnerRuleForChain(STANDBY, STANDBY_MATCH); - checkUidOwnerRuleForChain(POWERSAVE, POWERSAVE_MATCH); - checkUidOwnerRuleForChain(RESTRICTED, RESTRICTED_MATCH); - checkUidOwnerRuleForChain(LOW_POWER_STANDBY, LOW_POWER_STANDBY_MATCH); - checkUidOwnerRuleForChain(OEM_DENY_1, OEM_DENY_1_MATCH); - checkUidOwnerRuleForChain(OEM_DENY_2, OEM_DENY_2_MATCH); - checkUidOwnerRuleForChain(OEM_DENY_3, OEM_DENY_3_MATCH); - ASSERT_EQ(-EINVAL, mTc.changeUidOwnerRule(NONE, TEST_UID, ALLOW, ALLOWLIST)); - ASSERT_EQ(-EINVAL, mTc.changeUidOwnerRule(INVALID_CHAIN, TEST_UID, ALLOW, ALLOWLIST)); -} - -TEST_F(TrafficControllerTest, TestReplaceUidOwnerMap) { - std::vector<int32_t> uids = {TEST_UID, TEST_UID2, TEST_UID3}; - checkUidMapReplace("fw_dozable", uids, DOZABLE_MATCH); - checkUidMapReplace("fw_standby", uids, STANDBY_MATCH); - checkUidMapReplace("fw_powersave", uids, POWERSAVE_MATCH); - checkUidMapReplace("fw_restricted", uids, RESTRICTED_MATCH); - checkUidMapReplace("fw_low_power_standby", uids, LOW_POWER_STANDBY_MATCH); - checkUidMapReplace("fw_oem_deny_1", uids, OEM_DENY_1_MATCH); - checkUidMapReplace("fw_oem_deny_2", uids, OEM_DENY_2_MATCH); - checkUidMapReplace("fw_oem_deny_3", uids, OEM_DENY_3_MATCH); - ASSERT_EQ(-EINVAL, mTc.replaceUidOwnerMap("unknow", true, uids)); -} - -TEST_F(TrafficControllerTest, TestReplaceSameChain) { - std::vector<int32_t> uids = {TEST_UID, TEST_UID2, TEST_UID3}; - checkUidMapReplace("fw_dozable", uids, DOZABLE_MATCH); - std::vector<int32_t> newUids = {TEST_UID2, TEST_UID3}; - checkUidMapReplace("fw_dozable", newUids, DOZABLE_MATCH); -} - -TEST_F(TrafficControllerTest, TestDenylistUidMatch) { - std::vector<uint32_t> appUids = {1000, 1001, 10012}; - ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, PENALTY_BOX_MATCH, - TrafficController::IptOpInsert))); - expectUidOwnerMapValues(appUids, PENALTY_BOX_MATCH, 0); - ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, PENALTY_BOX_MATCH, - TrafficController::IptOpDelete))); - expectMapEmpty(mFakeUidOwnerMap); -} - -TEST_F(TrafficControllerTest, TestAllowlistUidMatch) { - std::vector<uint32_t> appUids = {1000, 1001, 10012}; - ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpInsert))); - expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH, 0); - ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpDelete))); - expectMapEmpty(mFakeUidOwnerMap); -} - -TEST_F(TrafficControllerTest, TestReplaceMatchUid) { - std::vector<uint32_t> appUids = {1000, 1001, 10012}; - // Add appUids to the denylist and expect that their values are all PENALTY_BOX_MATCH. - ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, PENALTY_BOX_MATCH, - TrafficController::IptOpInsert))); - expectUidOwnerMapValues(appUids, PENALTY_BOX_MATCH, 0); - - // Add the same UIDs to the allowlist and expect that we get PENALTY_BOX_MATCH | - // HAPPY_BOX_MATCH. - ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpInsert))); - expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH | PENALTY_BOX_MATCH, 0); - - // Remove the same UIDs from the allowlist and check the PENALTY_BOX_MATCH is still there. - ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpDelete))); - expectUidOwnerMapValues(appUids, PENALTY_BOX_MATCH, 0); - - // Remove the same UIDs from the denylist and check the map is empty. - ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, PENALTY_BOX_MATCH, - TrafficController::IptOpDelete))); - ASSERT_FALSE(mFakeUidOwnerMap.getFirstKey().ok()); -} - -TEST_F(TrafficControllerTest, TestDeleteWrongMatchSilentlyFails) { - std::vector<uint32_t> appUids = {1000, 1001, 10012}; - // If the uid does not exist in the map, trying to delete a rule about it will fail. - ASSERT_FALSE(isOk(updateUidOwnerMaps(appUids, HAPPY_BOX_MATCH, - TrafficController::IptOpDelete))); - expectMapEmpty(mFakeUidOwnerMap); - - // Add denylist rules for appUids. - ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, HAPPY_BOX_MATCH, - TrafficController::IptOpInsert))); - expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH, 0); - - // Delete (non-existent) denylist rules for appUids, and check that this silently does - // nothing if the uid is in the map but does not have denylist match. This is required because - // NetworkManagementService will try to remove a uid from denylist after adding it to the - // allowlist and if the remove fails it will not update the uid status. - ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, PENALTY_BOX_MATCH, - TrafficController::IptOpDelete))); - expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH, 0); -} - -TEST_F(TrafficControllerTest, TestAddUidInterfaceFilteringRules) { - int iif0 = 15; - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif0, {1000, 1001}))); - expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif0); - - // Add some non-overlapping new uids. They should coexist with existing rules - int iif1 = 16; - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {2000, 2001}))); - expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif0); - expectUidOwnerMapValues({2000, 2001}, IIF_MATCH, iif1); - - // Overwrite some existing uids - int iif2 = 17; - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif2, {1000, 2000}))); - expectUidOwnerMapValues({1001}, IIF_MATCH, iif0); - expectUidOwnerMapValues({2001}, IIF_MATCH, iif1); - expectUidOwnerMapValues({1000, 2000}, IIF_MATCH, iif2); -} - -TEST_F(TrafficControllerTest, TestRemoveUidInterfaceFilteringRules) { - int iif0 = 15; - int iif1 = 16; - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif0, {1000, 1001}))); - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {2000, 2001}))); - expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif0); - expectUidOwnerMapValues({2000, 2001}, IIF_MATCH, iif1); - - // Rmove some uids - ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({1001, 2001}))); - expectUidOwnerMapValues({1000}, IIF_MATCH, iif0); - expectUidOwnerMapValues({2000}, IIF_MATCH, iif1); - checkEachUidValue({1000, 2000}, IIF_MATCH); // Make sure there are only two uids remaining - - // Remove non-existent uids shouldn't fail - ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({2000, 3000}))); - expectUidOwnerMapValues({1000}, IIF_MATCH, iif0); - checkEachUidValue({1000}, IIF_MATCH); // Make sure there are only one uid remaining - - // Remove everything - ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({1000}))); - expectMapEmpty(mFakeUidOwnerMap); -} - -TEST_F(TrafficControllerTest, TestUpdateUidLockdownRule) { - // Add Lockdown rules - ASSERT_TRUE(isOk(mTc.updateUidLockdownRule(1000, true /* add */))); - ASSERT_TRUE(isOk(mTc.updateUidLockdownRule(1001, true /* add */))); - expectUidOwnerMapValues({1000, 1001}, LOCKDOWN_VPN_MATCH, 0); - - // Remove one of Lockdown rules - ASSERT_TRUE(isOk(mTc.updateUidLockdownRule(1000, false /* add */))); - expectUidOwnerMapValues({1001}, LOCKDOWN_VPN_MATCH, 0); - - // Remove remaining Lockdown rule - ASSERT_TRUE(isOk(mTc.updateUidLockdownRule(1001, false /* add */))); - expectMapEmpty(mFakeUidOwnerMap); -} - -TEST_F(TrafficControllerTest, TestUidInterfaceFilteringRulesCoexistWithExistingMatches) { - // Set up existing PENALTY_BOX_MATCH rules - ASSERT_TRUE(isOk(updateUidOwnerMaps({1000, 1001, 10012}, PENALTY_BOX_MATCH, - TrafficController::IptOpInsert))); - expectUidOwnerMapValues({1000, 1001, 10012}, PENALTY_BOX_MATCH, 0); - - // Add some partially-overlapping uid owner rules and check result - int iif1 = 32; - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {10012, 10013, 10014}))); - expectUidOwnerMapValues({1000, 1001}, PENALTY_BOX_MATCH, 0); - expectUidOwnerMapValues({10012}, PENALTY_BOX_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({10013, 10014}, IIF_MATCH, iif1); - - // Removing some PENALTY_BOX_MATCH rules should not change uid interface rule - ASSERT_TRUE(isOk(updateUidOwnerMaps({1001, 10012}, PENALTY_BOX_MATCH, - TrafficController::IptOpDelete))); - expectUidOwnerMapValues({1000}, PENALTY_BOX_MATCH, 0); - expectUidOwnerMapValues({10012, 10013, 10014}, IIF_MATCH, iif1); - - // Remove all uid interface rules - ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({10012, 10013, 10014}))); - expectUidOwnerMapValues({1000}, PENALTY_BOX_MATCH, 0); - // Make sure these are the only uids left - checkEachUidValue({1000}, PENALTY_BOX_MATCH); -} - -TEST_F(TrafficControllerTest, TestUidInterfaceFilteringRulesCoexistWithNewMatches) { - int iif1 = 56; - // Set up existing uid interface rules - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {10001, 10002}))); - expectUidOwnerMapValues({10001, 10002}, IIF_MATCH, iif1); - - // Add some partially-overlapping doze rules - EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_dozable", true, {10002, 10003})); - expectUidOwnerMapValues({10001}, IIF_MATCH, iif1); - expectUidOwnerMapValues({10002}, DOZABLE_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({10003}, DOZABLE_MATCH, 0); - - // Introduce a third rule type (powersave) on various existing UIDs - EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_powersave", true, {10000, 10001, 10002, 10003})); - expectUidOwnerMapValues({10000}, POWERSAVE_MATCH, 0); - expectUidOwnerMapValues({10001}, POWERSAVE_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({10002}, POWERSAVE_MATCH | DOZABLE_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({10003}, POWERSAVE_MATCH | DOZABLE_MATCH, 0); - - // Remove all doze rules - EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_dozable", true, {})); - expectUidOwnerMapValues({10000}, POWERSAVE_MATCH, 0); - expectUidOwnerMapValues({10001}, POWERSAVE_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({10002}, POWERSAVE_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({10003}, POWERSAVE_MATCH, 0); - - // Remove all powersave rules, expect ownerMap to only have uid interface rules left - EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_powersave", true, {})); - expectUidOwnerMapValues({10001, 10002}, IIF_MATCH, iif1); - // Make sure these are the only uids left - checkEachUidValue({10001, 10002}, IIF_MATCH); -} - -TEST_F(TrafficControllerTest, TestAddUidInterfaceFilteringRulesWithWildcard) { - // iif=0 is a wildcard - int iif = 0; - // Add interface rule with wildcard to uids - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif, {1000, 1001}))); - expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif); -} - -TEST_F(TrafficControllerTest, TestRemoveUidInterfaceFilteringRulesWithWildcard) { - // iif=0 is a wildcard - int iif = 0; - // Add interface rule with wildcard to two uids - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif, {1000, 1001}))); - expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif); - - // Remove interface rule from one of the uids - ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({1000}))); - expectUidOwnerMapValues({1001}, IIF_MATCH, iif); - checkEachUidValue({1001}, IIF_MATCH); - - // Remove interface rule from the remaining uid - ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({1001}))); - expectMapEmpty(mFakeUidOwnerMap); -} - -TEST_F(TrafficControllerTest, TestUidInterfaceFilteringRulesWithWildcardAndExistingMatches) { - // Set up existing DOZABLE_MATCH and POWERSAVE_MATCH rule - ASSERT_TRUE(isOk(updateUidOwnerMaps({1000}, DOZABLE_MATCH, - TrafficController::IptOpInsert))); - ASSERT_TRUE(isOk(updateUidOwnerMaps({1000}, POWERSAVE_MATCH, - TrafficController::IptOpInsert))); - - // iif=0 is a wildcard - int iif = 0; - // Add interface rule with wildcard to the existing uid - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif, {1000}))); - expectUidOwnerMapValues({1000}, POWERSAVE_MATCH | DOZABLE_MATCH | IIF_MATCH, iif); - - // Remove interface rule with wildcard from the existing uid - ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({1000}))); - expectUidOwnerMapValues({1000}, POWERSAVE_MATCH | DOZABLE_MATCH, 0); -} - -TEST_F(TrafficControllerTest, TestUidInterfaceFilteringRulesWithWildcardAndNewMatches) { - // iif=0 is a wildcard - int iif = 0; - // Set up existing interface rule with wildcard - ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif, {1000}))); - - // Add DOZABLE_MATCH and POWERSAVE_MATCH rule to the existing uid - ASSERT_TRUE(isOk(updateUidOwnerMaps({1000}, DOZABLE_MATCH, - TrafficController::IptOpInsert))); - ASSERT_TRUE(isOk(updateUidOwnerMaps({1000}, POWERSAVE_MATCH, - TrafficController::IptOpInsert))); - expectUidOwnerMapValues({1000}, POWERSAVE_MATCH | DOZABLE_MATCH | IIF_MATCH, iif); - - // Remove DOZABLE_MATCH and POWERSAVE_MATCH rule from the existing uid - ASSERT_TRUE(isOk(updateUidOwnerMaps({1000}, DOZABLE_MATCH, - TrafficController::IptOpDelete))); - ASSERT_TRUE(isOk(updateUidOwnerMaps({1000}, POWERSAVE_MATCH, - TrafficController::IptOpDelete))); - expectUidOwnerMapValues({1000}, IIF_MATCH, iif); -} - -TEST_F(TrafficControllerTest, TestGrantInternetPermission) { - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; - - mTc.setPermissionForUids(INetd::PERMISSION_INTERNET, appUids); - expectMapEmpty(mFakeUidPermissionMap); - expectPrivilegedUserSetEmpty(); -} - -TEST_F(TrafficControllerTest, TestRevokeInternetPermission) { - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; - - mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids); - expectUidPermissionMapValues(appUids, INetd::PERMISSION_NONE); -} - -TEST_F(TrafficControllerTest, TestPermissionUninstalled) { - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; - - mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids); - expectUidPermissionMapValues(appUids, INetd::PERMISSION_UPDATE_DEVICE_STATS); - expectPrivilegedUserSet(appUids); - - std::vector<uid_t> uidToRemove = {TEST_UID}; - mTc.setPermissionForUids(INetd::PERMISSION_UNINSTALLED, uidToRemove); - - std::vector<uid_t> uidRemain = {TEST_UID3, TEST_UID2}; - expectUidPermissionMapValues(uidRemain, INetd::PERMISSION_UPDATE_DEVICE_STATS); - expectPrivilegedUserSet(uidRemain); - - mTc.setPermissionForUids(INetd::PERMISSION_UNINSTALLED, uidRemain); - expectMapEmpty(mFakeUidPermissionMap); - expectPrivilegedUserSetEmpty(); -} - -TEST_F(TrafficControllerTest, TestGrantUpdateStatsPermission) { - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; - - mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids); - expectUidPermissionMapValues(appUids, INetd::PERMISSION_UPDATE_DEVICE_STATS); - expectPrivilegedUserSet(appUids); - - mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids); - expectPrivilegedUserSetEmpty(); - expectUidPermissionMapValues(appUids, INetd::PERMISSION_NONE); -} - -TEST_F(TrafficControllerTest, TestRevokeUpdateStatsPermission) { - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; - - mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids); - expectPrivilegedUserSet(appUids); - - std::vector<uid_t> uidToRemove = {TEST_UID}; - mTc.setPermissionForUids(INetd::PERMISSION_NONE, uidToRemove); - - std::vector<uid_t> uidRemain = {TEST_UID3, TEST_UID2}; - expectPrivilegedUserSet(uidRemain); - - mTc.setPermissionForUids(INetd::PERMISSION_NONE, uidRemain); - expectPrivilegedUserSetEmpty(); -} - -TEST_F(TrafficControllerTest, TestGrantWrongPermission) { - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; - - mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids); - expectPrivilegedUserSetEmpty(); - expectUidPermissionMapValues(appUids, INetd::PERMISSION_NONE); -} - -TEST_F(TrafficControllerTest, TestGrantDuplicatePermissionSlientlyFail) { - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; - - mTc.setPermissionForUids(INetd::PERMISSION_INTERNET, appUids); - expectMapEmpty(mFakeUidPermissionMap); - - std::vector<uid_t> uidToAdd = {TEST_UID}; - mTc.setPermissionForUids(INetd::PERMISSION_INTERNET, uidToAdd); - - expectPrivilegedUserSetEmpty(); - - mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids); - expectUidPermissionMapValues(appUids, INetd::PERMISSION_NONE); - - mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids); - expectPrivilegedUserSet(appUids); - - mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, uidToAdd); - expectPrivilegedUserSet(appUids); - - mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids); - expectPrivilegedUserSetEmpty(); -} - -TEST_F(TrafficControllerTest, getFirewallType) { - static const struct TestConfig { - ChildChain childChain; - FirewallType firewallType; - } testConfigs[] = { - // clang-format off - {NONE, DENYLIST}, - {DOZABLE, ALLOWLIST}, - {STANDBY, DENYLIST}, - {POWERSAVE, ALLOWLIST}, - {RESTRICTED, ALLOWLIST}, - {LOW_POWER_STANDBY, ALLOWLIST}, - {OEM_DENY_1, DENYLIST}, - {OEM_DENY_2, DENYLIST}, - {OEM_DENY_3, DENYLIST}, - {INVALID_CHAIN, DENYLIST}, - // clang-format on - }; - - for (const auto& config : testConfigs) { - SCOPED_TRACE(fmt::format("testConfig: [{}, {}]", config.childChain, config.firewallType)); - EXPECT_EQ(config.firewallType, mTc.getFirewallType(config.childChain)); - } -} - -constexpr uint32_t SOCK_CLOSE_WAIT_US = 30 * 1000; -constexpr uint32_t ENOBUFS_POLL_WAIT_US = 10 * 1000; - -using android::base::Error; -using android::base::Result; -using android::bpf::BpfMap; - -// This test set up a SkDestroyListener that is running parallel with the production -// SkDestroyListener. The test will create thousands of sockets and tag them on the -// production cookieUidTagMap and close them in a short time. When the number of -// sockets get closed exceeds the buffer size, it will start to return ENOBUFF -// error. The error will be ignored by the production SkDestroyListener and the -// test will clean up the tags in tearDown if there is any remains. - -// TODO: Instead of test the ENOBUFF error, we can test the production -// SkDestroyListener to see if it failed to delete a tagged socket when ENOBUFF -// triggered. -class NetlinkListenerTest : public testing::Test { - protected: - NetlinkListenerTest() {} - BpfMap<uint64_t, UidTagValue> mCookieTagMap; - - void SetUp() { - mCookieTagMap.init(COOKIE_TAG_MAP_PATH); - ASSERT_TRUE(mCookieTagMap.isValid()); - } - - void TearDown() { - const auto deleteTestCookieEntries = [](const uint64_t& key, const UidTagValue& value, - BpfMap<uint64_t, UidTagValue>& map) { - if ((value.uid == TEST_UID) && (value.tag == TEST_TAG)) { - Result<void> res = map.deleteValue(key); - if (res.ok() || (res.error().code() == ENOENT)) { - return Result<void>(); - } - ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s", key, - strerror(res.error().code())); - } - // Move forward to next cookie in the map. - return Result<void>(); - }; - EXPECT_RESULT_OK(mCookieTagMap.iterateWithValue(deleteTestCookieEntries)); - } - - Result<void> checkNoGarbageTagsExist() { - const auto checkGarbageTags = [](const uint64_t&, const UidTagValue& value, - const BpfMap<uint64_t, UidTagValue>&) -> Result<void> { - if ((TEST_UID == value.uid) && (TEST_TAG == value.tag)) { - return Error(EUCLEAN) << "Closed socket is not untagged"; - } - return {}; - }; - return mCookieTagMap.iterateWithValue(checkGarbageTags); - } - - bool checkMassiveSocketDestroy(int totalNumber, bool expectError) { - std::unique_ptr<android::netdutils::NetlinkListenerInterface> skDestroyListener; - auto result = android::net::TrafficController::makeSkDestroyListener(); - if (!isOk(result)) { - ALOGE("Unable to create SkDestroyListener: %s", toString(result).c_str()); - } else { - skDestroyListener = std::move(result.value()); - } - int rxErrorCount = 0; - // Rx handler extracts nfgenmsg looks up and invokes registered dispatch function. - const auto rxErrorHandler = [&rxErrorCount](const int, const int) { rxErrorCount++; }; - skDestroyListener->registerSkErrorHandler(rxErrorHandler); - int fds[totalNumber]; - for (int i = 0; i < totalNumber; i++) { - fds[i] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); - // The likely reason for a failure is running out of available file descriptors. - EXPECT_LE(0, fds[i]) << i << " of " << totalNumber; - if (fds[i] < 0) { - // EXPECT_LE already failed above, so test case is a failure, but we don't - // want potentially tens of thousands of extra failures creating and then - // closing all these fds cluttering up the logs. - totalNumber = i; - break; - }; - libnetd_updatable_tagSocket(fds[i], TEST_TAG, TEST_UID, 1000); - } - - // TODO: Use a separate thread that has its own fd table so we can - // close sockets even faster simply by terminating that thread. - for (int i = 0; i < totalNumber; i++) { - EXPECT_EQ(0, close(fds[i])); - } - // wait a bit for netlink listener to handle all the messages. - usleep(SOCK_CLOSE_WAIT_US); - if (expectError) { - // If ENOBUFS triggered, check it only called into the handler once, ie. - // that the netlink handler is not spinning. - int currentErrorCount = rxErrorCount; - // 0 error count is acceptable because the system has chances to close all sockets - // normally. - EXPECT_LE(0, rxErrorCount); - if (!rxErrorCount) return true; - - usleep(ENOBUFS_POLL_WAIT_US); - EXPECT_EQ(currentErrorCount, rxErrorCount); - } else { - EXPECT_RESULT_OK(checkNoGarbageTagsExist()); - EXPECT_EQ(0, rxErrorCount); - } - return false; - } -}; - -TEST_F(NetlinkListenerTest, TestAllSocketUntagged) { - checkMassiveSocketDestroy(10, false); - checkMassiveSocketDestroy(100, false); -} - -// Disabled because flaky on blueline-userdebug; this test relies on the main thread -// winning a race against the NetlinkListener::run() thread. There's no way to ensure -// things will be scheduled the same way across all architectures and test environments. -TEST_F(NetlinkListenerTest, DISABLED_TestSkDestroyError) { - bool needRetry = false; - int retryCount = 0; - do { - needRetry = checkMassiveSocketDestroy(32500, true); - if (needRetry) retryCount++; - } while (needRetry && retryCount < 3); - // Should review test if it can always close all sockets correctly. - EXPECT_GT(3, retryCount); -} - - -} // namespace net -} // namespace android diff --git a/service/native/include/TrafficController.h b/service/native/include/TrafficController.h deleted file mode 100644 index 86cf50a51cbca027e10084fcee020cfbcecadb92..0000000000000000000000000000000000000000 --- a/service/native/include/TrafficController.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <set> -#include <Common.h> - -#include "android-base/thread_annotations.h" -#include "bpf/BpfMap.h" -#include "netd.h" -#include "netdutils/NetlinkListener.h" -#include "netdutils/StatusOr.h" - -namespace android { -namespace net { - -using netdutils::StatusOr; - -class TrafficController { - public: - /* - * Initialize the whole controller - */ - netdutils::Status start(bool startSkDestroyListener); - - /* - * Swap the stats map config from current active stats map to the idle one. - */ - netdutils::Status swapActiveStatsMap() EXCLUDES(mMutex); - - int changeUidOwnerRule(ChildChain chain, const uid_t uid, FirewallRule rule, FirewallType type); - - int removeUidOwnerRule(const uid_t uid); - - int replaceUidOwnerMap(const std::string& name, bool isAllowlist, - const std::vector<int32_t>& uids); - - enum IptOp { IptOpInsert, IptOpDelete }; - - netdutils::Status updateOwnerMapEntry(UidOwnerMatchType match, uid_t uid, FirewallRule rule, - FirewallType type) EXCLUDES(mMutex); - - netdutils::Status replaceRulesInMap(UidOwnerMatchType match, const std::vector<int32_t>& uids) - EXCLUDES(mMutex); - - netdutils::Status addUidInterfaceRules(const int ifIndex, const std::vector<int32_t>& uids) - EXCLUDES(mMutex); - netdutils::Status removeUidInterfaceRules(const std::vector<int32_t>& uids) EXCLUDES(mMutex); - - netdutils::Status updateUidLockdownRule(const uid_t uid, const bool add) EXCLUDES(mMutex); - - netdutils::Status updateUidOwnerMap(const uint32_t uid, - UidOwnerMatchType matchType, IptOp op) EXCLUDES(mMutex); - - int toggleUidOwnerMap(ChildChain chain, bool enable) EXCLUDES(mMutex); - - static netdutils::StatusOr<std::unique_ptr<netdutils::NetlinkListenerInterface>> - makeSkDestroyListener(); - - void setPermissionForUids(int permission, const std::vector<uid_t>& uids) EXCLUDES(mMutex); - - FirewallType getFirewallType(ChildChain); - - static const char* LOCAL_DOZABLE; - static const char* LOCAL_STANDBY; - static const char* LOCAL_POWERSAVE; - static const char* LOCAL_RESTRICTED; - static const char* LOCAL_LOW_POWER_STANDBY; - static const char* LOCAL_OEM_DENY_1; - static const char* LOCAL_OEM_DENY_2; - static const char* LOCAL_OEM_DENY_3; - - private: - /* - * mCookieTagMap: Store the corresponding tag and uid for a specific socket. - * DO NOT hold any locks when modifying this map, otherwise when the untag - * operation is waiting for a lock hold by other process and there are more - * sockets being closed than can fit in the socket buffer of the netlink socket - * that receives them, then the kernel will drop some of these sockets and we - * won't delete their tags. - * Map Key: uint64_t socket cookie - * Map Value: UidTagValue, contains a uint32 uid and a uint32 tag. - */ - bpf::BpfMap<uint64_t, UidTagValue> mCookieTagMap GUARDED_BY(mMutex); - - /* - * mUidCounterSetMap: Store the counterSet of a specific uid. - * Map Key: uint32 uid. - * Map Value: uint32 counterSet specifies if the traffic is a background - * or foreground traffic. - */ - bpf::BpfMap<uint32_t, uint8_t> mUidCounterSetMap GUARDED_BY(mMutex); - - /* - * mAppUidStatsMap: Store the total traffic stats for a uid regardless of - * tag, counterSet and iface. The stats is used by TrafficStats.getUidStats - * API to return persistent stats for a specific uid since device boot. - */ - bpf::BpfMap<uint32_t, StatsValue> mAppUidStatsMap; - - /* - * mStatsMapA/mStatsMapB: Store the traffic statistics for a specific - * combination of uid, tag, iface and counterSet. These two maps contain - * both tagged and untagged traffic. - * Map Key: StatsKey contains the uid, tag, counterSet and ifaceIndex - * information. - * Map Value: Stats, contains packet count and byte count of each - * transport protocol on egress and ingress direction. - */ - bpf::BpfMap<StatsKey, StatsValue> mStatsMapA GUARDED_BY(mMutex); - - bpf::BpfMap<StatsKey, StatsValue> mStatsMapB GUARDED_BY(mMutex); - - /* - * mIfaceIndexNameMap: Store the index name pair of each interface show up - * on the device since boot. The interface index is used by the eBPF program - * to correctly match the iface name when receiving a packet. - */ - bpf::BpfMap<uint32_t, IfaceValue> mIfaceIndexNameMap; - - /* - * mIfaceStataMap: Store per iface traffic stats gathered from xt_bpf - * filter. - */ - bpf::BpfMap<uint32_t, StatsValue> mIfaceStatsMap; - - /* - * mConfigurationMap: Store the current network policy about uid filtering - * and the current stats map in use. There are two configuration entries in - * the map right now: - * - Entry with UID_RULES_CONFIGURATION_KEY: - * Store the configuration for the current uid rules. It indicates the device - * is in doze/powersave/standby/restricted/low power standby/oem deny mode. - * - Entry with CURRENT_STATS_MAP_CONFIGURATION_KEY: - * Stores the current live stats map that kernel program is writing to. - * Userspace can do scraping and cleaning job on the other one depending on the - * current configs. - */ - bpf::BpfMap<uint32_t, uint32_t> mConfigurationMap GUARDED_BY(mMutex); - - /* - * mUidOwnerMap: Store uids that are used for bandwidth control uid match. - */ - bpf::BpfMap<uint32_t, UidOwnerValue> mUidOwnerMap GUARDED_BY(mMutex); - - /* - * mUidOwnerMap: Store uids that are used for INTERNET permission check. - */ - bpf::BpfMap<uint32_t, uint8_t> mUidPermissionMap GUARDED_BY(mMutex); - - std::unique_ptr<netdutils::NetlinkListenerInterface> mSkDestroyListener; - - netdutils::Status removeRule(uint32_t uid, UidOwnerMatchType match) REQUIRES(mMutex); - - netdutils::Status addRule(uint32_t uid, UidOwnerMatchType match, uint32_t iif = 0) - REQUIRES(mMutex); - - std::mutex mMutex; - - netdutils::Status initMaps() EXCLUDES(mMutex); - - // Keep track of uids that have permission UPDATE_DEVICE_STATS so we don't - // need to call back to system server for permission check. - std::set<uid_t> mPrivilegedUser GUARDED_BY(mMutex); - - // For testing - friend class TrafficControllerTest; -}; - -} // namespace net -} // namespace android