shill: Enable and disable reverse-path filtering

When an http proxy request is active (as well as portal check, when
that gets written), we need to initiate connections over a non-default
network interface.  For this we temporarily disable reverse-path
filtering on the interface during the duration of the request.

BUG=chromium-os:24141
TEST=New unit tests for HTTPProxy and Connection

Change-Id: Ife2ad775fd8a3d2fb48f860e804eeb442dc5c58a
Reviewed-on: https://gerrit.chromium.org/gerrit/12889
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Commit-Ready: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/connection.cc b/connection.cc
index 460654d..a80271f 100644
--- a/connection.cc
+++ b/connection.cc
@@ -25,6 +25,7 @@
                        const std::string& interface_name,
                        const DeviceInfo *device_info)
     : is_default_(false),
+      routing_request_count_(0),
       interface_index_(interface_index),
       interface_name_(interface_name),
       device_info_(device_info),
@@ -37,6 +38,7 @@
 Connection::~Connection() {
   VLOG(2) << __func__;
 
+  DCHECK(!routing_request_count_);
   routing_table_->FlushRoutes(interface_index_);
   device_info_->FlushAddresses(interface_index_);
 }
@@ -90,6 +92,35 @@
   is_default_ = is_default;
 }
 
+void Connection::RequestRouting() {
+  if (routing_request_count_++ == 0) {
+    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
+    DCHECK(device.get());
+    if (!device.get()) {
+      LOG(ERROR) << "Device is NULL!";
+      return;
+    }
+    device->DisableReversePathFilter();
+  }
+}
+
+void Connection::ReleaseRouting() {
+  DCHECK(routing_request_count_ > 0);
+  if (--routing_request_count_ == 0) {
+    DeviceRefPtr device = device_info_->GetDevice(interface_index_);
+    DCHECK(device.get());
+    if (!device.get()) {
+      LOG(ERROR) << "Device is NULL!";
+      return;
+    }
+    device->EnableReversePathFilter();
+
+    // Clear any cached routes that might have accumulated while reverse-path
+    // filtering was disabled.
+    routing_table_->FlushCache();
+  }
+}
+
 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 f99447d..93a903a 100644
--- a/connection.h
+++ b/connection.h
@@ -38,8 +38,16 @@
   bool is_default() const { return is_default_; }
   virtual void SetIsDefault(bool is_default);
 
-  const std::string &interface_name() const { return interface_name_; }
-  const std::vector<std::string> &dns_servers() const { return dns_servers_; }
+  virtual const std::string &interface_name() const { return interface_name_; }
+  virtual const std::vector<std::string> &dns_servers() const {
+    return dns_servers_;
+  }
+
+  // Request to accept traffic routed to this connection even if it is not
+  // the default.  This request is ref-counted so the caller must call
+  // ReleaseRouting() when they no longer need this facility.
+  virtual void RequestRouting();
+  virtual void ReleaseRouting();
 
  private:
   friend class ConnectionTest;
@@ -54,6 +62,7 @@
   uint32 GetMetric(bool is_default);
 
   bool is_default_;
+  int routing_request_count_;
   int interface_index_;
   const std::string interface_name_;
   std::vector<std::string> dns_servers_;
diff --git a/connection_unittest.cc b/connection_unittest.cc
index e5b0cba..283b069 100644
--- a/connection_unittest.cc
+++ b/connection_unittest.cc
@@ -5,6 +5,7 @@
 #include <arpa/inet.h>
 #include <linux/rtnetlink.h>
 
+#include <string>
 #include <vector>
 
 #include <base/memory/scoped_ptr.h>
@@ -14,12 +15,14 @@
 #include "shill/connection.h"
 #include "shill/ipconfig.h"
 #include "shill/mock_control.h"
+#include "shill/mock_device.h"
 #include "shill/mock_device_info.h"
 #include "shill/mock_resolver.h"
 #include "shill/mock_routing_table.h"
 #include "shill/mock_rtnl_handler.h"
 #include "shill/routing_table_entry.h"
 
+using std::string;
 using std::vector;
 using testing::_;
 using testing::NiceMock;
@@ -57,10 +60,7 @@
         ipconfig_(new IPConfig(&control_, kTestDeviceName0)) {}
 
   virtual void SetUp() {
-    connection_->resolver_ = &resolver_;
-    connection_->routing_table_ = &routing_table_;
-    connection_->rtnl_handler_ = &rtnl_handler_;
-
+    ReplaceSingletons(connection_);
     IPConfig::Properties properties;
     properties.address = kIPAddress0;
     properties.gateway = kGatewayAddress0;
@@ -77,6 +77,12 @@
     EXPECT_CALL(*device_info_, FlushAddresses(kTestDeviceInterfaceIndex0));
   }
 
+  void ReplaceSingletons(ConnectionRefPtr connection) {
+    connection->resolver_ = &resolver_;
+    connection->routing_table_ = &routing_table_;
+    connection->rtnl_handler_ = &rtnl_handler_;
+  }
+
  protected:
   scoped_ptr<StrictMock<MockDeviceInfo> > device_info_;
   ConnectionRefPtr connection_;
@@ -91,6 +97,7 @@
   EXPECT_EQ(kTestDeviceInterfaceIndex0, connection_->interface_index_);
   EXPECT_EQ(kTestDeviceName0, connection_->interface_name_);
   EXPECT_FALSE(connection_->is_default());
+  EXPECT_FALSE(connection_->routing_request_count_);
 }
 
 TEST_F(ConnectionTest, AddConfig) {
@@ -123,7 +130,7 @@
 TEST_F(ConnectionTest, AddConfigReverse) {
   EXPECT_CALL(routing_table_, SetDefaultMetric(kTestDeviceInterfaceIndex0,
                                                Connection::kDefaultMetric));
-  vector<std::string> empty_list;
+  vector<string> empty_list;
   EXPECT_CALL(resolver_, SetDNSFromLists(empty_list, empty_list));
   connection_->SetIsDefault(true);
 
@@ -137,6 +144,40 @@
   connection_->UpdateFromIPConfig(ipconfig_);
 }
 
+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<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();
+
+    // 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));
+  }
+}
+
 TEST_F(ConnectionTest, Destructor) {
   EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex1));
   EXPECT_CALL(*device_info_, FlushAddresses(kTestDeviceInterfaceIndex1));
diff --git a/device.cc b/device.cc
index 5281909..68ab81b 100644
--- a/device.cc
+++ b/device.cc
@@ -52,6 +52,12 @@
 const char Device::kIPFlagUseTempAddr[] = "use_tempaddr";
 // static
 const char Device::kIPFlagUseTempAddrUsedAndDefault[] = "2";
+// static
+const char Device::kIPFlagReversePathFilter[] = "rp_filter";
+// static
+const char Device::kIPFlagReversePathFilterEnabled[] = "1";
+// static
+const char Device::kIPFlagReversePathFilterLooseMode[] = "2";
 
 // static
 const char Device::kStoragePowered[] = "Powered";
@@ -214,6 +220,18 @@
             kIPFlagUseTempAddrUsedAndDefault);
 }
 
+void Device::DisableReversePathFilter() {
+  // TODO(pstew): Current kernel doesn't offer reverse-path filtering flag
+  // for IPv6.  crosbug.com/24228
+  SetIPFlag(IPAddress::kFamilyIPv4, kIPFlagReversePathFilter,
+            kIPFlagReversePathFilterLooseMode);
+}
+
+void Device::EnableReversePathFilter() {
+  SetIPFlag(IPAddress::kFamilyIPv4, kIPFlagReversePathFilter,
+            kIPFlagReversePathFilterEnabled);
+}
+
 bool Device::IsConnected() const {
   if (selected_service_)
     return selected_service_->IsConnected();
@@ -328,10 +346,10 @@
 
 void Device::DestroyConnection() {
   VLOG(2) << __func__;
-  connection_ = NULL;
   if (selected_service_.get()) {
     selected_service_->SetConnection(NULL);
   }
+  connection_ = NULL;
 }
 
 void Device::SelectService(const ServiceRefPtr &service) {
diff --git a/device.h b/device.h
index c10d6d0..85bf29b 100644
--- a/device.h
+++ b/device.h
@@ -79,6 +79,14 @@
   virtual void EnableIPv6();
   virtual void EnableIPv6Privacy();
 
+  // Request the removal of reverse-path filtering for this interface.
+  // This will allow packets destined for this interface to be accepted,
+  // even if this is not the default route for such a packet to arrive.
+  virtual void DisableReversePathFilter();
+
+  // Request reverse-path filtering for this interface.
+  virtual void EnableReversePathFilter();
+
   // Returns true if the selected service on the device (if any) is connected.
   // Returns false if there is no selected service, or if the selected service
   // is not connected.
@@ -181,6 +189,9 @@
   static const char kIPFlagDisableIPv6[];
   static const char kIPFlagUseTempAddr[];
   static const char kIPFlagUseTempAddrUsedAndDefault[];
+  static const char kIPFlagReversePathFilter[];
+  static const char kIPFlagReversePathFilterEnabled[];
+  static const char kIPFlagReversePathFilterLooseMode[];
   static const char kStoragePowered[];
   static const char kStorageIPConfigs[];
 
diff --git a/device_info.h b/device_info.h
index 1632951..6303e52 100644
--- a/device_info.h
+++ b/device_info.h
@@ -53,7 +53,7 @@
   // messages, and registers it with the manager.
   void RegisterDevice(const DeviceRefPtr &device);
 
-  DeviceRefPtr GetDevice(int interface_index) const;
+  virtual DeviceRefPtr GetDevice(int interface_index) const;
   virtual bool GetMACAddress(int interface_index, ByteString *address) const;
   virtual bool GetFlags(int interface_index, unsigned int *flags) const;
   virtual bool GetAddresses(int interface_index,
diff --git a/http_proxy.cc b/http_proxy.cc
index 60bc281..1ba1d21 100644
--- a/http_proxy.cc
+++ b/http_proxy.cc
@@ -20,6 +20,7 @@
 #include <base/stringprintf.h>
 
 #include "shill/async_connection.h"
+#include "shill/connection.h"
 #include "shill/dns_client.h"
 #include "shill/event_dispatcher.h"
 #include "shill/ip_address.h"
@@ -46,12 +47,9 @@
 const char HTTPProxy::kHTTPVersionPrefix[] = " HTTP/1";
 const char HTTPProxy::kInternalErrorMsg[] = "Proxy Failed: Internal Error";
 
-
-HTTPProxy::HTTPProxy(const std::string &interface_name,
-                     const std::vector<std::string> &dns_servers)
+HTTPProxy::HTTPProxy(ConnectionRefPtr connection)
     : state_(kStateIdle),
-      interface_name_(interface_name),
-      dns_servers_(dns_servers),
+      connection_(connection),
       accept_callback_(NewCallback(this, &HTTPProxy::AcceptClient)),
       connect_completion_callback_(
           NewCallback(this, &HTTPProxy::OnConnectCompletion)),
@@ -70,6 +68,7 @@
       client_socket_(-1),
       server_port_(kDefaultServerPort),
       server_socket_(-1),
+      is_route_requested_(false),
       idle_timeout_(NULL) { }
 
 HTTPProxy::~HTTPProxy() {
@@ -115,14 +114,14 @@
                                      accept_callback_.get()));
   dispatcher_ = dispatcher;
   dns_client_.reset(new DNSClient(IPAddress::kFamilyIPv4,
-                                  interface_name_,
-                                  dns_servers_,
+                                  connection_->interface_name(),
+                                  connection_->dns_servers(),
                                   kDNSTimeoutSeconds * 1000,
                                   dispatcher,
                                   dns_client_callback_.get()));
   proxy_port_ = ntohs(addr.sin_port);
   server_async_connection_.reset(
-      new AsyncConnection(interface_name_, dispatcher, sockets,
+      new AsyncConnection(connection_->interface_name(), dispatcher, sockets,
                           connect_completion_callback_.get()));
   sockets_ = sockets;
   state_ = kStateWaitConnection;
@@ -280,6 +279,9 @@
     server_hostname_ = host;
   }
 
+  connection_->RequestRouting();
+  is_route_requested_ = true;
+
   IPAddress addr(IPAddress::kFamilyIPv4);
   if (addr.SetAddressFromString(server_hostname_)) {
     if (!ConnectServer(addr, server_port_)) {
@@ -574,6 +576,10 @@
 void HTTPProxy::StopClient() {
   VLOG(3) << "In " << __func__;
 
+  if (is_route_requested_) {
+    connection_->ReleaseRouting();
+    is_route_requested_ = false;
+  }
   write_client_handler_.reset();
   read_client_handler_.reset();
   if (client_socket_ != -1) {
diff --git a/http_proxy.h b/http_proxy.h
index 6bc0601..3b9c334 100644
--- a/http_proxy.h
+++ b/http_proxy.h
@@ -50,8 +50,7 @@
     kStateFlushResponse,
   };
 
-  HTTPProxy(const std::string &interface_name,
-            const std::vector<std::string> &dns_servers);
+  explicit HTTPProxy(ConnectionRefPtr connection);
   virtual ~HTTPProxy();
 
   // Start HTTP proxy.
@@ -111,8 +110,7 @@
 
   // State held for the lifetime of the proxy.
   State state_;
-  const std::string interface_name_;
-  std::vector<std::string> dns_servers_;
+  ConnectionRefPtr connection_;
   scoped_ptr<Callback1<int>::Type> accept_callback_;
   scoped_ptr<Callback2<bool, int>::Type> connect_completion_callback_;
   scoped_ptr<Callback1<bool>::Type> dns_client_callback_;
@@ -136,6 +134,7 @@
   std::string client_version_;
   int server_port_;
   int server_socket_;
+  bool is_route_requested_;
   CancelableTask *idle_timeout_;
   std::vector<std::string> client_headers_;
   std::string server_hostname_;
diff --git a/http_proxy_unittest.cc b/http_proxy_unittest.cc
index 07e9cb7..d7f4ddb 100644
--- a/http_proxy_unittest.cc
+++ b/http_proxy_unittest.cc
@@ -14,6 +14,9 @@
 
 #include "shill/ip_address.h"
 #include "shill/mock_async_connection.h"
+#include "shill/mock_connection.h"
+#include "shill/mock_control.h"
+#include "shill/mock_device_info.h"
 #include "shill/mock_dns_client.h"
 #include "shill/mock_event_dispatcher.h"
 #include "shill/mock_sockets.h"
@@ -25,6 +28,7 @@
 using ::testing::AtLeast;
 using ::testing::DoAll;
 using ::testing::Invoke;
+using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::ReturnArg;
 using ::testing::ReturnNew;
@@ -82,11 +86,23 @@
 class HTTPProxyTest : public Test {
  public:
   HTTPProxyTest()
-      : server_async_connection_(NULL),
+      : interface_name_(kInterfaceName),
+        server_async_connection_(NULL),
         dns_servers_(kDNSServers, kDNSServers + 2),
         dns_client_(NULL),
-        proxy_(kInterfaceName, dns_servers_) { }
+        device_info_(new NiceMock<MockDeviceInfo>(
+            &control_,
+            reinterpret_cast<EventDispatcher*>(NULL),
+            reinterpret_cast<Manager*>(NULL))),
+        connection_(new StrictMock<MockConnection>(device_info_.get())),
+        proxy_(connection_) { }
  protected:
+  virtual void SetUp() {
+    EXPECT_CALL(*connection_.get(), interface_name())
+        .WillRepeatedly(ReturnRef(interface_name_));
+    EXPECT_CALL(*connection_.get(), dns_servers())
+        .WillRepeatedly(ReturnRef(dns_servers_));
+  }
   virtual void TearDown() {
     if (proxy_.sockets_) {
       ExpectStop();
@@ -152,9 +168,11 @@
     EXPECT_FALSE(proxy_.write_client_handler_.get());
     EXPECT_FALSE(proxy_.read_server_handler_.get());
     EXPECT_FALSE(proxy_.write_server_handler_.get());
+    EXPECT_FALSE(proxy_.is_route_requested_);
   }
   void ExpectReset() {
     EXPECT_FALSE(proxy_.accept_handler_.get());
+    EXPECT_EQ(proxy_.connection_.get(), connection_.get());
     EXPECT_FALSE(proxy_.dispatcher_);
     EXPECT_FALSE(proxy_.dns_client_.get());
     EXPECT_EQ(-1, proxy_.proxy_port_);
@@ -189,6 +207,9 @@
        EXPECT_CALL(*server_async_connection_, Stop())
            .Times(AtLeast(1));
      }
+     if (proxy_.is_route_requested_) {
+       EXPECT_CALL(*connection_.get(), ReleaseRouting());
+     }
    }
    void ExpectClientInput(int fd) {
     EXPECT_CALL(sockets(), Accept(kProxyFD, _, _))
@@ -291,7 +312,6 @@
         .WillOnce(ReturnNew<IOHandler>());
     ExpectRepeatedInputTimeout();
   }
-
   void ExpectTunnelClose() {
     EXPECT_CALL(sockets(), Close(kClientFD))
         .WillOnce(Return(0));
@@ -299,6 +319,12 @@
         .WillOnce(Return(0));
     ExpectStop();
   }
+  void ExpectRouteRequest() {
+    EXPECT_CALL(*connection_.get(), RequestRouting());
+  }
+  void ExpectRouteRelease() {
+    EXPECT_CALL(*connection_.get(), ReleaseRouting());
+  }
 
   // Callers for various private routines in the proxy
   bool StartProxy() {
@@ -369,6 +395,7 @@
   void SetupConnectWithRequest(const string &url, const string &http_version,
                                const string &extra_lines) {
     ExpectDNSRequest("www.chromium.org", true);
+    ExpectRouteRequest();
     ReadFromClient(CreateRequest(url, http_version, extra_lines));
     IPAddress addr(IPAddress::kFamilyIPv4);
     EXPECT_TRUE(addr.SetAddressFromString(kServerAddress));
@@ -393,12 +420,16 @@
   }
 
  private:
+  const string interface_name_;
   // Owned by the HTTPProxy, but tracked here for EXPECT().
   StrictMock<MockAsyncConnection> *server_async_connection_;
   vector<string> dns_servers_;
   // Owned by the HTTPProxy, but tracked here for EXPECT().
   StrictMock<MockDNSClient> *dns_client_;
   MockEventDispatcher dispatcher_;
+  MockControl control_;
+  scoped_ptr<MockDeviceInfo> device_info_;
+  scoped_refptr<MockConnection> connection_;
   HTTPProxy proxy_;
   StrictMock<MockSockets> sockets_;
 };
@@ -553,6 +584,7 @@
 
 TEST_F(HTTPProxyTest, DNSRequestFailure) {
   SetupClient();
+  ExpectRouteRequest();
   ExpectDNSRequest("www.chromium.org", false);
   ExpectClientResult();
   ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:40506"));
@@ -561,6 +593,7 @@
 
 TEST_F(HTTPProxyTest, DNSRequestDelayedFailure) {
   SetupClient();
+  ExpectRouteRequest();
   ExpectDNSRequest("www.chromium.org", true);
   ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:40506"));
   ExpectClientResult();
@@ -573,6 +606,7 @@
 
 TEST_F(HTTPProxyTest, TrailingClientData) {
   SetupClient();
+  ExpectRouteRequest();
   ExpectDNSRequest("www.chromium.org", true);
   const string trailing_data("Trailing client data");
   ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:40506") +
@@ -584,6 +618,7 @@
 
 TEST_F(HTTPProxyTest, LineContinuation) {
   SetupClient();
+  ExpectRouteRequest();
   ExpectDNSRequest("www.chromium.org", true);
   string text_to_keep("X-Long-Header: this is one line\r\n"
                       "\tand this is another");
@@ -598,6 +633,7 @@
 //      continuation.
 TEST_F(HTTPProxyTest, LineContinuationRemoval) {
   SetupClient();
+  ExpectRouteRequest();
   ExpectDNSRequest("www.chromium.org", true);
   string text_to_remove("remove this text please");
   ReadFromClient(CreateRequest("http://www.chromium.org/", "1.1",
@@ -634,6 +670,7 @@
   SetupClient();
   ExpectSyncConnect(kServerAddress, 999);
   ExpectRepeatedServerOutput();
+  ExpectRouteRequest();
   ReadFromClient(CreateRequest("/", "1.1",
                                StringPrintf("Host: %s:999", kServerAddress)));
   EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState());
@@ -730,6 +767,7 @@
       .WillOnce(Return(0));
   EXPECT_CALL(sockets(), Close(kServerFD))
       .WillOnce(Return(0));
+  ExpectRouteRelease();
   StopClient();
   ExpectClientReset();
   EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState());
diff --git a/mock_connection.h b/mock_connection.h
index 06f067f..2d8af12 100644
--- a/mock_connection.h
+++ b/mock_connection.h
@@ -19,6 +19,10 @@
 
   MOCK_METHOD1(UpdateFromIPConfig, void(const IPConfigRefPtr &config));
   MOCK_METHOD1(SetIsDefault, void(bool is_default));
+  MOCK_METHOD0(RequestRouting, void());
+  MOCK_METHOD0(ReleaseRouting, void());
+  MOCK_CONST_METHOD0(interface_name, const std::string &());
+  MOCK_CONST_METHOD0(dns_servers, const std::vector<std::string> &());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockConnection);
diff --git a/mock_device.h b/mock_device.h
index 462bc21..de0c7b7 100644
--- a/mock_device.h
+++ b/mock_device.h
@@ -34,6 +34,8 @@
   MOCK_METHOD0(DisableIPv6, void());
   MOCK_METHOD0(EnableIPv6, void());
   MOCK_METHOD0(EnableIPv6Privacy, void());
+  MOCK_METHOD0(DisableReversePathFilter, void());
+  MOCK_METHOD0(EnableReversePathFilter, void());
   MOCK_CONST_METHOD0(technology, Technology::Identifier());
 
  private:
diff --git a/mock_device_info.h b/mock_device_info.h
index 5808f50..b630505 100644
--- a/mock_device_info.h
+++ b/mock_device_info.h
@@ -26,6 +26,7 @@
                  Manager *manager);
   virtual ~MockDeviceInfo();
 
+  MOCK_CONST_METHOD1(GetDevice, DeviceRefPtr(int interface_index));
   MOCK_CONST_METHOD2(GetMACAddress, bool(int interface_index,
                                          ByteString* address));
   MOCK_CONST_METHOD2(GetFlags, bool(int interface_index,
diff --git a/mock_routing_table.h b/mock_routing_table.h
index 1afd37e..9eaa870 100644
--- a/mock_routing_table.h
+++ b/mock_routing_table.h
@@ -28,6 +28,7 @@
                                      const IPConfigRefPtr &ipconfig,
                                      uint32 metric));
   MOCK_METHOD1(FlushRoutes, void(int interface_index));
+  MOCK_METHOD0(FlushCache, bool());
   MOCK_METHOD1(ResetTable, void(int interface_index));
   MOCK_METHOD2(SetDefaultMetric, void(int interface_index, uint32 metric));
 
diff --git a/modem_unittest.cc b/modem_unittest.cc
index ed19523..00646ca 100644
--- a/modem_unittest.cc
+++ b/modem_unittest.cc
@@ -164,6 +164,8 @@
   EXPECT_CALL(info_, GetMACAddress(kTestInterfaceIndex, _))
       .WillOnce(DoAll(SetArgumentPointee<1>(expected_address), Return(true)))
       .WillOnce(DoAll(SetArgumentPointee<1>(expected_address), Return(true)));
+  EXPECT_CALL(info_, GetDevice(kTestInterfaceIndex))
+      .WillRepeatedly(Return(modem_.device_));
   EXPECT_CALL(manager_, device_info()).WillRepeatedly(Return(&info_));
 
   modem_.CreateCellularDevice(props);
diff --git a/routing_table.h b/routing_table.h
index 08a0fa9..cf31f1c 100644
--- a/routing_table.h
+++ b/routing_table.h
@@ -36,10 +36,10 @@
   virtual void Start();
   virtual void Stop();
 
-  // Add an entry to the routing table
+  // Add an entry to the routing table.
   virtual bool AddRoute(int interface_index, const RoutingTableEntry &entry);
 
-  // Get the default route associated with an interface of a given addr family
+  // Get the default route associated with an interface of a given addr family.
   virtual bool GetDefaultRoute(int interface_index,
                                IPAddress::Family family,
                                RoutingTableEntry *entry);
@@ -49,13 +49,16 @@
                                const IPConfigRefPtr &ipconfig,
                                uint32 metric);
 
-  // Remove all routes associated with interface
+  // Remove all routes associated with interface.
   virtual void FlushRoutes(int interface_index);
 
-  // Reset local state for this interface
+  // Flush the routing cache for all interfaces.
+  virtual bool FlushCache();
+
+  // Reset local state for this interface.
   virtual void ResetTable(int interface_index);
 
-  // Set the metric (priority) on existing default routes for an interface
+  // Set the metric (priority) on existing default routes for an interface.
   virtual void SetDefaultMetric(int interface_index, uint32 metric);
 
  protected:
@@ -73,13 +76,12 @@
   void ReplaceMetric(uint32 interface_index,
                      const RoutingTableEntry &entry,
                      uint32 metric);
-  bool FlushCache();
 
   static const char kRouteFlushPath4[];
   static const char kRouteFlushPath6[];
 
   base::hash_map<int, std::vector<RoutingTableEntry> > tables_; // NOLINT
-  // NOLINT above: hash_map from base, no need to #include <hash_map>
+  // NOLINT above: hash_map from base, no need to #include <hash_map>.
 
   scoped_ptr<Callback1<const RTNLMessage &>::Type> route_callback_;
   scoped_ptr<RTNLListener> route_listener_;
diff --git a/service.cc b/service.cc
index a0f3347..65a5828 100644
--- a/service.cc
+++ b/service.cc
@@ -320,8 +320,7 @@
 
 void Service::SetConnection(ConnectionRefPtr connection) {
   if (connection.get()) {
-    http_proxy_.reset(new HTTPProxy(connection->interface_name(),
-                                    connection->dns_servers()));
+    http_proxy_.reset(new HTTPProxy(connection));
     http_proxy_->Start(dispatcher_, &sockets_);
   } else {
     http_proxy_.reset();