blob: fe5753307466b5a026ecf6fd34db73158344c750 [file] [log] [blame]
// Copyright 2019 The ChromiumOS Authors
// 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 <algorithm>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <base/functional/bind.h>
#include <base/functional/callback_helpers.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <metrics/metrics_library_mock.h>
#include <net-base/ipv4_address.h>
#include "patchpanel/address_manager.h"
#include "patchpanel/datapath.h"
#include "patchpanel/mock_datapath.h"
#include "patchpanel/shill_client.h"
using net_base::IPv4Address;
using net_base::IPv4CIDR;
using testing::_;
using testing::AnyNumber;
using testing::Eq;
using testing::Invoke;
using testing::Mock;
using testing::Pair;
using testing::Pointee;
using testing::Property;
using testing::Return;
using testing::ReturnRef;
using testing::StrEq;
using testing::UnorderedElementsAre;
namespace patchpanel {
namespace {
constexpr uint32_t kTestPID = 2;
constexpr uint32_t kTestCID = 2;
constexpr MacAddress kArcVmArc0MacAddr = {0x42, 0x37, 0x05, 0x13, 0x17, 0x01};
const IPv4CIDR kArcHostCIDR =
*IPv4CIDR::CreateFromCIDRString("100.115.92.1/30");
const IPv4CIDR kArcGuestCIDR =
*IPv4CIDR::CreateFromCIDRString("100.115.92.2/30");
const IPv4CIDR kFirstEthHostCIDR =
*IPv4CIDR::CreateFromCIDRString("100.115.92.5/30");
const IPv4Address kFirstEthGuestIP = IPv4Address(100, 115, 92, 6);
const IPv4CIDR kFirstEthGuestCIDR =
*IPv4CIDR::CreateFromAddressAndPrefix(kFirstEthGuestIP, 30);
const IPv4CIDR kSecondEthHostCIDR =
*IPv4CIDR::CreateFromCIDRString("100.115.92.9/30");
const IPv4CIDR kFirstWifiHostCIDR =
*IPv4CIDR::CreateFromCIDRString("100.115.92.13/30");
const IPv4CIDR kSecondWifiHostCIDR =
*IPv4CIDR::CreateFromCIDRString("100.115.92.17/30");
const IPv4CIDR kFirstCellHostCIDR =
*IPv4CIDR::CreateFromCIDRString("100.115.92.21/30");
ShillClient::Device MakeShillDevice(
const std::string& shill_device_interface_property,
ShillClient::Device::Type type,
std::optional<std::string> primary_multiplexed_interface = std::nullopt) {
ShillClient::Device dev;
dev.ifname = shill_device_interface_property;
dev.shill_device_interface_property = shill_device_interface_property;
dev.primary_multiplexed_interface = primary_multiplexed_interface;
dev.type = type;
return dev;
}
MATCHER_P(ShillDeviceHasInterfaceName, expected_ifname, "") {
return arg.ifname == expected_ifname;
}
} // namespace
class ArcServiceTest : public testing::Test {
public:
ArcServiceTest() : testing::Test() {}
protected:
void SetUp() override {
datapath_ = std::make_unique<MockDatapath>();
addr_mgr_ = std::make_unique<AddressManager>();
metrics_ = std::make_unique<MetricsLibraryMock>();
guest_devices_.clear();
shill_devices_.clear();
}
std::unique_ptr<ArcService> NewService(ArcService::ArcType arc_type) {
return std::make_unique<ArcService>(
datapath_.get(), addr_mgr_.get(), arc_type, metrics_.get(),
base::BindRepeating(&ArcServiceTest::DeviceHandler,
base::Unretained(this)));
}
void DeviceHandler(const ShillClient::Device& shill_device,
const Device& device,
Device::ChangeEvent event) {
guest_devices_[device.host_ifname()] = event;
shill_devices_[device.host_ifname()] = shill_device;
}
std::unique_ptr<AddressManager> addr_mgr_;
std::unique_ptr<MockDatapath> datapath_;
std::unique_ptr<MetricsLibraryMock> metrics_;
std::map<std::string, Device::ChangeEvent> guest_devices_;
std::map<std::string, ShillClient::Device> shill_devices_;
};
TEST_F(ArcServiceTest, NotStarted_AddDevice) {
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), _)).Times(0);
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc))
.Times(0);
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"), _))
.Times(0);
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->AddDevice(eth_dev);
EXPECT_TRUE(svc->devices_.find("eth0") == svc->devices_.end());
EXPECT_FALSE(svc->shill_devices_.find("eth0") == svc->shill_devices_.end());
}
TEST_F(ArcServiceTest, NotStarted_AddRemoveDevice) {
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), _)).Times(0);
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc))
.Times(0);
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"), _))
.Times(0);
EXPECT_CALL(*datapath_, StopRoutingDevice(StrEq("arc_eth0"))).Times(0);
EXPECT_CALL(*datapath_,
RemoveInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"), _))
.Times(0);
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arc_eth0"))).Times(0);
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->AddDevice(eth_dev);
svc->RemoveDevice(eth_dev);
EXPECT_TRUE(svc->devices_.find("eth0") == svc->devices_.end());
EXPECT_TRUE(svc->shill_devices_.find("eth0") == svc->shill_devices_.end());
}
TEST_F(ArcServiceTest, VerifyAddrConfigs) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth1"), kSecondEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_wlan0"), kFirstWifiHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_wlan1"), kSecondWifiHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_wwan0"), kFirstCellHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), _, _, _, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(_, _)).WillRepeatedly(Return(true));
auto eth0_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto eth1_dev = MakeShillDevice("eth1", ShillClient::Device::Type::kEthernet);
auto wlan0_dev = MakeShillDevice("wlan0", ShillClient::Device::Type::kWifi);
auto wlan1_dev = MakeShillDevice("wlan1", ShillClient::Device::Type::kWifi);
auto wwan_dev = MakeShillDevice("wwan0", ShillClient::Device::Type::kCellular,
"mbimmux0.1");
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
svc->AddDevice(eth0_dev);
svc->AddDevice(eth1_dev);
svc->AddDevice(wlan0_dev);
svc->AddDevice(wlan1_dev);
svc->AddDevice(wwan_dev);
}
TEST_F(ArcServiceTest, VerifyAddrOrder) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_wlan0"), kFirstWifiHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), _, _, _, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(_, _)).WillRepeatedly(Return(true));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto wlan_dev = MakeShillDevice("wlan0", ShillClient::Device::Type::kWifi);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
svc->AddDevice(wlan_dev);
svc->AddDevice(eth_dev);
svc->RemoveDevice(eth_dev);
svc->AddDevice(eth_dev);
}
TEST_F(ArcServiceTest, StableArcVmMacAddrs) {
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillRepeatedly(Return("vmtap"));
EXPECT_CALL(*datapath_, AddBridge(_, Property(&IPv4CIDR::prefix_length, 30)))
.WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(_, _)).WillRepeatedly(Return(true));
auto svc = NewService(ArcService::ArcType::kVM);
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
TEST_F(ArcServiceTest, ContainerImpl_Start) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, ContainerImpl_FailsToCreateInterface) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(false));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR)).Times(0);
EXPECT_CALL(*datapath_, RemoveBridge(_)).Times(0);
EXPECT_CALL(*datapath_, SetConntrackHelpers(_)).Times(0);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
EXPECT_FALSE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, ContainerImpl_FailsToAddInterfaceToBridge) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(false));
EXPECT_CALL(*datapath_, RemoveInterface(_)).Times(0);
EXPECT_CALL(*datapath_, RemoveBridge(_)).Times(0);
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).Times(0);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, ContainerImpl_OnStartDevice) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
// Expectations for arc0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetheth0"),
StrEq("eth0"), _, kFirstEthGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
svc->AddDevice(eth_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, ContainerImpl_GetDevices) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
// Expectations for arc0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto wlan_dev = MakeShillDevice("wlan0", ShillClient::Device::Type::kWifi);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
EXPECT_CALL(*datapath_, NetnsAttachName(_, _)).WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, ConnectVethPair(_, _, _, _, _, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, AddBridge(_, _)).WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(_, _)).WillRepeatedly(Return(true));
svc->AddDevice(eth_dev);
svc->AddDevice(wlan_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
const auto devs = svc->GetDevices();
EXPECT_EQ(devs.size(), 2);
const auto it1 = std::find_if(
devs.begin(), devs.end(),
[](const Device* dev) { return dev->shill_device()->ifname == "eth0"; });
ASSERT_NE(it1, devs.end());
EXPECT_EQ((*it1)->host_ifname(), "arc_eth0");
EXPECT_EQ((*it1)->guest_ifname(), "eth0");
EXPECT_EQ((*it1)->type(), Device::Type::kARCContainer);
const auto it2 = std::find_if(
devs.begin(), devs.end(),
[](const Device* dev) { return dev->shill_device()->ifname == "wlan0"; });
ASSERT_NE(it2, devs.end());
EXPECT_EQ((*it2)->host_ifname(), "arc_wlan0");
EXPECT_EQ((*it2)->guest_ifname(), "wlan0");
EXPECT_EQ((*it2)->type(), Device::Type::kARCContainer);
}
TEST_F(ArcServiceTest, ContainerImpl_DeviceHandler) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
// Expectations for arc0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto wlan_dev = MakeShillDevice("wlan0", ShillClient::Device::Type::kWifi);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
EXPECT_CALL(*datapath_, AddBridge(_, _)).WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(_, _)).WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, ConnectVethPair(_, _, _, _, _, _, _))
.WillRepeatedly(Return(true));
svc->AddDevice(eth_dev);
svc->AddDevice(wlan_dev);
EXPECT_EQ(guest_devices_.size(), 2);
EXPECT_THAT(guest_devices_,
UnorderedElementsAre(
Pair(StrEq("arc_eth0"), Device::ChangeEvent::kAdded),
Pair(StrEq("arc_wlan0"), Device::ChangeEvent::kAdded)));
guest_devices_.clear();
svc->RemoveDevice(wlan_dev);
EXPECT_THAT(guest_devices_,
UnorderedElementsAre(
Pair(StrEq("arc_wlan0"), Device::ChangeEvent::kRemoved)));
guest_devices_.clear();
svc->AddDevice(wlan_dev);
EXPECT_THAT(guest_devices_,
UnorderedElementsAre(
Pair(StrEq("arc_wlan0"), Device::ChangeEvent::kAdded)));
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, ContainerImpl_StartAfterDevice) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
// Expectations for arc0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(true));
// Expectations for eth0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetheth0"),
StrEq("eth0"), _, kFirstEthGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->AddDevice(eth_dev);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, ContainerImpl_IPConfigurationUpdate) {
auto svc = NewService(ArcService::ArcType::kContainer);
// New physical device eth0.
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
eth_dev.ipconfig.ipv4_cidr =
*net_base::IPv4CIDR::CreateFromCIDRString("192.168.1.16/24");
eth_dev.ipconfig.ipv4_gateway = net_base::IPv4Address(192, 168, 1, 1);
eth_dev.ipconfig.ipv4_dns_addresses = {"192.168.1.1", "8.8.8.8"};
svc->AddDevice(eth_dev);
// ArcService starts
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetheth0"),
StrEq("eth0"), _, kFirstEthGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
svc->Start(kTestPID);
Mock::VerifyAndClearExpectations(datapath_.get());
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
ASSERT_NE(shill_devices_.end(), shill_devices_.find("arc_eth0"));
EXPECT_EQ(*net_base::IPv4CIDR::CreateFromCIDRString("192.168.1.16/24"),
shill_devices_.find("arc_eth0")->second.ipconfig.ipv4_cidr);
EXPECT_EQ(net_base::IPv4Address(192, 168, 1, 1),
shill_devices_.find("arc_eth0")->second.ipconfig.ipv4_gateway);
eth_dev.ipconfig.ipv4_cidr =
*net_base::IPv4CIDR::CreateFromCIDRString("172.16.0.72/16");
eth_dev.ipconfig.ipv4_gateway = net_base::IPv4Address(172, 16, 0, 1);
eth_dev.ipconfig.ipv4_dns_addresses = {"172.17.1.1"};
svc->UpdateDeviceIPConfig(eth_dev);
// ArcService stops
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vetharc0"))).Times(1);
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arcbr0"))).Times(1);
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vetheth0"))).Times(1);
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arc_eth0"))).Times(1);
EXPECT_CALL(*datapath_, SetConntrackHelpers(false)).WillOnce(Return(true));
EXPECT_CALL(*datapath_, NetnsDeleteName(StrEq("arc_netns")))
.WillOnce(Return(true));
svc->Stop(kTestPID);
ASSERT_NE(shill_devices_.end(), shill_devices_.find("arc_eth0"));
EXPECT_EQ(*net_base::IPv4CIDR::CreateFromCIDRString("172.16.0.72/16"),
shill_devices_.find("arc_eth0")->second.ipconfig.ipv4_cidr);
EXPECT_EQ(net_base::IPv4Address(172, 16, 0, 1),
shill_devices_.find("arc_eth0")->second.ipconfig.ipv4_gateway);
}
TEST_F(ArcServiceTest, ContainerImpl_Stop) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
// Expectations for arc0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetheth0"),
StrEq("eth0"), _, kFirstEthGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0")))
.WillOnce(Return(true));
svc->AddDevice(eth_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for arc0 teardown.
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vetharc0"))).Times(1);
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arcbr0"))).Times(1);
// Expectations for eth0 teardown.
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vetheth0"))).Times(1);
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arc_eth0"))).Times(1);
// Expectations for container setup teardown.
EXPECT_CALL(*datapath_, SetConntrackHelpers(false)).WillOnce(Return(true));
EXPECT_CALL(*datapath_, NetnsDeleteName(StrEq("arc_netns")))
.WillOnce(Return(true));
svc->Stop(kTestPID);
EXPECT_FALSE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, ContainerImpl_OnStopDevice) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
// Expectations for arc0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(true));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetheth0"),
StrEq("eth0"), _, kFirstEthGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0")))
.WillOnce(Return(true));
svc->AddDevice(eth_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth0 teardown.
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vetheth0"))).Times(1);
EXPECT_CALL(*datapath_, StopRoutingDevice(StrEq("arc_eth0")));
EXPECT_CALL(*datapath_,
RemoveInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arc_eth0"))).Times(1);
svc->RemoveDevice(eth_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, ContainerImpl_Restart) {
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(true));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto svc = NewService(ArcService::ArcType::kContainer);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth0 setup.
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetheth0"),
StrEq("eth0"), _, kFirstEthGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
svc->AddDevice(eth_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for arc0, eth0, and arc netns teardown.
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vetharc0"))).Times(1);
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arcbr0"))).Times(1);
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vetheth0"))).Times(1);
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arc_eth0"))).Times(1);
EXPECT_CALL(*datapath_, SetConntrackHelpers(false)).WillOnce(Return(true));
EXPECT_CALL(*datapath_, NetnsDeleteName(StrEq("arc_netns")))
.WillOnce(Return(true));
svc->Stop(kTestPID);
EXPECT_FALSE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for arc0, eth0, and arc netns setup on restart.
EXPECT_CALL(*datapath_, NetnsAttachName(StrEq("arc_netns"), kTestPID))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetharc0"),
StrEq("arc0"), _, kArcGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ConnectVethPair(kTestPID, StrEq("arc_netns"), StrEq("vetheth0"),
StrEq("eth0"), _, kFirstEthGuestCIDR, false))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
}
// VM Impl
TEST_F(ArcServiceTest, VmImpl_Start) {
// Expectations for tap devices pre-creation.
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap0"))
.WillOnce(Return("vmtap1"))
.WillOnce(Return("vmtap2"))
.WillOnce(Return("vmtap3"))
.WillOnce(Return("vmtap4"))
.WillOnce(Return("vmtap5"));
// Expectations for "arc0" setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vmtap0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto svc = NewService(ArcService::ArcType::kVM);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, VmImpl_StartDevice) {
// Expectations for tap devices pre-creation.
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap0"))
.WillOnce(Return("vmtap1"))
.WillOnce(Return("vmtap2"))
.WillOnce(Return("vmtap3"))
.WillOnce(Return("vmtap4"))
.WillOnce(Return("vmtap5"));
// Expectations for "arc0" setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vmtap0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto svc = NewService(ArcService::ArcType::kVM);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth0 setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vmtap1")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
svc->AddDevice(eth_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, VmImpl_StartMultipleDevices) {
// Expectations for tap devices pre-creation.
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap0"))
.WillOnce(Return("vmtap1"))
.WillOnce(Return("vmtap2"))
.WillOnce(Return("vmtap3"))
.WillOnce(Return("vmtap4"))
.WillOnce(Return("vmtap5"));
// Expectations for "arc0" setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vmtap0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto eth0_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto eth1_dev = MakeShillDevice("eth1", ShillClient::Device::Type::kEthernet);
auto wlan_dev = MakeShillDevice("wlan0", ShillClient::Device::Type::kWifi);
auto svc = NewService(ArcService::ArcType::kVM);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth0 setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vmtap1")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
svc->AddDevice(eth0_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for wlan0 setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_wlan0"), kFirstWifiHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_wlan0"), StrEq("vmtap3")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("wlan0"),
StrEq("arc_wlan0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("wlan0"),
IPv4Address(100, 115, 92, 14)));
svc->AddDevice(wlan_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth1 setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth1"), kSecondEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth1"), StrEq("vmtap2")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth1"),
StrEq("arc_eth1"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth1"),
IPv4Address(100, 115, 92, 10)));
svc->AddDevice(eth1_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, VmImpl_Stop) {
// Expectations for tap devices pre-creation.
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap0"))
.WillOnce(Return("vmtap1"))
.WillOnce(Return("vmtap2"))
.WillOnce(Return("vmtap3"))
.WillOnce(Return("vmtap4"))
.WillOnce(Return("vmtap5"));
// Expectations for "arc0" setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vmtap0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto svc = NewService(ArcService::ArcType::kVM);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for "arc0" teardown.
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arcbr0"))).Times(1);
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vetharc0"))).Times(0);
// Expectations for tap devices teardown
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap0")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap1")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap2")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap3")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap4")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap5")));
EXPECT_CALL(*datapath_, SetConntrackHelpers(false)).WillOnce(Return(true));
svc->Stop(kTestPID);
EXPECT_FALSE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, VmImpl_Restart) {
// Expectations for tap devices pre-creation.
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap0"))
.WillOnce(Return("vmtap1"))
.WillOnce(Return("vmtap2"))
.WillOnce(Return("vmtap3"))
.WillOnce(Return("vmtap4"))
.WillOnce(Return("vmtap5"));
// Expectations for "arc0" setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vmtap0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto svc = NewService(ArcService::ArcType::kVM);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth0 setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vmtap1")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
svc->AddDevice(eth_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for arc0, eth0, and tap devices teardown.
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arcbr0"))).Times(1);
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vetharc0"))).Times(0);
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap0")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap1")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap2")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap3")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap4")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap5")));
EXPECT_CALL(*datapath_, SetConntrackHelpers(false)).WillOnce(Return(true));
EXPECT_CALL(*datapath_, StopRoutingDevice(StrEq("arc_eth0")));
EXPECT_CALL(*datapath_,
RemoveInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arc_eth0")));
svc->Stop(kTestPID);
EXPECT_FALSE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for arc0, eth0, and tap device pre-creation on restart.
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap0"))
.WillOnce(Return("vmtap1"))
.WillOnce(Return("vmtap2"))
.WillOnce(Return("vmtap3"))
.WillOnce(Return("vmtap4"))
.WillOnce(Return("vmtap5"));
// Expectations for "arc0" setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vmtap0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vmtap1")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, VmImpl_StopDevice) {
// Expectations for tap devices pre-creation.
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap0"))
.WillOnce(Return("vmtap1"))
.WillOnce(Return("vmtap2"))
.WillOnce(Return("vmtap3"))
.WillOnce(Return("vmtap4"))
.WillOnce(Return("vmtap5"));
// Expectations for "arc0" setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vmtap0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto svc = NewService(ArcService::ArcType::kVM);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth0 setup.
EXPECT_CALL(*datapath_, AddBridge(StrEq("arc_eth0"), kFirstEthHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vmtap1")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
StartRoutingDevice(ShillDeviceHasInterfaceName("eth0"),
StrEq("arc_eth0"), TrafficSource::kArc));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
svc->AddDevice(eth_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
// Expectations for eth0 teardown.
EXPECT_CALL(*datapath_, StopRoutingDevice(StrEq("arc_eth0")));
EXPECT_CALL(*datapath_,
RemoveInboundIPv4DNAT(AutoDNATTarget::kArc,
ShillDeviceHasInterfaceName("eth0"),
IPv4Address(100, 115, 92, 6)));
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arc_eth0")));
svc->RemoveDevice(eth_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, VmImpl_GetDevices) {
// Expectations for tap devices pre-creation.
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap0"))
.WillOnce(Return("vmtap1"))
.WillOnce(Return("vmtap2"))
.WillOnce(Return("vmtap3"))
.WillOnce(Return("vmtap4"))
.WillOnce(Return("vmtap5"));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vmtap0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto eth0_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto eth1_dev = MakeShillDevice("eth1", ShillClient::Device::Type::kEthernet);
auto wlan0_dev = MakeShillDevice("wlan0", ShillClient::Device::Type::kWifi);
auto svc = NewService(ArcService::ArcType::kVM);
svc->Start(kTestPID);
Mock::VerifyAndClearExpectations(datapath_.get());
EXPECT_CALL(*datapath_, AddBridge(_, _)).WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(_, _)).WillRepeatedly(Return(true));
svc->AddDevice(eth0_dev);
svc->AddDevice(eth1_dev);
svc->AddDevice(wlan0_dev);
Mock::VerifyAndClearExpectations(datapath_.get());
const auto devs = svc->GetDevices();
EXPECT_EQ(devs.size(), 3);
const auto it1 = std::find_if(
devs.begin(), devs.end(),
[](const Device* dev) { return dev->shill_device()->ifname == "eth0"; });
ASSERT_NE(it1, devs.end());
EXPECT_EQ((*it1)->host_ifname(), "arc_eth0");
EXPECT_EQ((*it1)->guest_ifname(), "eth1");
EXPECT_EQ((*it1)->type(), Device::Type::kARCVM);
const auto it2 = std::find_if(
devs.begin(), devs.end(),
[](const Device* dev) { return dev->shill_device()->ifname == "wlan0"; });
ASSERT_NE(it2, devs.end());
EXPECT_EQ((*it2)->host_ifname(), "arc_wlan0");
EXPECT_EQ((*it2)->guest_ifname(), "eth3");
EXPECT_EQ((*it2)->type(), Device::Type::kARCVM);
const auto it3 = std::find_if(
devs.begin(), devs.end(),
[](const Device* dev) { return dev->shill_device()->ifname == "eth1"; });
ASSERT_NE(it3, devs.end());
EXPECT_EQ((*it3)->host_ifname(), "arc_eth1");
EXPECT_EQ((*it3)->guest_ifname(), "eth2");
EXPECT_EQ((*it3)->type(), Device::Type::kARCVM);
}
TEST_F(ArcServiceTest, VmImpl_DeviceHandler) {
// Expectations for tap devices pre-creation.
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap0"))
.WillOnce(Return("vmtap1"))
.WillOnce(Return("vmtap2"))
.WillOnce(Return("vmtap3"))
.WillOnce(Return("vmtap4"))
.WillOnce(Return("vmtap5"));
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostCIDR))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vmtap0")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, SetConntrackHelpers(true)).WillOnce(Return(true));
auto eth_dev = MakeShillDevice("eth0", ShillClient::Device::Type::kEthernet);
auto wlan_dev = MakeShillDevice("wlan0", ShillClient::Device::Type::kWifi);
auto svc = NewService(ArcService::ArcType::kVM);
svc->Start(kTestPID);
EXPECT_TRUE(svc->IsStarted());
Mock::VerifyAndClearExpectations(datapath_.get());
EXPECT_CALL(*datapath_, AddBridge(_, _)).WillRepeatedly(Return(true));
EXPECT_CALL(*datapath_, AddToBridge(_, _)).WillRepeatedly(Return(true));
svc->AddDevice(eth_dev);
svc->AddDevice(wlan_dev);
EXPECT_EQ(guest_devices_.size(), 2);
EXPECT_THAT(guest_devices_,
UnorderedElementsAre(
Pair(StrEq("arc_eth0"), Device::ChangeEvent::kAdded),
Pair(StrEq("arc_wlan0"), Device::ChangeEvent::kAdded)));
guest_devices_.clear();
svc->RemoveDevice(wlan_dev);
EXPECT_THAT(guest_devices_,
UnorderedElementsAre(
Pair(StrEq("arc_wlan0"), Device::ChangeEvent::kRemoved)));
guest_devices_.clear();
svc->AddDevice(wlan_dev);
EXPECT_THAT(guest_devices_,
UnorderedElementsAre(
Pair(StrEq("arc_wlan0"), Device::ChangeEvent::kAdded)));
Mock::VerifyAndClearExpectations(datapath_.get());
}
TEST_F(ArcServiceTest, VmImpl_ArcvmInterfaceMapping) {
// Expectations for tap devices pre-creation.
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), _, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap2"))
.WillOnce(Return("vmtap3"))
.WillOnce(Return("vmtap4"))
.WillOnce(Return("vmtap5"))
.WillOnce(Return("vmtap6"))
.WillOnce(Return("vmtap8"));
auto svc = NewService(ArcService::ArcType::kVM);
svc->Start(kTestPID);
std::map<std::string, std::string> arcvm_guest_ifnames = {
{"vmtap2", "eth0"}, {"vmtap3", "eth1"}, {"vmtap4", "eth2"},
{"vmtap5", "eth3"}, {"vmtap6", "eth4"}, {"vmtap8", "eth5"},
};
for (const auto& [tap, arcvm_ifname] : arcvm_guest_ifnames) {
auto it = svc->arcvm_guest_ifnames_.find(tap);
EXPECT_TRUE(it != svc->arcvm_guest_ifnames_.end());
EXPECT_EQ(it->second, arcvm_ifname);
}
}
TEST_F(ArcServiceTest, ArcVethHostName) {
static struct {
std::string shill_device_interface_property;
std::string expected_veth_ifname;
} test_cases[] = {
{"eth0", "vetheth0"},
{"rmnet0", "vethrmnet0"},
{"rmnet_data0", "vethrmnet_data0"},
{"ifnamsiz_ifnam0", "vethifnamsiz_i0"},
{"exceeds_ifnamesiz_checkanyway", "vethexceeds_ify"},
};
for (const auto& tc : test_cases) {
ShillClient::Device device;
device.shill_device_interface_property = tc.shill_device_interface_property;
auto ifname = ArcService::ArcVethHostName(device);
EXPECT_EQ(tc.expected_veth_ifname, ifname);
EXPECT_LT(ifname.length(), IFNAMSIZ);
}
}
TEST_F(ArcServiceTest, ArcBridgeName) {
static struct {
std::string shill_device_interface_property;
std::string expected_bridge_name;
} test_cases[] = {
{"eth0", "arc_eth0"},
{"rmnet0", "arc_rmnet0"},
{"rmnet_data0", "arc_rmnet_data0"},
{"ifnamsiz_ifnam0", "arc_ifnamsiz_i0"},
{"ifnamesize0", "arc_ifnamesize0"},
{"if_namesize0", "arc_if_namesiz0"},
{"exceeds_ifnamesiz_checkanyway", "arc_exceeds_ify"},
};
for (const auto& tc : test_cases) {
ShillClient::Device device;
device.shill_device_interface_property = tc.shill_device_interface_property;
auto bridge = ArcService::ArcBridgeName(device);
EXPECT_EQ(tc.expected_bridge_name, bridge);
EXPECT_LT(bridge.length(), IFNAMSIZ);
}
}
} // namespace patchpanel