/* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "netd_hidl_test" #include #include #include #include #include #include #include #include #include "VtsHalNetNetdTestUtils.h" #include "tun_interface.h" using android::sp; using android::base::StringPrintf; using android::hardware::Return; using android::net::TunInterface; using android::system::net::netd::V1_1::INetd; namespace { const net_handle_t INVALID_NET_HANDLE = 0x6600FACADE; constexpr const char* IPV4_ROUTER = "192.0.2.1"; constexpr const char* IPV4_CONNECTED = "192.0.2.0/25"; constexpr const char* IPV4_SUBNET_1 = "192.0.2.192/28"; constexpr const char* IPV4_HOST_1 = "192.0.2.195"; constexpr const char* IPV4_SUBNET_2 = "192.0.2.240/28"; constexpr const char* IPV4_HOST_2 = "192.0.2.245"; constexpr const char* IPV4_UNREACHABLE = "192.0.2.239"; constexpr const char* IPV6_ROUTER = "2001:db8::cafe"; constexpr const char* IPV6_CONNECTED = "2001:db8::/64"; constexpr const char* IPV6_SUBNET_1 = "2001:db8:babe::/48"; constexpr const char* IPV6_HOST_1 = "2001:db8:babe::1"; constexpr const char* IPV6_SUBNET_2 = "2001:db8:d00d::/48"; constexpr const char* IPV6_HOST_2 = "2001:db8:d00d::1"; constexpr const char* IPV6_UNREACHABLE = "2001:db8:d0a::"; std::vector REACHABLE = { IPV4_ROUTER, IPV4_HOST_1, IPV4_HOST_2, IPV6_ROUTER, IPV6_HOST_1, IPV6_HOST_2, }; void checkAllReachable(net_handle_t handle) { int ret; for (const auto& dst : REACHABLE) { ret = checkReachability(handle, dst); EXPECT_EQ(0, ret) << "Expected reachability to " << dst << " but got %s" << strerror(-ret); } for (const auto& dst : {IPV4_UNREACHABLE, IPV6_UNREACHABLE}) { EXPECT_EQ(-ENETUNREACH, checkReachability(handle, dst)) << "Expected " << dst << " to be unreachable, but was reachable"; } } void checkAllUnreachable(net_handle_t handle) { for (const auto& dst : REACHABLE) { EXPECT_EQ(-ENETUNREACH, checkReachability(handle, dst)); } for (const auto& dst : {IPV4_UNREACHABLE, IPV6_UNREACHABLE}) { EXPECT_EQ(-ENETUNREACH, checkReachability(handle, dst)); } } } // namespace class NetdHidlTest : public ::testing::TestWithParam { public: // Netd HAL instance. sp netd; // The nethandle of our test network, and its packet mark. net_handle_t mNetHandle; uint32_t mPacketMark; // Two interfaces that we can add and remove from our test network. static TunInterface sTun1; static TunInterface sTun2; // The interface name of sTun1, for convenience. static const char* sIfaceName; static void SetUpTestCase() { ASSERT_EQ(0, sTun1.init()); ASSERT_EQ(0, sTun2.init()); ASSERT_LE(sTun1.name().size(), static_cast(IFNAMSIZ)); ASSERT_LE(sTun2.name().size(), static_cast(IFNAMSIZ)); ifc_init(); ASSERT_EQ(0, ifc_up(sTun1.name().c_str())); ASSERT_EQ(0, ifc_up(sTun2.name().c_str())); sIfaceName = sTun1.name().c_str(); } static void TearDownTestCase() { sTun1.destroy(); sTun2.destroy(); ifc_close(); } virtual void SetUp() override { netd = INetd::getService(GetParam()); ASSERT_NE(netd, nullptr) << "Could not get HIDL instance"; // Set up an OEM network. INetd::StatusCode status; Return ret = netd->createOemNetwork([&](net_handle_t n, uint32_t p, INetd::StatusCode s) { status = s; mNetHandle = n; mPacketMark = p; }); ASSERT_TRUE(ret.isOk()); ASSERT_EQ(INetd::StatusCode::OK, status); ASSERT_NE(NETWORK_UNSPECIFIED, mNetHandle); ASSERT_NE((uint32_t)0, mPacketMark); } virtual void TearDown() override { netd->destroyOemNetwork(mNetHandle); } void expectAddRoute(INetd::StatusCode expectedStatus, net_handle_t handle, const char* iface, const char* destination, const char* nexthop) { Return retStatus = netd->addRouteToOemNetwork(handle, iface, destination, nexthop); EXPECT_STATUS(expectedStatus, retStatus); } void expectAddRouteSuccess(net_handle_t h, const char* i, const char* d, const char* n) { expectAddRoute(INetd::StatusCode::OK, h, i, d, n); } void expectRemoveRoute(INetd::StatusCode expectedStatus, net_handle_t handle, const char* iface, const char* destination, const char* nexthop) { Return retStatus = netd->removeRouteFromOemNetwork(handle, iface, destination, nexthop); EXPECT_STATUS(expectedStatus, retStatus); } void expectRemoveRouteSuccess(net_handle_t h, const char* i, const char* d, const char* n) { expectRemoveRoute(INetd::StatusCode::OK, h, i, d, n); } }; TunInterface NetdHidlTest::sTun1; TunInterface NetdHidlTest::sTun2; const char* NetdHidlTest::sIfaceName; // Tests adding and removing interfaces from the OEM network. TEST_P(NetdHidlTest, TestAddRemoveInterfaces) { // HACK: mark out permissions bits. uint32_t packetMark = mPacketMark & 0xffff; EXPECT_EQ(0, checkNetworkExists(mNetHandle)); EXPECT_EQ(0, countRulesForFwmark(packetMark)); // Adding an interface creates the routing rules. Return retStatus = netd->addInterfaceToOemNetwork(mNetHandle, sIfaceName); EXPECT_TRUE(retStatus.isOk()); EXPECT_EQ(0, checkNetworkExists(mNetHandle)); EXPECT_EQ(2, countRulesForFwmark(packetMark)); // Adding an interface again silently succeeds. retStatus = netd->addInterfaceToOemNetwork(mNetHandle, sIfaceName); EXPECT_TRUE(retStatus.isOk()); EXPECT_EQ(0, checkNetworkExists(mNetHandle)); EXPECT_EQ(2, countRulesForFwmark(packetMark)); // More than one network can be created. net_handle_t netHandle2; uint32_t packetMark2; Return ret = netd->createOemNetwork([&](net_handle_t n, uint32_t p, INetd::StatusCode) { netHandle2 = n; packetMark2 = p & 0xffff; }); EXPECT_TRUE(ret.isOk()); EXPECT_NE(mNetHandle, netHandle2); EXPECT_NE(packetMark, packetMark2); EXPECT_EQ(0, checkNetworkExists(netHandle2)); EXPECT_EQ(0, countRulesForFwmark(packetMark2)); // An interface can only be in one network. retStatus = netd->addInterfaceToOemNetwork(netHandle2, sIfaceName); EXPECT_STATUS(INetd::StatusCode::UNKNOWN_ERROR, retStatus); // Removing the interface removes the rules. retStatus = netd->removeInterfaceFromOemNetwork(mNetHandle, sIfaceName); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); EXPECT_EQ(0, countRulesForFwmark(packetMark)); retStatus = netd->addInterfaceToOemNetwork(netHandle2, sIfaceName); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); EXPECT_EQ(2, countRulesForFwmark(packetMark2)); // When a network is removed the interfaces are deleted. retStatus = netd->destroyOemNetwork(netHandle2); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); EXPECT_EQ(-ENONET, checkNetworkExists(netHandle2)); EXPECT_EQ(0, countRulesForFwmark(packetMark2)); // Adding an interface to a non-existent network fails. retStatus = netd->addInterfaceToOemNetwork(INVALID_NET_HANDLE, sIfaceName); EXPECT_STATUS(INetd::StatusCode::INVALID_ARGUMENTS, retStatus); retStatus = netd->removeInterfaceFromOemNetwork(INVALID_NET_HANDLE, sIfaceName); EXPECT_STATUS(INetd::StatusCode::INVALID_ARGUMENTS, retStatus); } // Tests adding and removing routes from the OEM network. TEST_P(NetdHidlTest, TestAddRemoveRoutes) { Return retStatus = netd->addInterfaceToOemNetwork(mNetHandle, sIfaceName); ASSERT_TRUE(retStatus.isOk()); // Network exists, but has no routes and no connectivity. EXPECT_EQ(0, checkNetworkExists(mNetHandle)); checkAllUnreachable(mNetHandle); // Add a directly-connected route and two gatewayed routes through it. expectAddRouteSuccess(mNetHandle, sIfaceName, IPV4_CONNECTED, ""); expectAddRouteSuccess(mNetHandle, sIfaceName, IPV4_SUBNET_1, IPV4_ROUTER); expectAddRouteSuccess(mNetHandle, sIfaceName, IPV4_SUBNET_2, IPV4_ROUTER); expectAddRouteSuccess(mNetHandle, sIfaceName, IPV6_CONNECTED, ""); expectAddRouteSuccess(mNetHandle, sIfaceName, IPV6_SUBNET_1, IPV6_ROUTER); expectAddRouteSuccess(mNetHandle, sIfaceName, IPV6_SUBNET_2, IPV6_ROUTER); // Test some destinations. checkAllReachable(mNetHandle); // Remove the directly-connected routes and everything is unreachable again. expectRemoveRouteSuccess(mNetHandle, sIfaceName, IPV4_CONNECTED, ""); expectRemoveRouteSuccess(mNetHandle, sIfaceName, IPV6_CONNECTED, ""); expectRemoveRouteSuccess(mNetHandle, sIfaceName, IPV4_SUBNET_1, IPV4_ROUTER); expectRemoveRouteSuccess(mNetHandle, sIfaceName, IPV4_SUBNET_2, IPV4_ROUTER); expectRemoveRouteSuccess(mNetHandle, sIfaceName, IPV6_SUBNET_1, IPV6_ROUTER); expectRemoveRouteSuccess(mNetHandle, sIfaceName, IPV6_SUBNET_2, IPV6_ROUTER); checkAllUnreachable(mNetHandle); // Invalid: route doesn't exist so can't be deleted. expectRemoveRoute(INetd::StatusCode::UNKNOWN_ERROR, mNetHandle, sIfaceName, IPV4_CONNECTED, ""); // Invalid: IP address instead of prefix. expectAddRoute(INetd::StatusCode::INVALID_ARGUMENTS, mNetHandle, sIfaceName, IPV4_HOST_1, ""); expectAddRoute(INetd::StatusCode::INVALID_ARGUMENTS, mNetHandle, sIfaceName, IPV6_HOST_1, ""); // Invalid: both nexthop and interface are empty. expectAddRoute(INetd::StatusCode::UNKNOWN_ERROR, mNetHandle, "", IPV4_SUBNET_1, ""); expectAddRoute(INetd::StatusCode::UNKNOWN_ERROR, mNetHandle, "", IPV6_SUBNET_1, ""); // The kernel deletes the routes when the interfaces go away. } // Tests enabling and disabling forwarding between interfaces. TEST_P(NetdHidlTest, TestForwarding) { Return retStatus = netd->addInterfaceToOemNetwork(mNetHandle, sTun1.name().c_str()); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); retStatus = netd->addInterfaceToOemNetwork(mNetHandle, sTun2.name().c_str()); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); // TODO: move this test to netd and use ROUTE_TABLE_OFFSET_FROM_INDEX directly. uint32_t table1 = 1000 + sTun1.ifindex(); uint32_t table2 = 1000 + sTun1.ifindex(); const char* regexTemplate = "from all iif %s .*lookup (%s|%d)"; std::string regex1 = StringPrintf(regexTemplate, sTun1.name().c_str(), sTun2.name().c_str(), table2); std::string regex2 = StringPrintf(regexTemplate, sTun2.name().c_str(), sTun1.name().c_str(), table1); EXPECT_EQ(0, countMatchingIpRules(regex1)); EXPECT_EQ(0, countMatchingIpRules(regex2)); retStatus = netd->setForwardingBetweenInterfaces(sTun1.name(), sTun2.name(), true); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); EXPECT_EQ(2, countMatchingIpRules(regex1)); EXPECT_EQ(0, countMatchingIpRules(regex2)); // No attempt at deduplicating rules is made. retStatus = netd->setForwardingBetweenInterfaces(sTun1.name(), sTun2.name(), true); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); EXPECT_EQ(4, countMatchingIpRules(regex1)); retStatus = netd->setForwardingBetweenInterfaces(sTun1.name(), sTun2.name(), false); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); EXPECT_EQ(2, countMatchingIpRules(regex1)); retStatus = netd->setForwardingBetweenInterfaces(sTun2.name(), sTun1.name(), true); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); EXPECT_EQ(2, countMatchingIpRules(regex1)); EXPECT_EQ(2, countMatchingIpRules(regex2)); retStatus = netd->setForwardingBetweenInterfaces(sTun1.name(), sTun2.name(), false); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); EXPECT_EQ(0, countMatchingIpRules(regex1)); EXPECT_EQ(2, countMatchingIpRules(regex2)); retStatus = netd->setForwardingBetweenInterfaces(sTun2.name(), sTun1.name(), false); EXPECT_STATUS(INetd::StatusCode::OK, retStatus); EXPECT_EQ(0, countMatchingIpRules(regex1)); EXPECT_EQ(0, countMatchingIpRules(regex2)); // Deleting rules that don't exist fails. retStatus = netd->setForwardingBetweenInterfaces(sTun1.name(), sTun2.name(), false); EXPECT_STATUS(INetd::StatusCode::UNKNOWN_ERROR, retStatus); EXPECT_EQ(0, countMatchingIpRules(regex1)); EXPECT_EQ(0, countMatchingIpRules(regex2)); } INSTANTIATE_TEST_SUITE_P( PerInstance, NetdHidlTest, testing::ValuesIn(android::hardware::getAllHalInstanceNames(INetd::descriptor)), android::hardware::PrintInstanceNameToString);