shill: DeviceInfo: Decide between WiFi interface types
When a WiFi device appears, use the nl80211 "get interface"
call to retrieve the interface type, deferring creation of
the Device instance until a reply to this message is received.
Only create a WiFi device if the interface is in "station"
mode.
BUG=chrome-os-partner:18698
TEST=Unit tests; run on system with kernel-next patches for
chromeos-partner:18698 comment #5 applied, and
https://gerrit.chromium.org/gerrit/47863 reverted, ensure
only one WiFi device appears in list-devices (mlan0)
Change-Id: Ia559e0931a8bd4aaa067d71aae5d1bd1bf1ceedc
Reviewed-on: https://gerrit.chromium.org/gerrit/48250
Commit-Queue: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/device_info_unittest.cc b/device_info_unittest.cc
index 055cfd7..fbdbd1d 100644
--- a/device_info_unittest.cc
+++ b/device_info_unittest.cc
@@ -13,6 +13,7 @@
#include <linux/sockios.h>
#include <net/if_arp.h>
+#include <base/bind.h>
#include <base/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/memory/ref_counted.h>
@@ -28,14 +29,18 @@
#include "shill/mock_control.h"
#include "shill/mock_device.h"
#include "shill/mock_glib.h"
+#include "shill/mock_log.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/mock_modem_info.h"
+#include "shill/mock_netlink_manager.h"
#include "shill/mock_routing_table.h"
#include "shill/mock_rtnl_handler.h"
#include "shill/mock_sockets.h"
#include "shill/mock_vpn_provider.h"
#include "shill/mock_wimax_provider.h"
+#include "shill/netlink_attribute.h"
+#include "shill/nl80211_message.h"
#include "shill/rtnl_message.h"
#include "shill/wimax.h"
@@ -46,9 +51,11 @@
using std::string;
using std::vector;
using testing::_;
+using testing::AnyNumber;
using testing::ContainerEq;
using testing::DoAll;
using testing::ElementsAreArray;
+using testing::HasSubstr;
using testing::Mock;
using testing::NotNull;
using testing::Return;
@@ -81,6 +88,7 @@
virtual void SetUp() {
device_info_.rtnl_handler_ = &rtnl_handler_;
device_info_.routing_table_ = &routing_table_;
+ device_info_.netlink_manager_ = &netlink_manager_;
}
IPAddress CreateInterfaceAddress() {
@@ -153,6 +161,7 @@
DeviceInfo device_info_;
TestEventDispatcherForDeviceInfo dispatcher_;
MockRoutingTable routing_table_;
+ MockNetlinkManager netlink_manager_;
StrictMock<MockRTNLHandler> rtnl_handler_;
MockSockets *mock_sockets_; // Owned by DeviceInfo.
};
@@ -442,14 +451,39 @@
device = NULL;
}
+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) {
IPAddress address = CreateInterfaceAddress();
// WiFi looks a lot like Ethernet too.
- EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceIndex)).Times(1);
+ EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceIndex));
EXPECT_CALL(rtnl_handler_, RemoveInterfaceAddress(kTestDeviceIndex,
IsIPAddress(address)));
- EXPECT_TRUE(CreateDevice(
+
+ // Set the nl80211 message type to some non-default value.
+ Nl80211Message::SetMessageType(1234);
+
+ EXPECT_CALL(netlink_manager_,
+ SendMessage(IsGetInterfaceMessage(kTestDeviceIndex), _));
+ EXPECT_FALSE(CreateDevice(
kTestDeviceName, "address", kTestDeviceIndex, Technology::kWifi));
}
@@ -1064,6 +1098,10 @@
GetDelayedDevices().insert(kTestDeviceIndex);
}
+ void TriggerOnWiFiInterfaceInfoReceived(const NetlinkMessage &message) {
+ test_device_info_.OnWiFiInterfaceInfoReceived(message);
+ }
+
protected:
DeviceInfoForDelayedCreationTest test_device_info_;
};
@@ -1096,4 +1134,73 @@
EXPECT_TRUE(GetDelayedDevices().empty());
}
+TEST_F(DeviceInfoDelayedCreationTest, WiFiDevice) {
+ ScopedMockLog log;
+ EXPECT_CALL(manager_, RegisterDevice(_)).Times(0);
+ EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
+ HasSubstr("Unknown message type received")));
+ GenericNetlinkMessage non_nl80211_message(0, 0, "foo");
+ TriggerOnWiFiInterfaceInfoReceived(non_nl80211_message);
+ Mock::VerifyAndClearExpectations(&log);
+
+ EXPECT_CALL(log, Log(logging::LOG_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::LOG_ERROR, _,
+ HasSubstr("Message contains no interface index")));
+ NewInterfaceMessage message;
+ TriggerOnWiFiInterfaceInfoReceived(message);
+ Mock::VerifyAndClearExpectations(&log);
+
+ message.attributes()->CreateAttribute(
+ NL80211_ATTR_IFINDEX, base::Bind(
+ &NetlinkAttribute::NewNl80211AttributeFromId));
+ message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
+ kTestDeviceIndex);
+ EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
+ HasSubstr("Message contains no interface type")));
+ TriggerOnWiFiInterfaceInfoReceived(message);
+ Mock::VerifyAndClearExpectations(&log);
+
+ message.attributes()->CreateAttribute(
+ NL80211_ATTR_IFTYPE, base::Bind(
+ &NetlinkAttribute::NewNl80211AttributeFromId));
+ message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFTYPE,
+ NL80211_IFTYPE_AP);
+ EXPECT_CALL(log, Log(logging::LOG_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();
+
+ EXPECT_CALL(log, Log(logging::LOG_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::LOG_INFO, _,
+ HasSubstr("Creating WiFi device")));
+ TriggerOnWiFiInterfaceInfoReceived(message);
+ Mock::VerifyAndClearExpectations(&log);
+ Mock::VerifyAndClearExpectations(&manager_);
+
+ EXPECT_CALL(manager_, RegisterDevice(_)).Times(0);
+ EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
+ HasSubstr("Device already created for interface")));
+ TriggerOnWiFiInterfaceInfoReceived(message);
+}
+
} // namespace shill