Perform device discovery and registration in device_info
Also, do device classification, and create the beginnings of
Device subclasses for WiFi and Ethernet.
BUG=chromium-os:12933
TEST=Added unit tests. Also manual on a testbed machine to confirm WiFi
device detection.
Change-Id: I48b8fa2b3b966b22acf80f693d9522bff0221884
Reviewed-on: http://gerrit.chromium.org/gerrit/1084
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/device_info.cc b/device_info.cc
index 0986301..1e8608c 100644
--- a/device_info.cc
+++ b/device_info.cc
@@ -13,30 +13,51 @@
#include <net/if_arp.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#include <fcntl.h>
#include <string>
#include <base/callback_old.h>
#include <base/hash_tables.h>
#include <base/logging.h>
#include <base/memory/scoped_ptr.h>
+#include <base/stringprintf.h>
#include "shill/control_interface.h"
#include "shill/device.h"
#include "shill/device_info.h"
+#include "shill/ethernet.h"
+#include "shill/manager.h"
#include "shill/service.h"
+#include "shill/wifi.h"
using std::string;
namespace shill {
-DeviceInfo::DeviceInfo(EventDispatcher *dispatcher)
+
+// static
+const char *DeviceInfo::kInterfaceUevent= "/sys/class/net/%s/uevent";
+// static
+const char *DeviceInfo::kInterfaceDriver= "/sys/class/net/%s/device/driver";
+// static
+const char *DeviceInfo::kModemDrivers[] = {
+ "gobi",
+ "QCUSBNet2k",
+ "GobiNet",
+ NULL
+};
+
+
+DeviceInfo::DeviceInfo(ControlInterface *control_interface,
+ EventDispatcher *dispatcher,
+ Manager *manager)
: running_(false),
+ control_interface_(control_interface),
dispatcher_(dispatcher),
+ manager_(manager),
rtnl_callback_(NewCallback(this, &DeviceInfo::ParseRTNL)),
rtnl_socket_(-1),
request_flags_(0),
- request_sequence_(0) {
- LOG(INFO) << "DeviceInfo initialized";
-}
+ request_sequence_(0) {}
DeviceInfo::~DeviceInfo() {
Stop();
@@ -59,7 +80,8 @@
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
- if (bind(rtnl_socket_, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ if (bind(rtnl_socket_, reinterpret_cast<struct sockaddr *>(&addr),
+ sizeof(addr)) < 0) {
close(rtnl_socket_);
rtnl_socket_ = -1;
LOG(ERROR) << "RTNL socket bind failed";
@@ -73,7 +95,7 @@
request_flags_ = REQUEST_LINK | REQUEST_ADDR | REQUEST_ROUTE;
NextRequest(request_sequence_);
- LOG(INFO) << "DeviceInfo started";
+ VLOG(2) << "DeviceInfo started";
}
void DeviceInfo::Stop()
@@ -94,8 +116,6 @@
struct sockaddr_nl addr;
int flag = 0;
- LOG(INFO) << __func__ << ": " << seq << " :: " << request_sequence_;
-
if (seq != request_sequence_)
return;
@@ -124,35 +144,153 @@
addr.nl_family = AF_NETLINK;
if (sendto(rtnl_socket_, &req, sizeof(req), 0,
- (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) < 0) {
LOG(ERROR) << "RTNL sendto failed";
return;
}
request_flags_ &= ~flag;
}
+Device::Technology DeviceInfo::GetDeviceTechnology(const char *interface_name) {
+ char contents[1024];
+ int length;
+ int fd;
+ string uevent_file = StringPrintf(kInterfaceUevent, interface_name);
+ string driver_file = StringPrintf(kInterfaceDriver, interface_name);
+ const char *wifi_type;
+ const char *driver_name;
+ int modem_idx;
-void DeviceInfo::LinkMsg(unsigned char *buf, bool del) {
- struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
- struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+ fd = open(uevent_file.c_str(), O_RDONLY);
+ if (fd < 0)
+ return Device::kUnknown;
+
+ length = read(fd, contents, sizeof(contents) - 1);
+ if (length < 0)
+ return Device::kUnknown;
+
+ /*
+ * If the "uevent" file contains the string "DEVTYPE=wlan\n" at the
+ * start of the file or after a newline, we can safely assume this
+ * is a wifi device.
+ */
+ contents[length] = '\0';
+ wifi_type = strstr(contents, "DEVTYPE=wlan\n");
+ if (wifi_type != NULL && (wifi_type == contents || wifi_type[-1] == '\n'))
+ return Device::kWifi;
+
+ length = readlink(driver_file.c_str(), contents, sizeof(contents)-1);
+ if (length < 0)
+ return Device::kUnknown;
+
+ contents[length] = '\0';
+ driver_name = strrchr(contents, '/');
+ if (driver_name != NULL) {
+ driver_name++;
+ // See if driver for this interface is in a list of known modem driver names
+ for (modem_idx = 0; kModemDrivers[modem_idx] != NULL; modem_idx++)
+ if (strcmp(driver_name, kModemDrivers[modem_idx]) == 0)
+ return Device::kCellular;
+ }
+
+ return Device::kEthernet;
+}
+
+void DeviceInfo::AddLinkMsg(struct nlmsghdr *hdr) {
+ struct ifinfomsg *msg = reinterpret_cast<struct ifinfomsg *>(NLMSG_DATA(hdr));
+ base::hash_map<int, scoped_refptr<Device> >::iterator ndev =
+ devices_.find(msg->ifi_index);
int bytes = IFLA_PAYLOAD(hdr);
+ int dev_index = msg->ifi_index;
+ struct rtattr *rta;
+ int rta_bytes;
+ char *link_name = NULL;
+ Device *device;
+ Device::Technology technology;
+ bool is_stub = false;
- LOG(INFO) << "index " << msg->ifi_index << " flags " << msg->ifi_flags <<
- ": del = " << del;
+ VLOG(2) << "add link index " << dev_index << " flags " <<
+ msg->ifi_flags;
+
+ if (ndev == devices_.end()) {
+ rta_bytes = IFLA_PAYLOAD(hdr);
+ for (rta = IFLA_RTA(msg); RTA_OK(rta, rta_bytes);
+ rta = RTA_NEXT(rta, rta_bytes)) {
+ if (rta->rta_type == IFLA_IFNAME) {
+ link_name = reinterpret_cast<char *>(RTA_DATA(rta));
+ break;
+ } else {
+ VLOG(2) << " RTA type " << rta->rta_type;
+ }
+ }
+
+ if (link_name != NULL)
+ technology = GetDeviceTechnology(link_name);
+
+ switch (technology) {
+ case Device::kEthernet:
+ device = new Ethernet(control_interface_, dispatcher_,
+ link_name, dev_index);
+ break;
+ case Device::kWifi:
+ device = new WiFi(control_interface_, dispatcher_, link_name, dev_index);
+ break;
+ default:
+ device = new StubDevice(control_interface_, dispatcher_,
+ link_name, dev_index, technology);
+ is_stub = true;
+ }
+
+ devices_[dev_index] = device;
+
+ if (!is_stub)
+ manager_->RegisterDevice(device);
+ } else {
+ device = ndev->second;
+ }
+
+ // TODO(pstew): Send the the flags change upwards to the device
}
-void DeviceInfo::AddrMsg(unsigned char *buf, bool del) {
- struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
- struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
- LOG(INFO) << "index " << msg->ifi_index << " flags " << msg->ifi_flags <<
- ": del = " << del;
+void DeviceInfo::DelLinkMsg(struct nlmsghdr *hdr) {
+ struct ifinfomsg *msg = reinterpret_cast<struct ifinfomsg *>(NLMSG_DATA(hdr));
+ base::hash_map<int, scoped_refptr<Device> >::iterator ndev =
+ devices_.find(msg->ifi_index);
+ int dev_index = msg->ifi_index;
+ Device *device;
+
+ VLOG(2) << "del link index " << dev_index << " flags " <<
+ msg->ifi_flags;
+
+ if (ndev != devices_.end()) {
+ device = ndev->second;
+ devices_.erase(ndev);
+ manager_->DeregisterDevice(device);
+ }
}
-void DeviceInfo::RouteMsg(unsigned char *buf, bool del) {
- struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
- struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
- LOG(INFO) << "index " << msg->ifi_index << " flags " << msg->ifi_flags <<
- ": del = " << del;
+void DeviceInfo::AddAddrMsg(struct nlmsghdr *hdr) {
+ struct ifaddrmsg *msg = reinterpret_cast<struct ifaddrmsg *>(NLMSG_DATA(hdr));
+ VLOG(2) << "add addrmsg family " << (int) msg->ifa_family << " index " <<
+ msg->ifa_index;
+}
+
+void DeviceInfo::DelAddrMsg(struct nlmsghdr *hdr) {
+ struct ifaddrmsg *msg = reinterpret_cast<struct ifaddrmsg *>(NLMSG_DATA(hdr));
+ VLOG(2) << "del addrmsg family " << (int) msg->ifa_family << " index " <<
+ msg->ifa_index;
+}
+
+void DeviceInfo::AddRouteMsg(struct nlmsghdr *hdr) {
+ struct rtmsg *msg = reinterpret_cast<struct rtmsg *>(NLMSG_DATA(hdr));
+ VLOG(2) << "add routemsg family " << (int) msg->rtm_family << " table " <<
+ (int) msg->rtm_table << " proto " << (int) msg->rtm_protocol;
+}
+
+void DeviceInfo::DelRouteMsg(struct nlmsghdr *hdr) {
+ struct rtmsg *msg = reinterpret_cast<struct rtmsg *>(NLMSG_DATA(hdr));
+ VLOG(2) << "del routemsg family " << (int) msg->rtm_family << " table " <<
+ (int) msg->rtm_table << " proto " << (int) msg->rtm_protocol;
}
void DeviceInfo::ParseRTNL(InputData *data) {
@@ -160,7 +298,7 @@
unsigned char *end = buf + data->len;
while (buf < end) {
- struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
+ struct nlmsghdr *hdr = reinterpret_cast<struct nlmsghdr *>(buf);
struct nlmsgerr *err;
if (!NLMSG_OK(hdr, end - buf))
@@ -174,32 +312,32 @@
NextRequest(hdr->nlmsg_seq);
return;
case NLMSG_ERROR:
- err = (nlmsgerr *) NLMSG_DATA(hdr);
+ err = reinterpret_cast<nlmsgerr *>(NLMSG_DATA(hdr));
LOG(ERROR) << "error " << -err->error << " (" << strerror(-err->error) <<
")";
return;
case RTM_NEWLINK:
- LinkMsg(buf, false);
+ AddLinkMsg(hdr);
break;
case RTM_DELLINK:
- LinkMsg(buf, true);
+ DelLinkMsg(hdr);
break;
case RTM_NEWADDR:
- AddrMsg(buf, false);
+ AddAddrMsg(hdr);
break;
case RTM_DELADDR:
- AddrMsg(buf, true);
+ DelAddrMsg(hdr);
break;
case RTM_NEWROUTE:
- RouteMsg(buf, false);
+ AddRouteMsg(hdr);
break;
case RTM_DELROUTE:
- RouteMsg(buf, true);
+ DelRouteMsg(hdr);
break;
}
buf += hdr->nlmsg_len;
- }
+ }
}
} // namespace shill