diff options
author | Chiachang <chiachangwang@google.com> | 2022-06-03 01:42:07 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-06-03 01:42:07 +0000 |
commit | 8c0bc0eff8865fa860fa0dcf3bbf6a80597bc1d7 (patch) | |
tree | 9651166523c7e22a206f1bedbedfb60b513003ec | |
parent | 3521d59e697adabb574991d596e5eda43c4bf694 (diff) | |
parent | 89e504e6c3bbcce06850100a99f5f9f2874ca36c (diff) | |
download | netd-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.cpp | 55 | ||||
-rw-r--r-- | server/RouteController.h | 4 | ||||
-rw-r--r-- | tests/binder_test.cpp | 122 |
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 { |