shill: Connection: Add facility to add host routes

This requires a facility for tracking outstanding RTNL route requests,
and adding routes when they the response arrives.  A few small fixes
to RTNL handling needed to be added.

BUG=chromium-os:27483
TEST=New Unit Tests, manual: Assocated my new Neptune proto to test
network.

Change-Id: I701fa244041ad9e0d0a502a263d83792ab3c9114
Reviewed-on: https://gerrit.chromium.org/gerrit/17889
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/routing_table.cc b/routing_table.cc
index 88db886..4986f29 100644
--- a/routing_table.cc
+++ b/routing_table.cc
@@ -48,7 +48,8 @@
 
 RoutingTable::RoutingTable()
     : route_callback_(NewCallback(this, &RoutingTable::RouteMsgHandler)),
-      route_listener_(NULL) {
+      route_listener_(NULL),
+      rtnl_handler_(RTNLHandler::GetInstance()) {
   VLOG(2) << __func__;
 }
 
@@ -63,8 +64,7 @@
 
   route_listener_.reset(
       new RTNLListener(RTNLHandler::kRequestRoute, route_callback_.get()));
-  RTNLHandler::GetInstance()->RequestDump(
-      RTNLHandler::kRequestRoute);
+  rtnl_handler_->RequestDump(RTNLHandler::kRequestRoute);
 }
 
 void RoutingTable::Stop() {
@@ -75,10 +75,11 @@
 
 bool RoutingTable::AddRoute(int interface_index,
                             const RoutingTableEntry &entry) {
-  VLOG(2) << __func__ << " "
-          << "index " << interface_index << " "
-          << "gateway " << entry.gateway.ToString() << " "
-          << "metric " << entry.metric;
+  VLOG(2) << __func__ << ": "
+          << "destination " << entry.dst.ToString()
+          << " index " << interface_index
+          << " gateway " << entry.gateway.ToString()
+          << " metric " << entry.metric;
 
   CHECK(!entry.from_rtnl);
   if (!ApplyRoute(interface_index,
@@ -105,7 +106,8 @@
 bool RoutingTable::GetDefaultRouteInternal(int interface_index,
                                            IPAddress::Family family,
                                            RoutingTableEntry **entry) {
-  VLOG(2) << __func__ << " index " << interface_index << " family " << family;
+  VLOG(2) << __func__ << " index " << interface_index
+          << " family " << IPAddress::GetAddressFamilyName(family);
 
   base::hash_map<int, vector<RoutingTableEntry> >::iterator table =
     tables_.find(interface_index);
@@ -120,9 +122,9 @@
   for (nent = table->second.begin(); nent != table->second.end(); ++nent) {
     if (nent->dst.IsDefault() && nent->dst.family() == family) {
       *entry = &(*nent);
-      VLOG(2) << __func__ << " found "
-              << "gateway " << nent->gateway.ToString() << " "
-              << "metric " << nent->metric;
+      VLOG(2) << __func__ << ": found"
+              << " gateway " << nent->gateway.ToString()
+              << " metric " << nent->metric;
       return true;
     }
   }
@@ -195,8 +197,7 @@
 }
 
 void RoutingTable::SetDefaultMetric(int interface_index, uint32 metric) {
-  VLOG(2) << __func__ << " "
-          << "index " << interface_index << " metric " << metric;
+  VLOG(2) << __func__ << " index " << interface_index << " metric " << metric;
 
   RoutingTableEntry *entry;
   if (GetDefaultRouteInternal(
@@ -212,54 +213,100 @@
   }
 }
 
-void RoutingTable::RouteMsgHandler(const RTNLMessage &msg) {
-  if (msg.type() != RTNLMessage::kTypeRoute ||
-      msg.family() == IPAddress::kFamilyUnknown ||
-      !msg.HasAttribute(RTA_OIF)) {
-    return;
+// static
+bool RoutingTable::ParseRoutingTableMessage(const RTNLMessage &message,
+                                            int *interface_index,
+                                            RoutingTableEntry *entry) {
+  if (message.type() != RTNLMessage::kTypeRoute ||
+      message.family() == IPAddress::kFamilyUnknown ||
+      !message.HasAttribute(RTA_OIF)) {
+    return false;
   }
 
-  const RTNLMessage::RouteStatus &route_status = msg.route_status();
+  const RTNLMessage::RouteStatus &route_status = message.route_status();
 
   if (route_status.type != RTN_UNICAST ||
-      route_status.protocol != RTPROT_BOOT ||
       route_status.table != RT_TABLE_MAIN) {
-    return;
+    return false;
   }
 
-  uint32 interface_index = 0;
-  if (!msg.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&interface_index)) {
-    return;
+  uint32 interface_index_u32 = 0;
+  if (!message.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&interface_index_u32)) {
+    return false;
   }
+  *interface_index = interface_index_u32;
 
   uint32 metric = 0;
-  if (msg.HasAttribute(RTA_PRIORITY)) {
-    msg.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&metric);
+  if (message.HasAttribute(RTA_PRIORITY)) {
+    message.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&metric);
   }
 
-  IPAddress default_addr(msg.family());
+  IPAddress default_addr(message.family());
   default_addr.SetAddressToDefault();
 
   ByteString dst_bytes(default_addr.address());
-  if (msg.HasAttribute(RTA_DST)) {
-    dst_bytes = msg.GetAttribute(RTA_DST);
+  if (message.HasAttribute(RTA_DST)) {
+    dst_bytes = message.GetAttribute(RTA_DST);
   }
   ByteString src_bytes(default_addr.address());
-  if (msg.HasAttribute(RTA_SRC)) {
-    src_bytes = msg.GetAttribute(RTA_SRC);
+  if (message.HasAttribute(RTA_SRC)) {
+    src_bytes = message.GetAttribute(RTA_SRC);
   }
   ByteString gateway_bytes(default_addr.address());
-  if (msg.HasAttribute(RTA_GATEWAY)) {
-    gateway_bytes = msg.GetAttribute(RTA_GATEWAY);
+  if (message.HasAttribute(RTA_GATEWAY)) {
+    gateway_bytes = message.GetAttribute(RTA_GATEWAY);
   }
 
-  RoutingTableEntry entry(
-      IPAddress(msg.family(), dst_bytes, route_status.dst_prefix),
-      IPAddress(msg.family(), src_bytes, route_status.src_prefix),
-      IPAddress(msg.family(), gateway_bytes),
-      metric,
-      route_status.scope,
-      true);
+  entry->dst = IPAddress(message.family(), dst_bytes, route_status.dst_prefix);
+  entry->src = IPAddress(message.family(), src_bytes, route_status.src_prefix);
+  entry->gateway = IPAddress(message.family(), gateway_bytes);
+  entry->metric = metric;
+  entry->scope = route_status.scope;
+  entry->from_rtnl = true;
+
+  return true;
+}
+
+void RoutingTable::RouteMsgHandler(const RTNLMessage &message) {
+  int interface_index;
+  RoutingTableEntry entry;
+
+  if (!ParseRoutingTableMessage(message, &interface_index, &entry)) {
+    return;
+  }
+
+  if (!route_query_sequences_.empty() &&
+      message.route_status().protocol == RTPROT_UNSPEC) {
+    VLOG(3) << __func__ << ": Message seq: " << message.seq()
+            << " mode " << message.mode()
+            << ", next query seq: " << route_query_sequences_.front();
+
+    // Purge queries that have expired (sequence number of this message is
+    // greater than that of the head of the route query sequence).  Do the
+    // math in a way that's roll-over independent.
+    while (route_query_sequences_.front() - message.seq() > kuint32max / 2) {
+      LOG(ERROR) << __func__ << ": Purging un-replied route request sequence "
+                 << route_query_sequences_.front()
+                 << " (< " << message.seq() << ")";
+      route_query_sequences_.pop();
+      if (route_query_sequences_.empty())
+        return;
+    }
+
+    if (route_query_sequences_.front() == message.seq()) {
+      VLOG(2) << __func__ << ": Adding host route to " << entry.dst.ToString();
+      route_query_sequences_.pop();
+      RoutingTableEntry add_entry(entry);
+      add_entry.from_rtnl = false;
+      AddRoute(interface_index, add_entry);
+    }
+    return;
+  } else if (message.route_status().protocol != RTPROT_BOOT) {
+    // Responses to route queries come back with a protocol of
+    // RTPROT_UNSPEC.  Otherwise, normal route updates that we are
+    // interested in come with a protocol of RTPROT_BOOT.
+    return;
+  }
 
   vector<RoutingTableEntry> &table = tables_[interface_index];
   vector<RoutingTableEntry>::iterator nent;
@@ -268,10 +315,10 @@
         nent->src.Equals(entry.src) &&
         nent->gateway.Equals(entry.gateway) &&
         nent->scope == entry.scope) {
-      if (msg.mode() == RTNLMessage::kModeDelete &&
+      if (message.mode() == RTNLMessage::kModeDelete &&
           nent->metric == entry.metric) {
         table.erase(nent);
-      } else if (msg.mode() == RTNLMessage::kModeAdd) {
+      } else if (message.mode() == RTNLMessage::kModeAdd) {
         nent->from_rtnl = true;
         nent->metric = entry.metric;
       }
@@ -279,11 +326,12 @@
     }
   }
 
-  if (msg.mode() == RTNLMessage::kModeAdd) {
-    VLOG(2) << __func__ << " adding "
-            << "index " << interface_index
-            << "gateway " << entry.gateway.ToString() << " "
-            << "metric " << entry.metric;
+  if (message.mode() == RTNLMessage::kModeAdd) {
+    VLOG(2) << __func__ << " adding"
+            << " destination " << entry.dst.ToString()
+            << " index " << interface_index
+            << " gateway " << entry.gateway.ToString()
+            << " metric " << entry.metric;
     table.push_back(entry);
   }
 }
@@ -296,7 +344,7 @@
                                 __func__, entry.dst.ToString().c_str(),
                                 interface_index, mode, flags);
 
-  RTNLMessage msg(
+  RTNLMessage message(
       RTNLMessage::kTypeRoute,
       mode,
       NLM_F_REQUEST | flags,
@@ -305,7 +353,7 @@
       0,
       entry.dst.family());
 
-  msg.set_route_status(RTNLMessage::RouteStatus(
+  message.set_route_status(RTNLMessage::RouteStatus(
       entry.dst.prefix(),
       entry.src.prefix(),
       RT_TABLE_MAIN,
@@ -314,17 +362,19 @@
       RTN_UNICAST,
       0));
 
-  msg.SetAttribute(RTA_DST, entry.dst.address());
+  message.SetAttribute(RTA_DST, entry.dst.address());
   if (!entry.src.IsDefault()) {
-    msg.SetAttribute(RTA_SRC, entry.src.address());
+    message.SetAttribute(RTA_SRC, entry.src.address());
   }
   if (!entry.gateway.IsDefault()) {
-    msg.SetAttribute(RTA_GATEWAY, entry.gateway.address());
+    message.SetAttribute(RTA_GATEWAY, entry.gateway.address());
   }
-  msg.SetAttribute(RTA_PRIORITY, ByteString::CreateFromCPUUInt32(entry.metric));
-  msg.SetAttribute(RTA_OIF, ByteString::CreateFromCPUUInt32(interface_index));
+  message.SetAttribute(RTA_PRIORITY,
+                       ByteString::CreateFromCPUUInt32(entry.metric));
+  message.SetAttribute(RTA_OIF,
+                       ByteString::CreateFromCPUUInt32(interface_index));
 
-  return RTNLHandler::GetInstance()->SendMessage(&msg);
+  return rtnl_handler_->SendMessage(&message);
 }
 
 // Somewhat surprisingly, the kernel allows you to create multiple routes
@@ -336,8 +386,7 @@
 void RoutingTable::ReplaceMetric(uint32 interface_index,
                                  RoutingTableEntry *entry,
                                  uint32 metric) {
-  VLOG(2) << __func__ << " "
-          << "index " << interface_index << " metric " << metric;
+  VLOG(2) << __func__ << " index " << interface_index << " metric " << metric;
   RoutingTableEntry new_entry = *entry;
   new_entry.metric = metric;
   // First create the route at the new metric.
@@ -366,4 +415,33 @@
   return ret;
 }
 
+bool RoutingTable::RequestRouteToHost(const IPAddress &address,
+                                      int interface_index) {
+  RTNLMessage message(
+      RTNLMessage::kTypeRoute,
+      RTNLMessage::kModeQuery,
+      NLM_F_REQUEST,
+      0,
+      0,
+      interface_index,
+      address.family());
+
+  RTNLMessage::RouteStatus status;
+  status.dst_prefix = address.prefix();
+  message.set_route_status(status);
+  message.SetAttribute(RTA_DST, address.address());
+  message.SetAttribute(RTA_OIF,
+                       ByteString::CreateFromCPUUInt32(interface_index));
+
+  if (!rtnl_handler_->SendMessage(&message)) {
+    return false;
+  }
+
+  // Save the sequence number of the request so we can create a route for
+  // this host when we get a reply.
+  route_query_sequences_.push(message.seq());
+
+  return true;
+}
+
 }  // namespace shill