// // Copyright (C) 2014 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "apmanager/device_info.h" #include #include #include #include #include #include #include #include #include #include #include #include "shill/net/netlink_message_matchers.h" #include "shill/net/nl80211_attribute.h" #include "shill/net/nl80211_message.h" #include #include "apmanager/fake_device_adaptor.h" #include "apmanager/mock_control.h" #include "apmanager/mock_device.h" #include "apmanager/mock_manager.h" using shill::ByteString; using shill::Nl80211Message; using shill::RTNLMessage; using std::map; using std::string; using std::vector; using ::testing::_; using ::testing::Mock; using ::testing::ReturnNew; namespace apmanager { namespace { const char kTestDeviceName[] = "test-phy"; const char kTestInterface0Name[] = "test-interface0"; const char kTestInterface1Name[] = "test-interface1"; const uint32_t kTestInterface0Index = 1000; const uint32_t kTestInterface1Index = 1001; } // namespace class DeviceInfoTest : public testing::Test { public: DeviceInfoTest() : manager_(&control_interface_), device_info_(&manager_) {} virtual ~DeviceInfoTest() {} virtual void SetUp() { // Setup temporary directory for device info files. CHECK(temp_dir_.CreateUniqueTempDir()); device_info_root_ = temp_dir_.path().Append("sys/class/net"); device_info_.device_info_root_ = device_info_root_; // Setup mock pointers; device_info_.netlink_manager_ = &netlink_manager_; ON_CALL(control_interface_, CreateDeviceAdaptorRaw()) .WillByDefault(ReturnNew()); } bool IsWifiInterface(const string& interface_name) { return device_info_.IsWifiInterface(interface_name); } void CreateDeviceInfoFile(const string& interface_name, const string& file_name, const string& contents) { base::FilePath info_path = device_info_root_.Append(interface_name).Append(file_name); EXPECT_TRUE(base::CreateDirectory(info_path.DirName())); EXPECT_TRUE(base::WriteFile(info_path, contents.c_str(), contents.size())); } void SendLinkMsg(RTNLMessage::Mode mode, uint32_t interface_index, const string& interface_name) { RTNLMessage message(RTNLMessage::kTypeLink, mode, 0, 0, 0, interface_index, shill::IPAddress::kFamilyIPv4); message.SetAttribute(static_cast(IFLA_IFNAME), ByteString(interface_name, true)); device_info_.LinkMsgHandler(message); } void VerifyInterfaceList(const vector& interfaces) { // Verify number of elements in the interface infos map and interface index // of the elements in the map. EXPECT_EQ(interfaces.size(), device_info_.interface_infos_.size()); for (const auto& interface : interfaces) { map::iterator it = device_info_.interface_infos_.find(interface.iface_index); EXPECT_NE(device_info_.interface_infos_.end(), it); EXPECT_TRUE(interface.Equals(it->second)); } } void VerifyDeviceList(const vector>& devices) { // Verify number of elements in the device map and the elements in the map. EXPECT_EQ(devices.size(), device_info_.devices_.size()); for (const auto& device : devices) { map>::iterator it = device_info_.devices_.find(device->GetDeviceName()); EXPECT_NE(device_info_.devices_.end(), it); EXPECT_EQ(device, it->second); } } void AddInterface(const Device::WiFiInterface& interface) { device_info_.interface_infos_[interface.iface_index] = interface; } void OnWiFiPhyInfoReceived(const Nl80211Message& message) { device_info_.OnWiFiPhyInfoReceived(message); } void OnWiFiInterfaceInfoReceived(const Nl80211Message& message) { device_info_.OnWiFiInterfaceInfoReceived(message); } void OnWiFiInterfacePhyInfoReceived(uint32_t interface_index, const Nl80211Message& message) { device_info_.OnWiFiInterfacePhyInfoReceived(interface_index, message); } void RegisterDevice(const scoped_refptr& device) { device_info_.RegisterDevice(device); } protected: MockControl control_interface_; MockManager manager_; shill::MockNetlinkManager netlink_manager_; base::ScopedTempDir temp_dir_; base::FilePath device_info_root_; DeviceInfo device_info_; }; MATCHER_P2(IsGetInfoMessage, command, index, "") { if (arg->message_type() != Nl80211Message::GetMessageType()) { return false; } const Nl80211Message *msg = reinterpret_cast(arg); if (msg->command() != command) { 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; } MATCHER_P(IsInterface, interface, "") { return arg.Equals(interface); } MATCHER_P(IsDevice, device_name, "") { return arg->GetDeviceName() == device_name; } TEST_F(DeviceInfoTest, EnumerateDevices) { shill::NewWiphyMessage message; // No device name in the message, failed to create device. EXPECT_CALL(manager_, RegisterDevice(_)).Times(0); OnWiFiPhyInfoReceived(message); // Device name in the message, device should be created/register to manager. message.attributes()->CreateNl80211Attribute( NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext()); message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME, kTestDeviceName); EXPECT_CALL(manager_, RegisterDevice(IsDevice(kTestDeviceName))).Times(1); OnWiFiPhyInfoReceived(message); Mock::VerifyAndClearExpectations(&manager_); // Receive a message for a device already created, should not create/register // device again. EXPECT_CALL(manager_, RegisterDevice(_)).Times(0); OnWiFiPhyInfoReceived(message); } TEST_F(DeviceInfoTest, IsWiFiInterface) { // No device info file exist, not a wifi interface. EXPECT_FALSE(IsWifiInterface(kTestInterface0Name)); // Device info for an ethernet device, not a wifi interface CreateDeviceInfoFile(kTestInterface0Name, "uevent", "INTERFACE=eth0\n"); EXPECT_FALSE(IsWifiInterface(kTestInterface0Name)); // Device info for a wifi interface. CreateDeviceInfoFile(kTestInterface1Name, "uevent", "DEVTYPE=wlan\n"); EXPECT_TRUE(IsWifiInterface(kTestInterface1Name)); } TEST_F(DeviceInfoTest, InterfaceDetection) { vector interface_list; // Ignore non-wifi interface. SendLinkMsg(RTNLMessage::kModeAdd, kTestInterface0Index, kTestInterface0Name); VerifyInterfaceList(interface_list); // AddLink event for wifi interface. CreateDeviceInfoFile(kTestInterface0Name, "uevent", "DEVTYPE=wlan\n"); EXPECT_CALL(netlink_manager_, SendNl80211Message( IsGetInfoMessage(NL80211_CMD_GET_INTERFACE, kTestInterface0Index), _, _, _)).Times(1); SendLinkMsg(RTNLMessage::kModeAdd, kTestInterface0Index, kTestInterface0Name); interface_list.push_back(Device::WiFiInterface( kTestInterface0Name, "", kTestInterface0Index, 0)); VerifyInterfaceList(interface_list); Mock::VerifyAndClearExpectations(&netlink_manager_); // AddLink event for another wifi interface. CreateDeviceInfoFile(kTestInterface1Name, "uevent", "DEVTYPE=wlan\n"); EXPECT_CALL(netlink_manager_, SendNl80211Message( IsGetInfoMessage(NL80211_CMD_GET_INTERFACE, kTestInterface1Index), _, _, _)).Times(1); SendLinkMsg(RTNLMessage::kModeAdd, kTestInterface1Index, kTestInterface1Name); interface_list.push_back(Device::WiFiInterface( kTestInterface1Name, "", kTestInterface1Index, 0)); VerifyInterfaceList(interface_list); Mock::VerifyAndClearExpectations(&netlink_manager_); // AddLink event for an interface that's already added, no change to interface // list. EXPECT_CALL(netlink_manager_, SendNl80211Message(_, _, _, _)).Times(0); SendLinkMsg(RTNLMessage::kModeAdd, kTestInterface0Index, kTestInterface0Name); VerifyInterfaceList(interface_list); Mock::VerifyAndClearExpectations(&netlink_manager_); // Remove the first wifi interface. SendLinkMsg(RTNLMessage::kModeDelete, kTestInterface0Index, kTestInterface0Name); interface_list.clear(); interface_list.push_back(Device::WiFiInterface( kTestInterface1Name, "", kTestInterface1Index, 0)); VerifyInterfaceList(interface_list); // Remove the non-exist interface, no change to the list. SendLinkMsg(RTNLMessage::kModeDelete, kTestInterface0Index, kTestInterface0Name); VerifyInterfaceList(interface_list); // Remove the last interface, list should be empty now. SendLinkMsg(RTNLMessage::kModeDelete, kTestInterface1Index, kTestInterface1Name); interface_list.clear(); VerifyInterfaceList(interface_list); } TEST_F(DeviceInfoTest, ParseWifiInterfaceInfo) { // Add an interface without interface type info. Device::WiFiInterface interface( kTestInterface0Name, "", kTestInterface0Index, 0); AddInterface(interface); vector interface_list; interface_list.push_back(interface); // Message contain no interface index, no change to the interface info. shill::NewInterfaceMessage message; OnWiFiInterfaceInfoReceived(message); VerifyInterfaceList(interface_list); // Message contain no interface type, no change to the interface info. message.attributes()->CreateNl80211Attribute( NL80211_ATTR_IFINDEX, shill::NetlinkMessage::MessageContext()); message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX, kTestInterface0Index); OnWiFiInterfaceInfoReceived(message); // Message contain interface type, interface info should be updated with // the interface type, and a new Nl80211 message should be send to query for // the PHY info. EXPECT_CALL(netlink_manager_, SendNl80211Message( IsGetInfoMessage(NL80211_CMD_GET_WIPHY, kTestInterface0Index), _, _, _)).Times(1); message.attributes()->CreateNl80211Attribute( NL80211_ATTR_IFTYPE, shill::NetlinkMessage::MessageContext()); message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP); OnWiFiInterfaceInfoReceived(message); interface_list[0].iface_type = NL80211_IFTYPE_AP; VerifyInterfaceList(interface_list); } TEST_F(DeviceInfoTest, ParsePhyInfoForWifiInterface) { // Register a mock device. scoped_refptr device = new MockDevice(&manager_); device->SetDeviceName(kTestDeviceName); EXPECT_CALL(manager_, RegisterDevice(_)).Times(1); RegisterDevice(device); // PHY info message. shill::NewWiphyMessage message; message.attributes()->CreateNl80211Attribute( NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext()); message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME, kTestDeviceName); // Receive PHY info message for an interface that have not been detected yet. EXPECT_CALL(*device.get(), RegisterInterface(_)).Times(0); OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message); // Pretend interface is detected through AddLink with interface info already // received (interface type), and still missing PHY info for that interface. Device::WiFiInterface interface( kTestInterface0Name, "", kTestInterface0Index, NL80211_IFTYPE_AP); AddInterface(interface); // PHY info is received for a detected interface, should register that // interface to the corresponding Device. interface.device_name = kTestDeviceName; EXPECT_CALL(*device.get(), RegisterInterface(IsInterface(interface))).Times(1); OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message); } TEST_F(DeviceInfoTest, ReceivePhyInfoBeforePhyIsEnumerated) { // New interface is detected. Device::WiFiInterface interface( kTestInterface0Name, "", kTestInterface0Index, NL80211_IFTYPE_AP); AddInterface(interface); vector interface_list; interface_list.push_back(interface); // Received PHY info for the interface when the corresponding PHY is not // enumerated yet, new device should be created and register to manager. shill::NewWiphyMessage message; message.attributes()->CreateNl80211Attribute( NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext()); message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME, kTestDeviceName); EXPECT_CALL(manager_, RegisterDevice(IsDevice(kTestDeviceName))).Times(1); OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message); interface_list[0].device_name = kTestDeviceName; VerifyInterfaceList(interface_list); } TEST_F(DeviceInfoTest, RegisterDevice) { vector> device_list; // Register a nullptr. RegisterDevice(nullptr); VerifyDeviceList(device_list); // Register a device. device_list.push_back(new Device(&manager_, kTestDeviceName, 0)); EXPECT_CALL(manager_, RegisterDevice(device_list[0])); RegisterDevice(device_list[0]); VerifyDeviceList(device_list); } } // namespace apmanager