shill: Add IP Address tracking to DeviceInfo

Subscribe to IP Address messages in DeviceInfo, and create a
per-device list of assigned IP Addresses.  Provide a method
to flush all globally scoped addresses from a device.

As a result, we can now flush assigned IP addresses when a
Connection is terminated.  There is also some incidental cleanup
in RTNLHandler to remove some vestiges of hand-baked RTNL
message encoding.

BUG=chromium-os:19744
TEST=Run new unit tests.  Test using ethernet on a netbook to make sure
addresses are added and removed correctly.

Change-Id: I63fd09088e71c43cb1f11a89a8ef15e11074976c
Reviewed-on: http://gerrit.chromium.org/gerrit/7180
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/rtnl_handler.cc b/rtnl_handler.cc
index ce4447f..dc48c71 100644
--- a/rtnl_handler.cc
+++ b/rtnl_handler.cc
@@ -40,6 +40,7 @@
       rtnl_socket_(-1),
       request_flags_(0),
       request_sequence_(0),
+      last_dump_sequence_(0),
       rtnl_callback_(NewCallback(this, &RTNLHandler::ParseRTNL)) {
   VLOG(2) << "RTNLHandler created";
 }
@@ -68,7 +69,8 @@
 
   memset(&addr, 0, sizeof(addr));
   addr.nl_family = AF_NETLINK;
-  addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
+  addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
+      RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
 
   if (sockets->Bind(rtnl_socket_,
                     reinterpret_cast<struct sockaddr *>(&addr),
@@ -83,7 +85,7 @@
                                                      rtnl_callback_.get()));
   sockets_ = sockets;
 
-  NextRequest(request_sequence_);
+  NextRequest(last_dump_sequence_);
   VLOG(2) << "RTNLHandler started";
 }
 
@@ -153,7 +155,7 @@
           << std::dec << std::noshowbase;
 
   if (!in_request_ && sockets_)
-    NextRequest(request_sequence_);
+    NextRequest(last_dump_sequence_);
 }
 
 void RTNLHandler::DispatchEvent(int type, const RTNLMessage &msg) {
@@ -164,66 +166,43 @@
 }
 
 void RTNLHandler::NextRequest(uint32_t seq) {
-  struct rtnl_request {
-    struct nlmsghdr hdr;
-    struct rtgenmsg msg;
-  } req;
-  struct sockaddr_nl addr;
   int flag = 0;
+  RTNLMessage::Type type;
 
-  VLOG(2) << "RTNLHandler nextrequest " << seq << " " << request_sequence_
+  VLOG(2) << "RTNLHandler nextrequest " << seq << " " << last_dump_sequence_
           << std::showbase << std::hex
           << " " << request_flags_
           << std::dec << std::noshowbase;
 
-  if (seq != request_sequence_)
+  if (seq != last_dump_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_;
-
   if ((request_flags_ & kRequestLink) != 0) {
-    req.msg.rtgen_family = AF_INET;
-    req.hdr.nlmsg_type = RTM_GETLINK;
+    type = RTNLMessage::kTypeLink;
     flag = kRequestLink;
   } else if ((request_flags_ & kRequestAddr) != 0) {
-    req.msg.rtgen_family = AF_INET;
-    req.hdr.nlmsg_type = RTM_GETADDR;
+    type = RTNLMessage::kTypeAddress;
     flag = kRequestAddr;
   } else if ((request_flags_ & kRequestRoute) != 0) {
-    req.msg.rtgen_family = AF_INET;
-    req.hdr.nlmsg_type = RTM_GETROUTE;
+    type = RTNLMessage::kTypeRoute;
     flag = kRequestRoute;
-  } else if ((request_flags_ & kRequestAddr6) != 0) {
-    req.msg.rtgen_family = AF_INET6;
-    req.hdr.nlmsg_type = RTM_GETADDR;
-    flag = kRequestAddr6;
-  } else if ((request_flags_ & kRequestRoute6) != 0) {
-    req.msg.rtgen_family = AF_INET6;
-    req.hdr.nlmsg_type = RTM_GETROUTE;
-    flag = kRequestRoute6;
   } else {
+    VLOG(2) << "Done with requests";
     in_request_ = false;
     return;
   }
 
-  memset(&addr, 0, sizeof(addr));
-  addr.nl_family = AF_NETLINK;
+  RTNLMessage msg(
+      type,
+      RTNLMessage::kModeGet,
+      0,
+      0,
+      0,
+      0,
+      IPAddress::kAddressFamilyUnknown);
+  CHECK(SendMessage(&msg));
 
-  if (sockets_->SendTo(rtnl_socket_,
-                       &req,
-                       sizeof(req),
-                       0,
-                       reinterpret_cast<struct sockaddr *>(&addr),
-                       sizeof(addr)) < 0) {
-    LOG(ERROR) << "RTNL sendto failed";
-    return;
-  }
+  last_dump_sequence_ = msg.seq();
   request_flags_ &= ~flag;
   in_request_ = true;
 }
@@ -261,13 +240,13 @@
       }
     } else {
       switch (msg.type()) {
-        case RTNLMessage::kMessageTypeLink:
+        case RTNLMessage::kTypeLink:
           DispatchEvent(kRequestLink, msg);
           break;
-        case RTNLMessage::kMessageTypeAddress:
+        case RTNLMessage::kTypeAddress:
           DispatchEvent(kRequestAddr, msg);
           break;
-        case RTNLMessage::kMessageTypeRoute:
+        case RTNLMessage::kTypeRoute:
           DispatchEvent(kRequestRoute, msg);
           break;
         default:
@@ -278,92 +257,54 @@
   }
 }
 
-static bool AddAtribute(struct nlmsghdr *hdr, int max_msg_size, int attr_type,
-                        const void *attr_data, int attr_len) {
-  int len = RTA_LENGTH(attr_len);
-  int new_msg_size = NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(len);
-  struct rtattr *rt_attr;
+bool RTNLHandler::AddressRequest(int interface_index,
+                                 RTNLMessage::Mode mode,
+                                 int flags,
+                                 const IPAddress &local,
+                                 const IPAddress &gateway) {
+  CHECK(local.family() == gateway.family());
 
-  if (new_msg_size > max_msg_size)
-    return false;
+  RTNLMessage msg(
+      RTNLMessage::kTypeAddress,
+      mode,
+      NLM_F_REQUEST | flags,
+      0,
+      0,
+      interface_index,
+      local.family());
 
-  rt_attr = reinterpret_cast<struct rtattr *> (reinterpret_cast<unsigned char *>
-                                               (hdr) +
-                                               NLMSG_ALIGN(hdr->nlmsg_len));
-  rt_attr->rta_type = attr_type;
-  rt_attr->rta_len = len;
-  memcpy(RTA_DATA(rt_attr), attr_data, attr_len);
-  hdr->nlmsg_len = new_msg_size;
-  return true;
-}
-
-bool RTNLHandler::AddressRequest(int interface_index, int cmd, int flags,
-                                 const IPConfig &ipconfig) {
-  const IPConfig::Properties &properties = ipconfig.properties();
-  int address_family;
-  int address_size;
-  unsigned char *attrs, *attrs_end;
-  int max_msg_size;
-  struct {
-    struct nlmsghdr   hdr;
-    struct ifaddrmsg  ifa;
-    unsigned char     attrs[256];
-  } req;
-  union {
-    in_addr ip4;
-    in6_addr in6;
-  } addr;
-
-  if (properties.address_family == IPAddress::kAddressFamilyIPv4) {
-    address_family = AF_INET;
-    address_size = sizeof(struct in_addr);
-  } else if (properties.address_family == IPAddress::kAddressFamilyIPv6) {
-    address_family = AF_INET6;
-    address_size = sizeof(struct in6_addr);
-  } else {
-    return false;
-  }
-
-  request_sequence_++;
-  memset(&req, 0, sizeof(req));
-  req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
-  req.hdr.nlmsg_flags = NLM_F_REQUEST | flags;
-  req.hdr.nlmsg_type = cmd;
-  req.ifa.ifa_family = address_family;
-  req.ifa.ifa_index = interface_index;
-
-  max_msg_size = req.hdr.nlmsg_len + sizeof(req.attrs);
+  msg.set_address_status(RTNLMessage::AddressStatus(
+      local.prefix(),
+      0,
+      0));
 
   // TODO(pstew): This code only works for Ethernet-like setups,
   //              not with devices that have a peer address like PPP.
-  if (inet_pton(address_family, properties.address.c_str(), &addr) <= 0 ||
-      !AddAtribute(&req.hdr, max_msg_size, IFA_LOCAL, &addr, address_size))
-    return false;
-
-  if (inet_pton(address_family, properties.broadcast_address.c_str(),
-                &addr) <= 0 ||
-      !AddAtribute(&req.hdr, max_msg_size, IFA_BROADCAST, &addr, address_size))
-    return false;
-
-  req.ifa.ifa_prefixlen = properties.subnet_cidr;
-
-  if (sockets_->Send(rtnl_socket_, &req, req.hdr.nlmsg_len, 0) < 0) {
-    LOG(ERROR) << "RTNL sendto failed: " << strerror(errno);
-    return false;
+  msg.SetAttribute(IFA_LOCAL, local.address());
+  if (!gateway.IsDefault()) {
+    msg.SetAttribute(IFA_BROADCAST, gateway.address());
   }
 
-  return true;
+  return SendMessage(&msg);
 }
 
 bool RTNLHandler::AddInterfaceAddress(int interface_index,
-                                      const IPConfig &ipconfig) {
-  return AddressRequest(interface_index, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL,
-                        ipconfig);
+                                      const IPAddress &local,
+                                      const IPAddress &broadcast) {
+    return AddressRequest(interface_index,
+                          RTNLMessage::kModeAdd,
+                          NLM_F_CREATE | NLM_F_EXCL,
+                          local,
+                          broadcast);
 }
 
 bool RTNLHandler::RemoveInterfaceAddress(int interface_index,
-                                         const IPConfig &ipconfig) {
-  return AddressRequest(interface_index, RTM_DELADDR, 0, ipconfig);
+                                         const IPAddress &local) {
+  return AddressRequest(interface_index,
+                        RTNLMessage::kModeDelete,
+                        0,
+                        local,
+                        IPAddress(local.family()));
 }
 
 int RTNLHandler::GetInterfaceIndex(const string &interface_name) {
@@ -401,7 +342,7 @@
   }
 
   if (sockets_->Send(rtnl_socket_,
-                     msgdata.GetData(),
+                     msgdata.GetConstData(),
                      msgdata.GetLength(),
                      0) < 0) {
     PLOG(ERROR) << "RTNL send failed: " << strerror(errno);