diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-01-10 00:28:15 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-01-10 00:28:15 +0000 |
commit | da6ec1870c2ebe56723a4b69df4935048971fdee (patch) | |
tree | c4cf801c637d63ff48569fd3a3997ecf571522f5 | |
parent | 7b0b38848e4724df45d2f3cb2fd3e6fd33c78763 (diff) | |
parent | b7a6099aff35e8b2f7a88272e67a7feeb2b9f0c0 (diff) | |
download | netd-android13-qpr3-s10-release.tar.gz |
Snap for 9470583 from b7a6099aff35e8b2f7a88272e67a7feeb2b9f0c0 to tm-qpr3-releaseandroid-13.0.0_r83android-13.0.0_r79android-13.0.0_r78android-13.0.0_r77android-13.0.0_r76android-13.0.0_r75android-13.0.0_r71android-13.0.0_r70android-13.0.0_r69android-13.0.0_r68android-13.0.0_r67android-13.0.0_r63android-13.0.0_r62android-13.0.0_r61android-13.0.0_r56android-13.0.0_r54android-13.0.0_r53android-13.0.0_r52android-13.0.0_r51android-13.0.0_r50android13-qpr3-s9-releaseandroid13-qpr3-s8-releaseandroid13-qpr3-s7-releaseandroid13-qpr3-s6-releaseandroid13-qpr3-s5-releaseandroid13-qpr3-s4-releaseandroid13-qpr3-s3-releaseandroid13-qpr3-s2-releaseandroid13-qpr3-s14-releaseandroid13-qpr3-s13-releaseandroid13-qpr3-s12-releaseandroid13-qpr3-s11-releaseandroid13-qpr3-s10-releaseandroid13-qpr3-s1-releaseandroid13-qpr3-release
Change-Id: I8e625a1ccb28e04440927d39ca3f2f6c66985abb
-rw-r--r-- | server/Network.cpp | 8 | ||||
-rw-r--r-- | server/Network.h | 2 | ||||
-rw-r--r-- | server/PhysicalNetwork.cpp | 2 | ||||
-rw-r--r-- | server/RouteController.cpp | 36 | ||||
-rw-r--r-- | server/RouteController.h | 12 | ||||
-rw-r--r-- | server/UidRanges.cpp | 19 | ||||
-rw-r--r-- | server/UidRanges.h | 4 | ||||
-rw-r--r-- | server/UnreachableNetwork.cpp | 2 | ||||
-rw-r--r-- | server/VirtualNetwork.cpp | 2 | ||||
-rw-r--r-- | tests/binder_test.cpp | 430 |
10 files changed, 417 insertions, 100 deletions
diff --git a/server/Network.cpp b/server/Network.cpp index 85f942f4..156cfb3e 100644 --- a/server/Network.cpp +++ b/server/Network.cpp @@ -117,18 +117,12 @@ void Network::removeFromUidRangeMap(const UidRanges& uidRanges, int32_t subPrior } } -bool Network::canAddUidRanges(const UidRanges& uidRanges, int32_t subPriority) const { +bool Network::canAddUidRanges(const UidRanges& uidRanges) const { if (uidRanges.overlapsSelf()) { ALOGE("uid range %s overlaps self", uidRanges.toString().c_str()); return false; } - auto iter = mUidRangeMap.find(subPriority); - if (iter != mUidRangeMap.end() && uidRanges.overlaps(iter->second)) { - ALOGE("uid range %s overlaps priority %d %s", uidRanges.toString().c_str(), subPriority, - iter->second.toString().c_str()); - return false; - } return true; } diff --git a/server/Network.h b/server/Network.h index e18e1cdb..dfead177 100644 --- a/server/Network.h +++ b/server/Network.h @@ -65,7 +65,7 @@ public: protected: explicit Network(unsigned netId, bool secure = false); - bool canAddUidRanges(const UidRanges& uidRanges, int32_t subPriority) const; + bool canAddUidRanges(const UidRanges& uidRanges) const; const unsigned mNetId; std::set<std::string> mInterfaces; diff --git a/server/PhysicalNetwork.cpp b/server/PhysicalNetwork.cpp index bb3f653d..68130641 100644 --- a/server/PhysicalNetwork.cpp +++ b/server/PhysicalNetwork.cpp @@ -165,7 +165,7 @@ int PhysicalNetwork::removeAsDefault() { } int PhysicalNetwork::addUsers(const UidRanges& uidRanges, int32_t subPriority) { - if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) { + if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges)) { return -EINVAL; } diff --git a/server/RouteController.cpp b/server/RouteController.cpp index d2af9a37..86b23b6d 100644 --- a/server/RouteController.cpp +++ b/server/RouteController.cpp @@ -64,7 +64,7 @@ const char* const ROUTE_TABLE_NAME_MAIN = "main"; const char* const RouteController::LOCAL_MANGLE_INPUT = "routectrl_mangle_INPUT"; -const IPPrefix V4_LOCAL_ADDR[] = { +const IPPrefix V4_LOCAL_PREFIXES[] = { IPPrefix::forString("169.254.0.0/16"), // Link Local IPPrefix::forString("100.64.0.0/10"), // CGNAT IPPrefix::forString("10.0.0.0/8"), // RFC1918 @@ -667,6 +667,19 @@ int RouteController::modifyVpnLocalExclusionRule(bool add, const char* physicalI INVALID_UID); } +int RouteController::addFixedLocalRoutes(const char* interface) { + for (size_t i = 0; i < ARRAY_SIZE(V4_FIXED_LOCAL_PREFIXES); ++i) { + if (int ret = modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, + V4_FIXED_LOCAL_PREFIXES[i], nullptr /* nexthop */, + RouteController::INTERFACE, 0 /* mtu */, 0 /* priority */, + true /* isLocal */)) { + return ret; + } + } + + return 0; +} + // A rule to enable split tunnel VPNs. // // If a packet with a VPN's netId doesn't find a route in the VPN's routing table, it's allowed to @@ -1297,6 +1310,11 @@ int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* i maybeModifyQdiscClsact(interface, ACTION_ADD); updateTableNamesFile(); + + if (int ret = addFixedLocalRoutes(interface)) { + return ret; + } + return 0; } @@ -1392,8 +1410,8 @@ int RouteController::removeInterfaceFromDefaultNetwork(const char* interface, return modifyDefaultNetwork(RTM_DELRULE, interface, permission); } -bool RouteController::isTargetV4LocalRange(const char* dst) { - for (IPPrefix addr : V4_LOCAL_ADDR) { +bool RouteController::isWithinIpv4LocalPrefix(const char* dst) { + for (IPPrefix addr : V4_LOCAL_PREFIXES) { if (addr.contains(IPPrefix::forString(dst))) { return true; } @@ -1401,14 +1419,14 @@ bool RouteController::isTargetV4LocalRange(const char* dst) { return false; } -bool RouteController::isLocalAddress(TableType tableType, const char* destination, - const char* nexthop) { +bool RouteController::isLocalRoute(TableType tableType, const char* destination, + const char* nexthop) { IPPrefix prefix = IPPrefix::forString(destination); return nexthop == nullptr && tableType == RouteController::INTERFACE && // Skip default route to prevent network being modeled as point-to-point interfaces. ((prefix.family() == AF_INET6 && prefix != IPPrefix::forString("::/0")) || // Skip adding non-target local network range. - (prefix.family() == AF_INET && isTargetV4LocalRange(destination))); + (prefix.family() == AF_INET && isWithinIpv4LocalPrefix(destination))); } int RouteController::addRoute(const char* interface, const char* destination, const char* nexthop, @@ -1418,7 +1436,7 @@ int RouteController::addRoute(const char* interface, const char* destination, co return ret; } - if (isLocalAddress(tableType, destination, nexthop)) { + if (isLocalRoute(tableType, destination, nexthop)) { return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination, nexthop, tableType, mtu, priority, true /* isLocal */); } @@ -1433,7 +1451,7 @@ int RouteController::removeRoute(const char* interface, const char* destination, return ret; } - if (isLocalAddress(tableType, destination, nexthop)) { + if (isLocalRoute(tableType, destination, nexthop)) { return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop, tableType, 0 /* mtu */, priority, true /* isLocal */); } @@ -1447,7 +1465,7 @@ int RouteController::updateRoute(const char* interface, const char* destination, return ret; } - if (isLocalAddress(tableType, destination, nexthop)) { + if (isLocalRoute(tableType, destination, nexthop)) { return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination, nexthop, tableType, mtu, 0 /* priority */, true /* isLocal */); } diff --git a/server/RouteController.h b/server/RouteController.h index ff41678d..1b3a093a 100644 --- a/server/RouteController.h +++ b/server/RouteController.h @@ -85,6 +85,13 @@ constexpr int32_t RULE_PRIORITY_DEFAULT_NETWORK = 31000; constexpr int32_t RULE_PRIORITY_UNREACHABLE = 32000; // clang-format on +static const char* V4_FIXED_LOCAL_PREFIXES[] = { + // The multicast range is 224.0.0.0/4 but only limit it to 224.0.0.0/24 since the IPv4 + // definitions are not as precise as for IPv6, it is the only range that the standards + // (RFC 2365 and RFC 5771) specify is link-local and must not be forwarded. + "224.0.0.0/24" // Link-local multicast; non-internet routable +}; + class UidRanges; class RouteController { @@ -230,8 +237,9 @@ public: static int modifyUidLocalNetworkRule(const char* interface, uid_t uidStart, uid_t uidEnd, bool add); - static bool isLocalAddress(TableType tableType, const char* destination, const char* nexthop); - static bool isTargetV4LocalRange(const char* addrstr); + static bool isLocalRoute(TableType tableType, const char* destination, const char* nexthop); + static bool isWithinIpv4LocalPrefix(const char* addrstr); + static int addFixedLocalRoutes(const char* interface); }; // Public because they are called by by RouteControllerTest.cpp. diff --git a/server/UidRanges.cpp b/server/UidRanges.cpp index c90f30b9..765df322 100644 --- a/server/UidRanges.cpp +++ b/server/UidRanges.cpp @@ -145,25 +145,6 @@ bool UidRanges::overlapsSelf() const { return false; } -// std::binary_search cannot do partial match. For example, an uid range x-y not only overlaps with -// x-y, but also w-x, y-z, w-z, ...etc. Therefore, we need a specialized binary search. -bool UidRanges::overlaps(const UidRanges& other) const { - for (const auto& target : other.getRanges()) { - int first = 0; - int end = mRanges.size() - 1; - - while (first <= end) { - int middle = (first + end) / 2; - if (isOverlapped(mRanges[middle], target)) return true; - if (compUidRangeParcel(mRanges[middle], target)) - first = middle + 1; - else - end = middle - 1; - } - } - return false; -} - std::string UidRanges::toString() const { std::string s("uids{ "); for (const auto &range : mRanges) { diff --git a/server/UidRanges.h b/server/UidRanges.h index 9123eb17..f20dc443 100644 --- a/server/UidRanges.h +++ b/server/UidRanges.h @@ -51,12 +51,10 @@ public: // check if 'mRanges' has uid overlap between elements. bool overlapsSelf() const; - // check if this object has uid overlap with the input object. - bool overlaps(const UidRanges& other) const; + bool empty() const { return mRanges.empty(); } private: - // Keep it sorted. The overlaps() implements binary search, which requires a sorted data. std::vector<UidRangeParcel> mRanges; }; diff --git a/server/UnreachableNetwork.cpp b/server/UnreachableNetwork.cpp index 68802251..dd6318c0 100644 --- a/server/UnreachableNetwork.cpp +++ b/server/UnreachableNetwork.cpp @@ -27,7 +27,7 @@ namespace net { UnreachableNetwork::UnreachableNetwork(unsigned netId) : Network(netId) {} int UnreachableNetwork::addUsers(const UidRanges& uidRanges, int32_t subPriority) { - if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) { + if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges)) { return -EINVAL; } diff --git a/server/VirtualNetwork.cpp b/server/VirtualNetwork.cpp index 495fd161..e0f60407 100644 --- a/server/VirtualNetwork.cpp +++ b/server/VirtualNetwork.cpp @@ -33,7 +33,7 @@ VirtualNetwork::VirtualNetwork(unsigned netId, bool secure, bool excludeLocalRou VirtualNetwork::~VirtualNetwork() {} int VirtualNetwork::addUsers(const UidRanges& uidRanges, int32_t subPriority) { - if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) { + if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges)) { return -EINVAL; } diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp index bc1e7393..02a750d2 100644 --- a/tests/binder_test.cpp +++ b/tests/binder_test.cpp @@ -136,6 +136,7 @@ using android::net::TetherStatsParcel; using android::net::TunInterface; using android::net::UidRangeParcel; using android::net::UidRanges; +using android::net::V4_FIXED_LOCAL_PREFIXES; using android::net::mdns::aidl::DiscoveryInfo; using android::net::mdns::aidl::GetAddressInfo; using android::net::mdns::aidl::IMDns; @@ -144,6 +145,7 @@ using android::net::mdns::aidl::ResolutionInfo; using android::net::netd::aidl::NativeUidRangeConfig; using android::netdutils::getIfaceNames; using android::netdutils::IPAddress; +using android::netdutils::IPSockAddr; using android::netdutils::ScopedAddrinfo; using android::netdutils::sSyscalls; using android::netdutils::Stopwatch; @@ -243,6 +245,13 @@ class NetdBinderTest : public ::testing::Test { int vpnNetId, bool secure, std::vector<UidRangeParcel>&& appDefaultUidRanges, std::vector<UidRangeParcel>&& vpnUidRanges); + + void setupNetworkRoutesForVpnAndDefaultNetworks( + int systemDefaultNetId, int appDefaultNetId, int vpnNetId, int otherNetId, bool secure, + bool excludeLocalRoutes, bool testV6, bool differentLocalAddr, + std::vector<UidRangeParcel>&& appDefaultUidRanges, + std::vector<UidRangeParcel>&& vpnUidRanges); + protected: // Use -1 to represent that default network was not modified because // real netId must be an unsigned value. @@ -1692,6 +1701,13 @@ TEST_F(NetdBinderTest, NetworkAddRemoveRouteToLocalExcludeTable) { EXPECT_TRUE(mNetd->networkSetDefault(TEST_NETID1).isOk()); std::string localTableName = std::string(sTun.name() + "_local"); + + // Verify the fixed routes exist in the local table. + for (size_t i = 0; i < std::size(V4_FIXED_LOCAL_PREFIXES); i++) { + expectNetworkRouteExists(IP_RULE_V4, sTun.name(), V4_FIXED_LOCAL_PREFIXES[i], "", + localTableName.c_str()); + } + // Set up link-local routes for connectivity to the "gateway" for (size_t i = 0; i < std::size(kDirectlyConnectedRoutes); i++) { const auto& td = kDirectlyConnectedRoutes[i]; @@ -2460,6 +2476,13 @@ TEST_F(NetdBinderTest, TetherInterfaceAddRemoveList) { status = mNetd->tetherInterfaceList(&ifList); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); expectTetherInterfaceNotExists(ifList, sTun.name()); + + // Disable IPv6 tethering will disable IPv6 abilities by changing IPv6 settings(accept_ra, + // dad_transmits, accept_dad, disable_ipv6). See tetherInterfaceRemove in details. + // Re-init sTun to reset the interface to prevent affecting other test that requires IPv6 with + // the same interface. + sTun.destroy(); + sTun.init(); } TEST_F(NetdBinderTest, TetherDnsSetList) { @@ -3042,6 +3065,7 @@ TEST_F(NetdBinderTest, InterfaceSetEnableIPv6) { } TEST_F(NetdBinderTest, InterfaceSetMtu) { + const int currentMtu = getInterfaceMtu(sTun.name()); const int testMtu = 1200; // Add test physical network @@ -3054,6 +3078,10 @@ TEST_F(NetdBinderTest, InterfaceSetMtu) { EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); expectInterfaceMtu(sTun.name(), testMtu); + // restore the MTU back + status = mNetd->interfaceSetMtu(sTun.name(), currentMtu); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + // Remove test physical network EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk()); } @@ -3521,34 +3549,53 @@ void checkDataReceived(int udpSocket, int tunFd, sockaddr* dstAddr, int addrLen) EXPECT_GT(read(tunFd, buf, sizeof(buf)), 0); } -bool sendIPv6PacketFromUid(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, int tunFd, - bool doConnect = true) { +bool sendPacketFromUid(uid_t uid, IPSockAddr& dstAddr, Fwmark* fwmark, int tunFd, + bool doConnect = true) { + int family = dstAddr.family(); ScopedUidChange scopedUidChange(uid); - unique_fd testSocket(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)); - if (testSocket < 0) return false; + unique_fd testSocket(socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0)); - const sockaddr_in6 dst6 = { - .sin6_family = AF_INET6, - .sin6_port = 42, - .sin6_addr = dstAddr, - }; - if (doConnect && connect(testSocket, (sockaddr*)&dst6, sizeof(dst6)) == -1) return false; + if (testSocket < 0) return false; + const sockaddr_storage dst = IPSockAddr(dstAddr.ip(), dstAddr.port()); + if (doConnect && connect(testSocket, (sockaddr*)&dst, sizeof(dst)) == -1) return false; socklen_t fwmarkLen = sizeof(fwmark->intValue); EXPECT_NE(-1, getsockopt(testSocket, SOL_SOCKET, SO_MARK, &(fwmark->intValue), &fwmarkLen)); - char addr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &dstAddr, addr, INET6_ADDRSTRLEN); - SCOPED_TRACE(StringPrintf("sendIPv6Packet, addr: %s, uid: %u, doConnect: %s", addr, uid, + int addr_len = (family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; + char addr[addr_len]; + inet_ntop(family, &dstAddr, addr, addr_len); + SCOPED_TRACE(StringPrintf("sendPacket, addr: %s, uid: %u, doConnect: %s", addr, uid, doConnect ? "true" : "false")); if (doConnect) { checkDataReceived(testSocket, tunFd, nullptr, 0); } else { - checkDataReceived(testSocket, tunFd, (sockaddr*)&dst6, sizeof(dst6)); + checkDataReceived(testSocket, tunFd, (sockaddr*)&dst, sizeof(dst)); } + return true; } +bool sendIPv4PacketFromUid(uid_t uid, const in_addr& dstAddr, Fwmark* fwmark, int tunFd, + bool doConnect = true) { + const sockaddr_in dst = {.sin_family = AF_INET, .sin_port = 42, .sin_addr = dstAddr}; + IPSockAddr addr = IPSockAddr(dst); + + return sendPacketFromUid(uid, addr, fwmark, tunFd, doConnect); +} + +bool sendIPv6PacketFromUid(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, int tunFd, + bool doConnect = true) { + const sockaddr_in6 dst6 = { + .sin6_family = AF_INET6, + .sin6_port = 42, + .sin6_addr = dstAddr, + }; + IPSockAddr addr = IPSockAddr(dst6); + + return sendPacketFromUid(uid, addr, fwmark, tunFd, doConnect); +} + // 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) { @@ -3590,10 +3637,9 @@ void expectVpnFallthroughRuleExists(const std::string& ifName, int vpnNetId) { } void expectVpnFallthroughWorks(android::net::INetd* netdService, bool bypassable, uid_t uid, - uid_t uidNotInVpn, const TunInterface& fallthroughNetwork, + const TunInterface& fallthroughNetwork, const TunInterface& vpnNetwork, const TunInterface& otherNetwork, - int vpnNetId = TEST_NETID2, int fallthroughNetId = TEST_NETID1, - int otherNetId = TEST_NETID3) { + int vpnNetId = TEST_NETID2, int fallthroughNetId = TEST_NETID1) { // Set default network to NETID_UNSET EXPECT_TRUE(netdService->networkSetDefault(NETID_UNSET).isOk()); @@ -3670,25 +3716,6 @@ void expectVpnFallthroughWorks(android::net::INetd* netdService, bool bypassable EXPECT_FALSE(sendIPv6PacketFromUid(uid, outsideVpnAddr, &fwmark, fallthroughFd)); EXPECT_FALSE(sendIPv6PacketFromUid(uid, insideVpnAddr, &fwmark, fallthroughFd)); } - - // Add per-app uid ranges. - EXPECT_TRUE(netdService - ->networkAddUidRanges(otherNetId, - {makeUidRangeParcel(uidNotInVpn, uidNotInVpn)}) - .isOk()); - - int appDefaultFd = otherNetwork.getFdForTesting(); - - // UID is not inside the VPN range, so it won't go to vpn network. - // It won't fall into per app local rule because it's explicitly selected. - EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, outsideVpnAddr, &fwmark, fallthroughFd)); - EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, insideVpnAddr, &fwmark, fallthroughFd)); - - // Reset explicitly selection. - setNetworkForProcess(NETID_UNSET); - // Connections can go to app default network. - EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, insideVpnAddr, &fwmark, appDefaultFd)); - EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, outsideVpnAddr, &fwmark, appDefaultFd)); } } // namespace @@ -3697,16 +3724,14 @@ TEST_F(NetdBinderTest, SecureVPNFallthrough) { createVpnNetworkWithUid(true /* secure */, TEST_UID1); // Get current default network NetId ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk()); - expectVpnFallthroughWorks(mNetd.get(), false /* bypassable */, TEST_UID1, TEST_UID2, sTun, - sTun2, sTun3); + expectVpnFallthroughWorks(mNetd.get(), false /* bypassable */, TEST_UID1, sTun, sTun2, sTun3); } TEST_F(NetdBinderTest, BypassableVPNFallthrough) { createVpnNetworkWithUid(false /* secure */, TEST_UID1); // Get current default network NetId ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk()); - expectVpnFallthroughWorks(mNetd.get(), true /* bypassable */, TEST_UID1, TEST_UID2, sTun, sTun2, - sTun3); + expectVpnFallthroughWorks(mNetd.get(), true /* bypassable */, TEST_UID1, sTun, sTun2, sTun3); } namespace { @@ -3982,7 +4007,7 @@ void expectUnreachableError(uid_t uid, unsigned netId, int selectionMode) { } // namespace -// Verify whether API reject overlapped UID ranges +// Verify how the API handle overlapped UID ranges TEST_F(NetdBinderTest, PerAppDefaultNetwork_OverlappedUidRanges) { const auto& config = makeNativeNetworkConfig(APP_DEFAULT_NETID, NativeNetworkType::PHYSICAL, INetd::PERMISSION_NONE, false, false); @@ -3996,28 +4021,23 @@ TEST_F(NetdBinderTest, PerAppDefaultNetwork_OverlappedUidRanges) { binder::Status status; status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1)}); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + EXPECT_TRUE(status.isOk()); status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID + 9, BASE_UID + 10)}); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + EXPECT_TRUE(status.isOk()); status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID + 11, BASE_UID + 11)}); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + EXPECT_TRUE(status.isOk()); status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID + 12, BASE_UID + 13)}); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + EXPECT_TRUE(status.isOk()); status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID + 9, BASE_UID + 13)}); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + EXPECT_TRUE(status.isOk()); std::vector<UidRangeParcel> selfOverlappedUidRanges = { makeUidRangeParcel(BASE_UID + 20, BASE_UID + 20), @@ -4356,6 +4376,309 @@ TEST_P(VpnParameterizedTest, UnconnectedSocket) { expectPacketSentOnNetId(TEST_UID2, NETID_UNSET, vpnFd, UNCONNECTED_SOCKET); } +class VpnLocalRoutesParameterizedTest + : public NetdBinderTest, + public testing::WithParamInterface<std::tuple<int, int, bool, bool, bool, bool>> { + protected: + // Local/non-local addresses based on the route added above. + in_addr V4_LOCAL_ADDR = {htonl(0xC0A80008)}; // 192.168.0.8 + in_addr V4_APP_LOCAL_ADDR = {htonl(0xAC100008)}; // 172.16.0.8 + in_addr V4_GLOBAL_ADDR = {htonl(0x08080808)}; // 8.8.8.8 + + in6_addr V6_LOCAL_ADDR = { + {// 2001:db8:cafe::1 + .u6_addr8 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}}; + in6_addr V6_APP_LOCAL_ADDR = { + {// 2607:f0d0:1234::4 + .u6_addr8 = {0x26, 0x07, 0xf0, 0xd0, 0x12, 0x34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}}}; + in6_addr V6_GLOBAL_ADDR = { + {// 2607:1234:1002::4 + .u6_addr8 = {0x26, 0x07, 0x12, 0x34, 0x10, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}}}; +}; + +const int SEND_TO_GLOBAL = 0; +const int SEND_TO_SYSTEM_LOCAL = 1; +const int SEND_TO_APP_LOCAL = 2; + +// Exercise the combination of different explicitly selected network, different uid, local/non-local +// address on local route exclusion VPN. E.g. +// explicitlySelected systemDefault + uid in VPN range + no app default + non local address +// explicitlySelected systemDefault + uid in VPN range + has app default + non local address +// explicitlySelected systemDefault + uid in VPN range + has app default + local address +// explicitlySelected appDefault + uid not in VPN range + has app default + non local address +INSTANTIATE_TEST_SUITE_P( + PerAppDefaultNetwork, VpnLocalRoutesParameterizedTest, + testing::Combine(testing::Values(SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID, NETID_UNSET), + testing::Values(SEND_TO_GLOBAL, SEND_TO_SYSTEM_LOCAL, SEND_TO_APP_LOCAL), + testing::Bool(), testing::Bool(), testing::Bool(), testing::Bool()), + [](const testing::TestParamInfo<std::tuple<int, int, bool, bool, bool, bool>>& info) { + std::string explicitlySelected; + switch (std::get<0>(info.param)) { + case SYSTEM_DEFAULT_NETID: + explicitlySelected = "explicitlySelectedSystemDefault"; + break; + case APP_DEFAULT_NETID: + explicitlySelected = "explicitlySelectedAppDefault"; + break; + case NETID_UNSET: + explicitlySelected = "implicitlySelected"; + break; + default: + explicitlySelected = "InvalidParameter"; // Should not happen. + } + + std::string sendToAddr; + switch (std::get<1>(info.param)) { + case SEND_TO_GLOBAL: + sendToAddr = "GlobalAddr"; + break; + case SEND_TO_SYSTEM_LOCAL: + sendToAddr = "SystemLocal"; + break; + case SEND_TO_APP_LOCAL: + sendToAddr = "AppLocal"; + break; + default: + sendToAddr = "InvalidAddr"; // Should not happen. + } + + const std::string isSubjectToVpn = std::get<2>(info.param) + ? std::string("SubjectToVpn") + : std::string("NotSubjectToVpn"); + + const std::string hasAppDefaultNetwork = std::get<3>(info.param) + ? std::string("HasAppDefault") + : std::string("NothasAppDefault"); + + const std::string testV6 = + std::get<4>(info.param) ? std::string("v6") : std::string("v4"); + + // Apply the same or different local address in app default and system default. + const std::string differentLocalAddr = std::get<5>(info.param) + ? std::string("DifferentLocalAddr") + : std::string("SameLocalAddr"); + + return explicitlySelected + "_uid" + isSubjectToVpn + hasAppDefaultNetwork + + "Range_with" + testV6 + sendToAddr + differentLocalAddr; + }); + +int getTargetIfaceForLocalRoutesExclusion(bool isSubjectToVpn, bool hasAppDefaultNetwork, + bool differentLocalAddr, int sendToAddr, + int selectedNetId, int fallthroughFd, int appDefaultFd, + int vpnFd) { + int expectedIface; + + // Setup the expected interface based on the condition. + if (isSubjectToVpn && hasAppDefaultNetwork) { + switch (sendToAddr) { + case SEND_TO_GLOBAL: + expectedIface = vpnFd; + break; + case SEND_TO_SYSTEM_LOCAL: + // Go to app default if the app default and system default are the same range + // TODO(b/237351736): It should go to VPN if the system local and app local are + // different. + expectedIface = differentLocalAddr ? fallthroughFd : appDefaultFd; + break; + case SEND_TO_APP_LOCAL: + expectedIface = appDefaultFd; + break; + default: + expectedIface = -1; // should not happen + } + } else if (isSubjectToVpn && !hasAppDefaultNetwork) { + switch (sendToAddr) { + case SEND_TO_GLOBAL: + expectedIface = vpnFd; + break; + case SEND_TO_SYSTEM_LOCAL: + // TODO(b/237351736): It should go to app default if the system local and app local + // are different. + expectedIface = fallthroughFd; + break; + case SEND_TO_APP_LOCAL: + // Go to system default if the system default and app default are the same range. + expectedIface = differentLocalAddr ? vpnFd : fallthroughFd; + break; + default: + expectedIface = -1; // should not happen + } + } else if (!isSubjectToVpn && hasAppDefaultNetwork) { + expectedIface = appDefaultFd; + } else { // !isVpnUidRange && !isAppDefaultRange + expectedIface = fallthroughFd; + } + + // Override the target if it's explicitly selected. + switch (selectedNetId) { + case SYSTEM_DEFAULT_NETID: + expectedIface = fallthroughFd; + break; + case APP_DEFAULT_NETID: + expectedIface = appDefaultFd; + break; + default: + break; + // Based on the uid range. + } + + return expectedIface; +} + +// This routing configurations verify the worst case where both physical networks and vpn +// network have the same local address. +// This also set as system default routing for verifying different app default and system +// default routing. +std::vector<std::string> V6_ROUTES = {"2001:db8:cafe::/48", "::/0"}; +std::vector<std::string> V4_ROUTES = {"192.168.0.0/16", "0.0.0.0/0"}; + +// Routing configuration used for verifying different app default and system default routing +// configuration +std::vector<std::string> V6_APP_DEFAULT_ROUTES = {"2607:f0d0:1234::/48", "::/0"}; +std::vector<std::string> V4_APP_DEFAULT_ROUTES = {"172.16.0.0/16", "0.0.0.0/0"}; + +void NetdBinderTest::setupNetworkRoutesForVpnAndDefaultNetworks( + int systemDefaultNetId, int appDefaultNetId, int vpnNetId, int otherNetId, bool secure, + bool excludeLocalRoutes, bool testV6, bool differentLocalAddr, + std::vector<UidRangeParcel>&& appDefaultUidRanges, + std::vector<UidRangeParcel>&& vpnUidRanges) { + // Create a physical network on sTun, and set it as the system default network + createAndSetDefaultNetwork(systemDefaultNetId, sTun.name()); + + // Routes are configured to system default, app default and vpn network to verify if the packets + // are routed correctly. + + // Setup system default routing. + std::vector<std::string> systemDefaultRoutes = testV6 ? V6_ROUTES : V4_ROUTES; + for (const auto& route : systemDefaultRoutes) { + EXPECT_TRUE(mNetd->networkAddRoute(systemDefaultNetId, sTun.name(), route, "").isOk()); + } + + // Create another physical network on sTun2 as per app default network + createPhysicalNetwork(appDefaultNetId, sTun2.name()); + + // Setup app default routing. + std::vector<std::string> appDefaultRoutes = + testV6 ? (differentLocalAddr ? V6_APP_DEFAULT_ROUTES : V6_ROUTES) + : (differentLocalAddr ? V4_APP_DEFAULT_ROUTES : V4_ROUTES); + for (const auto& route : appDefaultRoutes) { + EXPECT_TRUE(mNetd->networkAddRoute(appDefaultNetId, sTun2.name(), route, "").isOk()); + } + + // Create a bypassable VPN on sTun3. + auto config = makeNativeNetworkConfig(vpnNetId, NativeNetworkType::VIRTUAL, + INetd::PERMISSION_NONE, secure, excludeLocalRoutes); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(vpnNetId, sTun3.name()).isOk()); + + // Setup vpn routing. + std::vector<std::string> vpnRoutes = testV6 ? V6_ROUTES : V4_ROUTES; + for (const auto& route : vpnRoutes) { + EXPECT_TRUE(mNetd->networkAddRoute(vpnNetId, sTun3.name(), route, "").isOk()); + } + + // Create another interface that is neither system default nor the app default to make sure + // the traffic won't be mis-routed. + createPhysicalNetwork(otherNetId, sTun4.name()); + + // Add per-app uid ranges. + EXPECT_TRUE(mNetd->networkAddUidRanges(appDefaultNetId, appDefaultUidRanges).isOk()); + + // Add VPN uid ranges. + EXPECT_TRUE(mNetd->networkAddUidRanges(vpnNetId, vpnUidRanges).isOk()); +} + +// Routes are in approximately the following order for bypassable VPNs that allow local network +// access: +// - Per-app default local routes (UID guarded) +// - System-wide default local routes +// - VPN catch-all routes (UID guarded) +// - Per-app default global routes (UID guarded) +// - System-wide default global routes +TEST_P(VpnLocalRoutesParameterizedTest, localRoutesExclusion) { + int selectedNetId; + int sendToAddr; + bool isSubjectToVpn; + bool hasAppDefaultNetwork; + bool testV6; + bool differentLocalAddr; + + std::tie(selectedNetId, sendToAddr, isSubjectToVpn, hasAppDefaultNetwork, testV6, + differentLocalAddr) = GetParam(); + + // std::vector<std::string> routes = testV6 ? V6_ROUTES : V4_ROUTES; + setupNetworkRoutesForVpnAndDefaultNetworks( + SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID, VPN_NETID, TEST_NETID4, false /* secure */, + true /* excludeLocalRoutes */, testV6, + // Add a local route first to setup local table. + differentLocalAddr, {makeUidRangeParcel(TEST_UID2, TEST_UID1)}, + {makeUidRangeParcel(TEST_UID3, TEST_UID2)}); + + int fallthroughFd = sTun.getFdForTesting(); + int appDefaultFd = sTun2.getFdForTesting(); + int vpnFd = sTun3.getFdForTesting(); + + // Explicitly select network + setNetworkForProcess(selectedNetId); + + int targetUid; + + // Setup the expected testing uid + if (isSubjectToVpn && hasAppDefaultNetwork) { + targetUid = TEST_UID2; + } else if (isSubjectToVpn && !hasAppDefaultNetwork) { + targetUid = TEST_UID3; + } else if (!isSubjectToVpn && hasAppDefaultNetwork) { + targetUid = TEST_UID1; + } else { + targetUid = AID_ROOT; + } + + // Get target interface for the traffic. + int targetIface = getTargetIfaceForLocalRoutesExclusion( + isSubjectToVpn, hasAppDefaultNetwork, differentLocalAddr, sendToAddr, selectedNetId, + fallthroughFd, appDefaultFd, vpnFd); + + // Verify the packets are sent to the expected interface. + Fwmark fwmark; + if (testV6) { + in6_addr addr; + switch (sendToAddr) { + case SEND_TO_GLOBAL: + addr = V6_GLOBAL_ADDR; + break; + case SEND_TO_SYSTEM_LOCAL: + addr = V6_LOCAL_ADDR; + break; + case SEND_TO_APP_LOCAL: + addr = differentLocalAddr ? V6_APP_LOCAL_ADDR : V6_LOCAL_ADDR; + break; + default: + break; + // should not happen + } + EXPECT_TRUE(sendIPv6PacketFromUid(targetUid, addr, &fwmark, targetIface)); + } else { + in_addr addr; + switch (sendToAddr) { + case SEND_TO_GLOBAL: + addr = V4_GLOBAL_ADDR; + break; + case SEND_TO_SYSTEM_LOCAL: + addr = V4_LOCAL_ADDR; + break; + case SEND_TO_APP_LOCAL: + addr = differentLocalAddr ? V4_APP_LOCAL_ADDR : V4_LOCAL_ADDR; + break; + default: + break; + // should not happen + } + + EXPECT_TRUE(sendIPv4PacketFromUid(targetUid, addr, &fwmark, targetIface)); + } +} + TEST_F(NetdBinderTest, NetworkCreate) { auto config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, INetd::PERMISSION_NONE, false, false); @@ -4410,11 +4733,6 @@ TEST_F(NetdBinderTest, UidRangeSubPriority_ValidateInputs) { uidRangeConfig.subPriority = SUB_PRIORITY_2; EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig).isOk()); - // For a single network, identical UID ranges with the same priority is invalid. - status = mNetd->networkAddUidRangesParcel(uidRangeConfig); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); - // Overlapping ranges is invalid. uidRangeConfig.uidRanges = {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1), makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1)}; |