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/connection.cc b/connection.cc
index fde6b0a..a69a690 100644
--- a/connection.cc
+++ b/connection.cc
@@ -10,6 +10,7 @@
 #include "shill/device_info.h"
 #include "shill/resolver.h"
 #include "shill/routing_table.h"
+#include "shill/routing_table_entry.h"
 #include "shill/rtnl_handler.h"
 
 using std::string;
@@ -127,6 +128,19 @@
   }
 }
 
+bool Connection::RequestHostRoute(const IPAddress &address) {
+  // Set the prefix to be the entire address size.
+  IPAddress address_prefix(address);
+  address_prefix.set_prefix(address_prefix.GetLength() * 8);
+
+  if (!routing_table_->RequestRouteToHost(address_prefix, interface_index_)) {
+    LOG(ERROR) << "Could not request route to " << address.ToString();
+    return false;
+  }
+
+  return true;
+}
+
 uint32 Connection::GetMetric(bool is_default) {
   // If this is not the default route, assign a metric based on the interface
   // index.  This way all non-default routes (even to the same gateway IP) end
diff --git a/connection.h b/connection.h
index 0e7b5ab..a2e4374 100644
--- a/connection.h
+++ b/connection.h
@@ -16,6 +16,7 @@
 namespace shill {
 
 class DeviceInfo;
+class IPAddress;
 class Resolver;
 class RoutingTable;
 class RTNLHandler;
@@ -49,6 +50,9 @@
   virtual void RequestRouting();
   virtual void ReleaseRouting();
 
+  // Request a host route through this connection.
+  virtual bool RequestHostRoute(const IPAddress &destination);
+
  private:
   friend class ConnectionTest;
   FRIEND_TEST(ConnectionTest, InitState);
diff --git a/connection_unittest.cc b/connection_unittest.cc
index 08d85b9..690c7a7 100644
--- a/connection_unittest.cc
+++ b/connection_unittest.cc
@@ -170,38 +170,36 @@
 }
 
 TEST_F(ConnectionTest, RouteRequest) {
-  {
-    ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex0,
-                                               kTestDeviceName0,
-                                               device_info_.get()));
-    ReplaceSingletons(connection);
-    scoped_refptr<MockDevice> device(new StrictMock<MockDevice>(
-        &control_,
-        reinterpret_cast<EventDispatcher *>(NULL),
-        reinterpret_cast<Metrics *>(NULL),
-        reinterpret_cast<Manager *>(NULL),
-        kTestDeviceName0,
-        string(),
-        kTestDeviceInterfaceIndex0));
-    EXPECT_CALL(*device_info_, GetDevice(kTestDeviceInterfaceIndex0))
-        .WillRepeatedly(Return(device));
-    EXPECT_CALL(*device.get(), DisableReversePathFilter()).Times(1);
-    connection->RequestRouting();
-    connection->RequestRouting();
+  ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex0,
+                                             kTestDeviceName0,
+                                             device_info_.get()));
+  ReplaceSingletons(connection);
+  scoped_refptr<MockDevice> device(new StrictMock<MockDevice>(
+      &control_,
+      reinterpret_cast<EventDispatcher *>(NULL),
+      reinterpret_cast<Metrics *>(NULL),
+      reinterpret_cast<Manager *>(NULL),
+      kTestDeviceName0,
+      string(),
+      kTestDeviceInterfaceIndex0));
+  EXPECT_CALL(*device_info_, GetDevice(kTestDeviceInterfaceIndex0))
+      .WillRepeatedly(Return(device));
+  EXPECT_CALL(*device.get(), DisableReversePathFilter()).Times(1);
+  connection->RequestRouting();
+  connection->RequestRouting();
 
-    // The first release should only decrement the reference counter.
-    connection->ReleaseRouting();
+  // The first release should only decrement the reference counter.
+  connection->ReleaseRouting();
 
-    // Another release will re-enable reverse-path filter.
-    EXPECT_CALL(*device.get(), EnableReversePathFilter());
-    EXPECT_CALL(routing_table_, FlushCache());
-    connection->ReleaseRouting();
+  // Another release will re-enable reverse-path filter.
+  EXPECT_CALL(*device.get(), EnableReversePathFilter());
+  EXPECT_CALL(routing_table_, FlushCache());
+  connection->ReleaseRouting();
 
-    // The destructor will remove the routes and addresses.
-    EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex0));
-    EXPECT_CALL(*device_info_.get(),
-                FlushAddresses(kTestDeviceInterfaceIndex0));
-  }
+  // The destructor will remove the routes and addresses.
+  EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex0));
+  EXPECT_CALL(*device_info_.get(),
+              FlushAddresses(kTestDeviceInterfaceIndex0));
 }
 
 TEST_F(ConnectionTest, Destructor) {
@@ -217,4 +215,29 @@
   }
 }
 
+MATCHER_P2(IsIPAddress, address, prefix, "") {
+  IPAddress match_address(address);
+  match_address.set_prefix(prefix);
+  return match_address.Equals(arg);
+}
+
+TEST_F(ConnectionTest, RequestHostRoute) {
+  ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex0,
+                                             kTestDeviceName0,
+                                             device_info_.get()));
+  ReplaceSingletons(connection);
+  IPAddress address(IPAddress::kFamilyIPv4);
+  ASSERT_TRUE(address.SetAddressFromString(kIPAddress0));
+  size_t prefix_len = address.GetLength() * 8;
+  EXPECT_CALL(routing_table_, RequestRouteToHost(
+      IsIPAddress(address, prefix_len), kTestDeviceInterfaceIndex0))
+      .WillOnce(Return(true));
+  EXPECT_TRUE(connection->RequestHostRoute(address));
+
+  // The destructor will remove the routes and addresses.
+  EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex0));
+  EXPECT_CALL(*device_info_.get(),
+              FlushAddresses(kTestDeviceInterfaceIndex0));
+}
+
 }  // namespace shill
diff --git a/ip_address.cc b/ip_address.cc
index 8dda572..5281d85 100644
--- a/ip_address.cc
+++ b/ip_address.cc
@@ -24,6 +24,13 @@
 // static
 const IPAddress::Family IPAddress::kFamilyIPv6 = AF_INET6;
 
+// static
+const char IPAddress::kFamilyNameUnknown[] = "Unknown";
+// static
+const char IPAddress::kFamilyNameIPv4[] = "IPv4";
+// static
+const char IPAddress::kFamilyNameIPv6[] = "IPv6";
+
 IPAddress::IPAddress(Family family, const ByteString &address)
     : family_(family) ,
       address_(address),
@@ -42,6 +49,7 @@
 
 IPAddress::~IPAddress() {}
 
+// static
 size_t IPAddress::GetAddressLength(Family family) {
   switch (family) {
   case kFamilyIPv4:
@@ -75,6 +83,18 @@
   return 0;
 }
 
+// static
+string IPAddress::GetAddressFamilyName(Family family) {
+  switch (family) {
+  case kFamilyIPv4:
+    return kFamilyNameIPv4;
+  case kFamilyIPv6:
+    return kFamilyNameIPv6;
+  default:
+    return kFamilyNameUnknown;
+  }
+}
+
 bool IPAddress::SetAddressFromString(const string &address_string) {
   size_t address_length = GetAddressLength(family_);
 
diff --git a/ip_address.h b/ip_address.h
index f13c303..bca47ed 100644
--- a/ip_address.h
+++ b/ip_address.h
@@ -17,6 +17,9 @@
   static const Family kFamilyUnknown;
   static const Family kFamilyIPv4;
   static const Family kFamilyIPv6;
+  static const char kFamilyNameUnknown[];
+  static const char kFamilyNameIPv4[];
+  static const char kFamilyNameIPv6[];
 
   explicit IPAddress(Family family);
   IPAddress(Family family, const ByteString &address);
@@ -43,6 +46,9 @@
   // example, returns 24 for an IPv4 mask 255.255.255.0.
   static int GetPrefixLengthFromMask(Family family, const std::string &mask);
 
+  // Returns the name of an address family.
+  static std::string GetAddressFamilyName(Family family);
+
   // Getters and Setters
   Family family() const { return family_; }
   const ByteString &address() const { return address_; }
diff --git a/mock_routing_table.h b/mock_routing_table.h
index ec65029..5fce84b 100644
--- a/mock_routing_table.h
+++ b/mock_routing_table.h
@@ -31,6 +31,8 @@
   MOCK_METHOD0(FlushCache, bool());
   MOCK_METHOD1(ResetTable, void(int interface_index));
   MOCK_METHOD2(SetDefaultMetric, void(int interface_index, uint32 metric));
+  MOCK_METHOD2(RequestRouteToHost, bool(const IPAddress &addresss,
+                                        int interface_index));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockRoutingTable);
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
diff --git a/routing_table.h b/routing_table.h
index 436acc0..c015379 100644
--- a/routing_table.h
+++ b/routing_table.h
@@ -5,6 +5,7 @@
 #ifndef SHILL_ROUTING_TABLE_
 #define SHILL_ROUTING_TABLE_
 
+#include <queue>
 #include <string>
 #include <vector>
 
@@ -21,7 +22,9 @@
 namespace shill {
 
 class RoutingTableEntry;
+class RTNLHandler;
 class RTNLListener;
+class RTNLMessage;
 
 // This singleton maintains an in-process copy of the routing table on
 // a per-interface basis.  It offers the ability for other modules to
@@ -63,6 +66,11 @@
   // Set the metric (priority) on existing default routes for an interface.
   virtual void SetDefaultMetric(int interface_index, uint32 metric);
 
+  // Get the default route to |destination| through |interface_index| and
+  // create a host route to that destination.
+  virtual bool RequestRouteToHost(const IPAddress &destination,
+                                  int interface_index);
+
  protected:
   RoutingTable();
 
@@ -70,6 +78,10 @@
   friend struct base::DefaultLazyInstanceTraits<RoutingTable>;
   friend class RoutingTableTest;
 
+
+  static bool ParseRoutingTableMessage(const RTNLMessage &message,
+                                       int *interface_index,
+                                       RoutingTableEntry *entry);
   void RouteMsgHandler(const RTNLMessage &msg);
   bool ApplyRoute(uint32 interface_index,
                   const RoutingTableEntry &entry,
@@ -93,6 +105,10 @@
 
   scoped_ptr<Callback1<const RTNLMessage &>::Type> route_callback_;
   scoped_ptr<RTNLListener> route_listener_;
+  std::queue<uint32> route_query_sequences_;
+
+  // Cache singleton pointer for performance and test purposes.
+  RTNLHandler *rtnl_handler_;
 
   DISALLOW_COPY_AND_ASSIGN(RoutingTable);
 };
diff --git a/routing_table_unittest.cc b/routing_table_unittest.cc
index c1d22e7..6e36cb5 100644
--- a/routing_table_unittest.cc
+++ b/routing_table_unittest.cc
@@ -12,14 +12,16 @@
 
 #include "shill/byte_string.h"
 #include "shill/mock_control.h"
-#include "shill/mock_sockets.h"
+#include "shill/mock_rtnl_handler.h"
 #include "shill/routing_table.h"
 #include "shill/routing_table_entry.h"
 #include "shill/rtnl_handler.h"
 #include "shill/rtnl_message.h"
 
 using testing::_;
+using testing::Invoke;
 using testing::Return;
+using testing::StrictMock;
 using testing::Test;
 
 namespace shill {
@@ -37,20 +39,41 @@
  public:
   RoutingTableTest() : routing_table_(RoutingTable::GetInstance()) {}
 
-  base::hash_map<int, std::vector<RoutingTableEntry> > *GetRoutingTables() {
-    return &routing_table_->tables_;
+  virtual void SetUp() {
+    routing_table_->rtnl_handler_ = &rtnl_handler_;
+    ON_CALL(rtnl_handler_, SendMessage(_)).WillByDefault(Return(true));
   }
 
-  void SendRouteMsg(RTNLMessage::Mode mode,
-                    uint32 interface_index,
-                    const RoutingTableEntry &entry);
-
   virtual void TearDown() {
     RTNLHandler::GetInstance()->Stop();
   }
 
+  base::hash_map<int, std::vector<RoutingTableEntry> > *GetRoutingTables() {
+    return &routing_table_->tables_;
+  }
+
+  std::queue<uint32> *GetQuerySequences() {
+    return &routing_table_->route_query_sequences_;
+  }
+
+  void SendRouteEntry(RTNLMessage::Mode mode,
+                      uint32 interface_index,
+                      const RoutingTableEntry &entry);
+
+  void SendRouteEntryWithSeqAndProto(RTNLMessage::Mode mode,
+                                     uint32 interface_index,
+                                     const RoutingTableEntry &entry,
+                                     uint32 seq,
+                                     unsigned char proto);
+
+  void SendRouteMessage(const RTNLMessage &msg);
+
+  bool SetSequenceForMessage(RTNLMessage *message) {
+    message->set_seq(RoutingTableTest::kTestRequestSeq);
+    return true;
+  }
+
  protected:
-  static const int kTestSocket;
   static const uint32 kTestDeviceIndex0;
   static const uint32 kTestDeviceIndex1;
   static const char kTestDeviceName0[];
@@ -59,18 +82,17 @@
   static const char kTestForeignNetGateway4[];
   static const char kTestForeignNetAddress6[];
   static const char kTestForeignNetGateway6[];
+  static const char kTestGatewayAddress4[];
   static const char kTestNetAddress0[];
   static const char kTestNetAddress1[];
+  static const char kTestRemoteAddress4[];
+  static const uint32 kTestRequestSeq;
 
-  void StartRTNLHandler();
-  void StopRTNLHandler();
-
-  MockSockets sockets_;
   RoutingTable *routing_table_;
   TestEventDispatcher dispatcher_;
+  StrictMock<MockRTNLHandler> rtnl_handler_;
 };
 
-const int RoutingTableTest::kTestSocket = 123;
 const uint32 RoutingTableTest::kTestDeviceIndex0 = 12345;
 const uint32 RoutingTableTest::kTestDeviceIndex1 = 67890;
 const char RoutingTableTest::kTestDeviceName0[] = "test-device0";
@@ -79,58 +101,57 @@
 const char RoutingTableTest::kTestForeignNetGateway4[] = "192.168.2.1";
 const char RoutingTableTest::kTestForeignNetAddress6[] = "2000::/3";
 const char RoutingTableTest::kTestForeignNetGateway6[] = "fe80:::::1";
+const char RoutingTableTest::kTestGatewayAddress4[] = "192.168.2.254";
 const char RoutingTableTest::kTestNetAddress0[] = "192.168.1.1";
 const char RoutingTableTest::kTestNetAddress1[] = "192.168.1.2";
-
+const char RoutingTableTest::kTestRemoteAddress4[] = "192.168.2.254";
+const uint32 RoutingTableTest::kTestRequestSeq = 456;
 
 MATCHER_P4(IsRoutingPacket, mode, index, entry, flags, "") {
-  // NB: Matchers don't get passed multiple arguments, so we can
-  //     get the address of a Send(), its length, but not both.
-  //     We have to punt and assume the length is correct -- which
-  //     should already be tested in rtnl_message_unittest.
-  struct nlmsghdr hdr;
-  memcpy(&hdr, arg, sizeof(hdr));
-
-  RTNLMessage msg;
-  if (!msg.Decode(ByteString(reinterpret_cast<const unsigned char *>(arg),
-                             hdr.nlmsg_len))) {
-    return false;
-  }
-
-  const RTNLMessage::RouteStatus &status = msg.route_status();
+  const RTNLMessage::RouteStatus &status = arg->route_status();
 
   uint32 oif;
   uint32 priority;
 
   return
-    msg.type() == RTNLMessage::kTypeRoute &&
-    msg.family() == entry.gateway.family() &&
-    msg.flags() == (NLM_F_REQUEST | flags) &&
-    status.table == RT_TABLE_MAIN &&
-    status.protocol == RTPROT_BOOT &&
-    status.scope == entry.scope &&
-    status.type == RTN_UNICAST &&
-    msg.HasAttribute(RTA_DST) &&
-    IPAddress(msg.family(),
-              msg.GetAttribute(RTA_DST)).Equals(entry.dst) &&
-    !msg.HasAttribute(RTA_SRC) &&
-    msg.HasAttribute(RTA_GATEWAY) &&
-    IPAddress(msg.family(),
-              msg.GetAttribute(RTA_GATEWAY)).Equals(entry.gateway) &&
-    msg.GetAttribute(RTA_OIF).ConvertToCPUUInt32(&oif) &&
-    oif == index &&
-    msg.GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&priority) &&
-    priority == entry.metric;
+      arg->type() == RTNLMessage::kTypeRoute &&
+      arg->family() == entry.gateway.family() &&
+      arg->flags() == (NLM_F_REQUEST | flags) &&
+      status.table == RT_TABLE_MAIN &&
+      status.protocol == RTPROT_BOOT &&
+      status.scope == entry.scope &&
+      status.type == RTN_UNICAST &&
+      arg->HasAttribute(RTA_DST) &&
+      IPAddress(arg->family(),
+                arg->GetAttribute(RTA_DST),
+                status.dst_prefix).Equals(entry.dst) &&
+      !arg->HasAttribute(RTA_SRC) &&
+      arg->HasAttribute(RTA_GATEWAY) &&
+      IPAddress(arg->family(),
+                arg->GetAttribute(RTA_GATEWAY)).Equals(entry.gateway) &&
+      arg->GetAttribute(RTA_OIF).ConvertToCPUUInt32(&oif) &&
+      oif == index &&
+      arg->GetAttribute(RTA_PRIORITY).ConvertToCPUUInt32(&priority) &&
+      priority == entry.metric;
 }
 
-void RoutingTableTest::SendRouteMsg(RTNLMessage::Mode mode,
-                                    uint32 interface_index,
-                                    const RoutingTableEntry &entry) {
+void RoutingTableTest::SendRouteEntry(RTNLMessage::Mode mode,
+                                      uint32 interface_index,
+                                      const RoutingTableEntry &entry) {
+  SendRouteEntryWithSeqAndProto(mode, interface_index, entry, 0, RTPROT_BOOT);
+}
+
+void RoutingTableTest::SendRouteEntryWithSeqAndProto(
+    RTNLMessage::Mode mode,
+    uint32 interface_index,
+    const RoutingTableEntry &entry,
+    uint32 seq,
+    unsigned char proto) {
   RTNLMessage msg(
       RTNLMessage::kTypeRoute,
       mode,
       0,
-      0,
+      seq,
       0,
       0,
       entry.dst.family());
@@ -139,7 +160,7 @@
       entry.dst.prefix(),
       entry.src.prefix(),
       RT_TABLE_MAIN,
-      RTPROT_BOOT,
+      proto,
       entry.scope,
       RTN_UNICAST,
       0));
@@ -154,31 +175,19 @@
   msg.SetAttribute(RTA_PRIORITY, ByteString::CreateFromCPUUInt32(entry.metric));
   msg.SetAttribute(RTA_OIF, ByteString::CreateFromCPUUInt32(interface_index));
 
-  ByteString msgdata = msg.Encode();
-  EXPECT_NE(0, msgdata.GetLength());
-
-  InputData data(msgdata.GetData(), msgdata.GetLength());
-  RTNLHandler::GetInstance()->ParseRTNL(&data);
+  routing_table_->RouteMsgHandler(msg);
 }
 
-void RoutingTableTest::StartRTNLHandler() {
-  EXPECT_CALL(sockets_, Socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE))
-      .WillOnce(Return(kTestSocket));
-  EXPECT_CALL(sockets_, Bind(kTestSocket, _, sizeof(sockaddr_nl)))
-      .WillOnce(Return(0));
-  RTNLHandler::GetInstance()->Start(&dispatcher_, &sockets_);
+void RoutingTableTest::SendRouteMessage(const RTNLMessage &msg) {
+  routing_table_->RouteMsgHandler(msg);
 }
 
-void RoutingTableTest::StopRTNLHandler() {
-  EXPECT_CALL(sockets_, Close(_)).WillOnce(Return(0));
-  RTNLHandler::GetInstance()->Stop();
+TEST_F(RoutingTableTest, Start) {
+  EXPECT_CALL(rtnl_handler_, RequestDump(RTNLHandler::kRequestRoute));
+  routing_table_->Start();
 }
 
 TEST_F(RoutingTableTest, RouteAddDelete) {
-  EXPECT_CALL(sockets_, Send(kTestSocket, _, _, 0));
-  StartRTNLHandler();
-  routing_table_->Start();
-
   // Expect the tables to be empty by default.
   EXPECT_EQ(0, GetRoutingTables()->size());
 
@@ -197,9 +206,9 @@
                            RT_SCOPE_UNIVERSE,
                            true);
   // Add a single entry.
-  SendRouteMsg(RTNLMessage::kModeAdd,
-               kTestDeviceIndex0,
-               entry0);
+  SendRouteEntry(RTNLMessage::kModeAdd,
+                 kTestDeviceIndex0,
+                 entry0);
 
   base::hash_map<int, std::vector<RoutingTableEntry> > *tables =
     GetRoutingTables();
@@ -213,9 +222,9 @@
   EXPECT_TRUE(entry0.Equals(test_entry));
 
   // Add a second entry for a different interface.
-  SendRouteMsg(RTNLMessage::kModeAdd,
-               kTestDeviceIndex1,
-               entry0);
+  SendRouteEntry(RTNLMessage::kModeAdd,
+                 kTestDeviceIndex1,
+                 entry0);
 
   // We should have two tables, which should have a single entry each.
   EXPECT_EQ(2, tables->size());
@@ -237,9 +246,9 @@
                            true);
 
   // Add a second gateway route to the second interface.
-  SendRouteMsg(RTNLMessage::kModeAdd,
-               kTestDeviceIndex1,
-               entry1);
+  SendRouteEntry(RTNLMessage::kModeAdd,
+                 kTestDeviceIndex1,
+                 entry1);
 
   // We should have two tables, one of which has a single entry, the other has
   // two.
@@ -251,9 +260,9 @@
   EXPECT_TRUE(entry1.Equals(test_entry));
 
   // Remove the first gateway route from the second interface.
-  SendRouteMsg(RTNLMessage::kModeDelete,
-               kTestDeviceIndex1,
-               entry0);
+  SendRouteEntry(RTNLMessage::kModeDelete,
+                 kTestDeviceIndex1,
+                 entry0);
 
   // We should be back to having one route per table.
   EXPECT_EQ(2, tables->size());
@@ -266,9 +275,9 @@
   // Send a duplicate of the second gatway route message, changing the metric.
   RoutingTableEntry entry2(entry1);
   entry2.metric++;
-  SendRouteMsg(RTNLMessage::kModeAdd,
-               kTestDeviceIndex1,
-               entry2);
+  SendRouteEntry(RTNLMessage::kModeAdd,
+                 kTestDeviceIndex1,
+                 entry2);
 
   // Routing table size shouldn't change, but the new metric should match.
   EXPECT_EQ(1, (*tables)[kTestDeviceIndex1].size());
@@ -287,9 +296,9 @@
                                                &test_entry));
 
   // Remove last entry from an existing interface and test that we now fail.
-  SendRouteMsg(RTNLMessage::kModeDelete,
-               kTestDeviceIndex1,
-               entry2);
+  SendRouteEntry(RTNLMessage::kModeDelete,
+                 kTestDeviceIndex1,
+                 entry2);
 
   EXPECT_FALSE(routing_table_->GetDefaultRoute(kTestDeviceIndex1,
                                                IPAddress::kFamilyIPv4,
@@ -304,14 +313,11 @@
   properties.address = kTestNetAddress1;
   ipconfig->UpdateProperties(properties, true);
 
-  EXPECT_CALL(sockets_,
-              Send(kTestSocket,
-                   IsRoutingPacket(RTNLMessage::kModeAdd,
-                                   kTestDeviceIndex1,
-                                   entry0,
-                                   NLM_F_CREATE | NLM_F_EXCL),
-                   _,
-                   0));
+  EXPECT_CALL(rtnl_handler_,
+              SendMessage(IsRoutingPacket(RTNLMessage::kModeAdd,
+                                          kTestDeviceIndex1,
+                                          entry0,
+                                          NLM_F_CREATE | NLM_F_EXCL)));
   EXPECT_TRUE(routing_table_->SetDefaultRoute(kTestDeviceIndex1,
                                               ipconfig,
                                               metric));
@@ -330,22 +336,16 @@
   // then it should delete the old entry.
   RoutingTableEntry entry4(entry3);
   entry4.metric += 10;
-  EXPECT_CALL(sockets_,
-              Send(kTestSocket,
-                   IsRoutingPacket(RTNLMessage::kModeAdd,
-                                   kTestDeviceIndex1,
-                                   entry4,
-                                   NLM_F_CREATE | NLM_F_REPLACE),
-                   _,
-                   0));
-  EXPECT_CALL(sockets_,
-              Send(kTestSocket,
-                   IsRoutingPacket(RTNLMessage::kModeDelete,
+  EXPECT_CALL(rtnl_handler_,
+              SendMessage(IsRoutingPacket(RTNLMessage::kModeAdd,
+                                          kTestDeviceIndex1,
+                                          entry4,
+                                          NLM_F_CREATE | NLM_F_REPLACE)));
+  EXPECT_CALL(rtnl_handler_,
+              SendMessage(IsRoutingPacket(RTNLMessage::kModeDelete,
                                    kTestDeviceIndex1,
                                    entry3,
-                                   0),
-                   _,
-                   0));
+                                   0)));
   EXPECT_TRUE(routing_table_->SetDefaultRoute(kTestDeviceIndex1,
                                               ipconfig,
                                               entry4.metric));
@@ -362,22 +362,16 @@
   // operation should occur.
   RoutingTableEntry entry5(entry4);
   entry5.metric += 10;
-  EXPECT_CALL(sockets_,
-              Send(kTestSocket,
-                   IsRoutingPacket(RTNLMessage::kModeAdd,
-                                   kTestDeviceIndex0,
-                                   entry5,
-                                   NLM_F_CREATE | NLM_F_REPLACE),
-                   _,
-                   0));
-  EXPECT_CALL(sockets_,
-              Send(kTestSocket,
-                   IsRoutingPacket(RTNLMessage::kModeDelete,
-                                   kTestDeviceIndex0,
-                                   entry0,
-                                   0),
-                   _,
-                   0));
+  EXPECT_CALL(rtnl_handler_,
+              SendMessage(IsRoutingPacket(RTNLMessage::kModeAdd,
+                                          kTestDeviceIndex0,
+                                          entry5,
+                                          NLM_F_CREATE | NLM_F_REPLACE)));
+  EXPECT_CALL(rtnl_handler_,
+              SendMessage(IsRoutingPacket(RTNLMessage::kModeDelete,
+                                          kTestDeviceIndex0,
+                                          entry0,
+                                          0)));
   routing_table_->SetDefaultMetric(kTestDeviceIndex0, entry5.metric);
   // Furthermore, the routing table should reflect the change in the metric
   // for the default route for the interface.
@@ -388,27 +382,122 @@
   EXPECT_EQ(entry5.metric, default_route.metric);
 
   // Ask to flush table0.  We should see a delete message sent.
-  EXPECT_CALL(sockets_,
-              Send(kTestSocket,
-                   IsRoutingPacket(RTNLMessage::kModeDelete,
-                                   kTestDeviceIndex0,
-                                   entry5,
-                                   0),
-                   _,
-                   0));
+  EXPECT_CALL(rtnl_handler_,
+              SendMessage(IsRoutingPacket(RTNLMessage::kModeDelete,
+                                          kTestDeviceIndex0,
+                                          entry5,
+                                          0)));
   routing_table_->FlushRoutes(kTestDeviceIndex0);
   EXPECT_EQ(0, (*tables)[kTestDeviceIndex0].size());
 
   // Test that the routing table size returns to zero.
-  SendRouteMsg(RTNLMessage::kModeAdd,
-               kTestDeviceIndex0,
-               entry5);
+  SendRouteEntry(RTNLMessage::kModeAdd,
+                 kTestDeviceIndex0,
+                 entry5);
   EXPECT_EQ(1, GetRoutingTables()->size());
   routing_table_->ResetTable(kTestDeviceIndex0);
   EXPECT_EQ(0, GetRoutingTables()->size());
 
   routing_table_->Stop();
-  StopRTNLHandler();
+}
+
+MATCHER_P2(IsRoutingQuery, destination, index, "") {
+  const RTNLMessage::RouteStatus &status = arg->route_status();
+
+  uint32 oif;
+
+  return
+      arg->type() == RTNLMessage::kTypeRoute &&
+      arg->family() == destination.family() &&
+      arg->flags() == NLM_F_REQUEST &&
+      status.table == 0 &&
+      status.protocol == 0 &&
+      status.scope == 0 &&
+      status.type == 0 &&
+      arg->HasAttribute(RTA_DST) &&
+      IPAddress(arg->family(),
+                arg->GetAttribute(RTA_DST),
+                status.dst_prefix).Equals(destination) &&
+      !arg->HasAttribute(RTA_SRC) &&
+      !arg->HasAttribute(RTA_GATEWAY) &&
+      arg->GetAttribute(RTA_OIF).ConvertToCPUUInt32(&oif) &&
+      oif == index &&
+      !arg->HasAttribute(RTA_PRIORITY);
+
+  return false;
+}
+
+TEST_F(RoutingTableTest, RequestHostRoute) {
+  IPAddress destination_address(IPAddress::kFamilyIPv4);
+  destination_address.SetAddressFromString(kTestRemoteAddress4);
+  destination_address.set_prefix(24);
+
+  EXPECT_CALL(rtnl_handler_,
+              SendMessage(IsRoutingQuery(destination_address,
+                                         kTestDeviceIndex0)))
+      .WillOnce(Invoke(this, &RoutingTableTest::SetSequenceForMessage));
+  EXPECT_TRUE(routing_table_->RequestRouteToHost(destination_address,
+                                                 kTestDeviceIndex0));
+
+  IPAddress gateway_address(IPAddress::kFamilyIPv4);
+  gateway_address.SetAddressFromString(kTestGatewayAddress4);
+
+  IPAddress local_address(IPAddress::kFamilyIPv4);
+  local_address.SetAddressFromString(kTestDeviceNetAddress4);
+
+  const int kMetric = 10;
+  RoutingTableEntry entry(destination_address,
+                          local_address,
+                          gateway_address,
+                          kMetric,
+                          RT_SCOPE_UNIVERSE,
+                          true);
+
+  EXPECT_CALL(rtnl_handler_,
+              SendMessage(IsRoutingPacket(RTNLMessage::kModeAdd,
+                                          kTestDeviceIndex0,
+                                          entry,
+                                          NLM_F_CREATE | NLM_F_EXCL)));
+  SendRouteEntryWithSeqAndProto(RTNLMessage::kModeAdd,
+                                kTestDeviceIndex0,
+                                entry,
+                                kTestRequestSeq,
+                                RTPROT_UNSPEC);
+}
+
+TEST_F(RoutingTableTest, RequestHostRouteBadSequence) {
+  IPAddress destination_address(IPAddress::kFamilyIPv4);
+  destination_address.SetAddressFromString(kTestRemoteAddress4);
+  EXPECT_CALL(rtnl_handler_, SendMessage(_))
+      .WillOnce(Invoke(this, &RoutingTableTest::SetSequenceForMessage));
+  EXPECT_TRUE(routing_table_->RequestRouteToHost(destination_address,
+                                                 kTestDeviceIndex0));
+  EXPECT_FALSE(GetQuerySequences()->empty());
+
+  RoutingTableEntry entry(destination_address,
+                          destination_address,
+                          destination_address,
+                          0,
+                          RT_SCOPE_UNIVERSE,
+                          true);
+
+  // Try a sequence arriving before the one RoutingTable is looking for.
+  // This should be a no-op.
+  SendRouteEntryWithSeqAndProto(RTNLMessage::kModeAdd,
+                                kTestDeviceIndex0,
+                                entry,
+                                kTestRequestSeq-1,
+                                RTPROT_UNSPEC);
+  EXPECT_FALSE(GetQuerySequences()->empty());
+
+  // Try a sequence arriving after the one RoutingTable is looking for.
+  // This should cause the request to be purged.
+  SendRouteEntryWithSeqAndProto(RTNLMessage::kModeAdd,
+                                kTestDeviceIndex0,
+                                entry,
+                                kTestRequestSeq+1,
+                                RTPROT_UNSPEC);
+  EXPECT_TRUE(GetQuerySequences()->empty());
 }
 
 }  // namespace shill
diff --git a/rtnl_handler.cc b/rtnl_handler.cc
index 66639f5..0c6cee8 100644
--- a/rtnl_handler.cc
+++ b/rtnl_handler.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -165,7 +165,7 @@
   }
 }
 
-void RTNLHandler::NextRequest(uint32_t seq) {
+void RTNLHandler::NextRequest(uint32 seq) {
   int flag = 0;
   RTNLMessage::Type type;
 
diff --git a/rtnl_handler.h b/rtnl_handler.h
index 6b88f0b..d9d815b 100644
--- a/rtnl_handler.h
+++ b/rtnl_handler.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -118,7 +118,7 @@
   // Dispatches an rtnl message to all listeners
   void DispatchEvent(int type, const RTNLMessage &msg);
   // Send the next table-dump request to the kernel
-  void NextRequest(uint32_t seq);
+  void NextRequest(uint32 seq);
   // Parse an incoming rtnl message from the kernel
   void ParseRTNL(InputData *data);
 
@@ -131,9 +131,9 @@
   bool in_request_;
 
   int rtnl_socket_;
-  uint32_t request_flags_;
-  uint32_t request_sequence_;
-  uint32_t last_dump_sequence_;
+  uint32 request_flags_;
+  uint32 request_sequence_;
+  uint32 last_dump_sequence_;
 
   std::vector<RTNLListener *> listeners_;
   scoped_ptr<Callback1<InputData *>::Type> rtnl_callback_;
diff --git a/rtnl_message.cc b/rtnl_message.cc
index bf9e8d0..6b4c6a0 100644
--- a/rtnl_message.cc
+++ b/rtnl_message.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -50,8 +50,7 @@
 bool RTNLMessage::Decode(const ByteString &msg) {
   bool ret = DecodeInternal(msg);
   if (!ret) {
-    mode_ = kModeUnknown;
-    type_ = kTypeUnknown;
+    Reset();
   }
   return ret;
 }
@@ -193,7 +192,7 @@
   return true;
 }
 
-ByteString RTNLMessage::Encode() {
+ByteString RTNLMessage::Encode() const {
   if (type_ != kTypeLink &&
       type_ != kTypeAddress &&
       type_ != kTypeRoute) {
@@ -204,7 +203,6 @@
   hdr.hdr.nlmsg_flags = flags_;
   hdr.hdr.nlmsg_seq = seq_;
   hdr.hdr.nlmsg_pid = pid_;
-  hdr.hdr.nlmsg_seq = 0;
 
   if (mode_ == kModeGet) {
     if (type_ == kTypeLink) {
@@ -213,6 +211,9 @@
       hdr.hdr.nlmsg_type = RTM_GETADDR;
     } else if (type_ == kTypeRoute) {
       hdr.hdr.nlmsg_type = RTM_GETROUTE;
+    } else {
+      NOTIMPLEMENTED();
+      return ByteString();
     }
     hdr.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr.gen));
     hdr.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
@@ -220,15 +221,21 @@
   } else {
     switch (type_) {
     case kTypeLink:
-      EncodeLink(&hdr);
+      if (!EncodeLink(&hdr)) {
+        return ByteString();
+      }
       break;
 
     case kTypeAddress:
-      EncodeAddress(&hdr);
+      if (!EncodeAddress(&hdr)) {
+        return ByteString();
+      }
       break;
 
     case kTypeRoute:
-      EncodeRoute(&hdr);
+      if (!EncodeRoute(&hdr)) {
+        return ByteString();
+      }
       break;
 
     default:
@@ -239,7 +246,7 @@
   size_t header_length = hdr.hdr.nlmsg_len;
   ByteString attributes;
 
-  base::hash_map<uint16, ByteString>::iterator attr;
+  base::hash_map<uint16, ByteString>::const_iterator attr;
   for (attr = attributes_.begin(); attr != attributes_.end(); ++attr) {
     size_t len = RTA_LENGTH(attr->second.GetLength());
     hdr.hdr.nlmsg_len = NLMSG_ALIGN(hdr.hdr.nlmsg_len) + RTA_ALIGN(len);
@@ -261,28 +268,69 @@
   return packet;
 }
 
-void RTNLMessage::EncodeLink(RTNLHeader *hdr) {
-  hdr->hdr.nlmsg_type = (mode_ == kModeAdd) ? RTM_NEWLINK : RTM_DELLINK;
+bool RTNLMessage::EncodeLink(RTNLHeader *hdr) const {
+  switch (mode_) {
+    case kModeAdd:
+      hdr->hdr.nlmsg_type = RTM_NEWLINK;
+      break;
+    case kModeDelete:
+      hdr->hdr.nlmsg_type = RTM_DELLINK;
+      break;
+    case kModeQuery:
+      hdr->hdr.nlmsg_type = RTM_GETLINK;
+      break;
+    default:
+      NOTIMPLEMENTED();
+      return false;
+  }
   hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifi));
   hdr->ifi.ifi_family = family_;
   hdr->ifi.ifi_index = interface_index_;
   hdr->ifi.ifi_type = link_status_.type;
   hdr->ifi.ifi_flags = link_status_.flags;
   hdr->ifi.ifi_change = link_status_.change;
+  return true;
 }
 
-void RTNLMessage::EncodeAddress(RTNLHeader *hdr) {
-  hdr->hdr.nlmsg_type = (mode_ == kModeAdd) ? RTM_NEWADDR : RTM_DELADDR;
+bool RTNLMessage::EncodeAddress(RTNLHeader *hdr) const {
+  switch (mode_) {
+    case kModeAdd:
+      hdr->hdr.nlmsg_type = RTM_NEWADDR;
+      break;
+    case kModeDelete:
+      hdr->hdr.nlmsg_type = RTM_DELADDR;
+      break;
+    case kModeQuery:
+      hdr->hdr.nlmsg_type = RTM_GETADDR;
+      break;
+    default:
+      NOTIMPLEMENTED();
+      return false;
+  }
   hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifa));
   hdr->ifa.ifa_family = family_;
   hdr->ifa.ifa_prefixlen = address_status_.prefix_len;
   hdr->ifa.ifa_flags = address_status_.flags;
   hdr->ifa.ifa_scope = address_status_.scope;
   hdr->ifa.ifa_index = interface_index_;
+  return true;
 }
 
-void RTNLMessage::EncodeRoute(RTNLHeader *hdr) {
-  hdr->hdr.nlmsg_type = (mode_ == kModeAdd) ? RTM_NEWROUTE : RTM_DELROUTE;
+bool RTNLMessage::EncodeRoute(RTNLHeader *hdr) const {
+  switch (mode_) {
+    case kModeAdd:
+      hdr->hdr.nlmsg_type = RTM_NEWROUTE;
+      break;
+    case kModeDelete:
+      hdr->hdr.nlmsg_type = RTM_DELROUTE;
+      break;
+    case kModeQuery:
+      hdr->hdr.nlmsg_type = RTM_GETROUTE;
+      break;
+    default:
+      NOTIMPLEMENTED();
+      return false;
+  }
   hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->rtm));
   hdr->rtm.rtm_family = family_;
   hdr->rtm.rtm_dst_len = route_status_.dst_prefix;
@@ -292,6 +340,20 @@
   hdr->rtm.rtm_scope = route_status_.scope;
   hdr->rtm.rtm_type = route_status_.type;
   hdr->rtm.rtm_flags = route_status_.flags;
+  return true;
+}
+
+void RTNLMessage::Reset() {
+  mode_ = kModeUnknown;
+  type_ = kTypeUnknown;
+  flags_ = 0;
+  seq_ = 0;
+  interface_index_ = 0;
+  family_ = IPAddress::kFamilyUnknown;
+  link_status_ = LinkStatus();
+  address_status_ = AddressStatus();
+  route_status_ = RouteStatus();
+  attributes_.clear();
 }
 
 }  // namespace shill
diff --git a/rtnl_message.h b/rtnl_message.h
index f626cd0..8b2e194 100644
--- a/rtnl_message.h
+++ b/rtnl_message.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -31,7 +31,8 @@
       kModeUnknown,
       kModeGet,
       kModeAdd,
-      kModeDelete
+      kModeDelete,
+      kModeQuery
     };
 
     struct LinkStatus {
@@ -112,7 +113,9 @@
     // Parse an RTNL message.  Returns true on success.
     bool Decode(const ByteString &data);
     // Encode an RTNL message.  Returns empty ByteString on failure.
-    ByteString Encode();
+    ByteString Encode() const;
+    // Reset all fields.
+    void Reset();
 
     // Getters and setters
     Type type() const { return type_; }
@@ -164,9 +167,9 @@
                      Mode mode,
                      rtattr **attr_data,
                      int *attr_length);
-    void EncodeLink(RTNLHeader *hdr);
-    void EncodeAddress(RTNLHeader *hdr);
-    void EncodeRoute(RTNLHeader *hdr);
+    bool EncodeLink(RTNLHeader *hdr) const;
+    bool EncodeAddress(RTNLHeader *hdr) const;
+    bool EncodeRoute(RTNLHeader *hdr) const;
 
     Type type_;
     Mode mode_;