Snap for 4693621 from 9a9a0772bc04427f3cc23289501a6b56463d1db1 to pi-release

Change-Id: I24dc0eba050216b702dc2e831055251fc87c0e52
diff --git a/OWNERS b/OWNERS
index d19f68e..289a174 100644
--- a/OWNERS
+++ b/OWNERS
@@ -3,3 +3,4 @@
 maco@google.com
 malchev@google.com
 smoreland@google.com
+yim@google.com # VTS
diff --git a/net/netd/1.0/vts/functional/Android.bp b/net/netd/1.0/vts/functional/Android.bp
index c79d116..9887638 100644
--- a/net/netd/1.0/vts/functional/Android.bp
+++ b/net/netd/1.0/vts/functional/Android.bp
@@ -1,20 +1,30 @@
+//
+// Copyright (C) 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.
+
 cc_test {
     name: "VtsHalNetNetdV1_0TargetTest",
     srcs: [
         "VtsHalNetNetdV1_0TargetTest.cpp",
     ],
+    defaults: ["VtsHalNetNetdTestDefaults"],
     shared_libs: [
-        "liblog",
-        "libhidlbase",
-        "libhidltransport",
-        "libutils",
         "android.system.net.netd@1.0",
-    ],
-    static_libs: ["VtsHalHidlTargetTestBase"],
-    cflags: [
-        "-O0",
-        "-g",
-        "-Wall",
-        "-Werror",
+        "libandroid_net",
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
     ],
 }
diff --git a/net/netd/1.0/vts/functional/VtsHalNetNetdV1_0TargetTest.cpp b/net/netd/1.0/vts/functional/VtsHalNetNetdV1_0TargetTest.cpp
index c332c10..0c52900 100644
--- a/net/netd/1.0/vts/functional/VtsHalNetNetdV1_0TargetTest.cpp
+++ b/net/netd/1.0/vts/functional/VtsHalNetNetdV1_0TargetTest.cpp
@@ -20,15 +20,33 @@
 #include <android/system/net/netd/1.0/INetd.h>
 #include <log/log.h>
 
+#include "VtsHalNetNetdTestUtils.h"
+
 using ::android::system::net::netd::V1_0::INetd;
 using ::android::hardware::Return;
 using ::android::sp;
 
+// Test environment for Netd HIDL HAL.
+class NetdHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+   public:
+    // get the test environment singleton
+    static NetdHidlEnvironment* Instance() {
+        static NetdHidlEnvironment* instance = new NetdHidlEnvironment;
+        return instance;
+    }
+
+    virtual void registerTestServices() override { registerTestService<INetd>(); }
+
+   private:
+    NetdHidlEnvironment() {}
+};
+
 class NetdHidlTest : public ::testing::VtsHalHidlTargetTestBase {
    public:
     virtual void SetUp() override {
-        netd = ::testing::VtsHalHidlTargetTestBase::getService<INetd>();
-        ASSERT_NE(nullptr, netd.get()) << "Could not get HIDL instance";
+        netd = ::testing::VtsHalHidlTargetTestBase::getService<INetd>(
+            NetdHidlEnvironment::Instance()->getServiceName<INetd>());
+        ASSERT_NE(netd, nullptr) << "Could not get HIDL instance";
     }
 
     sp<INetd> netd;
@@ -36,17 +54,28 @@
 
 // positive test. Ensure netd creates an oem network and returns valid netHandle, and destroys it.
 TEST_F(NetdHidlTest, TestCreateAndDestroyOemNetworkOk) {
-    auto cb = [this](uint64_t netHandle, uint32_t packetMark, INetd::StatusCode status) {
-        ASSERT_EQ(INetd::StatusCode::OK, status);
-        ASSERT_NE((uint64_t)0, netHandle);
-        ASSERT_NE((uint32_t)0, packetMark);
+    net_handle_t netHandle;
+    uint32_t packetMark;
+    INetd::StatusCode status;
 
-        Return<INetd::StatusCode> retStatus = netd->destroyOemNetwork(netHandle);
-        ASSERT_EQ(INetd::StatusCode::OK, retStatus);
-    };
+    Return<void> ret = netd->createOemNetwork([&](net_handle_t n, uint32_t p, INetd::StatusCode s) {
+        status = s;
+        netHandle = n;
+        packetMark = p;
+    });
 
-    Return<void> ret = netd->createOemNetwork(cb);
     ASSERT_TRUE(ret.isOk());
+    ASSERT_EQ(INetd::StatusCode::OK, status);
+    ASSERT_NE(NETWORK_UNSPECIFIED, netHandle);
+    ASSERT_NE((uint32_t)0, packetMark);
+
+    ASSERT_EQ(0, checkNetworkExists(netHandle));
+    ASSERT_EQ(0, countRulesForFwmark(packetMark));
+
+    Return<INetd::StatusCode> retStatus = netd->destroyOemNetwork(netHandle);
+    ASSERT_EQ(INetd::StatusCode::OK, retStatus);
+
+    ASSERT_EQ(-ENONET, checkNetworkExists(netHandle));
 }
 
 // negative test. Ensure destroy for invalid OEM network fails appropriately
@@ -58,7 +87,9 @@
 }
 
 int main(int argc, char** argv) {
+    ::testing::AddGlobalTestEnvironment(NetdHidlEnvironment::Instance());
     ::testing::InitGoogleTest(&argc, argv);
+    NetdHidlEnvironment::Instance()->init(&argc, argv);
     int status = RUN_ALL_TESTS();
     ALOGE("Test result with status=%d", status);
     return status;
diff --git a/net/netd/1.1/vts/functional/Android.bp b/net/netd/1.1/vts/functional/Android.bp
new file mode 100644
index 0000000..4b411ff
--- /dev/null
+++ b/net/netd/1.1/vts/functional/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 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.
+
+cc_test {
+    name: "VtsHalNetNetdV1_1TargetTest",
+    srcs: [
+        "VtsHalNetNetdV1_1TargetTest.cpp",
+    ],
+    defaults: ["VtsHalNetNetdTestDefaults"],
+    static_libs: [
+        "android.system.net.netd@1.0",
+        "android.system.net.netd@1.1",
+        "libnetd_test_tun_interface",
+    ],
+    shared_libs: [
+        "libnetutils",
+    ],
+}
diff --git a/net/netd/1.1/vts/functional/VtsHalNetNetdV1_1TargetTest.cpp b/net/netd/1.1/vts/functional/VtsHalNetNetdV1_1TargetTest.cpp
new file mode 100644
index 0000000..73cd3f4
--- /dev/null
+++ b/net/netd/1.1/vts/functional/VtsHalNetNetdV1_1TargetTest.cpp
@@ -0,0 +1,345 @@
+/*
+ * 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 <linux/if.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <android-base/stringprintf.h>
+#include <android/system/net/netd/1.1/INetd.h>
+#include <log/log.h>
+#include <netutils/ifc.h>
+
+#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<const char*> 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
+
+// Test environment for Netd HIDL HAL.
+class NetdHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+   public:
+    // get the test environment singleton
+    static NetdHidlEnvironment* Instance() {
+        static NetdHidlEnvironment* instance = new NetdHidlEnvironment;
+        return instance;
+    }
+
+    virtual void registerTestServices() override { registerTestService<INetd>(); }
+
+   private:
+    NetdHidlEnvironment() {}
+};
+
+class NetdHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+   public:
+    // Netd HAL instance.
+    sp<INetd> 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<size_t>(IFNAMSIZ));
+        ASSERT_LE(sTun2.name().size(), static_cast<size_t>(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 = ::testing::VtsHalHidlTargetTestBase::getService<INetd>(
+            NetdHidlEnvironment::Instance()->getServiceName<INetd>());
+
+        ASSERT_NE(netd, nullptr) << "Could not get HIDL instance";
+
+        // Set up an OEM network.
+        INetd::StatusCode status;
+        Return<void> 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<INetd::StatusCode> 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<INetd::StatusCode> 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_F(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<INetd::StatusCode> 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<void> 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_F(NetdHidlTest, TestAddRemoveRoutes) {
+    Return<INetd::StatusCode> 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_F(NetdHidlTest, TestForwarding) {
+    Return<INetd::StatusCode> 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));
+}
+
+int main(int argc, char** argv) {
+    ::testing::AddGlobalTestEnvironment(NetdHidlEnvironment::Instance());
+    ::testing::InitGoogleTest(&argc, argv);
+    NetdHidlEnvironment::Instance()->init(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    ALOGE("Test result with status=%d", status);
+    return status;
+}
diff --git a/net/netd/testutils/Android.bp b/net/netd/testutils/Android.bp
new file mode 100644
index 0000000..991c272
--- /dev/null
+++ b/net/netd/testutils/Android.bp
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 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.
+
+// Utility code common for all HAL versions.
+cc_library {
+    name: "VtsHalNetNetdTestUtils",
+    srcs: [
+        "VtsHalNetNetdTestUtils.cpp",
+    ],
+    export_include_dirs: ["."],
+    shared_libs: [
+        "libandroid_net",
+        "libbase",
+    ],
+    cflags: [
+        "-Og",
+	"-Wall",
+	"-Werror",
+    ],
+}
+
+// Common build settings for all HAL versions.
+cc_defaults {
+    name: "VtsHalNetNetdTestDefaults",
+    shared_libs: [
+        "libandroid_net",
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "VtsHalHidlTargetTestBase",
+        "VtsHalNetNetdTestUtils",
+    ],
+    cflags: [
+        "-Og",
+        "-Wall",
+        "-Werror",
+    ],
+    defaults: ["VtsHalTargetTestDefaults"],
+}
diff --git a/net/netd/testutils/VtsHalNetNetdTestUtils.cpp b/net/netd/testutils/VtsHalNetNetdTestUtils.cpp
new file mode 100644
index 0000000..da377c4
--- /dev/null
+++ b/net/netd/testutils/VtsHalNetNetdTestUtils.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#include <regex>
+#include <string>
+#include <vector>
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <android/multinetwork.h>
+
+#include "VtsHalNetNetdTestUtils.h"
+
+using android::base::StringPrintf;
+
+int checkNetworkExists(net_handle_t netHandle) {
+    int sock = socket(AF_INET6, SOCK_STREAM, 0);
+    if (sock == -1) {
+        return -ENOTSOCK;
+    }
+    int ret = android_setsocknetwork(netHandle, sock);
+    if (ret) {
+        ret = -errno;
+    }
+    close(sock);
+    return ret;
+}
+
+// TODO: deduplicate this with system/netd/tests/binder_test.cpp.
+static std::vector<std::string> runCommand(const std::string& command) {
+    std::vector<std::string> lines;
+    FILE* f;
+
+    if ((f = popen(command.c_str(), "r")) == nullptr) {
+        perror("popen");
+        return lines;
+    }
+
+    char* line = nullptr;
+    size_t bufsize = 0;
+    ssize_t linelen = 0;
+    while ((linelen = getline(&line, &bufsize, f)) >= 0) {
+        lines.push_back(std::string(line, linelen));
+    }
+
+    free(line);
+    pclose(f);
+    return lines;
+}
+
+static std::vector<std::string> listIpRules(const char* ipVersion) {
+    std::string command = StringPrintf("%s %s rule list", IP_PATH, ipVersion);
+    return runCommand(command);
+}
+
+int countMatchingIpRules(const std::string& regexString) {
+    const std::regex regex(regexString, std::regex_constants::extended);
+    int matches = 0;
+
+    for (const char* version : {"-4", "-6"}) {
+        std::vector<std::string> rules = listIpRules(version);
+        for (const auto& rule : rules) {
+            if (std::regex_search(rule, regex)) {
+                matches++;
+            }
+        }
+    }
+
+    return matches;
+}
+
+int countRulesForFwmark(const uint32_t fwmark) {
+    // Skip top nibble, which differs between rules.
+    std::string regex = StringPrintf("from all fwmark 0x[0-9a-f]+%x/.* lookup ", fwmark);
+    return countMatchingIpRules(regex);
+}
+
+int checkReachability(net_handle_t netHandle, const char* addrStr) {
+    addrinfo *ai, hints = {.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV};
+    int ret = getaddrinfo(addrStr, "53", &hints, &ai);
+    if (ret) {
+        return -EINVAL;
+    }
+
+    int sock = socket(ai->ai_family, SOCK_DGRAM, 0);
+    if (sock == -1 || android_setsocknetwork(netHandle, sock) == -1) {
+        ret = -errno;
+        freeaddrinfo(ai);
+        return ret;
+    }
+
+    ret = connect(sock, ai->ai_addr, ai->ai_addrlen);
+    close(sock);
+    if (ret == -1) {
+        ret = -errno;
+    }
+    freeaddrinfo(ai);
+    return ret;
+}
diff --git a/net/netd/testutils/VtsHalNetNetdTestUtils.h b/net/netd/testutils/VtsHalNetNetdTestUtils.h
new file mode 100644
index 0000000..ef0c7ed
--- /dev/null
+++ b/net/netd/testutils/VtsHalNetNetdTestUtils.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef VTS_HAL_NET_NETD_TEST_UTILS_H
+#define VTS_HAL_NET_NETD_TEST_UTILS_H
+
+#include <android/multinetwork.h>
+
+#define EXPECT_STATUS(expectedStatus, ret)  \
+    do {                                    \
+        EXPECT_TRUE((ret).isOk());          \
+        EXPECT_EQ((expectedStatus), (ret)); \
+    } while (0)
+
+constexpr const char* IP_PATH = "/system/bin/ip";
+
+// Checks that the given network exists.
+// Returns 0 if it exists or -errno if it does not.
+int checkNetworkExists(net_handle_t netHandle);
+
+// Checks that the given network provides reachability to the given address.
+// Returns 0 if it so or -errno if not.
+int checkReachability(net_handle_t netHandle, const char* addrStr);
+
+// Counts the number of IPv4 and IPv6 routing rules that match the given regexp string.
+int countMatchingIpRules(const std::string& regexString);
+
+// Counts the number of IPv4 and IPv6 routing rules that select the specified fwmark.
+int countRulesForFwmark(const uint32_t fwmark);
+
+#endif