diff options
author | Ken Chen <cken@google.com> | 2021-03-17 01:57:19 +0800 |
---|---|---|
committer | Ken Chen <cken@google.com> | 2021-03-26 10:32:49 +0800 |
commit | 4e8ef9b24e5f5c1f9760f593e2e022750c314f5e (patch) | |
tree | 27b326d21ae707a076d24c7b3277d39dae9928c7 | |
parent | b573648fce613ecd94dce54a8744e6e06544856c (diff) | |
download | netd-4e8ef9b24e5f5c1f9760f593e2e022750c314f5e.tar.gz |
PANS - Support unreachable default network
Framework provides several preferences in PANS feature. To meet those
preferences, Netd needs to support two operations for framework:
(1) Set OEM-paid network as default network for apps.
(2) Prohibit apps to use default network if it is not explicitly
selected.
The #1 is supported by previous commit already. This commit implements
the #2, which adds a new IP rule priority for unconnected socket, reuses
existing IP rule priorities in explicit and implicit network selection.
Rules are looks like:
15000: from all fwmark 0x10034/0x1ffff iif lo uidrange x-y unreachable
...
22000: from all fwmark 0x34/0x1ffff iif lo uidrange x-y unreachable
...
27000: from all fwmark 0x0/0xffff iif lo uidrange x-y unreachable
An UNREACHABLE network (netId 52) is created for framework to specify
that the default network is unavailable for designated apps.
Bug: 181579204
Test: atest
Change-Id: I21530928a85870df673e2d1387fde130fe5a0104
21 files changed, 510 insertions, 48 deletions
diff --git a/server/Android.bp b/server/Android.bp index 5cb4d77c..4b5f6137 100644 --- a/server/Android.bp +++ b/server/Android.bp @@ -273,6 +273,7 @@ cc_binary { "PhysicalNetwork.cpp", "PppController.cpp", "Process.cpp", + "UnreachableNetwork.cpp", "VirtualNetwork.cpp", "main.cpp", "oem_iptables_hook.cpp", diff --git a/server/DummyNetwork.cpp b/server/DummyNetwork.cpp index 8ce2826f..e6103a15 100644 --- a/server/DummyNetwork.cpp +++ b/server/DummyNetwork.cpp @@ -18,9 +18,6 @@ #include "DummyNetwork.h" -#include "RouteController.h" - -#include "log/log.h" #include "errno.h" namespace android { diff --git a/server/Network.cpp b/server/Network.cpp index ca53ccaa..8179d8b5 100644 --- a/server/Network.cpp +++ b/server/Network.cpp @@ -74,6 +74,9 @@ std::string Network::toString() const { case PHYSICAL: repr << "PHYSICAL"; break; + case UNREACHABLE: + repr << "UNREACHABLE"; + break; case VIRTUAL: repr << "VIRTUAL"; break; diff --git a/server/Network.h b/server/Network.h index e7384f9e..b12c67b7 100644 --- a/server/Network.h +++ b/server/Network.h @@ -31,6 +31,7 @@ public: DUMMY, LOCAL, PHYSICAL, + UNREACHABLE, VIRTUAL, }; @@ -56,7 +57,9 @@ public: [[nodiscard]] virtual int removeUsers(const UidRanges&) { return -EINVAL; }; bool isSecure() const; bool isPhysical() { return getType() == PHYSICAL; } + bool isUnreachable() { return getType() == UNREACHABLE; } bool isVirtual() { return getType() == VIRTUAL; } + bool canAddUsers() { return isPhysical() || isVirtual() || isUnreachable(); } protected: explicit Network(unsigned netId, bool mSecure = false); diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp index 9e13d454..65658a52 100644 --- a/server/NetworkController.cpp +++ b/server/NetworkController.cpp @@ -39,6 +39,7 @@ #include "OffloadUtils.h" #include "PhysicalNetwork.h" #include "RouteController.h" +#include "UnreachableNetwork.h" #include "VirtualNetwork.h" #include "netdutils/DumpWriter.h" #include "netid_client.h" @@ -142,6 +143,7 @@ NetworkController::NetworkController() : mProtectableUsers({AID_VPN}) { mNetworks[LOCAL_NET_ID] = new LocalNetwork(LOCAL_NET_ID); mNetworks[DUMMY_NET_ID] = new DummyNetwork(DUMMY_NET_ID); + mNetworks[UNREACHABLE_NET_ID] = new UnreachableNetwork(UNREACHABLE_NET_ID); // Clear all clsact stubs on all interfaces. // TODO: perhaps only remove the clsact on the interface which is added by @@ -205,7 +207,7 @@ uint32_t NetworkController::getNetworkForDnsLocked(unsigned* netId, uid_t uid) c fwmark.protectedFromVpn = true; fwmark.permission = PERMISSION_SYSTEM; - PhysicalNetwork* appDefaultNetwork = getPhysicalNetworkForUserLocked(uid); + Network* appDefaultNetwork = getPhysicalOrUnreachableNetworkForUserLocked(uid); unsigned defaultNetId = appDefaultNetwork ? appDefaultNetwork->getNetId() : mDefaultNetId; // Common case: there is no VPN that applies to the user, and the query did not specify a netId. @@ -252,15 +254,15 @@ uint32_t NetworkController::getNetworkForDnsLocked(unsigned* netId, uid_t uid) c } // Returns the NetId that a given UID would use if no network is explicitly selected. Specifically, -// the VPN that applies to the UID if any; otherwise, the default network for UID; lastly, the -// default network. +// the VPN that applies to the UID if any; Otherwise, the unreachable network that applies to the +// UID; Otherwise, the default network for UID; lastly, the default network. unsigned NetworkController::getNetworkForUser(uid_t uid) const { ScopedRLock lock(mRWLock); if (VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid)) { return virtualNetwork->getNetId(); } - if (PhysicalNetwork* physicalNetwork = getPhysicalNetworkForUserLocked(uid)) { - return physicalNetwork->getNetId(); + if (Network* network = getPhysicalOrUnreachableNetworkForUserLocked(uid)) { + return network->getNetId(); } return mDefaultNetId; } @@ -292,8 +294,8 @@ unsigned NetworkController::getNetworkForConnectLocked(uid_t uid) const { if (virtualNetwork && !virtualNetwork->isSecure()) { return virtualNetwork->getNetId(); } - if (PhysicalNetwork* physicalNetwork = getPhysicalNetworkForUserLocked(uid)) { - return physicalNetwork->getNetId(); + if (Network* network = getPhysicalOrUnreachableNetworkForUserLocked(uid)) { + return network->getNetId(); } return mDefaultNetId; } @@ -456,8 +458,8 @@ int NetworkController::createVirtualNetwork(unsigned netId, bool secure) { int NetworkController::destroyNetwork(unsigned netId) { ScopedWLock lock(mRWLock); - if (netId == LOCAL_NET_ID) { - ALOGE("cannot destroy local network"); + if (netId == LOCAL_NET_ID || netId == UNREACHABLE_NET_ID) { + ALOGE("cannot destroy local or unreachable network"); return -EINVAL; } if (!isValidNetworkLocked(netId)) { @@ -597,7 +599,7 @@ int isWrongNetworkForUidRanges(unsigned netId, Network* network) { ALOGE("no such netId %u", netId); return -ENONET; } - if (!network->isVirtual() && !network->isPhysical()) { + if (!network->canAddUsers()) { ALOGE("cannot add/remove users to/from network %u, type %d", netId, network->getType()); return -EINVAL; } @@ -774,13 +776,19 @@ VirtualNetwork* NetworkController::getVirtualNetworkForUserLocked(uid_t uid) con return nullptr; } -PhysicalNetwork* NetworkController::getPhysicalNetworkForUserLocked(uid_t uid) const { +Network* NetworkController::getPhysicalOrUnreachableNetworkForUserLocked(uid_t uid) const { + // Unreachable network take precedence over OEM-paid network. + auto iter = mNetworks.find(UNREACHABLE_NET_ID); + if (iter != mNetworks.end() && iter->second->appliesToUser(uid)) { + return iter->second; + } + for (const auto& [_, network] : mNetworks) { if (network->isPhysical() && network->appliesToUser(uid)) { // Return the first physical network that matches UID. // If there is more than one such network, the behaviour is undefined. // This is a configuration error. - return static_cast<PhysicalNetwork*>(network); + return network; } } return nullptr; @@ -821,6 +829,11 @@ int NetworkController::checkUserNetworkAccessLocked(uid_t uid, unsigned netId) c mProtectableUsers.find(uid) == mProtectableUsers.end()) { return -EPERM; } + // Anyone can use unreachable network if they want. That being said, PANS should be the only + // user so far. + if (network->isUnreachable()) { + return 0; + } // If the UID wants to use a physical network and it has a UID range that includes the UID, the // UID has permission to use it regardless of whether the permission bits match. if (network->isPhysical() && network->appliesToUser(uid)) { diff --git a/server/NetworkController.h b/server/NetworkController.h index a7871556..271f3e58 100644 --- a/server/NetworkController.h +++ b/server/NetworkController.h @@ -23,6 +23,7 @@ #include "NetdConstants.h" #include "Permission.h" #include "PhysicalNetwork.h" +#include "UnreachableNetwork.h" #include "android/net/INetd.h" #include "netdutils/DumpWriter.h" @@ -83,11 +84,12 @@ class VirtualNetwork; */ class NetworkController { public: - // NetIds 52..98 are reserved for future use. + // NetIds 53..98 are reserved for future use. static constexpr int MIN_OEM_ID = 1; static constexpr int MAX_OEM_ID = 50; static constexpr int LOCAL_NET_ID = INetd::LOCAL_NET_ID; static constexpr int DUMMY_NET_ID = INetd::DUMMY_NET_ID; + static constexpr int UNREACHABLE_NET_ID = INetd::UNREACHABLE_NET_ID; // Route mode for modify route enum RouteOperation { ROUTE_ADD, ROUTE_UPDATE, ROUTE_REMOVE }; @@ -159,7 +161,7 @@ public: bool canProtectLocked(uid_t uid) const; bool isVirtualNetworkLocked(unsigned netId) const; VirtualNetwork* getVirtualNetworkForUserLocked(uid_t uid) const; - PhysicalNetwork* getPhysicalNetworkForUserLocked(uid_t uid) const; + Network* getPhysicalOrUnreachableNetworkForUserLocked(uid_t uid) const; Permission getPermissionForUserLocked(uid_t uid) const; int checkUserNetworkAccessLocked(uid_t uid, unsigned netId) const; [[nodiscard]] int createPhysicalNetworkLocked(unsigned netId, Permission permission); diff --git a/server/RouteController.cpp b/server/RouteController.cpp index dad824f9..666a88a7 100644 --- a/server/RouteController.cpp +++ b/server/RouteController.cpp @@ -850,6 +850,71 @@ int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface return 0; } +[[nodiscard]] static int modifyUidUnreachableRule(unsigned netId, uid_t uidStart, uid_t uidEnd, + bool add, bool explicitSelect) { + if ((uidStart == INVALID_UID) || (uidEnd == INVALID_UID)) { + ALOGE("modifyUidUnreachableRule, invalid UIDs (%u, %u)", uidStart, uidEnd); + return -EUSERS; + } + + Fwmark fwmark; + Fwmark mask; + + fwmark.netId = netId; + mask.netId = FWMARK_NET_ID_MASK; + + fwmark.explicitlySelected = explicitSelect; + mask.explicitlySelected = true; + + // Access to this network is controlled by UID rules, not permission bits. + fwmark.permission = PERMISSION_NONE; + mask.permission = PERMISSION_NONE; + + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, + explicitSelect ? RULE_PRIORITY_UID_EXPLICIT_NETWORK + : RULE_PRIORITY_UID_IMPLICIT_NETWORK, + FR_ACT_UNREACHABLE, RT_TABLE_UNSPEC, fwmark.intValue, mask.intValue, + IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd); +} + +[[nodiscard]] static int modifyUidDefaultUnreachableRule(uid_t uidStart, uid_t uidEnd, bool add) { + if ((uidStart == INVALID_UID) || (uidEnd == INVALID_UID)) { + ALOGE("modifyUidDefaultNetworkRule, invalid UIDs (%u, %u)", uidStart, uidEnd); + return -EUSERS; + } + + Fwmark fwmark; + Fwmark mask; + + fwmark.netId = NETID_UNSET; + mask.netId = FWMARK_NET_ID_MASK; + + // Access to this network is controlled by UID rules, not permission bits. + fwmark.permission = PERMISSION_NONE; + mask.permission = PERMISSION_NONE; + + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_UID_DEFAULT_UNREACHABLE, + FR_ACT_UNREACHABLE, RT_TABLE_UNSPEC, fwmark.intValue, mask.intValue, + IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd); +} + +int RouteController::modifyUnreachableNetwork(unsigned netId, const UidRanges& uidRanges, + bool add) { + for (const UidRangeParcel& range : uidRanges.getRanges()) { + if (int ret = modifyUidUnreachableRule(netId, range.start, range.stop, add, EXPLICIT)) { + return ret; + } + if (int ret = modifyUidUnreachableRule(netId, range.start, range.stop, add, IMPLICIT)) { + return ret; + } + if (int ret = modifyUidDefaultUnreachableRule(range.start, range.stop, add)) { + return ret; + } + } + + return 0; +} + [[nodiscard]] static int modifyRejectNonSecureNetworkRule(const UidRanges& uidRanges, bool add) { Fwmark fwmark; Fwmark mask; @@ -1243,6 +1308,14 @@ int RouteController::removeUsersFromPhysicalNetwork(unsigned netId, const char* !MODIFY_NON_UID_BASED_RULES); } +int RouteController::addUsersToUnreachableNetwork(unsigned netId, const UidRanges& uidRanges) { + return modifyUnreachableNetwork(netId, uidRanges, ACTION_ADD); +} + +int RouteController::removeUsersFromUnreachableNetwork(unsigned netId, const UidRanges& uidRanges) { + return modifyUnreachableNetwork(netId, uidRanges, ACTION_DEL); +} + // Protects sInterfaceToTable. std::mutex RouteController::sInterfaceToTableLock; std::map<std::string, uint32_t> RouteController::sInterfaceToTable; diff --git a/server/RouteController.h b/server/RouteController.h index 8495462c..a6a8bb03 100644 --- a/server/RouteController.h +++ b/server/RouteController.h @@ -29,11 +29,11 @@ namespace android::net { // clang-format off -const uint32_t RULE_PRIORITY_VPN_OVERRIDE_SYSTEM = 10000; -const uint32_t RULE_PRIORITY_VPN_OVERRIDE_OIF = 11000; -const uint32_t RULE_PRIORITY_VPN_OUTPUT_TO_LOCAL = 12000; -const uint32_t RULE_PRIORITY_SECURE_VPN = 13000; -const uint32_t RULE_PRIORITY_PROHIBIT_NON_VPN = 14000; +const uint32_t RULE_PRIORITY_VPN_OVERRIDE_SYSTEM = 10000; +const uint32_t RULE_PRIORITY_VPN_OVERRIDE_OIF = 11000; +const uint32_t RULE_PRIORITY_VPN_OUTPUT_TO_LOCAL = 12000; +const uint32_t RULE_PRIORITY_SECURE_VPN = 13000; +const uint32_t RULE_PRIORITY_PROHIBIT_NON_VPN = 14000; // Rules used when applications explicitly select a network that they have permission to use only // because they are in the list of UID ranges for that network. // @@ -41,23 +41,35 @@ const uint32_t RULE_PRIORITY_PROHIBIT_NON_VPN = 14000; // not have the necessary permission bits in the fwmark. We cannot just give any socket on any of // these networks the permission bits, because if the UID that created the socket loses access to // the network, then the socket must not match any rule that selects that network. -const uint32_t RULE_PRIORITY_UID_EXPLICIT_NETWORK = 15000; -const uint32_t RULE_PRIORITY_EXPLICIT_NETWORK = 16000; -const uint32_t RULE_PRIORITY_OUTPUT_INTERFACE = 17000; -const uint32_t RULE_PRIORITY_LEGACY_SYSTEM = 18000; -const uint32_t RULE_PRIORITY_LEGACY_NETWORK = 19000; -const uint32_t RULE_PRIORITY_LOCAL_NETWORK = 20000; -const uint32_t RULE_PRIORITY_TETHERING = 21000; +const uint32_t RULE_PRIORITY_UID_EXPLICIT_NETWORK = 15000; +const uint32_t RULE_PRIORITY_EXPLICIT_NETWORK = 16000; +const uint32_t RULE_PRIORITY_OUTPUT_INTERFACE = 17000; +const uint32_t RULE_PRIORITY_LEGACY_SYSTEM = 18000; +const uint32_t RULE_PRIORITY_LEGACY_NETWORK = 19000; +const uint32_t RULE_PRIORITY_LOCAL_NETWORK = 20000; +const uint32_t RULE_PRIORITY_TETHERING = 21000; // Implicit rules for sockets that connected on a given network because the network was the default // network for the UID. -const uint32_t RULE_PRIORITY_UID_IMPLICIT_NETWORK = 22000; -const uint32_t RULE_PRIORITY_IMPLICIT_NETWORK = 23000; -const uint32_t RULE_PRIORITY_BYPASSABLE_VPN = 24000; -// reserved for RULE_PRIORITY_UID_VPN_FALLTHROUGH = 25000; -const uint32_t RULE_PRIORITY_VPN_FALLTHROUGH = 26000; -const uint32_t RULE_PRIORITY_UID_DEFAULT_NETWORK = 27000; -const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 28000; -const uint32_t RULE_PRIORITY_UNREACHABLE = 32000; +const uint32_t RULE_PRIORITY_UID_IMPLICIT_NETWORK = 22000; +const uint32_t RULE_PRIORITY_IMPLICIT_NETWORK = 23000; +const uint32_t RULE_PRIORITY_BYPASSABLE_VPN = 24000; +// reserved for RULE_PRIORITY_UID_VPN_FALLTHROUGH = 25000; +const uint32_t RULE_PRIORITY_VPN_FALLTHROUGH = 26000; +// Rule used when framework wants to disable default network from specified applications. There will +// be a small interval the same uid range exists in both UID_DEFAULT_UNREACHABLE and +// UID_DEFAULT_NETWORK when framework is switching user preferences. +// +// framework --> netd +// step 1: set uid to unreachable network +// step 2: remove uid from OEM-paid network list +// +// We have this priority so that the default network for apps will be blocked right after step 1. +// The rule also provides flexibility, just in case we need to support the same uid constantly +// exists in both UID_DEFAULT_UNREACHABLE and UID_DEFAULT_NETWORK in the future. +const uint32_t RULE_PRIORITY_UID_DEFAULT_UNREACHABLE = 27000; +const uint32_t RULE_PRIORITY_UID_DEFAULT_NETWORK = 28000; +const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 29000; +const uint32_t RULE_PRIORITY_UNREACHABLE = 32000; // clang-format on class UidRanges; @@ -148,6 +160,12 @@ public: [[nodiscard]] static int removeUsersFromPhysicalNetwork(unsigned netId, const char* interface, const UidRanges& uidRanges); + [[nodiscard]] static int addUsersToUnreachableNetwork(unsigned netId, + const UidRanges& uidRanges); + + [[nodiscard]] static int removeUsersFromUnreachableNetwork(unsigned netId, + const UidRanges& uidRanges); + // For testing. static int (*iptablesRestoreCommandFunction)(IptablesTarget, const std::string&, const std::string&, std::string *); @@ -168,6 +186,7 @@ private: static int modifyPhysicalNetwork(unsigned netId, const char* interface, const UidRanges& uidRanges, Permission permission, bool add, bool modifyNonUidBasedRules); + static int modifyUnreachableNetwork(unsigned netId, const UidRanges& uidRanges, bool add); static int modifyRoute(uint16_t action, uint16_t flags, const char* interface, const char* destination, const char* nexthop, TableType tableType, int mtu); diff --git a/server/UnreachableNetwork.cpp b/server/UnreachableNetwork.cpp new file mode 100644 index 00000000..0fb556b9 --- /dev/null +++ b/server/UnreachableNetwork.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 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 "Netd" + +#include "UnreachableNetwork.h" + +#include "RouteController.h" + +namespace android { +namespace net { + +// The unreachable network is used to reject traffic. It is used for system purposes only. +UnreachableNetwork::UnreachableNetwork(unsigned netId) : Network(netId) {} + +int UnreachableNetwork::addUsers(const UidRanges& uidRanges) { + if (hasInvalidUidRanges(uidRanges)) { + return -EINVAL; + } + + int ret = RouteController::addUsersToUnreachableNetwork(mNetId, uidRanges); + if (ret) { + ALOGE("failed to add users to unreachable network"); + return ret; + } + mUidRanges.add(uidRanges); + return 0; +} + +int UnreachableNetwork::removeUsers(const UidRanges& uidRanges) { + int ret = RouteController::removeUsersFromUnreachableNetwork(mNetId, uidRanges); + if (ret) { + ALOGE("failed to remove users from unreachable network"); + return ret; + } + mUidRanges.remove(uidRanges); + return 0; +} + +Network::Type UnreachableNetwork::getType() const { + return UNREACHABLE; +} + +} // namespace net +} // namespace android diff --git a/server/UnreachableNetwork.h b/server/UnreachableNetwork.h new file mode 100644 index 00000000..c0da5466 --- /dev/null +++ b/server/UnreachableNetwork.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 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 "Network.h" + +namespace android::net { + +class UnreachableNetwork : public Network { + public: + explicit UnreachableNetwork(unsigned netId); + [[nodiscard]] int addUsers(const UidRanges& uidRanges) override; + [[nodiscard]] int removeUsers(const UidRanges& uidRanges) override; + + private: + Type getType() const override; +}; + +} // namespace android::net
\ No newline at end of file diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl index a0d85a31..99a36e6e 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2016, 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// @@ -138,6 +153,7 @@ interface INetd { const int PENALTY_POLICY_REJECT = 3; const int LOCAL_NET_ID = 99; const int DUMMY_NET_ID = 51; + const int UNREACHABLE_NET_ID = 52; const String NEXTHOP_NONE = ""; const String NEXTHOP_UNREACHABLE = "unreachable"; const String NEXTHOP_THROW = "throw"; @@ -148,10 +164,14 @@ interface INetd { const int PERMISSION_INTERNET = 4; const int PERMISSION_UPDATE_DEVICE_STATS = 8; const int PERMISSION_UNINSTALLED = -1; - /* @deprecated use FIREWALL_ALLOWLIST. */ + /** + * @deprecated use FIREWALL_ALLOWLIST. + */ const int FIREWALL_WHITELIST = 0; const int FIREWALL_ALLOWLIST = 0; - /* @deprecated use FIREWALL_DENYLIST. */ + /** + * @deprecated use FIREWALL_DENYLIST. + */ const int FIREWALL_BLACKLIST = 1; const int FIREWALL_DENYLIST = 1; const int FIREWALL_RULE_ALLOW = 1; diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl index d86f4984..31775dfd 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2018, 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl index 95607576..1869d8d4 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2018 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl index 74930914..8ea20d11 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2019 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl index 691a77e6..5ef95e67 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2020, 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl index b568f4ad..7b39c22e 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2019 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl index a8e0a116..983e9860 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2020 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl index 9c268dff..5f1b7226 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2018 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl index 6ab3eee7..72e987a2 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2018 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl index b84d6913..ee263933 100644 --- a/server/binder/android/net/INetd.aidl +++ b/server/binder/android/net/INetd.aidl @@ -779,6 +779,14 @@ interface INetd { */ const int DUMMY_NET_ID = 51; + /** + * Constant net ID for the "unreachable" network. + * + * The unreachable network is used to reject traffic. Any attempt to use it will fail + * with ENETUNREACH. + */ + const int UNREACHABLE_NET_ID = 52; + // Route does not specify a next hop const String NEXTHOP_NONE = ""; // Route next hop is unreachable diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp index e8be7fa6..cfff6407 100644 --- a/tests/binder_test.cpp +++ b/tests/binder_test.cpp @@ -112,6 +112,7 @@ using android::net::RULE_PRIORITY_PROHIBIT_NON_VPN; using android::net::RULE_PRIORITY_SECURE_VPN; using android::net::RULE_PRIORITY_TETHERING; using android::net::RULE_PRIORITY_UID_DEFAULT_NETWORK; +using android::net::RULE_PRIORITY_UID_DEFAULT_UNREACHABLE; using android::net::RULE_PRIORITY_UID_EXPLICIT_NETWORK; using android::net::RULE_PRIORITY_UID_IMPLICIT_NETWORK; using android::net::RULE_PRIORITY_VPN_FALLTHROUGH; @@ -269,6 +270,7 @@ void testNetworkExistsButCannotConnect(const sp<INetd>& netd, const int netId) { TEST_F(NetdBinderTest, InitialNetworksExist) { testNetworkExistsButCannotConnect(mNetd, INetd::DUMMY_NET_ID); testNetworkExistsButCannotConnect(mNetd, INetd::LOCAL_NET_ID); + testNetworkExistsButCannotConnect(mNetd, INetd::UNREACHABLE_NET_ID); } TEST_F(NetdBinderTest, IpSecTunnelInterface) { @@ -3187,6 +3189,37 @@ bool sendIPv6PacketFromUid(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, i return true; } +// Send an IPv6 packet from the uid. Expect to fail and get specified errno. +bool sendIPv6PacketFromUidFail(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, bool doConnect, + int expectedErr) { + ScopedUidChange scopedUidChange(uid); + unique_fd s(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)); + if (s < 0) return false; + + const sockaddr_in6 dst6 = { + .sin6_family = AF_INET6, + .sin6_port = 42, + .sin6_addr = dstAddr, + }; + if (doConnect) { + if (connect(s, (sockaddr*)&dst6, sizeof(dst6)) == 0) return false; + if (errno != expectedErr) return false; + } + + socklen_t fwmarkLen = sizeof(fwmark->intValue); + EXPECT_NE(-1, getsockopt(s, SOL_SOCKET, SO_MARK, &(fwmark->intValue), &fwmarkLen)); + + char addr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &dstAddr, addr, INET6_ADDRSTRLEN); + SCOPED_TRACE(StringPrintf("sendIPv6PacketFail, addr: %s, uid: %u, doConnect: %s", addr, uid, + doConnect ? "true" : "false")); + if (!doConnect) { + if (sendto(s, "foo", sizeof("foo"), 0, (sockaddr*)&dst6, sizeof(dst6)) == 0) return false; + if (errno != expectedErr) return false; + } + return true; +} + void expectVpnFallthroughRuleExists(const std::string& ifName, int vpnNetId) { std::string vpnFallthroughRule = StringPrintf("%d:\tfrom all fwmark 0x%x/0xffff lookup %s", @@ -3761,15 +3794,27 @@ namespace { void verifyAppUidRules(std::vector<bool>&& expectedResults, std::vector<UidRangeParcel>& uidRanges, const std::string& iface) { ASSERT_EQ(expectedResults.size(), uidRanges.size()); - std::string action = StringPrintf("lookup %s ", iface.c_str()); - - for (unsigned long i = 0; i < uidRanges.size(); i++) { - EXPECT_EQ(expectedResults[i], - ipRuleExistsForRange(RULE_PRIORITY_UID_EXPLICIT_NETWORK, uidRanges[i], action)); - EXPECT_EQ(expectedResults[i], - ipRuleExistsForRange(RULE_PRIORITY_UID_IMPLICIT_NETWORK, uidRanges[i], action)); - EXPECT_EQ(expectedResults[i], - ipRuleExistsForRange(RULE_PRIORITY_UID_DEFAULT_NETWORK, uidRanges[i], action)); + if (iface.size()) { + std::string action = StringPrintf("lookup %s ", iface.c_str()); + for (unsigned long i = 0; i < uidRanges.size(); i++) { + EXPECT_EQ(expectedResults[i], ipRuleExistsForRange(RULE_PRIORITY_UID_EXPLICIT_NETWORK, + uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], ipRuleExistsForRange(RULE_PRIORITY_UID_IMPLICIT_NETWORK, + uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], ipRuleExistsForRange(RULE_PRIORITY_UID_DEFAULT_NETWORK, + uidRanges[i], action)); + } + } else { + std::string action = "unreachable"; + for (unsigned long i = 0; i < uidRanges.size(); i++) { + EXPECT_EQ(expectedResults[i], ipRuleExistsForRange(RULE_PRIORITY_UID_EXPLICIT_NETWORK, + uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], ipRuleExistsForRange(RULE_PRIORITY_UID_IMPLICIT_NETWORK, + uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], + ipRuleExistsForRange(RULE_PRIORITY_UID_DEFAULT_UNREACHABLE, uidRanges[i], + action)); + } } } @@ -3806,6 +3851,28 @@ void expectPacketSentOnNetId(uid_t uid, unsigned netId, int fd, int selectionMod EXPECT_EQ(expected.intValue, fwmark.intValue); } +void expectUnreachableError(uid_t uid, unsigned netId, int selectionMode) { + Fwmark fwmark; + const bool doConnect = (selectionMode != UNCONNECTED_SOCKET); + EXPECT_TRUE(sendIPv6PacketFromUidFail(uid, V6_ADDR, &fwmark, doConnect, ENETUNREACH)); + + Fwmark expected; + expected.netId = netId; + expected.explicitlySelected = (selectionMode == EXPLICITLY_SELECT); + if (uid == AID_ROOT && selectionMode == EXPLICITLY_SELECT) { + expected.protectedFromVpn = true; + } else { + expected.protectedFromVpn = false; + } + if (selectionMode == UNCONNECTED_SOCKET) { + expected.permission = PERMISSION_NONE; + } else { + expected.permission = (uid == AID_ROOT) ? PERMISSION_SYSTEM : PERMISSION_NONE; + } + + EXPECT_EQ(expected.intValue, fwmark.intValue); +} + } // namespace // Verify whether API reject overlapped UID ranges @@ -3865,6 +3932,13 @@ TEST_F(NetdBinderTest, PerAppDefaultNetwork_VerifyIpRules) { verifyAppUidRules({false, true} /*expectedResults*/, uidRanges, sTun.name()); EXPECT_TRUE(mNetd->networkRemoveUidRanges(APP_DEFAULT_NETID, {uidRanges.at(1)}).isOk()); verifyAppUidRules({false, false} /*expectedResults*/, uidRanges, sTun.name()); + + EXPECT_TRUE(mNetd->networkAddUidRanges(INetd::UNREACHABLE_NET_ID, uidRanges).isOk()); + verifyAppUidRules({true, true} /*expectedResults*/, uidRanges, ""); + EXPECT_TRUE(mNetd->networkRemoveUidRanges(INetd::UNREACHABLE_NET_ID, {uidRanges.at(0)}).isOk()); + verifyAppUidRules({false, true} /*expectedResults*/, uidRanges, ""); + EXPECT_TRUE(mNetd->networkRemoveUidRanges(INetd::UNREACHABLE_NET_ID, {uidRanges.at(1)}).isOk()); + verifyAppUidRules({false, false} /*expectedResults*/, uidRanges, ""); } // Verify whether packets go through the right network with and without per-app default network. @@ -3896,6 +3970,18 @@ TEST_F(NetdBinderTest, PerAppDefaultNetwork_ImplicitlySelectNetwork) { .isOk()); expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); expectPacketSentOnNetId(TEST_UID1, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); + + // Prohibit TEST_UID1 from using the default network. + EXPECT_TRUE(mNetd->networkAddUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); + expectUnreachableError(TEST_UID1, INetd::UNREACHABLE_NET_ID, IMPLICITLY_SELECT); + + // restore IP rules + EXPECT_TRUE(mNetd->networkRemoveUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); } // Verify whether packets go through the right network when app explicitly selects a network. @@ -3911,6 +3997,19 @@ TEST_F(NetdBinderTest, PerAppDefaultNetwork_ExplicitlySelectNetwork) { expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, EXPLICITLY_SELECT); expectPacketSentOnNetId(TEST_UID1, SYSTEM_DEFAULT_NETID, systemDefaultFd, EXPLICITLY_SELECT); + // Set TEST_UID1 to default unreachable, which won't affect the explicitly selected network. + // Connections go through the system default network. + EXPECT_TRUE(mNetd->networkAddUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, EXPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID1, SYSTEM_DEFAULT_NETID, systemDefaultFd, EXPLICITLY_SELECT); + + // restore IP rules + EXPECT_TRUE(mNetd->networkRemoveUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + // Add TEST_UID1 to per-app default network, which won't affect the explicitly selected network. EXPECT_TRUE(mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) @@ -3944,6 +4043,19 @@ TEST_F(NetdBinderTest, PerAppDefaultNetwork_UnconnectedSocket) { .isOk()); expectPacketSentOnNetId(AID_ROOT, NETID_UNSET, systemDefaultFd, UNCONNECTED_SOCKET); expectPacketSentOnNetId(TEST_UID1, NETID_UNSET, appDefaultFd, UNCONNECTED_SOCKET); + + // Set TEST_UID1's default network to unreachable. Its traffic should get ENETUNREACH error. + // Other traffic still go through the system default network. + EXPECT_TRUE(mNetd->networkAddUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, NETID_UNSET, systemDefaultFd, UNCONNECTED_SOCKET); + expectUnreachableError(TEST_UID1, NETID_UNSET, UNCONNECTED_SOCKET); + + // restore IP rules + EXPECT_TRUE(mNetd->networkRemoveUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); } TEST_F(NetdBinderTest, PerAppDefaultNetwork_PermissionCheck) { |