summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-10-03 23:32:27 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-10-03 23:32:27 +0000
commit2a381fcd7849d46c59608e48026dae31116915dc (patch)
treec4cf801c637d63ff48569fd3a3997ecf571522f5
parent7b0b38848e4724df45d2f3cb2fd3e6fd33c78763 (diff)
parentb7a6099aff35e8b2f7a88272e67a7feeb2b9f0c0 (diff)
downloadnetd-android13-qpr2-s11-release.tar.gz
Change-Id: I1dcf2f1a1832899fc7e07a609adc818f0791bb36
-rw-r--r--server/Network.cpp8
-rw-r--r--server/Network.h2
-rw-r--r--server/PhysicalNetwork.cpp2
-rw-r--r--server/RouteController.cpp36
-rw-r--r--server/RouteController.h12
-rw-r--r--server/UidRanges.cpp19
-rw-r--r--server/UidRanges.h4
-rw-r--r--server/UnreachableNetwork.cpp2
-rw-r--r--server/VirtualNetwork.cpp2
-rw-r--r--tests/binder_test.cpp430
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)};