blob: c90bb05ef9f87966a00e1bd034b9a57b3bcf8163 [file] [log] [blame]
// 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 "arc/network/arc_service.h"
#include <utility>
#include <vector>
#include <base/bind.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "arc/network/device_manager.h"
#include "arc/network/fake_process_runner.h"
#include "arc/network/fake_shill_client.h"
#include "arc/network/mock_datapath.h"
#include "arc/network/net_util.h"
using testing::_;
using testing::Return;
using testing::ReturnRef;
using testing::StrEq;
namespace arc_networkd {
namespace {
constexpr pid_t kTestPID = -2;
constexpr char kTestPIDStr[] = "-2";
constexpr uint32_t kTestCID = 2;
constexpr uint32_t kArcHostIP = Ipv4Addr(100, 115, 92, 1);
constexpr uint32_t kArcGuestIP = Ipv4Addr(100, 115, 92, 2);
static AddressManager addr_mgr({
AddressManager::Guest::ARC,
AddressManager::Guest::ARC_NET,
});
class MockDeviceManager : public DeviceManagerBase {
public:
MockDeviceManager() = default;
~MockDeviceManager() = default;
MOCK_METHOD2(RegisterDeviceAddedHandler,
void(GuestMessage::GuestType, const DeviceHandler&));
MOCK_METHOD2(RegisterDeviceRemovedHandler,
void(GuestMessage::GuestType, const DeviceHandler&));
MOCK_METHOD2(RegisterDefaultInterfaceChangedHandler,
void(GuestMessage::GuestType,
const ShillClient::DefaultInterfaceChangeHandler&));
MOCK_METHOD2(RegisterDeviceIPv6AddressFoundHandler,
void(GuestMessage::GuestType, const DeviceHandler&));
MOCK_METHOD1(UnregisterAllGuestHandlers, void(GuestMessage::GuestType));
MOCK_METHOD1(OnGuestStart, void(GuestMessage::GuestType));
MOCK_METHOD1(OnGuestStop, void(GuestMessage::GuestType));
MOCK_METHOD1(ProcessDevices, void(const DeviceHandler&));
MOCK_CONST_METHOD1(Exists, bool(const std::string& name));
MOCK_CONST_METHOD1(FindByHostInterface, Device*(const std::string& ifname));
MOCK_CONST_METHOD1(FindByGuestInterface, Device*(const std::string& ifname));
MOCK_CONST_METHOD0(DefaultInterface, const std::string&());
MOCK_METHOD1(Add, bool(const std::string&));
MOCK_METHOD2(AddWithContext,
bool(const std::string&, std::unique_ptr<Device::Context>));
MOCK_METHOD1(Remove, bool(const std::string&));
MOCK_METHOD1(StartForwarding, void(const Device&));
MOCK_METHOD1(StopForwarding, void(const Device&));
MOCK_CONST_METHOD0(addr_mgr, AddressManager*());
};
std::unique_ptr<Device> MakeDevice(const std::string& name,
const std::string& host,
const std::string& guest,
bool is_arc = true) {
Device::Options opt{
.use_default_interface = (name == kAndroidLegacyDevice),
.is_android = (name == kAndroidDevice) || (name == kAndroidLegacyDevice),
};
auto subnet = addr_mgr.AllocateIPv4Subnet(
opt.is_android ? AddressManager::Guest::ARC
: AddressManager::Guest::ARC_NET);
auto addr0 = subnet->AllocateAtOffset(0);
auto addr1 = subnet->AllocateAtOffset(1);
auto cfg = std::make_unique<Device::Config>(
host, guest, addr_mgr.GenerateMacAddress(), std::move(subnet),
std::move(addr0), std::move(addr1));
return std::make_unique<Device>(
name, std::move(cfg), opt,
is_arc ? GuestMessage::ARC : GuestMessage::UNKNOWN_GUEST);
}
} // namespace
class ArcServiceTest : public testing::Test {
public:
ArcServiceTest()
: testing::Test(),
addr_mgr_({
AddressManager::Guest::ARC,
AddressManager::Guest::ARC_NET,
}) {}
protected:
void SetUp() override {
runner_ = std::make_unique<FakeProcessRunner>();
runner_->Capture(false);
datapath_ = std::make_unique<MockDatapath>(runner_.get());
shill_client_ = shill_helper_.Client();
}
std::unique_ptr<ArcService> NewService(bool arc_legacy = false) {
EXPECT_CALL(dev_mgr_, RegisterDeviceAddedHandler(_, _));
EXPECT_CALL(dev_mgr_, RegisterDeviceRemovedHandler(_, _));
EXPECT_CALL(dev_mgr_, RegisterDefaultInterfaceChangedHandler(_, _));
EXPECT_CALL(dev_mgr_, UnregisterAllGuestHandlers(_));
arc_networkd::test::guest =
arc_legacy ? GuestMessage::ARC_LEGACY : GuestMessage::ARC;
return std::make_unique<ArcService>(shill_client_.get(), &dev_mgr_,
datapath_.get());
}
FakeShillClientHelper shill_helper_;
std::unique_ptr<ShillClient> shill_client_;
AddressManager addr_mgr_;
MockDeviceManager dev_mgr_;
std::unique_ptr<MockDatapath> datapath_;
std::unique_ptr<FakeProcessRunner> runner_;
};
TEST_F(ArcServiceTest, VerifyOnDeviceAddedDatapathForLegacyAndroid) {
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostIP, 30))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddLegacyIPv4DNAT(StrEq("100.115.92.2")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddOutboundIPv4(StrEq("arcbr0")))
.WillOnce(Return(true));
auto dev = MakeDevice(kAndroidLegacyDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
NewService(true)->OnDeviceAdded(dev.get());
}
TEST_F(ArcServiceTest,
VerifyOnDeviceAddedDoesNothingLegacyAndroidOtherInterface) {
EXPECT_CALL(*datapath_, AddBridge(_, _, _)).Times(0);
EXPECT_CALL(*datapath_, AddLegacyIPv4DNAT(_)).Times(0);
EXPECT_CALL(*datapath_, AddOutboundIPv4(_)).Times(0);
// In ARC N, only the legacy interface is added.
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
NewService(true)->OnDeviceAdded(dev.get());
}
TEST_F(ArcServiceTest, VerifyOnDeviceAddedDoesNothingNonArcDevice) {
EXPECT_CALL(*datapath_, AddBridge(_, _, _)).Times(0);
EXPECT_CALL(*datapath_, AddLegacyIPv4DNAT(_)).Times(0);
EXPECT_CALL(*datapath_, AddOutboundIPv4(_)).Times(0);
auto dev = MakeDevice("foo0", "bar0", "baz0", false);
ASSERT_TRUE(dev);
NewService()->OnDeviceAdded(dev.get());
}
TEST_F(ArcServiceTest, VerifyOnDeviceRemovedDatapathForLegacyAndroid) {
EXPECT_CALL(*datapath_, RemoveOutboundIPv4(StrEq("arcbr0")));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("veth_arc0")));
auto dev = MakeDevice(kAndroidLegacyDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
NewService()->OnDeviceRemoved(dev.get());
}
TEST_F(ArcServiceTest, VerifyOnDeviceAddedDatapathForAndroid) {
EXPECT_CALL(*datapath_, AddBridge(StrEq("arcbr0"), kArcHostIP, 30))
.WillOnce(Return(true));
auto dev = MakeDevice(kAndroidDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
NewService()->OnDeviceAdded(dev.get());
}
TEST_F(ArcServiceTest, VerifyOnDeviceRemovedDatapathForAndroid) {
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("veth_arc0")));
auto dev = MakeDevice(kAndroidDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
NewService()->OnDeviceRemoved(dev.get());
}
TEST_F(ArcServiceTest, VerifyOnDeviceAddedDatapath) {
EXPECT_CALL(*datapath_,
AddBridge(StrEq("arc_eth0"), Ipv4Addr(100, 115, 92, 9), 30))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
AddInboundIPv4DNAT(StrEq("eth0"), StrEq("100.115.92.10")))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, AddOutboundIPv4(StrEq("arc_eth0")))
.WillOnce(Return(true));
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
NewService()->OnDeviceAdded(dev.get());
}
TEST_F(ArcServiceTest, VerifyOnDeviceRemovedDatapath) {
EXPECT_CALL(*datapath_, RemoveBridge(StrEq("arc_eth0")));
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
NewService()->OnDeviceRemoved(dev.get());
}
// ContainerImpl
class ContainerImplTest : public testing::Test {
public:
ContainerImplTest()
: testing::Test(),
addr_mgr_({
AddressManager::Guest::ARC,
AddressManager::Guest::ARC_NET,
}) {}
protected:
void SetUp() override {
runner_ = std::make_unique<FakeProcessRunner>();
runner_->Capture(false);
datapath_ = std::make_unique<MockDatapath>(runner_.get());
}
std::unique_ptr<ArcService::ContainerImpl> Impl(bool arc_legacy = false) {
auto impl = std::make_unique<ArcService::ContainerImpl>(
&dev_mgr_, datapath_.get(),
arc_legacy ? GuestMessage::ARC_LEGACY : GuestMessage::ARC);
impl->Start(kTestPID);
return impl;
}
AddressManager addr_mgr_;
MockDeviceManager dev_mgr_;
std::unique_ptr<MockDatapath> datapath_;
std::unique_ptr<FakeProcessRunner> runner_;
};
TEST_F(ContainerImplTest, OnStartDevice_LegacyAndroid) {
EXPECT_CALL(*datapath_,
AddVirtualBridgedInterface(StrEq("android"), _, StrEq("arcbr0")))
.WillOnce(Return("peer_android"));
EXPECT_CALL(*datapath_,
AddInterfaceToContainer(_, StrEq("peer_android"), StrEq("arc0"),
kArcGuestIP, 30, _))
.WillOnce(Return(true));
auto dev = MakeDevice(kAndroidLegacyDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
EXPECT_TRUE(Impl(true)->OnStartDevice(dev.get()));
runner_->VerifyWriteSentinel(kTestPIDStr);
}
TEST_F(ContainerImplTest, OnStartDevice_FailsToCreateInterface_LegacyAndroid) {
EXPECT_CALL(*datapath_,
AddVirtualBridgedInterface(StrEq("android"), _, StrEq("arcbr0")))
.WillOnce(Return(""));
EXPECT_CALL(*datapath_, AddInterfaceToContainer(_, _, _, _, _, _)).Times(0);
auto dev = MakeDevice(kAndroidLegacyDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
EXPECT_FALSE(Impl(true)->OnStartDevice(dev.get()));
}
TEST_F(ContainerImplTest,
OnStartDevice_FailsToAddInterfaceToContainer_LegacyAndroid) {
EXPECT_CALL(*datapath_,
AddVirtualBridgedInterface(StrEq("android"), _, StrEq("arcbr0")))
.WillOnce(Return("peer_android"));
EXPECT_CALL(*datapath_,
AddInterfaceToContainer(_, StrEq("peer_android"), StrEq("arc0"),
kArcGuestIP, 30, _))
.WillOnce(Return(false));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("peer_android")));
EXPECT_CALL(*datapath_, RemoveBridge(_)).Times(0);
auto dev = MakeDevice(kAndroidLegacyDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
EXPECT_FALSE(Impl(true)->OnStartDevice(dev.get()));
}
TEST_F(ContainerImplTest, OnStartDevice_Android) {
EXPECT_CALL(*datapath_,
AddVirtualBridgedInterface(StrEq("arc0"), _, StrEq("arcbr0")))
.WillOnce(Return("peer_arc0"));
EXPECT_CALL(*datapath_,
AddInterfaceToContainer(_, StrEq("peer_arc0"), StrEq("arc0"),
kArcGuestIP, 30, _))
.WillOnce(Return(true));
auto dev = MakeDevice(kAndroidDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
Impl()->OnStartDevice(dev.get());
runner_->VerifyWriteSentinel(kTestPIDStr);
}
TEST_F(ContainerImplTest, OnStartDevice_FailsToCreateInterface_Android) {
EXPECT_CALL(*datapath_,
AddVirtualBridgedInterface(StrEq("arc0"), _, StrEq("arcbr0")))
.WillOnce(Return(""));
EXPECT_CALL(*datapath_, AddInterfaceToContainer(_, _, _, _, _, _)).Times(0);
auto dev = MakeDevice(kAndroidDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
EXPECT_FALSE(Impl(true)->OnStartDevice(dev.get()));
}
TEST_F(ContainerImplTest,
OnStartDevice_FailsToAddInterfaceToContainer_Android) {
EXPECT_CALL(*datapath_,
AddVirtualBridgedInterface(StrEq("arc0"), _, StrEq("arcbr0")))
.WillOnce(Return("peer_arc0"));
EXPECT_CALL(*datapath_,
AddInterfaceToContainer(_, StrEq("peer_arc0"), StrEq("arc0"),
kArcGuestIP, 30, _))
.WillOnce(Return(false));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("peer_arc0")));
EXPECT_CALL(*datapath_, RemoveBridge(_)).Times(0);
auto dev = MakeDevice(kAndroidDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
EXPECT_FALSE(Impl(true)->OnStartDevice(dev.get()));
}
TEST_F(ContainerImplTest, OnStartDevice_Other) {
EXPECT_CALL(*datapath_,
AddVirtualBridgedInterface(StrEq("eth0"), _, StrEq("arc_eth0")))
.WillOnce(Return("peer_eth0"));
EXPECT_CALL(*datapath_,
AddInterfaceToContainer(_, StrEq("peer_eth0"), StrEq("eth0"),
Ipv4Addr(100, 115, 92, 10), 30, _))
.WillOnce(Return(true));
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
Impl()->OnStartDevice(dev.get());
}
TEST_F(ContainerImplTest, OnStartDevice_FailsToCreateInterface_Other) {
EXPECT_CALL(*datapath_,
AddVirtualBridgedInterface(StrEq("eth0"), _, StrEq("arc_eth0")))
.WillOnce(Return(""));
EXPECT_CALL(*datapath_, AddInterfaceToContainer(_, _, _, _, _, _)).Times(0);
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
EXPECT_FALSE(Impl(true)->OnStartDevice(dev.get()));
}
TEST_F(ContainerImplTest, OnStartDevice_FailsToAddInterfaceToContainer_Other) {
EXPECT_CALL(*datapath_,
AddVirtualBridgedInterface(StrEq("eth0"), _, StrEq("arc_eth0")))
.WillOnce(Return("peer_eth0"));
EXPECT_CALL(*datapath_,
AddInterfaceToContainer(_, StrEq("peer_eth0"), StrEq("eth0"),
Ipv4Addr(100, 115, 92, 10), 30, _))
.WillOnce(Return(false));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("peer_eth0")));
EXPECT_CALL(*datapath_, RemoveBridge(_)).Times(0);
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
EXPECT_FALSE(Impl(true)->OnStartDevice(dev.get()));
}
// Verifies the veth interface is not removed.
TEST_F(ContainerImplTest, OnStopDevice_LegacyAndroid) {
EXPECT_CALL(*datapath_, RemoveInterface(_)).Times(0);
auto dev = MakeDevice(kAndroidLegacyDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
Impl(true)->OnStopDevice(dev.get());
}
// Verifies the veth interface is not removed.
TEST_F(ContainerImplTest, OnStopDevice_Android) {
EXPECT_CALL(*datapath_, RemoveInterface(_)).Times(0);
auto dev = MakeDevice(kAndroidDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
Impl()->OnStopDevice(dev.get());
}
TEST_F(ContainerImplTest, OnStopDevice_Other) {
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("veth_eth0")));
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
Impl()->OnStopDevice(dev.get());
}
TEST_F(ContainerImplTest, OnDefaultInterfaceChanged_LegacyAndroid) {
auto dev = MakeDevice(kAndroidLegacyDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
EXPECT_CALL(*datapath_, RemoveLegacyIPv4InboundDNAT);
EXPECT_CALL(dev_mgr_, FindByGuestInterface(StrEq("arc0")))
.WillOnce(Return(dev.get()));
EXPECT_CALL(*datapath_, AddLegacyIPv4InboundDNAT("wlan0"))
.WillOnce(Return(true));
Impl(true)->OnDefaultInterfaceChanged("wlan0", "");
}
TEST_F(ContainerImplTest, OnDefaultInterfaceChanged_LegacyAndroidNoIfname) {
auto dev = MakeDevice(kAndroidLegacyDevice, "arcbr0", "arc0");
ASSERT_TRUE(dev);
EXPECT_CALL(*datapath_, RemoveLegacyIPv4InboundDNAT);
EXPECT_CALL(dev_mgr_, FindByGuestInterface(StrEq("arc0")))
.WillOnce(Return(dev.get()));
Impl(true)->OnDefaultInterfaceChanged("", "");
}
TEST_F(ContainerImplTest, OnDefaultInterfaceChanged_Other) {
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
// NOTE: It doesn't matter what device is returned here as long as it's
// non-null.
EXPECT_CALL(dev_mgr_, FindByGuestInterface(StrEq("wlan0")))
.WillOnce(Return(dev.get()));
Impl()->OnDefaultInterfaceChanged("wlan0", "");
}
// Nothing happens in this case since it's only concerned about (re)connecting
// to a network.
TEST_F(ContainerImplTest, OnDefaultInterfaceChanged_OtherNoIfname) {
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
Impl()->OnDefaultInterfaceChanged("", "");
}
// VM Impl
class VmImplTest : public testing::Test {
public:
VmImplTest()
: testing::Test(),
addr_mgr_({
AddressManager::Guest::ARC,
AddressManager::Guest::ARC_NET,
AddressManager::Guest::VM_ARC,
}) {}
protected:
void SetUp() override {
runner_ = std::make_unique<FakeProcessRunner>();
runner_->Capture(false);
datapath_ = std::make_unique<MockDatapath>(runner_.get());
}
std::unique_ptr<ArcService::VmImpl> Impl() {
auto impl =
std::make_unique<ArcService::VmImpl>(&dev_mgr_, datapath_.get());
impl->Start(kTestCID);
return impl;
}
AddressManager addr_mgr_;
MockDeviceManager dev_mgr_;
std::unique_ptr<MockDatapath> datapath_;
std::unique_ptr<FakeProcessRunner> runner_;
};
TEST_F(VmImplTest, OnStartDevice) {
// For now, ARCVM uses the legacy device since it behaves similarly.
auto dev = MakeDevice(kAndroidLegacyDevice, "arc_br1", "arc1");
ASSERT_TRUE(dev);
auto context = std::make_unique<ArcService::Context>();
auto* ctx = context.get();
dev->set_context(std::move(context));
EXPECT_CALL(*datapath_, AddTAP(StrEq(""), nullptr, nullptr, StrEq("crosvm")))
.WillOnce(Return("vmtap0"));
EXPECT_CALL(*datapath_, AddToBridge(StrEq("arc_br1"), StrEq("vmtap0")))
.WillOnce(Return(true));
static const std::string eth0 = "eth0";
EXPECT_CALL(dev_mgr_, DefaultInterface()).WillOnce(ReturnRef(eth0));
// OnDefaultInterfaceChanged
EXPECT_CALL(*datapath_, RemoveLegacyIPv4InboundDNAT());
EXPECT_CALL(dev_mgr_, FindByGuestInterface(StrEq("arc1")))
.WillRepeatedly(Return(dev.get()));
EXPECT_CALL(*datapath_, AddLegacyIPv4InboundDNAT(StrEq("eth0")));
EXPECT_TRUE(Impl()->OnStartDevice(dev.get()));
EXPECT_EQ(ctx->TAP(), "vmtap0");
}
TEST_F(VmImplTest, OnStartDevice_NoContext) {
// For now, ARCVM uses the legacy device since it behaves similarly.
auto dev = MakeDevice(kAndroidLegacyDevice, "arc_br1", "arc1");
ASSERT_TRUE(dev);
EXPECT_FALSE(Impl()->OnStartDevice(dev.get()));
}
TEST_F(VmImplTest, OnStartDevice_OtherDevice) {
// For now, ARCVM uses the legacy device since it behaves similarly.
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
EXPECT_FALSE(Impl()->OnStartDevice(dev.get()));
}
TEST_F(VmImplTest, OnStopDevice) {
// For now, ARCVM uses the legacy device since it behaves similarly.
auto dev = MakeDevice(kAndroidLegacyDevice, "arc_br1", "arc1");
ASSERT_TRUE(dev);
auto context = std::make_unique<ArcService::Context>();
auto* ctx = context.get();
ctx->SetTAP("vmtap0");
dev->set_context(std::move(context));
EXPECT_CALL(*datapath_, RemoveInterface(StrEq("vmtap0")));
Impl()->OnStopDevice(dev.get());
}
TEST_F(VmImplTest, OnStopDevice_OtherDevice) {
EXPECT_CALL(*datapath_, RemoveInterface(_)).Times(0);
// For now, ARCVM uses the legacy device since it behaves similarly.
auto dev = MakeDevice("eth0", "arc_eth0", "eth0");
ASSERT_TRUE(dev);
Impl()->OnStopDevice(dev.get());
}
} // namespace arc_networkd