shill: Connection: UnPin routes on destruction

Tag pinned routes with the interface index associated with the request,
so they can be removed when the connection is destroyed.  Also move
PinHostRoute() out of the VPN code and into the Connection.

BUG=chromium-os:29911
TEST=New unit tests

Change-Id: I46019255276469929642db4a6395e64f53e3b7d5
Reviewed-on: https://gerrit.chromium.org/gerrit/20982
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_unittest.cc b/connection_unittest.cc
index 3acc866..5c74f28 100644
--- a/connection_unittest.cc
+++ b/connection_unittest.cc
@@ -87,6 +87,9 @@
 
   virtual void TearDown() {
     EXPECT_CALL(*device_info_, FlushAddresses(kTestDeviceInterfaceIndex0));
+    EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex0));
+    EXPECT_CALL(routing_table_, FlushRoutesWithTag(kTestDeviceInterfaceIndex0));
+    connection_ = NULL;
   }
 
   void ReplaceSingletons(ConnectionRefPtr connection) {
@@ -99,6 +102,11 @@
     ipconfig_->UpdateProperties(properties_, true);
   }
 
+  bool PinHostRoute(ConnectionRefPtr connection,
+                    const IPConfig::Properties &properties) {
+    return connection->PinHostRoute(properties);
+  }
+
  protected:
   scoped_ptr<StrictMock<MockDeviceInfo> > device_info_;
   ConnectionRefPtr connection_;
@@ -301,12 +309,14 @@
 
   // The destructor will remove the routes and addresses.
   EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex0));
+  EXPECT_CALL(routing_table_, FlushRoutesWithTag(kTestDeviceInterfaceIndex0));
   EXPECT_CALL(*device_info_.get(),
               FlushAddresses(kTestDeviceInterfaceIndex0));
 }
 
 TEST_F(ConnectionTest, Destructor) {
   EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex1));
+  EXPECT_CALL(routing_table_, FlushRoutesWithTag(kTestDeviceInterfaceIndex1));
   EXPECT_CALL(*device_info_, FlushAddresses(kTestDeviceInterfaceIndex1));
   {
     ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex1,
@@ -329,12 +339,58 @@
   ASSERT_TRUE(address.SetAddressFromString(kIPAddress0));
   size_t prefix_len = address.GetLength() * 8;
   EXPECT_CALL(routing_table_, RequestRouteToHost(
-      IsIPAddress(address, prefix_len), -1))
+      IsIPAddress(address, prefix_len), -1, 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(routing_table_, FlushRoutesWithTag(kTestDeviceInterfaceIndex0));
+  EXPECT_CALL(*device_info_.get(),
+              FlushAddresses(kTestDeviceInterfaceIndex0));
+}
+
+TEST_F(ConnectionTest, PinHostRoute) {
+  static const char kGateway[] = "10.242.2.13";
+  static const char kNetwork[] = "10.242.2.1";
+
+  ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex0,
+                                             kTestDeviceName0,
+                                             Technology::kUnknown,
+                                             device_info_.get()));
+  ReplaceSingletons(connection);
+
+  IPConfig::Properties props;
+  props.address_family = IPAddress::kFamilyIPv4;
+  EXPECT_FALSE(PinHostRoute(connection, props));
+
+  props.gateway = kGateway;
+  EXPECT_FALSE(PinHostRoute(connection, props));
+
+  props.gateway.clear();
+  props.trusted_ip = "xxx";
+  EXPECT_FALSE(PinHostRoute(connection, props));
+
+  props.gateway = kGateway;
+  EXPECT_FALSE(PinHostRoute(connection, props));
+
+  props.trusted_ip = kNetwork;
+  IPAddress address(IPAddress::kFamilyIPv4);
+  ASSERT_TRUE(address.SetAddressFromString(kNetwork));
+  size_t prefix_len = address.GetLength() * 8;
+  EXPECT_CALL(routing_table_, RequestRouteToHost(
+      IsIPAddress(address, prefix_len), -1, kTestDeviceInterfaceIndex0))
+      .WillOnce(Return(false));
+  EXPECT_FALSE(PinHostRoute(connection, props));
+
+  EXPECT_CALL(routing_table_, RequestRouteToHost(
+      IsIPAddress(address, prefix_len), -1, kTestDeviceInterfaceIndex0))
+      .WillOnce(Return(true));
+  EXPECT_TRUE(PinHostRoute(connection, props));
+
+  // The destructor will remove the routes and addresses.
+  EXPECT_CALL(routing_table_, FlushRoutes(kTestDeviceInterfaceIndex0));
+  EXPECT_CALL(routing_table_, FlushRoutesWithTag(kTestDeviceInterfaceIndex0));
   EXPECT_CALL(*device_info_.get(),
               FlushAddresses(kTestDeviceInterfaceIndex0));
 }