summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChiachang <chiachangwang@google.com>2022-06-03 01:42:07 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-06-03 01:42:07 +0000
commit8c0bc0eff8865fa860fa0dcf3bbf6a80597bc1d7 (patch)
tree9651166523c7e22a206f1bedbedfb60b513003ec
parent3521d59e697adabb574991d596e5eda43c4bf694 (diff)
parent89e504e6c3bbcce06850100a99f5f9f2874ca36c (diff)
downloadnetd-8c0bc0eff8865fa860fa0dcf3bbf6a80597bc1d7.tar.gz
Update local routes based on network assigned range am: 89e504e6c3
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/netd/+/18716246 Change-Id: Ie1af14e841654f8d5d7f7c4979e419c5a18bbd71 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--server/RouteController.cpp55
-rw-r--r--server/RouteController.h4
-rw-r--r--tests/binder_test.cpp122
3 files changed, 163 insertions, 18 deletions
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index ea8baea8..465e5b90 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -21,9 +21,9 @@
#include <fcntl.h>
#include <linux/fib_rules.h>
#include <net/if.h>
-#include <sys/stat.h>
-
+#include <netdutils/InternetAddresses.h>
#include <private/android_filesystem_config.h>
+#include <sys/stat.h>
#include <map>
@@ -43,6 +43,7 @@
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
+using android::netdutils::IPPrefix;
namespace android::net {
@@ -1107,11 +1108,11 @@ int RouteController::modifyTetheredNetwork(uint16_t action, const char* inputInt
// Returns 0 on success or negative errno on failure.
int RouteController::modifyRoute(uint16_t action, uint16_t flags, const char* interface,
const char* destination, const char* nexthop, TableType tableType,
- int mtu, int priority) {
+ int mtu, int priority, bool isLocal) {
uint32_t table;
switch (tableType) {
case RouteController::INTERFACE: {
- table = getRouteTableForInterface(interface, false /* local */);
+ table = getRouteTableForInterface(interface, isLocal);
if (table == RT_TABLE_UNSPEC) {
return -ESRCH;
}
@@ -1383,22 +1384,56 @@ int RouteController::removeInterfaceFromDefaultNetwork(const char* interface,
return modifyDefaultNetwork(RTM_DELRULE, interface, permission);
}
+bool RouteController::isLocalAddress(TableType tableType, const char* destination,
+ const char* nexthop) {
+ IPPrefix prefix = IPPrefix::forString(destination);
+ // TODO: Intersect with RFC1918/CGNAT/LINK LOCAL range.
+ 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"));
+}
+
int RouteController::addRoute(const char* interface, const char* destination, const char* nexthop,
TableType tableType, int mtu, int priority) {
- return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination, nexthop,
- tableType, mtu, priority);
+ if (int ret = modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination,
+ nexthop, tableType, mtu, priority, false /* isLocal */)) {
+ return ret;
+ }
+
+ if (isLocalAddress(tableType, destination, nexthop)) {
+ return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination,
+ nexthop, tableType, mtu, priority, true /* isLocal */);
+ }
+
+ return 0;
}
int RouteController::removeRoute(const char* interface, const char* destination,
const char* nexthop, TableType tableType, int priority) {
- return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop,
- tableType, 0 /* mtu */, priority);
+ if (int ret = modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop,
+ tableType, 0 /* mtu */, priority, false /* isLocal */)) {
+ return ret;
+ }
+
+ if (isLocalAddress(tableType, destination, nexthop)) {
+ return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop,
+ tableType, 0 /* mtu */, priority, true /* isLocal */);
+ }
+ return 0;
}
int RouteController::updateRoute(const char* interface, const char* destination,
const char* nexthop, TableType tableType, int mtu) {
- return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination, nexthop,
- tableType, mtu, 0 /* priority */);
+ if (int ret = modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination,
+ nexthop, tableType, mtu, 0 /* priority */, false /* isLocal */)) {
+ return ret;
+ }
+
+ if (isLocalAddress(tableType, destination, nexthop)) {
+ return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination,
+ nexthop, tableType, mtu, 0 /* priority */, true /* isLocal */);
+ }
+ return 0;
}
int RouteController::enableTethering(const char* inputInterface, const char* outputInterface) {
diff --git a/server/RouteController.h b/server/RouteController.h
index 887187c8..74d005ac 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -217,7 +217,7 @@ public:
static int modifyUnreachableNetwork(unsigned netId, const UidRangeMap& uidRangeMap, bool add);
static int modifyRoute(uint16_t action, uint16_t flags, const char* interface,
const char* destination, const char* nexthop, TableType tableType,
- int mtu, int priority);
+ int mtu, int priority, bool isLocal);
static int modifyTetheredNetwork(uint16_t action, const char* inputInterface,
const char* outputInterface);
static int modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId,
@@ -227,8 +227,10 @@ public:
bool modifyNonUidBasedRules, bool excludeLocalRoutes);
static void updateTableNamesFile() EXCLUDES(sInterfaceToTableLock);
static int modifyVpnLocalExclusionRule(bool add, const char* physicalInterface);
+
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);
};
// Public because they are called by by RouteControllerTest.cpp.
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index 5f5dff11..2133e1da 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -1627,6 +1627,90 @@ void expectProcessDoesNotExist(const std::string& processName) {
} // namespace
+TEST_F(NetdBinderTest, NetworkAddRemoveRouteToLocalExcludeTable) {
+ static const struct {
+ const char* ipVersion;
+ const char* testDest;
+ const char* testNextHop;
+ const bool expectInLocalTable;
+ } kTestData[] = {
+ {IP_RULE_V6, "::/0", "fe80::", false},
+ {IP_RULE_V6, "::/0", "", false},
+ {IP_RULE_V6, "2001:db8:cafe::/64", "fe80::", false},
+ {IP_RULE_V6, "fe80::/64", "", true},
+ {IP_RULE_V6, "2001:db8:cafe::/48", "", true},
+ {IP_RULE_V6, "2001:db8:cafe::/64", "unreachable", false},
+ {IP_RULE_V6, "2001:db8:ca00::/40", "", true},
+ };
+
+ static const struct {
+ const char* ipVersion;
+ const char* testDest;
+ const char* testNextHop;
+ } kLinkLocalRoutes[] = {{IP_RULE_V6, "2001:db8::/32", ""}};
+
+ // Add test physical network
+ const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL,
+ INetd::PERMISSION_NONE, false, false);
+ EXPECT_TRUE(mNetd->networkCreate(config).isOk());
+ EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
+
+ // Get current default network NetId
+ binder::Status status = mNetd->networkGetDefault(&mStoredDefaultNetwork);
+ ASSERT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ // Set default network
+ EXPECT_TRUE(mNetd->networkSetDefault(TEST_NETID1).isOk());
+
+ std::string localTableName = std::string(sTun.name() + "_local");
+ // Set up link-local routes for connectivity to the "gateway"
+ for (size_t i = 0; i < std::size(kLinkLocalRoutes); i++) {
+ const auto& td = kLinkLocalRoutes[i];
+
+ binder::Status status =
+ mNetd->networkAddRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectNetworkRouteExists(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+ sTun.name().c_str());
+ // Verify routes in local table
+ expectNetworkRouteExists(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+ localTableName.c_str());
+ }
+
+ for (size_t i = 0; i < std::size(kTestData); i++) {
+ const auto& td = kTestData[i];
+
+ binder::Status status =
+ mNetd->networkAddRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ // Verify routes in local table
+ if (td.expectInLocalTable) {
+ expectNetworkRouteExists(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+ localTableName.c_str());
+ } else {
+ expectNetworkRouteDoesNotExist(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+ localTableName.c_str());
+ }
+
+ status = mNetd->networkRemoveRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectNetworkRouteDoesNotExist(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+ localTableName.c_str());
+ }
+
+ for (size_t i = 0; i < std::size(kLinkLocalRoutes); i++) {
+ const auto& td = kLinkLocalRoutes[i];
+ status = mNetd->networkRemoveRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ }
+
+ // Set default network back
+ status = mNetd->networkSetDefault(mStoredDefaultNetwork);
+
+ // Remove test physical network
+ EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
+}
+
namespace {
bool getIpfwdV4Enable() {
@@ -3309,6 +3393,9 @@ void NetdBinderTest::createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetI
EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID1, sTun.name(), "::/0", "").isOk());
// Add limited route
EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID2, sTun2.name(), "2001:db8::/32", "").isOk());
+
+ // Also add default route to non-default network for per app default use.
+ EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID3, sTun3.name(), "::/0", "").isOk());
}
void NetdBinderTest::createAndSetDefaultNetwork(int netId, const std::string& interface,
@@ -3473,10 +3560,10 @@ void expectVpnFallthroughRuleExists(const std::string& ifName, int vpnNetId) {
}
void expectVpnFallthroughWorks(android::net::INetd* netdService, bool bypassable, uid_t uid,
- const TunInterface& fallthroughNetwork,
- const TunInterface& vpnNetwork,
- const TunInterface& nonDefaultNetwork, int vpnNetId = TEST_NETID2,
- int fallthroughNetId = TEST_NETID1) {
+ uid_t uidNotInVpn, const TunInterface& fallthroughNetwork,
+ const TunInterface& vpnNetwork, const TunInterface& otherNetwork,
+ int vpnNetId = TEST_NETID2, int fallthroughNetId = TEST_NETID1,
+ int otherNetId = TEST_NETID3) {
// Set default network to NETID_UNSET
EXPECT_TRUE(netdService->networkSetDefault(NETID_UNSET).isOk());
@@ -3513,7 +3600,7 @@ void expectVpnFallthroughWorks(android::net::INetd* netdService, bool bypassable
// Check if local exclusion rule exists for default network
expectVpnLocalExclusionRuleExists(fallthroughNetwork.name(), true);
// No local exclusion rule for non-default network
- expectVpnLocalExclusionRuleExists(nonDefaultNetwork.name(), false);
+ expectVpnLocalExclusionRuleExists(otherNetwork.name(), false);
// Expect fallthrough to default network
// The fwmark differs depending on whether the VPN is bypassable or not.
@@ -3553,6 +3640,25 @@ 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
@@ -3561,14 +3667,16 @@ 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, sTun, sTun2, sTun3);
+ expectVpnFallthroughWorks(mNetd.get(), false /* bypassable */, TEST_UID1, TEST_UID2, 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, sTun, sTun2, sTun3);
+ expectVpnFallthroughWorks(mNetd.get(), true /* bypassable */, TEST_UID1, TEST_UID2, sTun, sTun2,
+ sTun3);
}
namespace {