| // 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 <array> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <chromeos/net-base/ip_address.h> |
| #include <chromeos/net-base/ipv4_address.h> |
| #include <chromeos/net-base/mac_address.h> |
| #include <chromeos/net-base/technology.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <metrics/metrics_library_mock.h> |
| #include <patchpanel/proto_bindings/patchpanel_service.pb.h> |
| |
| #include "patchpanel/address_manager.h" |
| #include "patchpanel/datapath.h" |
| #include "patchpanel/dbus_client_notifier.h" |
| #include "patchpanel/fake_process_runner.h" |
| #include "patchpanel/mock_datapath.h" |
| #include "patchpanel/mock_forwarding_service.h" |
| #include "patchpanel/noop_system.h" |
| #include "patchpanel/routing_service.h" |
| #include "patchpanel/shill_client.h" |
| |
| using net_base::IPv4Address; |
| using net_base::IPv4CIDR; |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::AnyOfArray; |
| using testing::Eq; |
| using testing::Invoke; |
| using testing::Mock; |
| using testing::NiceMock; |
| 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; |
| const IPv4CIDR kArc0HostCIDR = |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.1/30"); |
| const IPv4CIDR kArc0GuestCIDR = |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.2/30"); |
| const std::array<IPv4CIDR, 5> kArcPhysicalHostCIDRs = { |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.5/30"), |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.9/30"), |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.13/30"), |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.17/30"), |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.21/30")}; |
| const std::array<IPv4CIDR, 5> kArcPhysicalGuestCIDRs = { |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.6/30"), |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.10/30"), |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.14/30"), |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.18/30"), |
| *IPv4CIDR::CreateFromCIDRString("100.115.92.22/30")}; |
| const std::array<IPv4Address, 5> kArcPhysicalGuestIPs = { |
| IPv4Address(100, 115, 92, 6), IPv4Address(100, 115, 92, 10), |
| IPv4Address(100, 115, 92, 14), IPv4Address(100, 115, 92, 18), |
| IPv4Address(100, 115, 92, 22)}; |
| constexpr std::array<std::string_view, 6> kArcTapIfnames = { |
| "vmtap0", "vmtap1", "vmtap2", "vmtap3", "vmtap4", "vmtap5"}; |
| constexpr std::string_view kArc0TapIfname = kArcTapIfnames[0]; |
| constexpr std::array<std::string_view, 5> kArcPhysicalTapIfnames{ |
| kArcTapIfnames[1], kArcTapIfnames[2], kArcTapIfnames[3], kArcTapIfnames[4], |
| kArcTapIfnames[5]}; |
| |
| ShillClient::Device MakeShillDevice( |
| const std::string& shill_device_interface_property, |
| net_base::Technology technology, |
| std::optional<std::string> primary_multiplexed_interface = std::nullopt) { |
| ShillClient::Device dev; |
| dev.shill_device_interface_property = shill_device_interface_property; |
| dev.primary_multiplexed_interface = primary_multiplexed_interface; |
| dev.technology = technology; |
| dev.ifname = |
| primary_multiplexed_interface.value_or(shill_device_interface_property); |
| dev.service_logname = "mock_service_1"; |
| dev.logging_tag = dev.ifname + " " + dev.service_logname + " sid=1"; |
| return dev; |
| } |
| |
| MATCHER_P(IsShillDevice, expected_ifname, "") { |
| return !arg.primary_multiplexed_interface.has_value() && |
| arg.ifname == expected_ifname; |
| } |
| |
| MATCHER_P2(IsShillMultiplexedDevice, |
| expected_shill_device_ifname, |
| expected_ifname, |
| "") { |
| return arg.shill_device_interface_property == expected_shill_device_ifname && |
| arg.ifname == expected_ifname && arg.primary_multiplexed_interface && |
| arg.ifname == expected_ifname; |
| } |
| |
| } // namespace |
| |
| class ArcServiceTest : public testing::Test, |
| public patchpanel::DbusClientNotifier { |
| protected: |
| ArcServiceTest() : datapath_(&process_runner_, &system_) {} |
| |
| std::unique_ptr<ArcService> NewService(ArcService::ArcType arc_type) { |
| return std::make_unique<ArcService>(arc_type, &datapath_, &addr_mgr_, |
| &forwarding_service_, &metrics_, |
| &system_, this); |
| } |
| |
| void ArcDeviceEventHandler(const ShillClient::Device& shill_device, |
| const ArcService::ArcDevice& arc_device, |
| ArcService::ArcDeviceEvent event) {} |
| // DbusClientNotifier overrides |
| void OnNetworkDeviceChanged( |
| std::unique_ptr<NetworkDevice> virtual_device, |
| NetworkDeviceChangedSignal::Event event) override { |
| guest_device_events_[virtual_device->ifname()] = event; |
| network_device_signals_[virtual_device->ifname()].CopyFrom(*virtual_device); |
| } |
| void OnNetworkConfigurationChanged() override {} |
| void OnNeighborReachabilityEvent( |
| int ifindex, |
| const net_base::IPAddress& ip_addr, |
| NeighborLinkMonitor::NeighborRole role, |
| NeighborReachabilityEventSignal::EventType event_type) override {} |
| |
| FakeProcessRunner process_runner_; |
| NoopSystem system_; |
| AddressManager addr_mgr_; |
| NiceMock<MockDatapath> datapath_; |
| NiceMock<MockForwardingService> forwarding_service_; |
| NiceMock<MetricsLibraryMock> metrics_; |
| std::map<std::string, NetworkDeviceChangedSignal::Event> guest_device_events_; |
| std::map<std::string, NetworkDevice> network_device_signals_; |
| }; |
| |
| TEST_F(ArcServiceTest, Arc0IPAddress) { |
| auto svc = NewService(ArcService::ArcType::kVM); |
| ASSERT_TRUE(svc->GetArc0IPv4Address().has_value()); |
| EXPECT_EQ(*net_base::IPv4Address::CreateFromString("100.115.92.2"), |
| svc->GetArc0IPv4Address()); |
| } |
| |
| TEST_F(ArcServiceTest, NotStarted_AddDevice) { |
| EXPECT_CALL(datapath_, AddBridge).Times(0); |
| EXPECT_CALL(datapath_, StartRoutingDevice).Times(0); |
| EXPECT_CALL(datapath_, AddInboundIPv4DNAT).Times(0); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::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(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)) |
| .Times(0); |
| EXPECT_CALL(datapath_, AddInboundIPv4DNAT(AutoDNATTarget::kArc, |
| IsShillDevice("eth0"), _)) |
| .Times(0); |
| EXPECT_CALL(datapath_, StopRoutingDevice(StrEq("arc_eth0"), _)).Times(0); |
| EXPECT_CALL(datapath_, RemoveInboundIPv4DNAT(AutoDNATTarget::kArc, |
| IsShillDevice("eth0"), _)) |
| .Times(0); |
| EXPECT_CALL(datapath_, RemoveBridge(StrEq("arc_eth0"))).Times(0); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::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"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth1"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_wlan0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_wlan1"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_wwan0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, ConnectVethPair(kTestPID, StrEq("arc_netns"), _, _, _, |
| _, _, _, /*up=*/false)) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge).WillRepeatedly(Return(true)); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth1"), "arc_eth1", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth1"), "arc_eth1", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth1"), "arc_eth1")); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| MulticastForwarder::Direction::kOutboundOnly)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("wlan0"), "arc_wlan0")); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("wlan1"), "arc_wlan1", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("wlan1"), "arc_wlan1", |
| MulticastForwarder::Direction::kOutboundOnly)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("wlan1"), "arc_wlan1")); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartIPv6NDPForwarding(IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), |
| "arc_wwan0", Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL(forwarding_service_, |
| StartMulticastForwarding( |
| IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), "arc_wwan0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding( |
| IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), "arc_wwan0")) |
| .Times(0); |
| |
| auto eth0_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto eth1_dev = MakeShillDevice("eth1", net_base::Technology::kEthernet); |
| auto wlan0_dev = MakeShillDevice("wlan0", net_base::Technology::kWiFi); |
| auto wlan1_dev = MakeShillDevice("wlan1", net_base::Technology::kWiFi); |
| auto wwan_dev = |
| MakeShillDevice("wwan0", net_base::Technology::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"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .Times(2) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_wlan0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, ConnectVethPair(kTestPID, StrEq("arc_netns"), _, _, _, |
| _, _, _, /*up=*/false)) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge).WillRepeatedly(Return(true)); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto wlan_dev = MakeShillDevice("wlan0", net_base::Technology::kWiFi); |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| MulticastForwarder::Direction::kOutboundOnly)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("wlan0"), "arc_wlan0")); |
| svc->AddDevice(wlan_dev); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->AddDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| EXPECT_CALL(forwarding_service_, |
| StopIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| EXPECT_CALL(forwarding_service_, |
| StopMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StopBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->RemoveDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->AddDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| } |
| |
| TEST_F(ArcServiceTest, StableArcVmMacAddrs) { |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly(Return("vmtap")); |
| EXPECT_CALL(datapath_, AddBridge(_, Property(&IPv4CIDR::prefix_length, 30))) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge).WillRepeatedly(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| auto taps = svc->GetStaticTapDevices(); |
| EXPECT_EQ(taps.size(), 1); |
| } |
| |
| // 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, SetConntrackHelpers(true)).WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)).Times(0); |
| EXPECT_CALL(datapath_, RemoveBridge).Times(0); |
| EXPECT_CALL(datapath_, SetConntrackHelpers); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .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)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, SetConntrackHelpers(true)).WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for eth0 setup. |
| EXPECT_CALL(datapath_, ConnectVethPair(kTestPID, StrEq("arc_netns"), |
| StrEq("vetheth0"), StrEq("eth0"), _, |
| AnyOfArray(kArcPhysicalGuestCIDRs), _, |
| true, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| svc->AddDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| TEST_F(ArcServiceTest, ContainerImpl_OnStartCellularMultiplexedDevice) { |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, SetConntrackHelpers(true)).WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for mbimmux0.1 setup. |
| EXPECT_CALL(datapath_, ConnectVethPair(kTestPID, StrEq("arc_netns"), |
| StrEq("vethwwan0"), StrEq("wwan0"), _, |
| AnyOfArray(kArcPhysicalGuestCIDRs), _, |
| true, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_wwan0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_wwan0"), StrEq("vethwwan0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, StartRoutingDevice( |
| IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), |
| StrEq("arc_wwan0"), TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, AddInboundIPv4DNAT( |
| AutoDNATTarget::kArc, |
| IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartIPv6NDPForwarding(IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), |
| "arc_wwan0", Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding( |
| IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), "arc_wwan0")) |
| .Times(0); |
| EXPECT_CALL(forwarding_service_, |
| StartMulticastForwarding( |
| IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), "arc_wwan0", |
| MulticastForwarder::Direction::kTwoWays)); |
| |
| auto wwan_dev = |
| MakeShillDevice("wwan0", net_base::Technology::kCellular, "mbimmux0.1"); |
| svc->AddDevice(wwan_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .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", net_base::Technology::kEthernet); |
| auto wlan_dev = MakeShillDevice("wlan0", net_base::Technology::kWiFi); |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| |
| 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_); |
| |
| const auto devs = svc->GetDevices(); |
| EXPECT_EQ(devs.size(), 2); |
| |
| const auto it1 = std::find_if(devs.begin(), devs.end(), |
| [](const ArcService::ArcDevice* dev) { |
| return dev->shill_device_ifname() == "eth0"; |
| }); |
| ASSERT_NE(it1, devs.end()); |
| EXPECT_EQ((*it1)->arc_device_ifname(), "vetheth0"); |
| EXPECT_EQ((*it1)->bridge_ifname(), "arc_eth0"); |
| EXPECT_EQ((*it1)->guest_device_ifname(), "eth0"); |
| EXPECT_EQ((*it1)->type(), ArcService::ArcType::kContainer); |
| |
| const auto it2 = std::find_if(devs.begin(), devs.end(), |
| [](const ArcService::ArcDevice* dev) { |
| return dev->shill_device_ifname() == "wlan0"; |
| }); |
| ASSERT_NE(it2, devs.end()); |
| EXPECT_EQ((*it2)->arc_device_ifname(), "vethwlan0"); |
| EXPECT_EQ((*it2)->bridge_ifname(), "arc_wlan0"); |
| EXPECT_EQ((*it2)->guest_device_ifname(), "wlan0"); |
| EXPECT_EQ((*it2)->type(), ArcService::ArcType::kContainer); |
| } |
| |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, SetConntrackHelpers(true)).WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto wlan_dev = MakeShillDevice("wlan0", net_base::Technology::kWiFi); |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| EXPECT_CALL(datapath_, AddBridge).WillRepeatedly(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge).WillRepeatedly(Return(true)); |
| EXPECT_CALL(datapath_, ConnectVethPair).WillRepeatedly(Return(true)); |
| |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| MulticastForwarder::Direction::kOutboundOnly)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("wlan0"), "arc_wlan0")); |
| svc->AddDevice(eth_dev); |
| svc->AddDevice(wlan_dev); |
| EXPECT_EQ(guest_device_events_.size(), 2); |
| EXPECT_THAT( |
| guest_device_events_, |
| UnorderedElementsAre( |
| Pair(StrEq("arc_eth0"), NetworkDeviceChangedSignal::DEVICE_ADDED), |
| Pair(StrEq("arc_wlan0"), NetworkDeviceChangedSignal::DEVICE_ADDED))); |
| guest_device_events_.clear(); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // EXPECT_CALL(forwarding_service_, StopForwarding(IsShillDevice("wlan0"), _, |
| // kWiFiForwardingSet)); |
| svc->RemoveDevice(wlan_dev); |
| EXPECT_THAT( |
| guest_device_events_, |
| UnorderedElementsAre(Pair(StrEq("arc_wlan0"), |
| NetworkDeviceChangedSignal::DEVICE_REMOVED))); |
| guest_device_events_.clear(); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| MulticastForwarder::Direction::kOutboundOnly)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("wlan0"), "arc_wlan0")); |
| svc->AddDevice(wlan_dev); |
| EXPECT_THAT( |
| guest_device_events_, |
| UnorderedElementsAre( |
| Pair(StrEq("arc_wlan0"), NetworkDeviceChangedSignal::DEVICE_ADDED))); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| } |
| |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .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"), _, |
| AnyOfArray(kArcPhysicalGuestCIDRs), _, |
| true, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->AddDevice(eth_dev); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| } |
| |
| TEST_F(ArcServiceTest, ContainerImpl_IPConfigurationUpdate) { |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| |
| // New physical device eth0. |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| eth_dev.network_config.ipv4_address = |
| *net_base::IPv4CIDR::CreateFromCIDRString("192.168.1.16/24"); |
| eth_dev.network_config.ipv4_gateway = net_base::IPv4Address(192, 168, 1, 1); |
| eth_dev.network_config.dns_servers = { |
| *net_base::IPAddress::CreateFromString("192.168.1.1"), |
| *net_base::IPAddress::CreateFromString("8.8.8.8")}; |
| // guest IP and host IP addresses in network order. |
| std::vector<uint32_t> guest_ips, host_ips; |
| for (const auto& cidr : kArcPhysicalGuestCIDRs) { |
| guest_ips.push_back(cidr.address().ToInAddr().s_addr); |
| } |
| for (const auto& cidr : kArcPhysicalHostCIDRs) { |
| host_ips.push_back(cidr.address().ToInAddr().s_addr); |
| } |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .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"), _, |
| AnyOfArray(kArcPhysicalGuestCIDRs), _, |
| true, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->Start(kTestPID); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| auto device_signal_it = network_device_signals_.find("arc_eth0"); |
| ASSERT_NE(network_device_signals_.end(), device_signal_it); |
| EXPECT_THAT(device_signal_it->second.ipv4_addr(), AnyOfArray(guest_ips)); |
| EXPECT_THAT(device_signal_it->second.host_ipv4_addr(), AnyOfArray(host_ips)); |
| |
| eth_dev.network_config.ipv4_address = |
| *net_base::IPv4CIDR::CreateFromCIDRString("172.16.0.72/16"); |
| eth_dev.network_config.ipv4_gateway = net_base::IPv4Address(172, 16, 0, 1); |
| eth_dev.network_config.dns_servers = { |
| *net_base::IPAddress::CreateFromString("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)); |
| EXPECT_CALL(forwarding_service_, |
| StopIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| EXPECT_CALL(forwarding_service_, |
| StopMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StopBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->Stop(kTestPID); |
| device_signal_it = network_device_signals_.find("arc_eth0"); |
| ASSERT_NE(network_device_signals_.end(), device_signal_it); |
| EXPECT_THAT(device_signal_it->second.ipv4_addr(), AnyOfArray(guest_ips)); |
| EXPECT_THAT(device_signal_it->second.host_ipv4_addr(), AnyOfArray(host_ips)); |
| } |
| |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .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", net_base::Technology::kEthernet); |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| |
| // Expectations for eth0 setup. |
| EXPECT_CALL(datapath_, ConnectVethPair(kTestPID, StrEq("arc_netns"), |
| StrEq("vetheth0"), StrEq("eth0"), _, |
| AnyOfArray(kArcPhysicalGuestCIDRs), _, |
| true, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| |
| svc->AddDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // 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)); |
| EXPECT_CALL(forwarding_service_, |
| StopIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| EXPECT_CALL(forwarding_service_, |
| StopMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StopBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| |
| svc->Stop(kTestPID); |
| EXPECT_FALSE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for eth0 setup. |
| EXPECT_CALL(datapath_, ConnectVethPair(kTestPID, StrEq("arc_netns"), |
| StrEq("vetheth0"), StrEq("eth0"), _, |
| AnyOfArray(kArcPhysicalGuestCIDRs), _, |
| true, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| |
| svc->AddDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for eth0 teardown. |
| EXPECT_CALL(datapath_, RemoveInterface(StrEq("vetheth0"))).Times(1); |
| EXPECT_CALL(datapath_, |
| StopRoutingDevice(StrEq("arc_eth0"), TrafficSource::kArc)); |
| EXPECT_CALL(datapath_, |
| RemoveInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(datapath_, RemoveBridge(StrEq("arc_eth0"))).Times(1); |
| EXPECT_CALL(forwarding_service_, |
| StopIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| EXPECT_CALL(forwarding_service_, |
| StopMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StopBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| |
| svc->RemoveDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq("vetharc0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for eth0 setup. |
| EXPECT_CALL(datapath_, ConnectVethPair(kTestPID, StrEq("arc_netns"), |
| StrEq("vetheth0"), StrEq("eth0"), _, |
| AnyOfArray(kArcPhysicalGuestCIDRs), _, |
| true, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->AddDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // 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)); |
| EXPECT_CALL(forwarding_service_, |
| StopIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| EXPECT_CALL(forwarding_service_, |
| StopMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StopBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->Stop(kTestPID); |
| EXPECT_FALSE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // 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"), _, |
| kArc0GuestCIDR, _, _, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .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"), _, |
| AnyOfArray(kArcPhysicalGuestCIDRs), _, |
| true, /*up=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), StrEq("vetheth0"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| TEST_F(ArcServiceTest, ContainerImpl_WiFiMulticastForwarding) { |
| EXPECT_CALL(datapath_, NetnsAttachName).WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, ConnectVethPair).WillRepeatedly(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge).WillRepeatedly(Return(true)); |
| EXPECT_CALL(datapath_, SetConntrackHelpers).WillRepeatedly(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto svc = NewService(ArcService::ArcType::kContainer); |
| |
| EXPECT_FALSE(svc->IsWiFiMulticastForwardingRunning()); |
| svc->NotifyAndroidWifiMulticastLockChange(true); |
| EXPECT_FALSE(svc->IsWiFiMulticastForwardingRunning()); |
| |
| svc->Start(kTestPID); |
| EXPECT_TRUE(svc->IsStarted()); |
| EXPECT_FALSE(svc->IsWiFiMulticastForwardingRunning()); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Add WiFi Device. Lock is not taken yet. |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| MulticastForwarder::Direction::kOutboundOnly)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("wlan0"), "arc_wlan0")); |
| auto wlan0_dev = MakeShillDevice("wlan0", net_base::Technology::kWiFi); |
| svc->AddDevice(wlan0_dev); |
| EXPECT_FALSE(svc->IsWiFiMulticastForwardingRunning()); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Android Multicast lock is taken, this should only affects multicast |
| // traffic. |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| MulticastForwarder::Direction::kInboundOnly)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StopIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StopBroadcastForwarding).Times(0); |
| svc->NotifyAndroidWifiMulticastLockChange(true); |
| EXPECT_TRUE(svc->IsWiFiMulticastForwardingRunning()); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Android WiFi multicast lock is released. |
| EXPECT_CALL( |
| forwarding_service_, |
| StopMulticastForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| MulticastForwarder::Direction::kInboundOnly)); |
| svc->NotifyAndroidWifiMulticastLockChange(false); |
| EXPECT_FALSE(svc->IsWiFiMulticastForwardingRunning()); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| } |
| |
| // VM Impl |
| |
| TEST_F(ArcServiceTest, VmImpl_Start) { |
| // Expectations for tap devices pre-creation. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| // Expectations for "arc0" setup. |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq(kArc0TapIfname))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| TEST_F(ArcServiceTest, VmImpl_StartEthernetDevice) { |
| // Expectations for tap devices pre-creation. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| // Expectations for "arc0" setup. |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq(kArc0TapIfname))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for eth0 setup. |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), |
| AnyOfArray(kArcPhysicalTapIfnames))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| |
| svc->AddDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| TEST_F(ArcServiceTest, VmImpl_StartCellularMultiplexedDevice) { |
| // Expectations for tap devices pre-creation. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| // Expectations for "arc0" setup. |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq(kArc0TapIfname))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto wwan_dev = |
| MakeShillDevice("wwan0", net_base::Technology::kCellular, "mbimmux0.1"); |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for mbimmux0.1 setup. |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_wwan0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_wwan0"), |
| AnyOfArray(kArcPhysicalTapIfnames))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, StartRoutingDevice( |
| IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), |
| StrEq("arc_wwan0"), TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, AddInboundIPv4DNAT( |
| AutoDNATTarget::kArc, |
| IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartIPv6NDPForwarding(IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), |
| "arc_wwan0", Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding( |
| IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), "arc_wwan0")) |
| .Times(0); |
| EXPECT_CALL(forwarding_service_, |
| StartMulticastForwarding( |
| IsShillMultiplexedDevice("wwan0", "mbimmux0.1"), "arc_wwan0", |
| MulticastForwarder::Direction::kTwoWays)); |
| |
| svc->AddDevice(wwan_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| TEST_F(ArcServiceTest, VmImpl_StartMultipleDevices) { |
| // Expectations for tap devices pre-creation. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| // Expectations for "arc0" setup. |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq(kArc0TapIfname))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto eth0_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto eth1_dev = MakeShillDevice("eth1", net_base::Technology::kEthernet); |
| auto wlan_dev = MakeShillDevice("wlan0", net_base::Technology::kWiFi); |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for eth0 setup. |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), |
| AnyOfArray(kArcPhysicalTapIfnames))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| |
| svc->AddDevice(eth0_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for wlan0 setup. |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_wlan0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_wlan0"), |
| AnyOfArray(kArcPhysicalTapIfnames))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("wlan0"), StrEq("arc_wlan0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("wlan0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("wlan0"), "arc_wlan0")); |
| |
| svc->AddDevice(wlan_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for eth1 setup. |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth1"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth1"), |
| AnyOfArray(kArcPhysicalTapIfnames))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth1"), StrEq("arc_eth1"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth1"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth1"), "arc_eth1", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth1"), "arc_eth1", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth1"), "arc_eth1")); |
| |
| svc->AddDevice(eth1_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| TEST_F(ArcServiceTest, VmImpl_Stop) { |
| // Expectations for tap devices pre-creation. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| // Expectations for "arc0" setup. |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq(kArc0TapIfname))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // 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(kArc0TapIfname))); |
| for (const auto& tap_ifname : kArcPhysicalTapIfnames) { |
| EXPECT_CALL(datapath_, RemoveInterface(StrEq(tap_ifname))); |
| } |
| EXPECT_CALL(datapath_, SetConntrackHelpers(false)).WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StopIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StopBroadcastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StopMulticastForwarding).Times(0); |
| |
| svc->Stop(kTestCID); |
| EXPECT_FALSE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| TEST_F(ArcServiceTest, VmImpl_Restart) { |
| // Expectations for tap devices pre-creation. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| // Expectations for "arc0" setup. |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq(kArc0TapIfname))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for eth0 setup. |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), |
| AnyOfArray(kArcPhysicalTapIfnames))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->AddDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // 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(kArc0TapIfname))); |
| for (const auto& tap_ifname : kArcPhysicalTapIfnames) { |
| EXPECT_CALL(datapath_, RemoveInterface(StrEq(tap_ifname))); |
| } |
| EXPECT_CALL(datapath_, |
| StopRoutingDevice(StrEq("arc_eth0"), TrafficSource::kArc)); |
| EXPECT_CALL(datapath_, |
| RemoveInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(datapath_, RemoveBridge(StrEq("arc_eth0"))); |
| EXPECT_CALL(forwarding_service_, |
| StopIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| EXPECT_CALL(forwarding_service_, |
| StopMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StopBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->Stop(kTestCID); |
| EXPECT_FALSE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for arc0, eth0, and tap device pre-creation on restart. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| // Expectations for "arc0" setup. |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq(kArc0TapIfname))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), |
| AnyOfArray(kArcPhysicalTapIfnames))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| svc->Start(kTestCID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| TEST_F(ArcServiceTest, VmImpl_StopDevice) { |
| // Expectations for tap devices pre-creation. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| // Expectations for "arc0" setup. |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq(kArc0TapIfname))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for eth0 setup. |
| EXPECT_CALL(datapath_, |
| AddBridge(StrEq("arc_eth0"), AnyOfArray(kArcPhysicalHostCIDRs))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arc_eth0"), |
| AnyOfArray(kArcPhysicalTapIfnames))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, |
| StartRoutingDevice(IsShillDevice("eth0"), StrEq("arc_eth0"), |
| TrafficSource::kArc, |
| /*static_ipv6=*/false)); |
| EXPECT_CALL(datapath_, |
| AddInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| |
| svc->AddDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // Expectations for eth0 teardown. |
| EXPECT_CALL(datapath_, |
| StopRoutingDevice(StrEq("arc_eth0"), TrafficSource::kArc)); |
| EXPECT_CALL(datapath_, |
| RemoveInboundIPv4DNAT(AutoDNATTarget::kArc, IsShillDevice("eth0"), |
| AnyOfArray(kArcPhysicalGuestIPs))); |
| EXPECT_CALL(datapath_, RemoveBridge(StrEq("arc_eth0"))); |
| EXPECT_CALL(forwarding_service_, |
| StopIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| EXPECT_CALL(forwarding_service_, |
| StopMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StopBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| |
| svc->RemoveDevice(eth_dev); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| TEST_F(ArcServiceTest, VmImpl_GetDevices) { |
| // Expectations for tap devices pre-creation. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq(kArc0TapIfname))) |
| .WillOnce(Return(true)); |
| |
| auto eth0_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto eth1_dev = MakeShillDevice("eth1", net_base::Technology::kEthernet); |
| auto wlan0_dev = MakeShillDevice("wlan0", net_base::Technology::kWiFi); |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| |
| 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_); |
| |
| const auto devs = svc->GetDevices(); |
| EXPECT_EQ(devs.size(), 3); |
| |
| const std::map<std::string_view, std::string_view> arcvm_guest_ifnames = { |
| {kArc0TapIfname, "eth0"}, |
| {kArcPhysicalTapIfnames[0], "eth1"}, |
| {kArcPhysicalTapIfnames[1], "eth2"}, |
| {kArcPhysicalTapIfnames[2], "eth3"}, |
| {kArcPhysicalTapIfnames[3], "eth4"}, |
| {kArcPhysicalTapIfnames[4], "eth5"}, |
| }; |
| |
| const auto it1 = std::find_if(devs.begin(), devs.end(), |
| [](const ArcService::ArcDevice* dev) { |
| return dev->shill_device_ifname() == "eth0"; |
| }); |
| ASSERT_NE(it1, devs.end()); |
| EXPECT_EQ((*it1)->bridge_ifname(), "arc_eth0"); |
| EXPECT_EQ(arcvm_guest_ifnames.at((*it1)->arc_device_ifname()), |
| (*it1)->guest_device_ifname()); |
| EXPECT_EQ((*it1)->type(), ArcService::ArcType::kVM); |
| |
| const auto it2 = std::find_if(devs.begin(), devs.end(), |
| [](const ArcService::ArcDevice* dev) { |
| return dev->shill_device_ifname() == "wlan0"; |
| }); |
| ASSERT_NE(it2, devs.end()); |
| EXPECT_EQ((*it2)->bridge_ifname(), "arc_wlan0"); |
| EXPECT_EQ(arcvm_guest_ifnames.at((*it2)->arc_device_ifname()), |
| (*it2)->guest_device_ifname()); |
| EXPECT_EQ((*it2)->type(), ArcService::ArcType::kVM); |
| |
| const auto it3 = std::find_if(devs.begin(), devs.end(), |
| [](const ArcService::ArcDevice* dev) { |
| return dev->shill_device_ifname() == "eth1"; |
| }); |
| ASSERT_NE(it3, devs.end()); |
| EXPECT_EQ((*it3)->bridge_ifname(), "arc_eth1"); |
| EXPECT_EQ(arcvm_guest_ifnames.at((*it3)->arc_device_ifname()), |
| (*it3)->guest_device_ifname()); |
| EXPECT_EQ((*it3)->type(), ArcService::ArcType::kVM); |
| } |
| |
| TEST_F(ArcServiceTest, VmImpl_DeviceHandler) { |
| // Expectations for tap devices pre-creation. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| EXPECT_CALL(datapath_, AddBridge(StrEq("arcbr0"), kArc0HostCIDR)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge(StrEq("arcbr0"), StrEq(kArc0TapIfname))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(forwarding_service_, StartIPv6NDPForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartMulticastForwarding).Times(0); |
| EXPECT_CALL(forwarding_service_, StartBroadcastForwarding).Times(0); |
| |
| auto eth_dev = MakeShillDevice("eth0", net_base::Technology::kEthernet); |
| auto wlan_dev = MakeShillDevice("wlan0", net_base::Technology::kWiFi); |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| EXPECT_TRUE(svc->IsStarted()); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| EXPECT_CALL(datapath_, AddBridge).WillRepeatedly(Return(true)); |
| EXPECT_CALL(datapath_, AddToBridge).WillRepeatedly(Return(true)); |
| |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("eth0"), "arc_eth0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("eth0"), "arc_eth0", |
| MulticastForwarder::Direction::kTwoWays)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("eth0"), "arc_eth0")); |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| MulticastForwarder::Direction::kOutboundOnly)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("wlan0"), "arc_wlan0")); |
| svc->AddDevice(eth_dev); |
| svc->AddDevice(wlan_dev); |
| EXPECT_THAT( |
| guest_device_events_, |
| UnorderedElementsAre( |
| Pair(StrEq("arc_eth0"), NetworkDeviceChangedSignal::DEVICE_ADDED), |
| Pair(StrEq("arc_wlan0"), NetworkDeviceChangedSignal::DEVICE_ADDED))); |
| guest_device_events_.clear(); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| // EXPECT_CALL(forwarding_service_, StopForwarding(IsShillDevice("wlan0"), _, |
| // kWiFiForwardingSet)); |
| svc->RemoveDevice(wlan_dev); |
| EXPECT_THAT( |
| guest_device_events_, |
| UnorderedElementsAre(Pair(StrEq("arc_wlan0"), |
| NetworkDeviceChangedSignal::DEVICE_REMOVED))); |
| guest_device_events_.clear(); |
| Mock::VerifyAndClearExpectations(&forwarding_service_); |
| |
| EXPECT_CALL(forwarding_service_, |
| StartIPv6NDPForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| Eq(std::nullopt), Eq(std::nullopt))); |
| EXPECT_CALL( |
| forwarding_service_, |
| StartMulticastForwarding(IsShillDevice("wlan0"), "arc_wlan0", |
| MulticastForwarder::Direction::kOutboundOnly)); |
| EXPECT_CALL(forwarding_service_, |
| StartBroadcastForwarding(IsShillDevice("wlan0"), "arc_wlan0")); |
| svc->AddDevice(wlan_dev); |
| EXPECT_THAT( |
| guest_device_events_, |
| UnorderedElementsAre( |
| Pair(StrEq("arc_wlan0"), NetworkDeviceChangedSignal::DEVICE_ADDED))); |
| Mock::VerifyAndClearExpectations(&datapath_); |
| } |
| |
| TEST_F(ArcServiceTest, VmImpl_ArcvmInterfaceMapping) { |
| // Expectations for tap devices pre-creation. |
| EXPECT_CALL(datapath_, AddTunTap(StrEq(""), _, Eq(std::nullopt), |
| StrEq("crosvm"), DeviceMode::kTap)) |
| .WillRepeatedly([]() { |
| static auto tap_itr = kArcTapIfnames.begin(); |
| return std::string(*tap_itr++); |
| }); |
| |
| auto svc = NewService(ArcService::ArcType::kVM); |
| svc->Start(kTestCID); |
| |
| const std::map<std::string_view, std::string_view> arcvm_guest_ifnames = { |
| {kArc0TapIfname, "eth0"}, |
| {kArcPhysicalTapIfnames[0], "eth1"}, |
| {kArcPhysicalTapIfnames[1], "eth2"}, |
| {kArcPhysicalTapIfnames[2], "eth3"}, |
| {kArcPhysicalTapIfnames[3], "eth4"}, |
| {kArcPhysicalTapIfnames[4], "eth5"}, |
| }; |
| |
| for (const auto& [tap, arcvm_ifname] : arcvm_guest_ifnames) { |
| EXPECT_EQ(arcvm_ifname, |
| *svc->guest_if_manager_->GetGuestIfName(std::string(tap))); |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| TEST_F(ArcServiceTest, ConvertARCContainerWiFiDevice) { |
| const net_base::MacAddress mac_addr(addr_mgr_.GenerateMacAddress(0)); |
| auto ipv4_subnet = |
| addr_mgr_.AllocateIPv4Subnet(AddressManager::GuestType::kArcNet, 0); |
| auto expected_host_ipv4 = |
| ipv4_subnet->CIDRAtOffset(1)->address().ToInAddr().s_addr; |
| auto expected_guest_ipv4 = |
| ipv4_subnet->CIDRAtOffset(2)->address().ToInAddr().s_addr; |
| auto expected_base_cidr = ipv4_subnet->base_cidr(); |
| |
| ArcService::ArcConfig arc_config(mac_addr, std::move(ipv4_subnet)); |
| ArcService::ArcDevice arc_device( |
| ArcService::ArcType::kContainer, net_base::Technology::kWiFi, "wlan0", |
| "vethwlan0", mac_addr, arc_config, "arc_wlan0", "wlan0"); |
| NetworkDevice proto_device; |
| arc_device.ConvertToProto(&proto_device); |
| |
| ASSERT_EQ("arc_wlan0", proto_device.ifname()); |
| ASSERT_EQ("wlan0", proto_device.phys_ifname()); |
| // For ARC container, the name of the veth half set inside the container is |
| // renamed to match the name of the host upstream network interface managed by |
| // shill. |
| ASSERT_EQ("wlan0", proto_device.guest_ifname()); |
| ASSERT_EQ(NetworkDevice::WIFI, proto_device.technology_type()); |
| ASSERT_EQ(expected_guest_ipv4, proto_device.ipv4_addr()); |
| ASSERT_EQ(expected_host_ipv4, proto_device.host_ipv4_addr()); |
| ASSERT_EQ(expected_base_cidr.address(), |
| net_base::IPv4Address::CreateFromBytes( |
| proto_device.ipv4_subnet().addr())); |
| ASSERT_EQ(expected_base_cidr.prefix_length(), |
| proto_device.ipv4_subnet().prefix_len()); |
| ASSERT_EQ(NetworkDevice::ARC, proto_device.guest_type()); |
| } |
| |
| TEST_F(ArcServiceTest, ConvertARCContainerCellularDevice) { |
| const net_base::MacAddress mac_addr(addr_mgr_.GenerateMacAddress(0)); |
| auto ipv4_subnet = |
| addr_mgr_.AllocateIPv4Subnet(AddressManager::GuestType::kArcNet, 0); |
| auto expected_host_ipv4 = |
| ipv4_subnet->CIDRAtOffset(1)->address().ToInAddr().s_addr; |
| auto expected_guest_ipv4 = |
| ipv4_subnet->CIDRAtOffset(2)->address().ToInAddr().s_addr; |
| auto expected_base_cidr = ipv4_subnet->base_cidr(); |
| |
| ArcService::ArcConfig arc_config(mac_addr, std::move(ipv4_subnet)); |
| ArcService::ArcDevice arc_device( |
| ArcService::ArcType::kContainer, net_base::Technology::kCellular, "wwan0", |
| "vethwwan0", mac_addr, arc_config, "arc_wwan0", "wwan0"); |
| NetworkDevice proto_device; |
| arc_device.ConvertToProto(&proto_device); |
| |
| ASSERT_EQ("arc_wwan0", proto_device.ifname()); |
| ASSERT_EQ("wwan0", proto_device.phys_ifname()); |
| // For ARC container, the name of the veth half set inside the container is |
| // renamed to match the name of the host upstream network interface managed by |
| // shill. |
| ASSERT_EQ("wwan0", proto_device.guest_ifname()); |
| ASSERT_EQ(NetworkDevice::CELLULAR, proto_device.technology_type()); |
| ASSERT_EQ(expected_guest_ipv4, proto_device.ipv4_addr()); |
| ASSERT_EQ(expected_host_ipv4, proto_device.host_ipv4_addr()); |
| ASSERT_EQ(expected_base_cidr.address(), |
| net_base::IPv4Address::CreateFromBytes( |
| proto_device.ipv4_subnet().addr())); |
| ASSERT_EQ(expected_base_cidr.prefix_length(), |
| proto_device.ipv4_subnet().prefix_len()); |
| ASSERT_EQ(NetworkDevice::ARC, proto_device.guest_type()); |
| } |
| |
| TEST_F(ArcServiceTest, ConvertARCVMWiFiDevice) { |
| const net_base::MacAddress mac_addr(addr_mgr_.GenerateMacAddress(3)); |
| auto ipv4_subnet = |
| addr_mgr_.AllocateIPv4Subnet(AddressManager::GuestType::kArcNet, 0); |
| auto expected_host_ipv4 = |
| ipv4_subnet->CIDRAtOffset(1)->address().ToInAddr().s_addr; |
| auto expected_guest_ipv4 = |
| ipv4_subnet->CIDRAtOffset(2)->address().ToInAddr().s_addr; |
| auto expected_base_cidr = ipv4_subnet->base_cidr(); |
| |
| ArcService::ArcConfig arc_config(mac_addr, std::move(ipv4_subnet)); |
| ArcService::ArcDevice arc_device( |
| ArcService::ArcType::kVM, net_base::Technology::kWiFi, "wlan0", "vmtap1", |
| mac_addr, arc_config, "arc_wlan0", "eth3"); |
| NetworkDevice proto_device; |
| arc_device.ConvertToProto(&proto_device); |
| |
| ASSERT_EQ("arc_wlan0", proto_device.ifname()); |
| ASSERT_EQ("wlan0", proto_device.phys_ifname()); |
| // For ARCVM, the name of the virtio interface is controlled by the virtio |
| // driver and follows a ethernet-like pattern. |
| ASSERT_EQ("eth3", proto_device.guest_ifname()); |
| ASSERT_EQ(NetworkDevice::WIFI, proto_device.technology_type()); |
| ASSERT_EQ(expected_guest_ipv4, proto_device.ipv4_addr()); |
| ASSERT_EQ(expected_host_ipv4, proto_device.host_ipv4_addr()); |
| ASSERT_EQ(expected_base_cidr.address(), |
| net_base::IPv4Address::CreateFromBytes( |
| proto_device.ipv4_subnet().addr())); |
| ASSERT_EQ(expected_base_cidr.prefix_length(), |
| proto_device.ipv4_subnet().prefix_len()); |
| ASSERT_EQ(NetworkDevice::ARCVM, proto_device.guest_type()); |
| } |
| |
| TEST_F(ArcServiceTest, ConvertARCVMCellularDevice) { |
| const net_base::MacAddress mac_addr(addr_mgr_.GenerateMacAddress(3)); |
| auto ipv4_subnet = |
| addr_mgr_.AllocateIPv4Subnet(AddressManager::GuestType::kArcNet, 0); |
| auto expected_host_ipv4 = |
| ipv4_subnet->CIDRAtOffset(1)->address().ToInAddr().s_addr; |
| auto expected_guest_ipv4 = |
| ipv4_subnet->CIDRAtOffset(2)->address().ToInAddr().s_addr; |
| auto expected_base_cidr = ipv4_subnet->base_cidr(); |
| |
| ArcService::ArcConfig arc_config(mac_addr, std::move(ipv4_subnet)); |
| ArcService::ArcDevice arc_device( |
| ArcService::ArcType::kVM, net_base::Technology::kCellular, "wwan0", |
| "vmtap5", mac_addr, arc_config, "arc_wwan0", "eth5"); |
| NetworkDevice proto_device; |
| arc_device.ConvertToProto(&proto_device); |
| |
| ASSERT_EQ("arc_wwan0", proto_device.ifname()); |
| ASSERT_EQ("wwan0", proto_device.phys_ifname()); |
| // For ARCVM, the name of the virtio interface is controlled by the virtio |
| // driver and follows a ethernet-like pattern. |
| ASSERT_EQ("eth5", proto_device.guest_ifname()); |
| ASSERT_EQ(NetworkDevice::CELLULAR, proto_device.technology_type()); |
| ASSERT_EQ(expected_guest_ipv4, proto_device.ipv4_addr()); |
| ASSERT_EQ(expected_host_ipv4, proto_device.host_ipv4_addr()); |
| ASSERT_EQ(expected_base_cidr.address(), |
| net_base::IPv4Address::CreateFromBytes( |
| proto_device.ipv4_subnet().addr())); |
| ASSERT_EQ(expected_base_cidr.prefix_length(), |
| proto_device.ipv4_subnet().prefix_len()); |
| ASSERT_EQ(NetworkDevice::ARCVM, proto_device.guest_type()); |
| } |
| |
| TEST_F(ArcServiceTest, ConvertARC0ForARCContainer) { |
| const net_base::MacAddress mac_addr(addr_mgr_.GenerateMacAddress(0)); |
| auto ipv4_subnet = |
| addr_mgr_.AllocateIPv4Subnet(AddressManager::GuestType::kArc0, 0); |
| auto expected_host_ipv4 = |
| ipv4_subnet->CIDRAtOffset(1)->address().ToInAddr().s_addr; |
| auto expected_guest_ipv4 = |
| ipv4_subnet->CIDRAtOffset(2)->address().ToInAddr().s_addr; |
| auto expected_base_cidr = ipv4_subnet->base_cidr(); |
| |
| ArcService::ArcConfig arc_config(mac_addr, std::move(ipv4_subnet)); |
| ArcService::ArcDevice arc_device(ArcService::ArcType::kContainer, |
| std::nullopt, std::nullopt, "vetharc0", |
| mac_addr, arc_config, "arcbr0", "arc0"); |
| NetworkDevice proto_device; |
| arc_device.ConvertToProto(&proto_device); |
| |
| ASSERT_EQ("arcbr0", proto_device.ifname()); |
| // Convention for arc0 is to reuse the virtual interface name in |
| // place of the interface name of the upstream network used by other ARC |
| // Devices. |
| ASSERT_EQ("arc0", proto_device.phys_ifname()); |
| // For arc0 with ARC container, the name of the veth half inside ARC is set |
| // to "arc0" for legacy compatibility with old ARC N code, and ARC P code |
| // prior to ARC multinetworking support. |
| ASSERT_EQ("arc0", proto_device.guest_ifname()); |
| ASSERT_EQ(expected_guest_ipv4, proto_device.ipv4_addr()); |
| ASSERT_EQ(expected_host_ipv4, proto_device.host_ipv4_addr()); |
| ASSERT_EQ(expected_base_cidr.address(), |
| net_base::IPv4Address::CreateFromBytes( |
| proto_device.ipv4_subnet().addr())); |
| ASSERT_EQ(expected_base_cidr.prefix_length(), |
| proto_device.ipv4_subnet().prefix_len()); |
| ASSERT_EQ(NetworkDevice::ARC, proto_device.guest_type()); |
| } |
| |
| TEST_F(ArcServiceTest, ConvertARC0ForARCVM) { |
| const net_base::MacAddress mac_addr(addr_mgr_.GenerateMacAddress(0)); |
| auto ipv4_subnet = |
| addr_mgr_.AllocateIPv4Subnet(AddressManager::GuestType::kArc0, 0); |
| auto expected_host_ipv4 = |
| ipv4_subnet->CIDRAtOffset(1)->address().ToInAddr().s_addr; |
| auto expected_guest_ipv4 = |
| ipv4_subnet->CIDRAtOffset(2)->address().ToInAddr().s_addr; |
| auto expected_base_cidr = ipv4_subnet->base_cidr(); |
| |
| ArcService::ArcConfig arc_config(mac_addr, std::move(ipv4_subnet)); |
| ArcService::ArcDevice arc_device(ArcService::ArcType::kVM, std::nullopt, |
| std::nullopt, "vetharc0", mac_addr, |
| arc_config, "arcbr0", "eth0"); |
| NetworkDevice proto_device; |
| arc_device.ConvertToProto(&proto_device); |
| |
| ASSERT_EQ("arcbr0", proto_device.ifname()); |
| // Convention for arc0 is to reuse the virtual interface name in |
| // place of the interface name of the upstream network used by other ARC |
| // Devices. |
| ASSERT_EQ("arc0", proto_device.phys_ifname()); |
| // For arc0 with ARC container, the name of the veth half inside ARC is set |
| // to "arc0" for legacy compatibility with old ARC N code, and ARC P code |
| // prior to ARC multinetworking support. |
| ASSERT_EQ("eth0", proto_device.guest_ifname()); |
| ASSERT_EQ(expected_guest_ipv4, proto_device.ipv4_addr()); |
| ASSERT_EQ(expected_host_ipv4, proto_device.host_ipv4_addr()); |
| ASSERT_EQ(expected_base_cidr.address(), |
| net_base::IPv4Address::CreateFromBytes( |
| proto_device.ipv4_subnet().addr())); |
| ASSERT_EQ(expected_base_cidr.prefix_length(), |
| proto_device.ipv4_subnet().prefix_len()); |
| ASSERT_EQ(NetworkDevice::ARCVM, proto_device.guest_type()); |
| } |
| |
| } // namespace patchpanel |