| // Copyright 2018 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/device_info.h" |
| |
| #include <array> |
| #include <memory> |
| #include <optional> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include <linux/ethtool.h> |
| #include <linux/if.h> |
| #include <linux/if_link.h> |
| #include <linux/if_tun.h> |
| #include <linux/netlink.h> // Needs typedefs from sys/socket.h. |
| #include <linux/rtnetlink.h> |
| #include <linux/sockios.h> |
| #include <net/if_arp.h> |
| #include <sys/socket.h> |
| |
| #include <base/check.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/functional/bind.h> |
| #include <base/memory/ref_counted.h> |
| #include <base/notreached.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/test/bind.h> |
| #include <brillo/files/file_util.h> |
| #include <chromeos/patchpanel/dbus/fake_client.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <net-base/byte_utils.h> |
| #include <net-base/ipv4_address.h> |
| #include <net-base/ipv6_address.h> |
| #include <net-base/mac_address.h> |
| #include <net-base/mock_netlink_manager.h> |
| #include <net-base/mock_rtnl_handler.h> |
| #include <net-base/mock_socket.h> |
| #include <net-base/network_config.h> |
| #include <net-base/rtnl_message.h> |
| |
| #include "shill/cellular/mock_modem_info.h" |
| #include "shill/manager.h" |
| #include "shill/mock_control.h" |
| #include "shill/mock_device.h" |
| #include "shill/mock_log.h" |
| #include "shill/mock_manager.h" |
| #include "shill/mock_metrics.h" |
| #include "shill/network/mock_network.h" |
| #include "shill/network/network.h" |
| #include "shill/test_event_dispatcher.h" |
| #include "shill/vpn/vpn_provider.h" |
| #include "shill/wifi/nl80211_attribute.h" |
| #include "shill/wifi/nl80211_message.h" |
| |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::AnyOf; |
| using testing::ContainerEq; |
| using testing::DoAll; |
| using testing::ElementsAreArray; |
| using testing::Eq; |
| using testing::HasSubstr; |
| using testing::Invoke; |
| using testing::Mock; |
| using testing::NiceMock; |
| using testing::NotNull; |
| using testing::Return; |
| using testing::SetArgPointee; |
| using testing::StrictMock; |
| using testing::Test; |
| using testing::WithArg; |
| |
| namespace shill { |
| namespace { |
| |
| const net_base::IPAddress kTestIPAddress0 = |
| *net_base::IPAddress::CreateFromString("192.168.1.1"); |
| const net_base::IPAddress kTestIPAddress1 = |
| *net_base::IPAddress::CreateFromString("fe80::1aa9:5ff:abcd:1234"); |
| const net_base::IPAddress kTestIPAddress2 = |
| *net_base::IPAddress::CreateFromString("fe80::1aa9:5ff:abcd:1235"); |
| constexpr int kTestDeviceIndex = 123456; |
| constexpr char kTestDeviceName[] = "test-device"; |
| constexpr net_base::MacAddress kTestMacAddress0( |
| 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff); |
| constexpr net_base::MacAddress kTestMacAddress1( |
| 0x11, 0x22, 0x33, 0x44, 0x55, 0x66); |
| constexpr net_base::MacAddress kTestPermMacAddress( |
| 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc); |
| constexpr int kReceiveByteCount = 1234; |
| constexpr int kTransmitByteCount = 5678; |
| constexpr char kVendorIdString[] = "0x0123"; |
| constexpr char kProductIdString[] = "0x4567"; |
| constexpr char kSubsystemIdString[] = "0x89ab"; |
| constexpr char kInvalidIdString[] = "invalid"; |
| constexpr int kVendorId = 0x0123; |
| constexpr int kProductId = 0x4567; |
| constexpr int kSubsystemId = 0x89ab; |
| constexpr int kDefaultTestHardwareId = -42; |
| |
| } // namespace |
| |
| class DeviceInfoTest : public Test { |
| public: |
| DeviceInfoTest() |
| : manager_(&control_interface_, &dispatcher_, &metrics_), |
| device_info_(&manager_), |
| test_device_name_(kTestDeviceName) {} |
| ~DeviceInfoTest() override = default; |
| |
| void SetUp() override { |
| auto socket_factory = std::make_unique<net_base::MockSocketFactory>(); |
| socket_factory_ = socket_factory.get(); |
| EXPECT_CALL(manager_, device_info()).WillRepeatedly(Return(&device_info_)); |
| device_info_.set_socket_factory_for_test(std::move(socket_factory)); |
| |
| device_info_.rtnl_handler_ = &rtnl_handler_; |
| device_info_.netlink_manager_ = &netlink_manager_; |
| patchpanel_client_ = new patchpanel::FakeClient(); |
| manager_.patchpanel_client_.reset(patchpanel_client_); |
| CreateSysfsRoot(); |
| } |
| |
| DeviceRefPtr CreateDevice(const std::string& link_name, |
| std::optional<net_base::MacAddress> mac_address, |
| int interface_index, |
| Technology technology) { |
| return device_info_.CreateDevice(link_name, mac_address, interface_index, |
| technology); |
| } |
| |
| void RegisterDevice(const DeviceRefPtr& device) { |
| device_info_.RegisterDevice(device); |
| } |
| |
| virtual std::set<int>& GetDelayedDevices() { |
| return device_info_.delayed_devices_; |
| } |
| |
| // Takes ownership of |provider|. |
| void SetVPNProvider(VPNProvider* provider) { |
| manager_.vpn_provider_.reset(provider); |
| manager_.UpdateProviderMapping(); |
| } |
| |
| void SetManagerRunning(bool running) { manager_.running_ = running; } |
| |
| void CreateSysfsRoot() { |
| CHECK(temp_dir_.CreateUniqueTempDir()); |
| device_info_root_ = temp_dir_.GetPath().Append("sys/class/net"); |
| device_info_.device_info_root_ = device_info_root_; |
| } |
| |
| void CreateInfoFile(const std::string& name, const std::string& contents) { |
| base::FilePath info_path = GetInfoPath(name); |
| LOG(INFO) << "Path " << info_path; |
| EXPECT_TRUE(base::CreateDirectory(info_path.DirName())); |
| std::string contents_newline(contents + "\n"); |
| EXPECT_TRUE(base::WriteFile(info_path, contents_newline.c_str(), |
| contents_newline.size())); |
| } |
| |
| base::FilePath GetInfoPath(const std::string& name) { |
| return device_info_root_.Append(test_device_name_).Append(name); |
| } |
| |
| protected: |
| std::unique_ptr<net_base::RTNLMessage> BuildLinkMessage( |
| net_base::RTNLMessage::Mode mode); |
| std::unique_ptr<net_base::RTNLMessage> BuildLinkMessageWithInterfaceName( |
| net_base::RTNLMessage::Mode mode, |
| const std::string& interface_name, |
| int interface_index = kTestDeviceIndex); |
| void SendMessageToDeviceInfo(const net_base::RTNLMessage& message); |
| |
| void CreateWiFiDevice(); |
| |
| MockControl control_interface_; |
| MockMetrics metrics_; |
| StrictMock<MockManager> manager_; |
| DeviceInfo device_info_; |
| EventDispatcherForTest dispatcher_; |
| net_base::MockNetlinkManager netlink_manager_; |
| StrictMock<net_base::MockRTNLHandler> rtnl_handler_; |
| patchpanel::FakeClient* patchpanel_client_; // Owned by Manager |
| net_base::MockSocketFactory* socket_factory_; // Owned by DeviceInfo |
| |
| base::ScopedTempDir temp_dir_; |
| base::FilePath device_info_root_; |
| std::string test_device_name_; |
| }; |
| |
| std::unique_ptr<net_base::RTNLMessage> |
| DeviceInfoTest::BuildLinkMessageWithInterfaceName( |
| net_base::RTNLMessage::Mode mode, |
| const std::string& interface_name, |
| int interface_index) { |
| auto message = std::make_unique<net_base::RTNLMessage>( |
| net_base::RTNLMessage::kTypeLink, mode, 0, 0, 0, interface_index, |
| AF_INET); |
| message->SetAttribute( |
| static_cast<uint16_t>(IFLA_IFNAME), |
| net_base::byte_utils::StringToCStringBytes(interface_name)); |
| message->SetAttribute(IFLA_ADDRESS, kTestMacAddress0.ToBytes()); |
| message->SetAttribute(IFLA_PERM_ADDRESS, kTestPermMacAddress.ToBytes()); |
| return message; |
| } |
| |
| std::unique_ptr<net_base::RTNLMessage> DeviceInfoTest::BuildLinkMessage( |
| net_base::RTNLMessage::Mode mode) { |
| return BuildLinkMessageWithInterfaceName(mode, kTestDeviceName); |
| } |
| |
| void DeviceInfoTest::SendMessageToDeviceInfo( |
| const net_base::RTNLMessage& message) { |
| if (message.type() == net_base::RTNLMessage::kTypeLink) { |
| device_info_.LinkMsgHandler(message); |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| void DeviceInfoTest::CreateWiFiDevice() { |
| // Mock a WiFi adapter. |
| CreateInfoFile("uevent", "DEVTYPE=wlan"); |
| DeviceRefPtr device = CreateDevice(kTestDeviceName, kTestMacAddress0, |
| kTestDeviceIndex, Technology::kWiFi); |
| if (device) { |
| RegisterDevice(device); |
| } |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| message->set_link_status( |
| net_base::RTNLMessage::LinkStatus(0, IFF_LOWER_UP, 0)); |
| SendMessageToDeviceInfo(*message); |
| } |
| |
| MATCHER_P(IsIPAddress, address, "") { |
| // NB: IPAddress objects don't support the "==" operator as per style, so |
| // we need a custom matcher. |
| return address.Equals(arg); |
| } |
| |
| TEST_F(DeviceInfoTest, StartStop) { |
| auto& task_environment = dispatcher_.task_environment(); |
| EXPECT_EQ(nullptr, device_info_.link_listener_); |
| EXPECT_TRUE(device_info_.infos_.empty()); |
| |
| EXPECT_CALL(rtnl_handler_, RequestDump(net_base::RTNLHandler::kRequestLink)); |
| device_info_.Start(); |
| EXPECT_NE(nullptr, device_info_.link_listener_); |
| EXPECT_TRUE(device_info_.infos_.empty()); |
| Mock::VerifyAndClearExpectations(&rtnl_handler_); |
| |
| // Start() should set up a periodic task to request link statistics. |
| EXPECT_EQ(1, task_environment.GetPendingMainThreadTaskCount()); |
| EXPECT_CALL(rtnl_handler_, RequestDump(net_base::RTNLHandler::kRequestLink)); |
| task_environment.FastForwardBy( |
| task_environment.NextMainThreadPendingTaskDelay()); |
| EXPECT_EQ(1, task_environment.GetPendingMainThreadTaskCount()); |
| EXPECT_CALL(rtnl_handler_, RequestDump(net_base::RTNLHandler::kRequestLink)); |
| task_environment.FastForwardBy( |
| task_environment.NextMainThreadPendingTaskDelay()); |
| |
| device_info_.Stop(); |
| EXPECT_EQ(nullptr, device_info_.link_listener_); |
| EXPECT_TRUE(device_info_.infos_.empty()); |
| } |
| |
| TEST_F(DeviceInfoTest, RegisterDevice) { |
| scoped_refptr<MockDevice> device0( |
| new MockDevice(&manager_, "null0", kTestMacAddress0, kTestDeviceIndex)); |
| |
| EXPECT_CALL(*device0, Initialize()); |
| device_info_.RegisterDevice(device0); |
| } |
| |
| TEST_F(DeviceInfoTest, DeviceEnumeration) { |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| message->set_link_status( |
| net_base::RTNLMessage::LinkStatus(0, IFF_LOWER_UP, 0)); |
| EXPECT_EQ(nullptr, device_info_.GetDevice(kTestDeviceIndex)); |
| EXPECT_EQ(-1, device_info_.GetIndex(kTestDeviceName)); |
| SendMessageToDeviceInfo(*message); |
| EXPECT_NE(nullptr, device_info_.GetDevice(kTestDeviceIndex)); |
| unsigned int flags = 0; |
| EXPECT_TRUE(device_info_.GetFlags(kTestDeviceIndex, &flags)); |
| EXPECT_EQ(IFF_LOWER_UP, flags); |
| const auto address = device_info_.GetMacAddress(kTestDeviceIndex); |
| EXPECT_EQ(address, kTestMacAddress0); |
| EXPECT_EQ(kTestDeviceIndex, device_info_.GetIndex(kTestDeviceName)); |
| |
| message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| message->set_link_status( |
| net_base::RTNLMessage::LinkStatus(0, IFF_UP | IFF_RUNNING, 0)); |
| SendMessageToDeviceInfo(*message); |
| EXPECT_TRUE(device_info_.GetFlags(kTestDeviceIndex, &flags)); |
| EXPECT_EQ(IFF_UP | IFF_RUNNING, flags); |
| |
| message = BuildLinkMessage(net_base::RTNLMessage::kModeDelete); |
| EXPECT_CALL(manager_, DeregisterDevice(_)).Times(1); |
| SendMessageToDeviceInfo(*message); |
| EXPECT_EQ(nullptr, device_info_.GetDevice(kTestDeviceIndex)); |
| EXPECT_FALSE(device_info_.GetFlags(kTestDeviceIndex, nullptr)); |
| EXPECT_EQ(-1, device_info_.GetIndex(kTestDeviceName)); |
| } |
| |
| TEST_F(DeviceInfoTest, DeviceRemovedEvent) { |
| // Remove a Wifi device. |
| scoped_refptr<MockDevice> device0( |
| new MockDevice(&manager_, "null0", kTestMacAddress0, kTestDeviceIndex)); |
| device_info_.infos_[kTestDeviceIndex].device = device0; |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeDelete); |
| EXPECT_CALL(*device0, technology()).WillRepeatedly(Return(Technology::kWiFi)); |
| EXPECT_CALL(manager_, DeregisterDevice(_)).Times(1); |
| EXPECT_CALL(metrics_, DeregisterDevice(kTestDeviceIndex)).Times(1); |
| SendMessageToDeviceInfo(*message); |
| Mock::VerifyAndClearExpectations(device0.get()); |
| |
| // Remove a Cellular device. |
| scoped_refptr<MockDevice> device1( |
| new MockDevice(&manager_, "null0", kTestMacAddress0, kTestDeviceIndex)); |
| device_info_.infos_[kTestDeviceIndex].device = device1; |
| EXPECT_CALL(*device1, technology()) |
| .WillRepeatedly(Return(Technology::kCellular)); |
| EXPECT_CALL(manager_, DeregisterDevice(_)).Times(1); |
| EXPECT_CALL(metrics_, DeregisterDevice(kTestDeviceIndex)).Times(1); |
| message = BuildLinkMessage(net_base::RTNLMessage::kModeDelete); |
| SendMessageToDeviceInfo(*message); |
| } |
| |
| TEST_F(DeviceInfoTest, GetUninitializedTechnologies) { |
| std::vector<std::string> technologies = |
| device_info_.GetUninitializedTechnologies(); |
| std::set<std::string> expected_technologies; |
| |
| EXPECT_THAT(std::set<std::string>(technologies.begin(), technologies.end()), |
| ContainerEq(expected_technologies)); |
| |
| device_info_.infos_[0].technology = Technology::kUnknown; |
| EXPECT_THAT(std::set<std::string>(technologies.begin(), technologies.end()), |
| ContainerEq(expected_technologies)); |
| |
| device_info_.infos_[1].technology = Technology::kCellular; |
| technologies = device_info_.GetUninitializedTechnologies(); |
| expected_technologies.insert(TechnologyName(Technology::kCellular)); |
| EXPECT_THAT(std::set<std::string>(technologies.begin(), technologies.end()), |
| ContainerEq(expected_technologies)); |
| |
| device_info_.infos_[2].technology = Technology::kWiFi; |
| technologies = device_info_.GetUninitializedTechnologies(); |
| expected_technologies.insert(TechnologyName(Technology::kWiFi)); |
| EXPECT_THAT(std::set<std::string>(technologies.begin(), technologies.end()), |
| ContainerEq(expected_technologies)); |
| |
| scoped_refptr<MockDevice> device( |
| new MockDevice(&manager_, "null0", kTestMacAddress0, 1)); |
| device_info_.infos_[1].device = device; |
| technologies = device_info_.GetUninitializedTechnologies(); |
| expected_technologies.erase(TechnologyName(Technology::kCellular)); |
| EXPECT_THAT(std::set<std::string>(technologies.begin(), technologies.end()), |
| ContainerEq(expected_technologies)); |
| |
| device_info_.infos_[3].technology = Technology::kCellular; |
| technologies = device_info_.GetUninitializedTechnologies(); |
| EXPECT_THAT(std::set<std::string>(technologies.begin(), technologies.end()), |
| ContainerEq(expected_technologies)); |
| |
| device_info_.infos_[3].device = device; |
| device_info_.infos_[1].device = nullptr; |
| technologies = device_info_.GetUninitializedTechnologies(); |
| EXPECT_THAT(std::set<std::string>(technologies.begin(), technologies.end()), |
| ContainerEq(expected_technologies)); |
| } |
| |
| TEST_F(DeviceInfoTest, GetByteCounts) { |
| uint64_t rx_bytes, tx_bytes; |
| EXPECT_FALSE( |
| device_info_.GetByteCounts(kTestDeviceIndex, &rx_bytes, &tx_bytes)); |
| |
| // No link statistics in the message. |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| SendMessageToDeviceInfo(*message); |
| EXPECT_TRUE( |
| device_info_.GetByteCounts(kTestDeviceIndex, &rx_bytes, &tx_bytes)); |
| EXPECT_EQ(0, rx_bytes); |
| EXPECT_EQ(0, tx_bytes); |
| |
| // Short link statistics message. |
| message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| struct rtnl_link_stats64 stats; |
| memset(&stats, 0, sizeof(stats)); |
| stats.rx_bytes = kReceiveByteCount; |
| stats.tx_bytes = kTransmitByteCount; |
| message->SetAttribute(IFLA_STATS64, {reinterpret_cast<const uint8_t*>(&stats), |
| sizeof(stats) - 1}); |
| SendMessageToDeviceInfo(*message); |
| EXPECT_TRUE( |
| device_info_.GetByteCounts(kTestDeviceIndex, &rx_bytes, &tx_bytes)); |
| EXPECT_EQ(0, rx_bytes); |
| EXPECT_EQ(0, tx_bytes); |
| |
| // Correctly sized link statistics message. |
| message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| message->SetAttribute( |
| IFLA_STATS64, {reinterpret_cast<const uint8_t*>(&stats), sizeof(stats)}); |
| SendMessageToDeviceInfo(*message); |
| EXPECT_TRUE( |
| device_info_.GetByteCounts(kTestDeviceIndex, &rx_bytes, &tx_bytes)); |
| EXPECT_EQ(kReceiveByteCount, rx_bytes); |
| EXPECT_EQ(kTransmitByteCount, tx_bytes); |
| } |
| |
| TEST_F(DeviceInfoTest, CreateDeviceCellular) { |
| // A cellular device should be offered to ModemInfo. |
| StrictMock<MockModemInfo> modem_info(nullptr, nullptr); |
| EXPECT_CALL(manager_, modem_info()).WillOnce(Return(&modem_info)); |
| EXPECT_CALL(modem_info, OnDeviceInfoAvailable(kTestDeviceName)).Times(1); |
| EXPECT_FALSE(CreateDevice(kTestDeviceName, kTestMacAddress0, kTestDeviceIndex, |
| Technology::kCellular)); |
| } |
| |
| TEST_F(DeviceInfoTest, CreateDeviceEthernet) { |
| DeviceRefPtr device = CreateDevice(kTestDeviceName, kTestMacAddress0, |
| kTestDeviceIndex, Technology::kEthernet); |
| EXPECT_NE(nullptr, device); |
| Mock::VerifyAndClearExpectations(&rtnl_handler_); |
| |
| // The Ethernet device destructor should not call DeregisterService() |
| // while being destructed, since the Manager may itself be partially |
| // destructed at this time. |
| EXPECT_CALL(manager_, DeregisterService(_)).Times(0); |
| device = nullptr; |
| } |
| |
| TEST_F(DeviceInfoTest, CreateDeviceVirtioEthernet) { |
| // VirtioEthernet is identical to Ethernet from the perspective of this test. |
| DeviceRefPtr device = |
| CreateDevice(kTestDeviceName, kTestMacAddress0, kTestDeviceIndex, |
| Technology::kVirtioEthernet); |
| EXPECT_NE(nullptr, device); |
| Mock::VerifyAndClearExpectations(&rtnl_handler_); |
| } |
| |
| MATCHER_P(IsGetInterfaceMessage, index, "") { |
| if (arg->message_type() != Nl80211Message::GetMessageType()) { |
| return false; |
| } |
| const Nl80211Message* msg = reinterpret_cast<const Nl80211Message*>(arg); |
| if (msg->command() != NL80211_CMD_GET_INTERFACE) { |
| return false; |
| } |
| uint32_t interface_index; |
| if (!msg->const_attributes()->GetU32AttributeValue(NL80211_ATTR_IFINDEX, |
| &interface_index)) { |
| return false; |
| } |
| // kInterfaceIndex is signed, but the attribute as handed from the kernel |
| // is unsigned. We're silently casting it away with this assignment. |
| uint32_t test_interface_index = index; |
| return interface_index == test_interface_index; |
| } |
| |
| TEST_F(DeviceInfoTest, CreateDeviceWiFi) { |
| // Set the nl80211 message type to some non-default value. |
| Nl80211Message::SetMessageType(1234); |
| |
| EXPECT_CALL(netlink_manager_, |
| SendOrPostMessage(IsGetInterfaceMessage(kTestDeviceIndex), _)); |
| EXPECT_FALSE(CreateDevice(kTestDeviceName, kTestMacAddress0, kTestDeviceIndex, |
| Technology::kWiFi)); |
| } |
| |
| class MockLinkReadyListener { |
| public: |
| MOCK_METHOD(void, LinkReadyCallback, (const std::string&, int), ()); |
| |
| DeviceInfo::LinkReadyCallback GetOnceCallback() { |
| return base::BindOnce(&MockLinkReadyListener::LinkReadyCallback, |
| weak_factory_.GetWeakPtr()); |
| } |
| |
| private: |
| base::WeakPtrFactory<MockLinkReadyListener> weak_factory_{this}; |
| }; |
| |
| TEST_F(DeviceInfoTest, CreateDeviceTunnel) { |
| // We do not remove tunnel interfaces even if they are not claimed anywhere in |
| // shill. |
| MockLinkReadyListener listener; |
| device_info_.pending_links_.emplace(kTestDeviceName, |
| listener.GetOnceCallback()); |
| EXPECT_CALL(listener, LinkReadyCallback(kTestDeviceName, kTestDeviceIndex)) |
| .Times(1); |
| EXPECT_CALL(rtnl_handler_, RemoveInterface(_)).Times(0); |
| EXPECT_FALSE(CreateDevice(kTestDeviceName, kTestMacAddress0, kTestDeviceIndex, |
| Technology::kTunnel)); |
| } |
| |
| TEST_F(DeviceInfoTest, CreateDevicePPP) { |
| // We do not remove PPP interfaces even if the provider does not accept it. |
| EXPECT_CALL(rtnl_handler_, RemoveInterface(_)).Times(0); |
| EXPECT_FALSE(CreateDevice(kTestDeviceName, kTestMacAddress0, kTestDeviceIndex, |
| Technology::kPPP)); |
| } |
| |
| TEST_F(DeviceInfoTest, CreateDeviceLoopback) { |
| // A loopback device should be brought up, and nothing else done to it. |
| EXPECT_CALL(rtnl_handler_, RemoveInterfaceAddress(_, _)).Times(0); |
| EXPECT_CALL(rtnl_handler_, |
| SetInterfaceFlags(kTestDeviceIndex, IFF_UP, IFF_UP)) |
| .Times(1); |
| EXPECT_FALSE(CreateDevice(kTestDeviceName, kTestMacAddress0, kTestDeviceIndex, |
| Technology::kLoopback)); |
| } |
| |
| TEST_F(DeviceInfoTest, CreateDeviceCDCEthernet) { |
| // A cdc_ether / cdc_ncm device should be postponed to a task. |
| EXPECT_CALL(manager_, modem_info()).Times(0); |
| EXPECT_CALL(rtnl_handler_, RemoveInterfaceAddress(_, _)).Times(0); |
| EXPECT_TRUE(GetDelayedDevices().empty()); |
| EXPECT_FALSE(CreateDevice(kTestDeviceName, kTestMacAddress0, kTestDeviceIndex, |
| Technology::kCDCEthernet)); |
| EXPECT_FALSE(GetDelayedDevices().empty()); |
| EXPECT_EQ(1, GetDelayedDevices().size()); |
| EXPECT_EQ(kTestDeviceIndex, *GetDelayedDevices().begin()); |
| EXPECT_EQ(1, dispatcher_.task_environment().GetPendingMainThreadTaskCount()); |
| } |
| |
| TEST_F(DeviceInfoTest, CreateDeviceUnknown) { |
| // An unknown (blocked, unhandled, etc) device won't be flushed or |
| // registered. |
| EXPECT_CALL(rtnl_handler_, RemoveInterfaceAddress(_, _)).Times(0); |
| EXPECT_TRUE(CreateDevice(kTestDeviceName, kTestMacAddress0, kTestDeviceIndex, |
| Technology::kUnknown) |
| .get()); |
| } |
| |
| TEST_F(DeviceInfoTest, BlockedDevices) { |
| // Manager is not running by default. |
| EXPECT_CALL(rtnl_handler_, RequestDump(net_base::RTNLHandler::kRequestLink)) |
| .Times(0); |
| device_info_.BlockDevice(kTestDeviceName); |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| SendMessageToDeviceInfo(*message); |
| |
| DeviceRefPtr device = device_info_.GetDevice(kTestDeviceIndex); |
| ASSERT_NE(nullptr, device); |
| EXPECT_TRUE(device->technology() == Technology::kBlocked); |
| } |
| |
| TEST_F(DeviceInfoTest, BlockDeviceWithManagerRunning) { |
| SetManagerRunning(true); |
| EXPECT_CALL(rtnl_handler_, RequestDump(net_base::RTNLHandler::kRequestLink)) |
| .Times(1); |
| device_info_.BlockDevice(kTestDeviceName); |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| SendMessageToDeviceInfo(*message); |
| |
| DeviceRefPtr device = device_info_.GetDevice(kTestDeviceIndex); |
| ASSERT_NE(nullptr, device); |
| EXPECT_TRUE(device->technology() == Technology::kBlocked); |
| } |
| |
| TEST_F(DeviceInfoTest, RenamedBlockedDevice) { |
| device_info_.BlockDevice(kTestDeviceName); |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| SendMessageToDeviceInfo(*message); |
| |
| DeviceRefPtr device = device_info_.GetDevice(kTestDeviceIndex); |
| ASSERT_NE(nullptr, device); |
| EXPECT_TRUE(device->technology() == Technology::kBlocked); |
| |
| // Rename the test device. |
| const char kRenamedDeviceName[] = "renamed-device"; |
| auto rename_message = BuildLinkMessageWithInterfaceName( |
| net_base::RTNLMessage::kModeAdd, kRenamedDeviceName); |
| EXPECT_CALL(manager_, DeregisterDevice(_)); |
| EXPECT_CALL(metrics_, DeregisterDevice(kTestDeviceIndex)); |
| SendMessageToDeviceInfo(*rename_message); |
| |
| DeviceRefPtr renamed_device = device_info_.GetDevice(kTestDeviceIndex); |
| ASSERT_NE(nullptr, renamed_device); |
| |
| // Expect that a different device has been created. |
| EXPECT_NE(device, renamed_device); |
| |
| // Since we didn't create a uevent file for kRenamedDeviceName, its |
| // technology should be unknown. |
| EXPECT_TRUE(renamed_device->technology() == Technology::kUnknown); |
| } |
| |
| TEST_F(DeviceInfoTest, RenamedNonBlockedDevice) { |
| const char kInitialDeviceName[] = "initial-device"; |
| auto initial_message = BuildLinkMessageWithInterfaceName( |
| net_base::RTNLMessage::kModeAdd, kInitialDeviceName); |
| SendMessageToDeviceInfo(*initial_message); |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| |
| DeviceRefPtr initial_device = device_info_.GetDevice(kTestDeviceIndex); |
| ASSERT_NE(nullptr, initial_device); |
| |
| // Since we didn't create a uevent file for kInitialDeviceName, its |
| // technology should be unknown. |
| EXPECT_TRUE(initial_device->technology() == Technology::kUnknown); |
| |
| // Rename the test device. |
| const char kRenamedDeviceName[] = "renamed-device"; |
| device_info_.BlockDevice(kRenamedDeviceName); |
| auto rename_message = BuildLinkMessageWithInterfaceName( |
| net_base::RTNLMessage::kModeAdd, kRenamedDeviceName); |
| EXPECT_CALL(manager_, DeregisterDevice(_)).Times(0); |
| EXPECT_CALL(metrics_, DeregisterDevice(kTestDeviceIndex)).Times(0); |
| SendMessageToDeviceInfo(*rename_message); |
| |
| DeviceRefPtr renamed_device = device_info_.GetDevice(kTestDeviceIndex); |
| ASSERT_NE(nullptr, renamed_device); |
| |
| // Expect that the the presence of a renamed device does not cause a new |
| // Device entry to be created if the initial device was not blocked. |
| EXPECT_EQ(initial_device, renamed_device); |
| EXPECT_TRUE(initial_device->technology() == Technology::kUnknown); |
| } |
| |
| TEST_F(DeviceInfoTest, HasSubdir) { |
| base::ScopedTempDir temp_dir; |
| EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); |
| EXPECT_TRUE(base::CreateDirectory(temp_dir.GetPath().Append("child1"))); |
| base::FilePath child2 = temp_dir.GetPath().Append("child2"); |
| EXPECT_TRUE(base::CreateDirectory(child2)); |
| base::FilePath grandchild = child2.Append("grandchild"); |
| EXPECT_TRUE(base::CreateDirectory(grandchild)); |
| EXPECT_TRUE(base::CreateDirectory(grandchild.Append("greatgrandchild"))); |
| EXPECT_TRUE( |
| DeviceInfo::HasSubdir(temp_dir.GetPath(), base::FilePath("grandchild"))); |
| EXPECT_TRUE(DeviceInfo::HasSubdir(temp_dir.GetPath(), |
| base::FilePath("greatgrandchild"))); |
| EXPECT_FALSE( |
| DeviceInfo::HasSubdir(temp_dir.GetPath(), base::FilePath("nonexistent"))); |
| } |
| |
| TEST_F(DeviceInfoTest, GetMacAddressesFromKernelUnknownDevice) { |
| // We should not create socket when queying an unknown device. |
| EXPECT_CALL(*socket_factory_, Create(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) |
| .Times(0); |
| |
| const auto mac_address = |
| device_info_.GetMacAddressFromKernel(kTestDeviceIndex); |
| EXPECT_EQ(mac_address, std::nullopt); |
| const auto perm_mac_address = |
| device_info_.GetPermAddressFromKernel(kTestDeviceIndex); |
| EXPECT_EQ(perm_mac_address, std::nullopt); |
| } |
| |
| TEST_F(DeviceInfoTest, GetMacAddressesFromKernelUnableToOpenSocket) { |
| // Fails to create a socket. |
| EXPECT_CALL(*socket_factory_, Create(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) |
| .Times(2) |
| .WillOnce(Return(nullptr)); |
| |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| message->set_link_status( |
| net_base::RTNLMessage::LinkStatus(0, IFF_LOWER_UP, 0)); |
| SendMessageToDeviceInfo(*message); |
| EXPECT_NE(nullptr, device_info_.GetDevice(kTestDeviceIndex)); |
| const auto mac_address = |
| device_info_.GetMacAddressFromKernel(kTestDeviceIndex); |
| EXPECT_EQ(mac_address, std::nullopt); |
| const auto perm_mac_address = |
| device_info_.GetPermAddressFromKernel(kTestDeviceIndex); |
| EXPECT_EQ(perm_mac_address, std::nullopt); |
| } |
| |
| TEST_F(DeviceInfoTest, GetMacAddressesFromKernelIoctlFails) { |
| // Creates a socket successfully, but fails to call ioctl. |
| EXPECT_CALL(*socket_factory_, Create(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) |
| .Times(2) |
| .WillOnce([]() { |
| auto socket = std::make_unique<net_base::MockSocket>(); |
| EXPECT_CALL(*socket, |
| Ioctl(AnyOf(Eq(SIOCGIFHWADDR), Eq(SIOCETHTOOL)), NotNull())) |
| .WillOnce(Return(std::nullopt)); |
| return socket; |
| }); |
| |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| message->set_link_status( |
| net_base::RTNLMessage::LinkStatus(0, IFF_LOWER_UP, 0)); |
| SendMessageToDeviceInfo(*message); |
| EXPECT_NE(nullptr, device_info_.GetDevice(kTestDeviceIndex)); |
| |
| const auto mac_address = |
| device_info_.GetMacAddressFromKernel(kTestDeviceIndex); |
| EXPECT_EQ(mac_address, std::nullopt); |
| const auto perm_mac_address = |
| device_info_.GetPermAddressFromKernel(kTestDeviceIndex); |
| EXPECT_EQ(perm_mac_address, std::nullopt); |
| } |
| |
| MATCHER_P2(IfreqEquals, ifindex, ifname, "") { |
| const struct ifreq* const ifr = static_cast<struct ifreq*>(arg); |
| return (ifr != nullptr) && (ifindex < 0 || ifr->ifr_ifindex == ifindex) && |
| (strcmp(ifname, ifr->ifr_name) == 0); |
| } |
| |
| ACTION_P(SetIfreq, ifr) { |
| struct ifreq* const ifr_arg = static_cast<struct ifreq*>(arg1); |
| *ifr_arg = ifr; |
| } |
| |
| TEST_F(DeviceInfoTest, GetMacAddressFromKernel) { |
| static std::array<uint8_t, 6> kMacAddress = {0x00, 0x01, 0x02, |
| 0xaa, 0xbb, 0xcc}; |
| |
| EXPECT_CALL(*socket_factory_, Create(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) |
| .WillOnce([&]() { |
| struct ifreq ifr; |
| memcpy(ifr.ifr_hwaddr.sa_data, kMacAddress.data(), kMacAddress.size()); |
| |
| auto socket = std::make_unique<net_base::MockSocket>(); |
| EXPECT_CALL(*socket, Ioctl(SIOCGIFHWADDR, IfreqEquals(kTestDeviceIndex, |
| kTestDeviceName))) |
| .WillOnce(DoAll(SetIfreq(ifr), Return(0))); |
| |
| return socket; |
| }); |
| |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| message->set_link_status( |
| net_base::RTNLMessage::LinkStatus(0, IFF_LOWER_UP, 0)); |
| SendMessageToDeviceInfo(*message); |
| EXPECT_NE(nullptr, device_info_.GetDevice(kTestDeviceIndex)); |
| |
| const auto mac_address = |
| device_info_.GetMacAddressFromKernel(kTestDeviceIndex); |
| EXPECT_EQ(mac_address, net_base::MacAddress(kMacAddress)); |
| } |
| |
| TEST_F(DeviceInfoTest, GetPermAddressFromKernel) { |
| EXPECT_CALL(*socket_factory_, Create(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) |
| .WillOnce([&]() { |
| auto socket = std::make_unique<net_base::MockSocket>(); |
| EXPECT_CALL(*socket, |
| Ioctl(SIOCETHTOOL, IfreqEquals(-1, kTestDeviceName))) |
| .WillOnce(DoAll(WithArg<1>(Invoke([&](auto arg) { |
| auto ifreq = static_cast<struct ifreq*>(arg); |
| auto addr = static_cast<ethtool_perm_addr*>( |
| ifreq->ifr_data); |
| memcpy(addr->data, kTestPermMacAddress.data(), |
| net_base::MacAddress::kAddressLength); |
| addr->size = net_base::MacAddress::kAddressLength; |
| })), |
| Return(0))); |
| |
| return socket; |
| }); |
| |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| message->set_link_status( |
| net_base::RTNLMessage::LinkStatus(0, IFF_LOWER_UP, 0)); |
| SendMessageToDeviceInfo(*message); |
| EXPECT_NE(nullptr, device_info_.GetDevice(kTestDeviceIndex)); |
| |
| const auto perm_mac_address = |
| device_info_.GetPermAddressFromKernel(kTestDeviceIndex); |
| EXPECT_EQ(perm_mac_address, kTestPermMacAddress); |
| } |
| |
| MATCHER_P2(ArpreqEquals, ifname, peer, "") { |
| const struct arpreq* const areq = static_cast<struct arpreq*>(arg); |
| if (areq == nullptr) { |
| return false; |
| } |
| |
| const struct sockaddr_in* const protocol_address = |
| reinterpret_cast<const struct sockaddr_in*>(&areq->arp_pa); |
| const struct sockaddr_in* const mac_address = |
| reinterpret_cast<const struct sockaddr_in*>(&areq->arp_ha); |
| |
| return strcmp(ifname, areq->arp_dev) == 0 && |
| protocol_address->sin_family == AF_INET && |
| memcmp(&protocol_address->sin_addr.s_addr, |
| peer.address().GetConstData(), |
| peer.address().GetLength()) == 0 && |
| mac_address->sin_family == ARPHRD_ETHER; |
| } |
| |
| ACTION_P(SetArpreq, areq) { |
| struct arpreq* const areq_arg = static_cast<struct arpreq*>(arg2); |
| *areq_arg = areq; |
| } |
| |
| TEST_F(DeviceInfoTest, OnNeighborReachabilityEvent) { |
| device_info_.OnPatchpanelClientReady(); |
| |
| scoped_refptr<MockDevice> device0( |
| new MockDevice(&manager_, "null0", kTestMacAddress0, kTestDeviceIndex)); |
| device_info_.RegisterDevice(device0); |
| device0->set_network_for_testing(Network::CreateForTesting( |
| kTestDeviceIndex, "null0", Technology::kEthernet, |
| /*fixed_ip_params=*/false, |
| /*control_interface=*/&control_interface_, |
| /*dispatcher=*/&dispatcher_, |
| /*metrics=*/&metrics_, |
| /*patchpanel_client=*/patchpanel_client_)); |
| MockNetworkEventHandler event_handler0; |
| device0->GetPrimaryNetwork()->set_state_for_testing( |
| Network::State::kConnected); |
| device0->GetPrimaryNetwork()->RegisterEventHandler(&event_handler0); |
| |
| auto config0 = std::make_unique<net_base::NetworkConfig>(); |
| // Placeholder addresses to let Network believe this is a valid configuration. |
| config0->ipv4_address = net_base::IPv4CIDR::CreateFromAddressAndPrefix( |
| *kTestIPAddress0.ToIPv4Address(), 32); |
| config0->ipv4_gateway = kTestIPAddress0.ToIPv4Address(); |
| device0->GetPrimaryNetwork()->set_link_protocol_network_config( |
| std::move(config0)); |
| |
| scoped_refptr<MockDevice> device1(new MockDevice( |
| &manager_, "null1", kTestMacAddress1, kTestDeviceIndex + 1)); |
| MockNetworkEventHandler event_handler1; |
| device1->set_network_for_testing(Network::CreateForTesting( |
| kTestDeviceIndex + 1, "null1", Technology::kWiFi, |
| /*fixed_ip_params=*/false, |
| /*control_interface=*/&control_interface_, |
| /*dispatcher=*/&dispatcher_, |
| /*metrics=*/&metrics_, |
| /*patchpanel_client=*/patchpanel_client_)); |
| device_info_.RegisterDevice(device1); |
| device1->GetPrimaryNetwork()->set_state_for_testing( |
| Network::State::kConnected); |
| device1->GetPrimaryNetwork()->RegisterEventHandler(&event_handler1); |
| |
| auto config1 = std::make_unique<net_base::NetworkConfig>(); |
| config1->ipv6_addresses = {*net_base::IPv6CIDR::CreateFromAddressAndPrefix( |
| *kTestIPAddress2.ToIPv6Address(), 120)}; |
| config1->ipv6_gateway = kTestIPAddress2.ToIPv6Address(); |
| device1->GetPrimaryNetwork()->set_link_protocol_network_config( |
| std::move(config1)); |
| |
| using Role = patchpanel::Client::NeighborRole; |
| using Status = patchpanel::Client::NeighborStatus; |
| |
| patchpanel::Client::NeighborReachabilityEvent event0; |
| event0.ifindex = kTestDeviceIndex; |
| event0.ip_addr = kTestIPAddress0.ToString(); |
| event0.role = Role::kGateway; |
| event0.status = Status::kFailed; |
| EXPECT_CALL(event_handler0, |
| OnNeighborReachabilityEvent( |
| device0->GetPrimaryNetwork()->interface_index(), |
| kTestIPAddress0, Role::kGateway, Status::kFailed)); |
| patchpanel_client_->TriggerNeighborReachabilityEvent(event0); |
| Mock::VerifyAndClearExpectations(&event_handler0); |
| |
| patchpanel::Client::NeighborReachabilityEvent event1; |
| event1.ifindex = kTestDeviceIndex; |
| event1.ip_addr = kTestIPAddress1.ToString(); |
| event1.role = Role::kDnsServer; |
| event1.status = Status::kFailed; |
| EXPECT_CALL(event_handler0, |
| OnNeighborReachabilityEvent( |
| device0->GetPrimaryNetwork()->interface_index(), |
| kTestIPAddress1, Role::kDnsServer, Status::kFailed)); |
| patchpanel_client_->TriggerNeighborReachabilityEvent(event1); |
| Mock::VerifyAndClearExpectations(&event_handler0); |
| |
| patchpanel::Client::NeighborReachabilityEvent event2; |
| event2.ifindex = kTestDeviceIndex + 1; |
| event2.ip_addr = kTestIPAddress2.ToString(); |
| event2.role = Role::kGatewayAndDnsServer; |
| event2.status = Status::kReachable; |
| EXPECT_CALL( |
| event_handler1, |
| OnNeighborReachabilityEvent( |
| device1->GetPrimaryNetwork()->interface_index(), kTestIPAddress2, |
| Role::kGatewayAndDnsServer, Status::kReachable)); |
| patchpanel_client_->TriggerNeighborReachabilityEvent(event2); |
| |
| Mock::VerifyAndClearExpectations(&event_handler1); |
| |
| device0->GetPrimaryNetwork()->UnregisterEventHandler(&event_handler0); |
| device1->GetPrimaryNetwork()->UnregisterEventHandler(&event_handler1); |
| } |
| |
| TEST_F(DeviceInfoTest, CreateWireGuardInterface) { |
| const std::string kIfName = "wg0"; |
| const std::string kLinkKind = "wireguard"; |
| int link_ready_calls_num = 0; |
| int on_failure_calls_num = 0; |
| auto link_ready_cb = [&](const std::string&, int) { link_ready_calls_num++; }; |
| auto on_failure_cb = [&]() { on_failure_calls_num++; }; |
| |
| net_base::RTNLHandler::ResponseCallback registered_response_cb; |
| |
| auto call_create_wireguard_interface = [&]() { |
| return device_info_.CreateWireGuardInterface( |
| kIfName, base::BindLambdaForTesting(link_ready_cb), |
| base::BindLambdaForTesting(on_failure_cb)); |
| }; |
| |
| // net_base::RTNLHandler::AddInterface() returns false directly. |
| EXPECT_CALL(rtnl_handler_, AddInterface(kIfName, kLinkKind, _, _)) |
| .WillOnce(Return(false)); |
| EXPECT_FALSE(call_create_wireguard_interface()); |
| EXPECT_EQ(link_ready_calls_num, 0); |
| EXPECT_EQ(on_failure_calls_num, 0); |
| |
| // net_base::RTNLHandler::AddInterface() returns true, but the kernel returns |
| // false. |
| EXPECT_CALL(rtnl_handler_, AddInterface(kIfName, kLinkKind, _, _)) |
| .WillRepeatedly( |
| [&](const std::string& interface_name, const std::string& link_kind, |
| base::span<const uint8_t>, |
| net_base::RTNLHandler::ResponseCallback response_callback) { |
| registered_response_cb = std::move(response_callback); |
| return true; |
| }); |
| EXPECT_TRUE(call_create_wireguard_interface()); |
| std::move(registered_response_cb).Run(100); |
| EXPECT_EQ(link_ready_calls_num, 0); |
| EXPECT_EQ(on_failure_calls_num, 1); |
| |
| // net_base::RTNLHandler::AddInterface() returns true, and the kernel returns |
| // ack. No callback to the client should be invoked now. |
| EXPECT_TRUE(call_create_wireguard_interface()); |
| std::move(registered_response_cb).Run(0); |
| EXPECT_EQ(link_ready_calls_num, 0); |
| EXPECT_EQ(on_failure_calls_num, 1); |
| |
| // Link is ready. |
| CreateDevice(kIfName, kTestMacAddress0, 123, Technology::kTunnel); |
| EXPECT_EQ(link_ready_calls_num, 1); |
| EXPECT_EQ(on_failure_calls_num, 1); |
| } |
| |
| TEST_F(DeviceInfoTest, CreateXFRMInterface) { |
| const std::string kIfName = "xfrm0"; |
| const std::string kLinkKind = "xfrm"; |
| constexpr int kUnderlyingIfIndex = 5; |
| constexpr int kIfId = 1; |
| |
| int link_ready_calls_num = 0; |
| int on_failure_calls_num = 0; |
| auto link_ready_cb = [&](const std::string&, int) { link_ready_calls_num++; }; |
| auto on_failure_cb = [&]() { on_failure_calls_num++; }; |
| |
| std::vector<uint8_t> actual_link_info_data; |
| net_base::RTNLHandler::ResponseCallback registered_response_cb; |
| |
| auto call_create_xfrm_interface = [&]() { |
| return device_info_.CreateXFRMInterface( |
| kIfName, kUnderlyingIfIndex, kIfId, |
| base::BindLambdaForTesting(link_ready_cb), |
| base::BindLambdaForTesting(on_failure_cb)); |
| }; |
| |
| // net_base::RTNLHandler::AddInterface() returns false directly. |
| EXPECT_CALL(rtnl_handler_, AddInterface(kIfName, kLinkKind, _, _)) |
| .WillOnce(Return(false)); |
| EXPECT_FALSE(call_create_xfrm_interface()); |
| EXPECT_EQ(link_ready_calls_num, 0); |
| EXPECT_EQ(on_failure_calls_num, 0); |
| |
| // net_base::RTNLHandler::AddInterface() returns true, but the kernel returns |
| // false. |
| EXPECT_CALL(rtnl_handler_, AddInterface(kIfName, kLinkKind, _, _)) |
| .WillRepeatedly([&](const std::string& interface_name, |
| const std::string& link_kind, |
| base::span<const uint8_t> link_info_data, |
| net_base::RTNLHandler::ResponseCallback |
| response_callback) { |
| actual_link_info_data = {link_info_data.begin(), link_info_data.end()}; |
| registered_response_cb = std::move(response_callback); |
| return true; |
| }); |
| EXPECT_TRUE(call_create_xfrm_interface()); |
| EXPECT_EQ(actual_link_info_data, |
| net_base::RTNLMessage::PackAttrs( |
| {{1, net_base::byte_utils::ToBytes(kUnderlyingIfIndex)}, |
| {2, net_base::byte_utils::ToBytes(kIfId)}})); |
| std::move(registered_response_cb).Run(100); |
| EXPECT_EQ(link_ready_calls_num, 0); |
| EXPECT_EQ(on_failure_calls_num, 1); |
| |
| // net_base::RTNLHandler::AddInterface() returns true, and the kernel returns |
| // ack. No callback to the client should be invoked now. |
| EXPECT_TRUE(call_create_xfrm_interface()); |
| std::move(registered_response_cb).Run(0); |
| EXPECT_EQ(link_ready_calls_num, 0); |
| EXPECT_EQ(on_failure_calls_num, 1); |
| |
| // Link is ready. |
| CreateDevice(kIfName, kTestMacAddress0, 123, Technology::kTunnel); |
| EXPECT_EQ(link_ready_calls_num, 1); |
| EXPECT_EQ(on_failure_calls_num, 1); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIds) { |
| CreateWiFiDevice(); |
| |
| CreateInfoFile("device/vendor", kVendorIdString); |
| CreateInfoFile("device/device", kProductIdString); |
| CreateInfoFile("device/subsystem_device", kSubsystemIdString); |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| EXPECT_TRUE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| EXPECT_EQ(vendor, kVendorId); |
| EXPECT_EQ(product, kProductId); |
| EXPECT_EQ(subsystem, kSubsystemId); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIdsNoDevice) { |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| EXPECT_FALSE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| // No device, all IDs left untouched. |
| EXPECT_EQ(vendor, kDefaultTestHardwareId); |
| EXPECT_EQ(product, kDefaultTestHardwareId); |
| EXPECT_EQ(subsystem, kDefaultTestHardwareId); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIdsNotWiFi) { |
| // Adapter is NOT a WiFi adapter, expect failure. |
| CreateInfoFile("uevent", "DEVTYPE=NOTwlan"); |
| |
| auto device = CreateDevice(kTestDeviceName, kTestMacAddress0, |
| kTestDeviceIndex, Technology::kWiFi); |
| if (device) { |
| RegisterDevice(device); |
| } |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| message->set_link_status( |
| net_base::RTNLMessage::LinkStatus(0, IFF_LOWER_UP, 0)); |
| SendMessageToDeviceInfo(*message); |
| |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| EXPECT_FALSE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| // Not a WiFi device, all IDs left untouched. |
| EXPECT_EQ(vendor, kDefaultTestHardwareId); |
| EXPECT_EQ(product, kDefaultTestHardwareId); |
| EXPECT_EQ(subsystem, kDefaultTestHardwareId); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIdsNoVendor) { |
| CreateWiFiDevice(); |
| |
| // Vendor ID file is missing, expect failure. |
| CreateInfoFile("device/device", kProductIdString); |
| CreateInfoFile("device/subsystem_device", kSubsystemIdString); |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| EXPECT_FALSE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| // No vendor file, detection exits and will leave all IDs untouched. |
| // This behavior will change once we add support for integrated chipsets. |
| EXPECT_EQ(vendor, kDefaultTestHardwareId); |
| EXPECT_EQ(product, kDefaultTestHardwareId); |
| EXPECT_EQ(subsystem, kDefaultTestHardwareId); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIdsIntegrated) { |
| CreateWiFiDevice(); |
| |
| // Vendor ID file is missing, but the file for integrated chipsets is present |
| // and valid. |
| CreateInfoFile("device/uevent", |
| "TEST TEST \n OF_COMPATIBLE_0=qcom,wcn3990-wifi\n TEST TEST"); |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| EXPECT_TRUE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| // Integrated chipsets are given the unassigned vendor ID 0x0000. |
| EXPECT_EQ(vendor, Metrics::kWiFiIntegratedAdapterVendorId); |
| // product and subsystem IDs for WCN3990. |
| EXPECT_EQ(product, 3990); |
| EXPECT_EQ(subsystem, 0); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIdsIntegratedInvalid) { |
| CreateWiFiDevice(); |
| |
| // Vendor ID file is missing, but the file for integrated chipsets is present. |
| // However its format is invalid (missing "OF_COMPATIBLE_0=" prefix), expect |
| // failure. |
| CreateInfoFile("device/uevent", "qcom,wcn3990-wifi"); |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| EXPECT_FALSE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| EXPECT_EQ(vendor, kDefaultTestHardwareId); |
| EXPECT_EQ(product, kDefaultTestHardwareId); |
| EXPECT_EQ(subsystem, kDefaultTestHardwareId); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIdsInvalidVendor) { |
| CreateWiFiDevice(); |
| |
| // Content of the vendor ID file is not a hexadecimal number, expect failure. |
| CreateInfoFile("device/vendor", kInvalidIdString); |
| CreateInfoFile("device/device", kProductIdString); |
| CreateInfoFile("device/subsystem_device", kSubsystemIdString); |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| EXPECT_FALSE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| // Invalid vendor file, vendor ID left untouched. |
| EXPECT_EQ(vendor, kDefaultTestHardwareId); |
| EXPECT_EQ(product, kProductId); |
| EXPECT_EQ(subsystem, kSubsystemId); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIdsNoProduct) { |
| CreateWiFiDevice(); |
| |
| CreateInfoFile("device/vendor", kVendorIdString); |
| // Product ID file is missing, expect failure. |
| CreateInfoFile("device/subsystem_device", kSubsystemIdString); |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| EXPECT_FALSE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| // No product file, product ID left untouched. |
| EXPECT_EQ(vendor, kVendorId); |
| EXPECT_EQ(product, kDefaultTestHardwareId); |
| EXPECT_EQ(subsystem, kSubsystemId); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIdsInvalidProduct) { |
| CreateWiFiDevice(); |
| |
| CreateInfoFile("device/vendor", kVendorIdString); |
| // Content of the product ID file is not a hexadecimal number, expect failure. |
| CreateInfoFile("device/device", kInvalidIdString); |
| CreateInfoFile("device/subsystem_device", kSubsystemIdString); |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| EXPECT_FALSE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| // Invalid product file, product ID left untouched. |
| EXPECT_EQ(vendor, kVendorId); |
| EXPECT_EQ(product, kDefaultTestHardwareId); |
| EXPECT_EQ(subsystem, kSubsystemId); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIdsNoSubsystem) { |
| CreateWiFiDevice(); |
| |
| CreateInfoFile("device/vendor", kVendorIdString); |
| CreateInfoFile("device/device", kProductIdString); |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| // Lack of subsystem is expected for SDIO adapters. |
| EXPECT_TRUE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| EXPECT_EQ(vendor, kVendorId); |
| EXPECT_EQ(product, kProductId); |
| // SDIO adapters return subsystem ID 0. |
| EXPECT_EQ(subsystem, 0); |
| } |
| |
| TEST_F(DeviceInfoTest, GetWiFiHardwareIdsInvalidSubsystem) { |
| CreateWiFiDevice(); |
| |
| CreateInfoFile("device/vendor", kVendorIdString); |
| CreateInfoFile("device/device", kProductIdString); |
| // Content of the subsystem ID file is not a hexadecimal number, |
| // expect failure. |
| CreateInfoFile("device/subsystem_device", kInvalidIdString); |
| int vendor = kDefaultTestHardwareId; |
| int product = kDefaultTestHardwareId; |
| int subsystem = kDefaultTestHardwareId; |
| EXPECT_FALSE(device_info_.GetWiFiHardwareIds(kTestDeviceIndex, &vendor, |
| &product, &subsystem)); |
| // Invalid subsystem file, subsystem ID left untouched. |
| EXPECT_EQ(vendor, kVendorId); |
| EXPECT_EQ(product, kProductId); |
| EXPECT_EQ(subsystem, kDefaultTestHardwareId); |
| } |
| |
| class DeviceInfoTechnologyTest : public DeviceInfoTest { |
| public: |
| DeviceInfoTechnologyTest() = default; |
| ~DeviceInfoTechnologyTest() override = default; |
| |
| void SetUp() override { |
| CreateSysfsRoot(); |
| // Most tests require that the uevent file exist. |
| CreateInfoFile("uevent", "xxx"); |
| } |
| |
| Technology GetDeviceTechnology() { |
| return device_info_.GetDeviceTechnology(test_device_name_, std::nullopt); |
| } |
| Technology GetDeviceTechnology(const std::string& kind) { |
| return device_info_.GetDeviceTechnology(test_device_name_, kind); |
| } |
| |
| void CreateInfoSymLink(const std::string& name, const std::string& contents); |
| void SetDeviceName(const std::string& name) { |
| test_device_name_ = name; |
| EXPECT_TRUE(temp_dir_.Delete()); // nuke old temp dir |
| SetUp(); |
| } |
| }; |
| |
| void DeviceInfoTechnologyTest::CreateInfoSymLink(const std::string& name, |
| const std::string& contents) { |
| base::FilePath info_path = GetInfoPath(name); |
| EXPECT_TRUE(base::CreateDirectory(info_path.DirName())); |
| EXPECT_TRUE(base::CreateSymbolicLink(base::FilePath(contents), info_path)); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, Unknown) { |
| // With a uevent file but no driver symlink, we should get a pseudo-technology |
| // which specifies this condition explicitly. |
| EXPECT_EQ(Technology::kNoDeviceSymlink, GetDeviceTechnology()); |
| |
| // Should be unknown without a uevent file. |
| EXPECT_TRUE(brillo::DeleteFile(GetInfoPath("uevent"))); |
| EXPECT_EQ(Technology::kUnknown, GetDeviceTechnology()); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, IgnoredVeth) { |
| test_device_name_ = "veth0"; |
| // A new uevent file is needed since the device name has changed. |
| CreateInfoFile("uevent", "xxx"); |
| // A device with a "veth" prefix should be ignored. |
| EXPECT_EQ(Technology::kUnknown, GetDeviceTechnology("veth")); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, IgnoredArcMultinetBridgeDevice) { |
| test_device_name_ = "arc_eth0"; |
| // A new uevent file is needed since the device name has changed. |
| CreateInfoFile("uevent", "xxx"); |
| // A device with a "arc_" prefix should be ignored. |
| EXPECT_EQ(Technology::kUnknown, GetDeviceTechnology("bridge")); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, Loopback) { |
| CreateInfoFile("type", base::NumberToString(ARPHRD_LOOPBACK)); |
| EXPECT_EQ(Technology::kLoopback, GetDeviceTechnology()); |
| } |
| |
| // As long as it's not named 'veth*', we should detect it as Ethernet. |
| TEST_F(DeviceInfoTechnologyTest, Veth) { |
| CreateInfoFile("uevent", "xxx"); |
| EXPECT_EQ(Technology::kEthernet, GetDeviceTechnology("veth")); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, PPP) { |
| CreateInfoFile("type", base::NumberToString(ARPHRD_PPP)); |
| EXPECT_EQ(Technology::kPPP, GetDeviceTechnology()); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, Tunnel) { |
| CreateInfoFile("tun_flags", base::NumberToString(IFF_TUN)); |
| EXPECT_EQ(Technology::kTunnel, GetDeviceTechnology()); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, WiFi) { |
| CreateInfoFile("uevent", "DEVTYPE=wlan"); |
| EXPECT_EQ(Technology::kWiFi, GetDeviceTechnology()); |
| CreateInfoFile("uevent", "foo\nDEVTYPE=wlan"); |
| EXPECT_EQ(Technology::kWiFi, GetDeviceTechnology()); |
| CreateInfoFile("type", base::NumberToString(ARPHRD_IEEE80211_RADIOTAP)); |
| EXPECT_EQ(Technology::kWiFiMonitor, GetDeviceTechnology()); |
| // mac80211_hwsim creates ARPHRD_IEEE80211_RADIOTAP devices that don't list |
| // DEVTYPE=wlan. |
| CreateInfoFile("uevent", "INTERFACE=hwsim0"); |
| EXPECT_EQ(Technology::kWiFiMonitor, GetDeviceTechnology()); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, Bridge) { |
| CreateInfoFile("uevent", "DEVTYPE=bridge"); |
| EXPECT_EQ(Technology::kEthernet, GetDeviceTechnology("bridge")); |
| CreateInfoFile("uevent", "bar\nDEVTYPE=bridge"); |
| EXPECT_EQ(Technology::kEthernet, GetDeviceTechnology("bridge")); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, Ifb) { |
| test_device_name_ = "ifb0"; |
| CreateInfoFile("uevent", "INTERFACE=ifb0"); |
| EXPECT_EQ(Technology::kUnknown, GetDeviceTechnology("ifb")); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, Qmapmux) { |
| test_device_name_ = "qmapmux0.0"; |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology("rmnet")); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, RmnetIPA) { |
| test_device_name_ = "rmnet_ipa0"; |
| CreateInfoFile("type", base::NumberToString(ARPHRD_RAWIP)); |
| EXPECT_EQ(Technology::kUnknown, GetDeviceTechnology()); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, Ethernet) { |
| CreateInfoSymLink("device/driver", "xxx"); |
| EXPECT_EQ(Technology::kEthernet, GetDeviceTechnology()); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, CellularCdcMbim) { |
| CreateInfoSymLink("device/driver", "cdc_mbim"); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology()); |
| } |
| |
| // Test path to the driver of an FM350 device. This is temporary coverage until |
| // the mtkt7xx driver exposes the driver symlink at the same "device/driver" |
| // endpoint as expected (b/225373673) |
| TEST_F(DeviceInfoTechnologyTest, CellularMtkt7xx) { |
| CreateInfoSymLink("device/device/driver", "mtk_t7xx"); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology()); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, CellularQmiWwan) { |
| CreateInfoSymLink("device/driver", "qmi_wwan"); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology()); |
| } |
| |
| // Modem with absolute driver path with top-level tty file: |
| // /sys/class/net/dev0/device -> /sys/devices/virtual/0/00 |
| // /sys/devices/virtual/0/00/driver -> /drivers/cdc_ether or /drivers/cdc_ncm |
| // /sys/devices/virtual/0/01/tty [empty directory] |
| TEST_F(DeviceInfoTechnologyTest, CDCEthernetModem1) { |
| base::FilePath device_root( |
| temp_dir_.GetPath().Append("sys/devices/virtual/0")); |
| base::FilePath device_path(device_root.Append("00")); |
| base::FilePath driver_symlink(device_path.Append("driver")); |
| EXPECT_TRUE(base::CreateDirectory(device_path)); |
| CreateInfoSymLink("device", device_path.value()); |
| EXPECT_TRUE(base::CreateSymbolicLink(base::FilePath("/drivers/cdc_ether"), |
| driver_symlink)); |
| EXPECT_TRUE(base::CreateDirectory(device_root.Append("01/tty"))); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology()); |
| |
| EXPECT_TRUE(brillo::DeleteFile(driver_symlink)); |
| EXPECT_TRUE(base::CreateSymbolicLink(base::FilePath("/drivers/cdc_ncm"), |
| driver_symlink)); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology()); |
| } |
| |
| // Modem with relative driver path with top-level tty file. |
| // /sys/class/net/dev0/device -> ../../../device_dir/0/00 |
| // /sys/device_dir/0/00/driver -> /drivers/cdc_ether or /drivers/cdc_ncm |
| // /sys/device_dir/0/01/tty [empty directory] |
| TEST_F(DeviceInfoTechnologyTest, CDCEthernetModem2) { |
| CreateInfoSymLink("device", "../../../device_dir/0/00"); |
| base::FilePath device_root(temp_dir_.GetPath().Append("sys/device_dir/0")); |
| base::FilePath device_path(device_root.Append("00")); |
| base::FilePath driver_symlink(device_path.Append("driver")); |
| EXPECT_TRUE(base::CreateDirectory(device_path)); |
| EXPECT_TRUE(base::CreateSymbolicLink(base::FilePath("/drivers/cdc_ether"), |
| driver_symlink)); |
| EXPECT_TRUE(base::CreateDirectory(device_root.Append("01/tty"))); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology()); |
| |
| EXPECT_TRUE(brillo::DeleteFile(driver_symlink)); |
| EXPECT_TRUE(base::CreateSymbolicLink(base::FilePath("/drivers/cdc_ncm"), |
| driver_symlink)); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology()); |
| } |
| |
| // Modem with relative driver path with lower-level tty file. |
| // /sys/class/net/dev0/device -> ../../../device_dir/0/00 |
| // /sys/device_dir/0/00/driver -> /drivers/cdc_ether or /drivers/cdc_ncm |
| // /sys/device_dir/0/01/yyy/tty [empty directory] |
| TEST_F(DeviceInfoTechnologyTest, CDCEthernetModem3) { |
| CreateInfoSymLink("device", "../../../device_dir/0/00"); |
| base::FilePath device_root(temp_dir_.GetPath().Append("sys/device_dir/0")); |
| base::FilePath device_path(device_root.Append("00")); |
| base::FilePath driver_symlink(device_path.Append("driver")); |
| EXPECT_TRUE(base::CreateDirectory(device_path)); |
| EXPECT_TRUE(base::CreateSymbolicLink(base::FilePath("/drivers/cdc_ether"), |
| driver_symlink)); |
| EXPECT_TRUE(base::CreateDirectory(device_root.Append("01/yyy/tty"))); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology()); |
| |
| EXPECT_TRUE(brillo::DeleteFile(driver_symlink)); |
| EXPECT_TRUE(base::CreateSymbolicLink(base::FilePath("/drivers/cdc_ncm"), |
| driver_symlink)); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology()); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, CDCEtherNonModem) { |
| CreateInfoSymLink("device", "device_dir"); |
| CreateInfoSymLink("device_dir/driver", "cdc_ether"); |
| EXPECT_EQ(Technology::kCDCEthernet, GetDeviceTechnology()); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, CDCNcmNonModem) { |
| CreateInfoSymLink("device", "device_dir"); |
| CreateInfoSymLink("device_dir/driver", "cdc_ncm"); |
| EXPECT_EQ(Technology::kCDCEthernet, GetDeviceTechnology()); |
| } |
| |
| TEST_F(DeviceInfoTechnologyTest, PseudoModem) { |
| SetDeviceName("pseudomodem"); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology("veth")); |
| |
| SetDeviceName("pseudomodem9"); |
| EXPECT_EQ(Technology::kCellular, GetDeviceTechnology("veth")); |
| } |
| |
| class DeviceInfoForDelayedCreationTest : public DeviceInfo { |
| public: |
| explicit DeviceInfoForDelayedCreationTest(Manager* manager) |
| : DeviceInfo(manager) {} |
| MOCK_METHOD(DeviceRefPtr, |
| CreateDevice, |
| (const std::string&, |
| std::optional<net_base::MacAddress>, |
| int, |
| Technology), |
| (override)); |
| MOCK_METHOD(Technology, |
| GetDeviceTechnology, |
| (const std::string&, const std::optional<std::string>& kind), |
| (const, override)); |
| }; |
| |
| class DeviceInfoDelayedCreationTest : public DeviceInfoTest { |
| public: |
| DeviceInfoDelayedCreationTest() : test_device_info_(&manager_) {} |
| ~DeviceInfoDelayedCreationTest() override = default; |
| |
| std::set<int>& GetDelayedDevices() override { |
| return test_device_info_.delayed_devices_; |
| } |
| |
| void DelayedDeviceCreationTask() { |
| test_device_info_.DelayedDeviceCreationTask(); |
| } |
| |
| void AddDelayedDevice(Technology delayed_technology) { |
| auto message = BuildLinkMessage(net_base::RTNLMessage::kModeAdd); |
| EXPECT_CALL(test_device_info_, GetDeviceTechnology(kTestDeviceName, _)) |
| .WillOnce(Return(delayed_technology)); |
| EXPECT_CALL( |
| test_device_info_, |
| CreateDevice(kTestDeviceName, _, kTestDeviceIndex, delayed_technology)) |
| .WillOnce(Return(DeviceRefPtr())); |
| test_device_info_.AddLinkMsgHandler(*message); |
| Mock::VerifyAndClearExpectations(&test_device_info_); |
| // We need to insert the device index ourselves since we have mocked |
| // out CreateDevice. This insertion is tested in CreateDeviceCDCEthernet |
| // above. |
| GetDelayedDevices().insert(kTestDeviceIndex); |
| } |
| |
| void AddDeviceWithNoIFLAAddress(Technology delayed_technology) { |
| auto message = std::make_unique<net_base::RTNLMessage>( |
| net_base::RTNLMessage::kTypeLink, net_base::RTNLMessage::kModeAdd, 0, 0, |
| 0, kTestDeviceIndex, AF_INET); |
| message->SetAttribute( |
| static_cast<uint16_t>(IFLA_IFNAME), |
| net_base::byte_utils::StringToCStringBytes(kTestDeviceName)); |
| |
| EXPECT_CALL(test_device_info_, GetDeviceTechnology(kTestDeviceName, _)) |
| .WillOnce(Return(delayed_technology)); |
| // When message does not have IFLA_ADDRESS and technology is either WiFi |
| // or Ethernet, the AddLinkMsgHandler function does not create device |
| EXPECT_CALL(test_device_info_, CreateDevice(_, _, _, _)).Times(0); |
| test_device_info_.AddLinkMsgHandler(*message); |
| } |
| |
| void EnsureDelayedDevice(Technology reported_device_technology, |
| Technology created_device_technology) { |
| EXPECT_CALL(test_device_info_, GetDeviceTechnology(_, _)) |
| .WillOnce(Return(reported_device_technology)); |
| EXPECT_CALL(test_device_info_, |
| CreateDevice(kTestDeviceName, _, kTestDeviceIndex, |
| created_device_technology)) |
| .WillOnce(Return(DeviceRefPtr())); |
| DelayedDeviceCreationTask(); |
| EXPECT_TRUE(GetDelayedDevices().empty()); |
| } |
| |
| void EnsureNoDelayedDevice() { EXPECT_TRUE(GetDelayedDevices().empty()); } |
| |
| void TriggerOnWiFiInterfaceInfoReceived(const Nl80211Message& message) { |
| test_device_info_.OnWiFiInterfaceInfoReceived(message); |
| } |
| |
| protected: |
| DeviceInfoForDelayedCreationTest test_device_info_; |
| }; |
| |
| TEST_F(DeviceInfoDelayedCreationTest, NoDevices) { |
| EXPECT_TRUE(GetDelayedDevices().empty()); |
| EXPECT_CALL(test_device_info_, GetDeviceTechnology(_, _)).Times(0); |
| DelayedDeviceCreationTask(); |
| } |
| |
| TEST_F(DeviceInfoDelayedCreationTest, CDCEthernetDevice) { |
| AddDelayedDevice(Technology::kCDCEthernet); |
| EnsureDelayedDevice(Technology::kCDCEthernet, Technology::kEthernet); |
| } |
| |
| TEST_F(DeviceInfoDelayedCreationTest, CellularDevice) { |
| AddDelayedDevice(Technology::kCDCEthernet); |
| EnsureDelayedDevice(Technology::kCellular, Technology::kCellular); |
| } |
| |
| TEST_F(DeviceInfoDelayedCreationTest, TunnelDevice) { |
| AddDelayedDevice(Technology::kNoDeviceSymlink); |
| EnsureDelayedDevice(Technology::kTunnel, Technology::kTunnel); |
| } |
| |
| TEST_F(DeviceInfoDelayedCreationTest, GuestInterface) { |
| AddDelayedDevice(Technology::kNoDeviceSymlink); |
| EnsureDelayedDevice(Technology::kGuestInterface, Technology::kGuestInterface); |
| } |
| |
| TEST_F(DeviceInfoDelayedCreationTest, WiFiInterface) { |
| AddDeviceWithNoIFLAAddress(Technology::kWiFi); |
| EnsureNoDelayedDevice(); |
| } |
| |
| TEST_F(DeviceInfoDelayedCreationTest, EthernetInterface) { |
| AddDeviceWithNoIFLAAddress(Technology::kEthernet); |
| EnsureNoDelayedDevice(); |
| } |
| |
| TEST_F(DeviceInfoDelayedCreationTest, WiFiDevice) { |
| ScopedMockLog log; |
| EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _, |
| HasSubstr("Message is not a new interface response"))); |
| GetInterfaceMessage non_interface_response_message; |
| TriggerOnWiFiInterfaceInfoReceived(non_interface_response_message); |
| Mock::VerifyAndClearExpectations(&log); |
| |
| EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _, |
| HasSubstr("Message contains no interface index"))); |
| NewInterfaceMessage message; |
| TriggerOnWiFiInterfaceInfoReceived(message); |
| Mock::VerifyAndClearExpectations(&log); |
| |
| CreateNl80211Attribute(message.attributes().get(), NL80211_ATTR_IFINDEX); |
| message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX, |
| kTestDeviceIndex); |
| EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _, |
| HasSubstr("Message contains no interface type"))); |
| TriggerOnWiFiInterfaceInfoReceived(message); |
| Mock::VerifyAndClearExpectations(&log); |
| |
| CreateNl80211Attribute(message.attributes().get(), NL80211_ATTR_IFTYPE); |
| message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFTYPE, |
| NL80211_IFTYPE_AP); |
| EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _, |
| HasSubstr("Message contains no phy index"))); |
| TriggerOnWiFiInterfaceInfoReceived(message); |
| Mock::VerifyAndClearExpectations(&log); |
| |
| CreateNl80211Attribute(message.attributes().get(), NL80211_ATTR_WIPHY); |
| message.attributes()->SetU32AttributeValue(NL80211_ATTR_WIPHY, 0); |
| EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _, |
| HasSubstr("Could not find device info for interface"))); |
| TriggerOnWiFiInterfaceInfoReceived(message); |
| Mock::VerifyAndClearExpectations(&log); |
| |
| // Use the AddDelayedDevice() method to create a device info entry with no |
| // associated device. |
| AddDelayedDevice(Technology::kNoDeviceSymlink); |
| |
| EXPECT_CALL(log, Log(logging::LOGGING_INFO, _, |
| HasSubstr("it is not in station mode"))); |
| TriggerOnWiFiInterfaceInfoReceived(message); |
| Mock::VerifyAndClearExpectations(&log); |
| Mock::VerifyAndClearExpectations(&manager_); |
| message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFTYPE, |
| NL80211_IFTYPE_STATION); |
| EXPECT_CALL(manager_, RegisterDevice(_)); |
| EXPECT_CALL(manager_, device_info()) |
| .WillRepeatedly(Return(&test_device_info_)); |
| EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber()); |
| EXPECT_CALL(log, |
| Log(logging::LOGGING_INFO, _, HasSubstr("Creating WiFi device"))); |
| TriggerOnWiFiInterfaceInfoReceived(message); |
| Mock::VerifyAndClearExpectations(&log); |
| Mock::VerifyAndClearExpectations(&manager_); |
| |
| EXPECT_CALL(manager_, RegisterDevice(_)).Times(0); |
| EXPECT_CALL(log, Log(logging::LOGGING_ERROR, _, |
| HasSubstr("Device already created for interface"))); |
| TriggerOnWiFiInterfaceInfoReceived(message); |
| } |
| |
| } // namespace shill |