arc: Move platform2/arc/network/ to platform2/patchpanel
Next step in the arc-networkd -> patchpanel rename, this patch moves the
location of the code.
BUG=b:151879931
TEST=units,flashed image to atlas
TEST=tasts arc.PlayStore, crostini.LaunchTerminal.download
Change-Id: I1b5cf8d670e1631d46f6449b725395157bf88dde
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2115863
Tested-by: Garrick Evans <garrick@chromium.org>
Commit-Queue: Garrick Evans <garrick@chromium.org>
Reviewed-by: Hidehiko Abe <hidehiko@chromium.org>
Reviewed-by: Eric Caruso <ejcaruso@chromium.org>
Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
Reviewed-by: Hugo Benichi <hugobenichi@google.com>
diff --git a/patchpanel/arc_service_test.cc b/patchpanel/arc_service_test.cc
new file mode 100644
index 0000000..6897956
--- /dev/null
+++ b/patchpanel/arc_service_test.cc
@@ -0,0 +1,550 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "patchpanel/arc_service.h"
+
+#include <net/if.h>
+
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "patchpanel/address_manager.h"
+#include "patchpanel/fake_process_runner.h"
+#include "patchpanel/fake_shill_client.h"
+#include "patchpanel/mock_datapath.h"
+#include "patchpanel/net_util.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::Eq;
+using testing::Pointee;
+using testing::Return;
+using testing::ReturnRef;
+using testing::StrEq;
+
+namespace patchpanel {
+namespace {
+constexpr pid_t kTestPID = -2;
+constexpr uint32_t kTestCID = 2;
+constexpr uint32_t kArcHostIP = Ipv4Addr(100, 115, 92, 1);
+constexpr uint32_t kArcGuestIP = Ipv4Addr(100, 115, 92, 2);
+constexpr uint32_t kArcVmHostIP = Ipv4Addr(100, 115, 92, 5);
+constexpr uint32_t kArcVmGuestIP = Ipv4Addr(100, 115, 92, 6);
+constexpr uint32_t kFirstArcEthHostIP = kArcVmHostIP;
+constexpr uint32_t kFirstArcEthGuestIP = kArcVmGuestIP;
+constexpr uint32_t kSecondEthHostIP = Ipv4Addr(100, 115, 92, 9);
+constexpr uint32_t kFirstWifiHostIP = Ipv4Addr(100, 115, 92, 13);
+constexpr uint32_t kSecondWifiHostIP = Ipv4Addr(100, 115, 92, 17);
+constexpr uint32_t kFirstCellHostIP = Ipv4Addr(100, 115, 92, 21);
+constexpr MacAddress kArcVmArc0MacAddr = {0x42, 0x37, 0x05, 0x13, 0x17, 0x01};
+
+class MockTrafficForwarder : public TrafficForwarder {
+ public:
+ MockTrafficForwarder() = default;
+ ~MockTrafficForwarder() = default;
+
+ MOCK_METHOD4(StartForwarding,
+ void(const std::string& ifname_physical,
+ const std::string& ifname_virtual,
+ bool ipv6,
+ bool multicast));
+
+ MOCK_METHOD4(StopForwarding,
+ void(const std::string& ifname_physical,
+ const std::string& ifname_virtual,
+ bool ipv6,
+ bool multicast));
+};
+
+class MockImpl : public ArcService::Impl {
+ public:
+ MockImpl() = default;
+ ~MockImpl() = default;
+
+ MOCK_CONST_METHOD0(guest, GuestMessage::GuestType());
+ MOCK_CONST_METHOD0(id, uint32_t());
+ MOCK_CONST_METHOD0(GetDeviceConfigs, std::vector<const Device::Config*>());
+ MOCK_METHOD1(Start, bool(uint32_t));
+ MOCK_METHOD1(Stop, void(uint32_t));
+ MOCK_CONST_METHOD1(IsStarted, bool(uint32_t*));
+ MOCK_METHOD1(OnStartDevice, bool(Device*));
+ MOCK_METHOD1(OnStopDevice, void(Device*));
+ MOCK_METHOD2(OnDefaultInterfaceChanged,
+ void(const std::string&, const std::string&));
+};
+
+} // namespace
+
+class ArcServiceTest : public testing::Test {
+ public:
+ ArcServiceTest() : testing::Test() {}
+
+ protected:
+ void SetUp() override {
+ runner_ = std::make_unique<FakeProcessRunner>();
+ runner_->Capture(false);
+ datapath_ = std::make_unique<MockDatapath>(runner_.get());
+ shill_client_ = shill_helper_.Client();
+ addr_mgr_ = std::make_unique<AddressManager>();
+ }
+
+ std::unique_ptr<ArcService> NewService(
+ GuestMessage::GuestType guest = GuestMessage::ARC) {
+ patchpanel::test::guest = guest;
+ return std::make_unique<ArcService>(shill_client_.get(), datapath_.get(),
+ addr_mgr_.get(), &forwarder_,
+ guest == GuestMessage::ARC_VM);
+ }
+
+ FakeShillClientHelper shill_helper_;
+ std::unique_ptr<ShillClient> shill_client_;
+ std::unique_ptr<AddressManager> addr_mgr_;
+ MockTrafficForwarder forwarder_;
+ std::unique_ptr<MockDatapath> datapath_;
+ std::unique_ptr<FakeProcessRunner> runner_;
+};
+
+TEST_F(ArcServiceTest, StartDevice) {
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstArcEthHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_,
+ AddInboundIPv4DNAT(StrEq("eth0"),
+ IPv4AddressToString(kFirstArcEthGuestIP)))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddOutboundIPv4(StrEq("arc_eth0")))
+ .WillOnce(Return(true));
+
+ auto svc = NewService();
+ auto impl = std::make_unique<MockImpl>();
+ auto* mock_impl = impl.get();
+ svc->impl_ = std::move(impl);
+
+ EXPECT_CALL(*mock_impl, guest()).WillRepeatedly(Return(GuestMessage::ARC));
+ EXPECT_CALL(*mock_impl, IsStarted(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_impl, OnStartDevice(_)).WillOnce(Return(true));
+ svc->AddDevice("eth0");
+ EXPECT_TRUE(svc->devices_.find("eth0") != svc->devices_.end());
+}
+
+TEST_F(ArcServiceTest, StopDevice) {
+ EXPECT_CALL(*datapath_, RemoveOutboundIPv4(StrEq("arc_eth0")));
+ EXPECT_CALL(*datapath_,
+ RemoveInboundIPv4DNAT(StrEq("eth0"),
+ IPv4AddressToString(kFirstArcEthGuestIP)));
+ EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arc_eth0")));
+
+ auto svc = NewService();
+ auto impl = std::make_unique<MockImpl>();
+ auto* mock_impl = impl.get();
+ svc->impl_ = std::move(impl);
+
+ EXPECT_CALL(*mock_impl, guest()).WillRepeatedly(Return(GuestMessage::ARC));
+ EXPECT_CALL(*mock_impl, IsStarted(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_impl, OnStopDevice(_));
+ svc->AddDevice("eth0");
+ svc->RemoveDevice("eth0");
+ EXPECT_TRUE(svc->devices_.find("eth0") == svc->devices_.end());
+}
+
+TEST_F(ArcServiceTest, VerifyAddrConfigs) {
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstArcEthHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth1"), kSecondEthHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_wlan0"), kFirstWifiHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_wlan1"), kSecondWifiHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_wwan0"), kFirstCellHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddInboundIPv4DNAT(_, _))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*datapath_, AddOutboundIPv4(_)).WillRepeatedly(Return(true));
+
+ auto svc = NewService();
+ auto impl = std::make_unique<MockImpl>();
+ auto* mock_impl = impl.get();
+ svc->impl_ = std::move(impl);
+
+ EXPECT_CALL(*mock_impl, guest()).WillRepeatedly(Return(GuestMessage::ARC));
+ EXPECT_CALL(*mock_impl, IsStarted(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_impl, OnStartDevice(_)).WillRepeatedly(Return(true));
+ svc->AddDevice("eth0");
+ svc->AddDevice("eth1");
+ svc->AddDevice("wlan0");
+ svc->AddDevice("wlan1");
+ svc->AddDevice("wwan0");
+}
+
+TEST_F(ArcServiceTest, VerifyAddrOrder) {
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstArcEthHostIP, 30))
+ .Times(2)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_wlan0"), kFirstWifiHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddInboundIPv4DNAT(_, _))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*datapath_, AddOutboundIPv4(_)).WillRepeatedly(Return(true));
+
+ auto svc = NewService();
+ auto impl = std::make_unique<MockImpl>();
+ auto* mock_impl = impl.get();
+ svc->impl_ = std::move(impl);
+
+ EXPECT_CALL(*mock_impl, guest()).WillRepeatedly(Return(GuestMessage::ARC));
+ EXPECT_CALL(*mock_impl, IsStarted(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_impl, OnStartDevice(_)).WillRepeatedly(Return(true));
+ svc->AddDevice("wlan0");
+ svc->AddDevice("eth0");
+ svc->RemoveDevice("eth0");
+ svc->AddDevice("eth0");
+}
+
+TEST_F(ArcServiceTest, StableArcVmMacAddrs) {
+ EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
+ .WillRepeatedly(Return("vmtap"));
+ EXPECT_CALL(*datapath_, AddBridge(_, _, 30)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(_, _)).WillRepeatedly(Return(true));
+
+ auto svc = NewService(GuestMessage::ARC_VM);
+ svc->Start(kTestCID);
+ auto configs = svc->GetDeviceConfigs();
+ EXPECT_EQ(configs.size(), 6);
+ auto mac_addr = kArcVmArc0MacAddr;
+ for (const auto* config : configs) {
+ EXPECT_EQ(config->mac_addr(), mac_addr);
+ mac_addr[5]++;
+ }
+}
+
+// ContainerImpl
+
+class ContainerImplTest : public testing::Test {
+ public:
+ ContainerImplTest() : testing::Test() {}
+
+ protected:
+ void SetUp() override {
+ addr_mgr_ = std::make_unique<AddressManager>();
+ runner_ = std::make_unique<FakeProcessRunner>();
+ runner_->Capture(false);
+ datapath_ = std::make_unique<MockDatapath>(runner_.get());
+ addr_mgr_ = std::make_unique<AddressManager>();
+ }
+
+ std::unique_ptr<ArcService::ContainerImpl> Impl(bool start = true) {
+ auto impl = std::make_unique<ArcService::ContainerImpl>(
+ datapath_.get(), addr_mgr_.get(), &forwarder_, GuestMessage::ARC);
+ if (start) {
+ // Set expectations for tests that want it started already.
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_,
+ AddVirtualInterfacePair(StrEq("veth_arc0"), StrEq("arc0")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, ConfigureInterface(StrEq("arc0"), _, kArcGuestIP,
+ 30, true, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, ToggleInterface(StrEq("veth_arc0"), true))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("veth_arc0")))
+ .WillOnce(Return(true));
+
+ impl->Start(kTestPID);
+ }
+ return impl;
+ }
+
+ std::unique_ptr<Device> MakeDevice(const std::string& name,
+ const std::string& host,
+ const std::string& guest) {
+ Device::Options opt{};
+ auto subnet = addr_mgr_->AllocateIPv4Subnet(AddressManager::Guest::ARC_NET);
+ auto addr0 = subnet->AllocateAtOffset(0);
+ auto addr1 = subnet->AllocateAtOffset(1);
+ auto cfg = std::make_unique<Device::Config>(
+ addr_mgr_->GenerateMacAddress(), std::move(subnet), std::move(addr0),
+ std::move(addr1));
+ return std::make_unique<Device>(name, host, guest, std::move(cfg), opt);
+ }
+
+ std::unique_ptr<AddressManager> addr_mgr_;
+ std::unique_ptr<MockDatapath> datapath_;
+ std::unique_ptr<FakeProcessRunner> runner_;
+ MockTrafficForwarder forwarder_;
+};
+
+TEST_F(ContainerImplTest, Start) {
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_,
+ AddVirtualInterfacePair(StrEq("veth_arc0"), StrEq("arc0")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_,
+ ConfigureInterface(StrEq("arc0"), _, kArcGuestIP, 30, true, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, ToggleInterface(StrEq("veth_arc0"), true))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("veth_arc0")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(forwarder_, StartForwarding(_, _, _, _)).Times(0);
+ Impl(false)->Start(kTestPID);
+}
+
+TEST_F(ContainerImplTest, Start_FailsToCreateInterface_Android) {
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_,
+ AddVirtualInterfacePair(StrEq("veth_arc0"), StrEq("arc0")))
+ .WillOnce(Return(false));
+ EXPECT_CALL(*datapath_, ConfigureInterface(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(*datapath_, RemoveBridge(_)).Times(0);
+ Impl(false)->Start(kTestPID);
+}
+
+TEST_F(ContainerImplTest, OnStartDevice_FailsToConfigureInterface_Android) {
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_,
+ AddVirtualInterfacePair(StrEq("veth_arc0"), StrEq("arc0")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_,
+ ConfigureInterface(StrEq("arc0"), _, kArcGuestIP, 30, true, _))
+ .WillOnce(Return(false));
+ EXPECT_CALL(*datapath_, ToggleInterface(StrEq("veth_arc0"), true)).Times(0);
+ EXPECT_CALL(*datapath_, RemoveInterface(StrEq("arc0")));
+ EXPECT_CALL(*datapath_, RemoveBridge(_)).Times(0);
+ Impl(false)->Start(kTestPID);
+}
+
+TEST_F(ContainerImplTest, OnStartDevice) {
+ EXPECT_CALL(*datapath_,
+ AddVirtualInterfacePair(StrEq("veth_eth0"), StrEq("eth0")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_,
+ ConfigureInterface(StrEq("eth0"), _, Ipv4Addr(100, 115, 92, 10),
+ 30, true, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, ToggleInterface(StrEq("veth_eth0"), true))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("veth_eth0")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(forwarder_,
+ StartForwarding(StrEq("eth0"), StrEq("arc_eth0"), _, _));
+ auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
+ ASSERT_TRUE(dev);
+ Impl()->OnStartDevice(dev.get());
+}
+
+TEST_F(ContainerImplTest, Stop) {
+ EXPECT_CALL(*datapath_,
+ MaskInterfaceFlags(StrEq("arcbr0"), IFF_DEBUG, IFF_UP));
+ EXPECT_CALL(*datapath_, RemoveInterface(StrEq("veth_arc0")));
+ EXPECT_CALL(forwarder_, StopForwarding(_, _, _, _)).Times(0);
+
+ Impl()->Stop(kTestPID);
+}
+
+TEST_F(ContainerImplTest, OnStopDevice) {
+ EXPECT_CALL(*datapath_, RemoveInterface(StrEq("veth_eth0")));
+ EXPECT_CALL(forwarder_,
+ StopForwarding(StrEq("eth0"), StrEq("arc_eth0"), _, _));
+
+ auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
+ ASSERT_TRUE(dev);
+ Impl()->OnStopDevice(dev.get());
+}
+
+// VM Impl
+
+class VmImplTest : public testing::Test {
+ public:
+ VmImplTest() : testing::Test() {}
+
+ protected:
+ void SetUp() override {
+ test::guest = GuestMessage::ARC_VM;
+ addr_mgr_ = std::make_unique<AddressManager>();
+ runner_ = std::make_unique<FakeProcessRunner>();
+ runner_->Capture(false);
+ datapath_ = std::make_unique<MockDatapath>(runner_.get());
+ shill_client_ = helper_.FakeClient();
+ shill_client_->SetFakeDefaultInterface("eth0");
+ addr_mgr_ = std::make_unique<AddressManager>();
+ }
+
+ std::unique_ptr<ArcService::VmImpl> Impl(
+ bool start = true, const std::vector<Device::Config*> configs = {}) {
+ auto impl = std::make_unique<ArcService::VmImpl>(
+ shill_client_.get(), datapath_.get(), addr_mgr_.get(), &forwarder_,
+ configs);
+ if (start) {
+ impl->Start(kTestCID);
+ }
+
+ return impl;
+ }
+
+ std::unique_ptr<Device> MakeDevice(const std::string& name,
+ const std::string& host,
+ const std::string& guest) {
+ Device::Options opt{};
+ auto subnet = addr_mgr_->AllocateIPv4Subnet(AddressManager::Guest::ARC_NET);
+ auto addr0 = subnet->AllocateAtOffset(0);
+ auto addr1 = subnet->AllocateAtOffset(1);
+ auto cfg = std::make_unique<Device::Config>(
+ addr_mgr_->GenerateMacAddress(), std::move(subnet), std::move(addr0),
+ std::move(addr1));
+ return std::make_unique<Device>(name, host, guest, std::move(cfg), opt);
+ }
+
+ std::unique_ptr<AddressManager> addr_mgr_;
+ std::unique_ptr<MockDatapath> datapath_;
+ std::unique_ptr<FakeProcessRunner> runner_;
+ std::unique_ptr<FakeShillClient> shill_client_;
+ FakeShillClientHelper helper_;
+ MockTrafficForwarder forwarder_;
+};
+
+TEST_F(VmImplTest, Start) {
+ EXPECT_CALL(*datapath_, AddTAP(StrEq(""), Pointee(Eq(kArcVmArc0MacAddr)),
+ nullptr, StrEq("crosvm")))
+ .WillOnce(Return("vmtap0"));
+ // OnStartDevice
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_br1"), kArcVmHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_br1"), StrEq("vmtap0")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_,
+ AddLegacyIPv4DNAT(StrEq(IPv4AddressToString(kArcVmGuestIP))))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddOutboundIPv4(StrEq("arc_br1")))
+ .WillOnce(Return(true));
+ // OnDefaultInterfaceChanged
+ EXPECT_CALL(forwarder_,
+ StopForwarding(StrEq(""), StrEq("arc_br1"), true, true));
+ EXPECT_CALL(*datapath_, RemoveLegacyIPv4InboundDNAT());
+ EXPECT_CALL(forwarder_,
+ StartForwarding(StrEq("eth0"), StrEq("arc_br1"), true, true));
+ EXPECT_CALL(*datapath_, AddLegacyIPv4InboundDNAT(StrEq("eth0")));
+
+ Impl(false)->Start(kTestCID);
+}
+
+// Verifies TAPs are added for each provided config.
+TEST_F(VmImplTest, StartWithConfigs) {
+ EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
+ .WillOnce(Return("vmtap0"))
+ .WillOnce(Return("vmtap1"))
+ .WillOnce(Return("vmtap2"));
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_br1"), kArcVmHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_br1"), StrEq("vmtap0")))
+ .WillOnce(Return(true));
+
+ Device::Config config1({0, 0, 0, 0, 0, 0}, nullptr, nullptr, nullptr);
+ Device::Config config2({0, 0, 0, 0, 0, 0}, nullptr, nullptr, nullptr);
+ Impl(false, {&config1, &config2})->Start(kTestCID);
+ EXPECT_EQ(config1.tap_ifname(), "vmtap1");
+ EXPECT_EQ(config2.tap_ifname(), "vmtap2");
+}
+
+TEST_F(VmImplTest, StartDeviceWithConfigs) {
+ EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
+ .WillOnce(Return("vmtap0"))
+ .WillOnce(Return("vmtap1"));
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_br1"), kArcVmHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_br1"), StrEq("vmtap0")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vmtap1")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(forwarder_,
+ StartForwarding(StrEq("eth0"), StrEq("arc_eth0"), _, _));
+
+ auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
+ auto* config = &dev->config();
+ Impl(true, {config})->OnStartDevice(dev.get());
+ EXPECT_EQ(config->tap_ifname(), "vmtap1");
+}
+
+TEST_F(VmImplTest, Stop) {
+ // Start
+ EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
+ .WillOnce(Return("vmtap0"));
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_br1"), kArcVmHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_br1"), StrEq("vmtap0")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_,
+ AddLegacyIPv4DNAT(StrEq(IPv4AddressToString(kArcVmGuestIP))))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddOutboundIPv4(StrEq("arc_br1")))
+ .WillOnce(Return(true));
+ // OnDefaultInterfaceChanged
+ EXPECT_CALL(forwarder_,
+ StopForwarding(StrEq(""), StrEq("arc_br1"), true, true));
+ EXPECT_CALL(forwarder_,
+ StartForwarding(StrEq("eth0"), StrEq("arc_br1"), true, true));
+ EXPECT_CALL(*datapath_, AddLegacyIPv4InboundDNAT(StrEq("eth0")));
+
+ // Stop
+ EXPECT_CALL(*datapath_, RemoveOutboundIPv4(StrEq("arc_br1")));
+ EXPECT_CALL(*datapath_, RemoveLegacyIPv4DNAT());
+ EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap0")));
+ EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arc_br1")));
+ // OnDefaultInterfaceChanged
+ EXPECT_CALL(*datapath_, RemoveLegacyIPv4InboundDNAT())
+ .Times(2); // +1 for Start
+ EXPECT_CALL(forwarder_,
+ StopForwarding(StrEq("eth0"), StrEq("arc_br1"), true, true));
+ Impl()->Stop(kTestCID);
+}
+
+// Verifies TAPs are added for each provided config.
+TEST_F(VmImplTest, StopWithConfigs) {
+ EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
+ .WillOnce(Return("vmtap0"))
+ .WillOnce(Return("vmtap1"))
+ .WillOnce(Return("vmtap2"));
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_br1"), kArcVmHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_br1"), StrEq("vmtap0")))
+ .WillOnce(Return(true));
+ // Stop
+ EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap0")));
+ EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap1")));
+ EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap2")));
+
+ Device::Config config1({0, 0, 0, 0, 0, 0}, nullptr, nullptr, nullptr);
+ Device::Config config2({0, 0, 0, 0, 0, 0}, nullptr, nullptr, nullptr);
+ Impl(true, {&config1, &config2})->Stop(kTestCID);
+ EXPECT_TRUE(config1.tap_ifname().empty());
+ EXPECT_TRUE(config2.tap_ifname().empty());
+}
+
+TEST_F(VmImplTest, StopDeviceWithConfigs) {
+ EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
+ .WillOnce(Return("vmtap0"))
+ .WillOnce(Return("vmtap1"));
+ EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_br1"), kArcVmHostIP, 30))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_br1"), StrEq("vmtap0")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(forwarder_,
+ StopForwarding(StrEq("eth0"), StrEq("arc_eth0"), _, _));
+
+ auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
+ auto* config = &dev->config();
+ config->set_tap_ifname("vmtap1"); // Usually happens in OnStartDevice.
+ Impl(true, {config})->OnStopDevice(dev.get());
+ EXPECT_TRUE(config->tap_ifname().empty());
+}
+
+} // namespace patchpanel