Split off rtnl facility from device_info

Create an "rtnl_handler" singleton for conversing with rtnl socket.
Create "listener" objects that hold callbacks to interested parties.

BUG=chromium-os:15804
TEST=Rerun unit tests

Change-Id: Ica845b39ce6a0885a4e6d2560146ff8f5f45b900
Reviewed-on: http://gerrit.chromium.org/gerrit/1632
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 1e8608c..d1452d2 100644
--- a/device_info.cc
+++ b/device_info.cc
@@ -25,8 +25,11 @@
 #include "shill/control_interface.h"
 #include "shill/device.h"
 #include "shill/device_info.h"
+#include "shill/device_stub.h"
 #include "shill/ethernet.h"
 #include "shill/manager.h"
+#include "shill/rtnl_handler.h"
+#include "shill/rtnl_listener.h"
 #include "shill/service.h"
 #include "shill/wifi.h"
 
@@ -50,105 +53,23 @@
 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) {}
-
-DeviceInfo::~DeviceInfo() {
-  Stop();
+    : control_interface_(control_interface),
+      dispatcher_(dispatcher),
+      manager_(manager),
+      link_callback_(NewCallback(this, &DeviceInfo::LinkMsgHandler)),
+      link_listener_(NULL) {
 }
 
-void DeviceInfo::Start()
-{
-  struct sockaddr_nl addr;
+DeviceInfo::~DeviceInfo() {}
 
-  if (running_)
-    return;
-
-  rtnl_socket_ = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
-  if (rtnl_socket_ < 0) {
-    LOG(ERROR) << "Failed to open rtnl socket";
-    return;
-  }
-
-  memset(&addr, 0, sizeof(addr));
-  addr.nl_family = AF_NETLINK;
-  addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
-
-  if (bind(rtnl_socket_, reinterpret_cast<struct sockaddr *>(&addr),
-           sizeof(addr)) < 0) {
-    close(rtnl_socket_);
-    rtnl_socket_ = -1;
-    LOG(ERROR) << "RTNL socket bind failed";
-    return;
-  }
-
-  rtnl_handler_.reset(dispatcher_->CreateInputHandler(rtnl_socket_,
-                                                      rtnl_callback_.get()));
-  running_ = true;
-
-  request_flags_ = REQUEST_LINK | REQUEST_ADDR | REQUEST_ROUTE;
-  NextRequest(request_sequence_);
-
-  VLOG(2) << "DeviceInfo started";
+void DeviceInfo::Start() {
+  link_listener_.reset(
+      new RTNLListener(RTNLHandler::kRequestLink, link_callback_.get()));
+  RTNLHandler::GetInstance()->RequestDump(RTNLHandler::kRequestLink);
 }
 
-void DeviceInfo::Stop()
-{
-  if (!running_)
-    return;
-
-  rtnl_handler_.reset(NULL);
-  close(rtnl_socket_);
-  running_ = false;
-}
-
-void DeviceInfo::NextRequest(uint32_t seq) {
-  struct rtnl_request {
-    struct nlmsghdr hdr;
-    struct rtgenmsg msg;
-  } req;
-  struct sockaddr_nl addr;
-  int flag = 0;
-
-  if (seq != request_sequence_)
-    return;
-
-  request_sequence_++;
-  memset(&req, 0, sizeof(req));
-
-  req.hdr.nlmsg_len = sizeof(req);
-  req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-  req.hdr.nlmsg_pid = 0;
-  req.hdr.nlmsg_seq = request_sequence_;
-  req.msg.rtgen_family = AF_INET;
-
-  if ((request_flags_ & REQUEST_LINK) != 0) {
-    req.hdr.nlmsg_type = RTM_GETLINK;
-    flag = REQUEST_LINK;
-  } else if ((request_flags_ & REQUEST_ADDR) != 0) {
-    req.hdr.nlmsg_type = RTM_GETADDR;
-    flag = REQUEST_ADDR;
-  } else if ((request_flags_ & REQUEST_ROUTE) != 0) {
-    req.hdr.nlmsg_type = RTM_GETROUTE;
-    flag = REQUEST_ROUTE;
-  } else
-    return;
-
-  memset(&addr, 0, sizeof(addr));
-  addr.nl_family = AF_NETLINK;
-
-  if (sendto(rtnl_socket_, &req, sizeof(req), 0,
-             reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) < 0) {
-    LOG(ERROR) << "RTNL sendto failed";
-    return;
-  }
-  request_flags_ &= ~flag;
+void DeviceInfo::Stop() {
+  link_listener_.reset(NULL);
 }
 
 Device::Technology DeviceInfo::GetDeviceTechnology(const char *interface_name) {
@@ -196,7 +117,7 @@
   return Device::kEthernet;
 }
 
-void DeviceInfo::AddLinkMsg(struct nlmsghdr *hdr) {
+void DeviceInfo::AddLinkMsgHandler(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);
@@ -209,9 +130,6 @@
   Device::Technology technology;
   bool is_stub = false;
 
-  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);
@@ -224,19 +142,22 @@
       }
     }
 
+    VLOG(2) << "add link index "  << dev_index << " name " << link_name;
+
     if (link_name != NULL)
       technology = GetDeviceTechnology(link_name);
 
     switch (technology) {
     case Device::kEthernet:
-      device = new Ethernet(control_interface_, dispatcher_,
+      device = new Ethernet(control_interface_, dispatcher_, manager_,
                             link_name, dev_index);
       break;
     case Device::kWifi:
-      device = new WiFi(control_interface_, dispatcher_, link_name, dev_index);
+      device = new WiFi(control_interface_, dispatcher_, manager_,
+                        link_name, dev_index);
       break;
     default:
-      device = new StubDevice(control_interface_, dispatcher_,
+      device = new DeviceStub(control_interface_, dispatcher_, manager_,
                               link_name, dev_index, technology);
       is_stub = true;
     }
@@ -249,10 +170,10 @@
     device = ndev->second;
   }
 
-  // TODO(pstew): Send the the flags change upwards to the device
+  device->LinkEvent(msg->ifi_flags, msg->ifi_change);
 }
 
-void DeviceInfo::DelLinkMsg(struct nlmsghdr *hdr) {
+void DeviceInfo::DelLinkMsgHandler(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);
@@ -269,74 +190,11 @@
   }
 }
 
-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) {
-  unsigned char *buf = data->buf;
-  unsigned char *end = buf + data->len;
-
-  while (buf < end) {
-    struct nlmsghdr *hdr = reinterpret_cast<struct nlmsghdr *>(buf);
-    struct nlmsgerr *err;
-
-    if (!NLMSG_OK(hdr, end - buf))
-      break;
-
-    switch (hdr->nlmsg_type) {
-    case NLMSG_NOOP:
-    case NLMSG_OVERRUN:
-      return;
-    case NLMSG_DONE:
-      NextRequest(hdr->nlmsg_seq);
-      return;
-    case NLMSG_ERROR:
-      err = reinterpret_cast<nlmsgerr *>(NLMSG_DATA(hdr));
-      LOG(ERROR) << "error " << -err->error << " (" << strerror(-err->error) <<
-                 ")";
-      return;
-    case RTM_NEWLINK:
-      AddLinkMsg(hdr);
-      break;
-    case RTM_DELLINK:
-      DelLinkMsg(hdr);
-      break;
-    case RTM_NEWADDR:
-      AddAddrMsg(hdr);
-      break;
-    case RTM_DELADDR:
-      DelAddrMsg(hdr);
-      break;
-    case RTM_NEWROUTE:
-      AddRouteMsg(hdr);
-      break;
-    case RTM_DELROUTE:
-      DelRouteMsg(hdr);
-      break;
-    }
-
-    buf += hdr->nlmsg_len;
+void DeviceInfo::LinkMsgHandler(struct nlmsghdr *hdr) {
+  if (hdr->nlmsg_type == RTM_NEWLINK) {
+    AddLinkMsgHandler(hdr);
+  } else if (hdr->nlmsg_type == RTM_DELLINK) {
+    DelLinkMsgHandler(hdr);
   }
 }