shill: Connection: Create a link route if we are bound

Due to the behavior of some VPN servers, we may lose our route
to the default gateway of a physical interface due to the routes
assigned by a virtual interfaces.  To avert this, set a host route
for the default gateway as we bind a lower connection.

BUG=chromium-os:32643
TEST=New unit tests; tried on a known-misbehaving net (actually
working from there right now); VPN autotests

Change-Id: I68db1da339f61c415f9ba8c2c26d8cdb0276b209
Reviewed-on: https://gerrit.chromium.org/gerrit/27705
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_unittest.cc b/routing_table_unittest.cc
index 04add74..746bec2 100644
--- a/routing_table_unittest.cc
+++ b/routing_table_unittest.cc
@@ -175,10 +175,13 @@
       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->HasAttribute(RTA_SRC) && entry.src.IsDefault()) ||
+       (arg->HasAttribute(RTA_SRC) && IPAddress(arg->family(),
+                 arg->GetAttribute(RTA_SRC),
+                 status.src_prefix).Equals(entry.src))) &&
+      ((!arg->HasAttribute(RTA_GATEWAY) && entry.gateway.IsDefault()) ||
+       (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) &&
@@ -770,4 +773,36 @@
                                 RTPROT_UNSPEC);
 }
 
+TEST_F(RoutingTableTest, CreateLinkRoute) {
+  IPAddress local_address(IPAddress::kFamilyIPv4);
+  ASSERT_TRUE(local_address.SetAddressFromString(kTestNetAddress0));
+  local_address.set_prefix(kTestRemotePrefix4);
+  IPAddress remote_address(IPAddress::kFamilyIPv4);
+  ASSERT_TRUE(remote_address.SetAddressFromString(kTestNetAddress1));
+  IPAddress default_address(IPAddress::kFamilyIPv4);
+  IPAddress remote_address_with_prefix(remote_address);
+  remote_address_with_prefix.set_prefix(
+      IPAddress::GetMaxPrefixLength(remote_address_with_prefix.family()));
+  RoutingTableEntry entry(remote_address_with_prefix,
+                          local_address,
+                          default_address,
+                          0,
+                          RT_SCOPE_LINK,
+                          false);
+  EXPECT_CALL(rtnl_handler_,
+              SendMessage(IsRoutingPacket(RTNLMessage::kModeAdd,
+                                          kTestDeviceIndex0,
+                                          entry,
+                                          NLM_F_CREATE | NLM_F_EXCL)))
+      .Times(1);
+  EXPECT_TRUE(routing_table_->CreateLinkRoute(kTestDeviceIndex0,
+                                              local_address,
+                                              remote_address));
+
+  ASSERT_TRUE(remote_address.SetAddressFromString(kTestRemoteAddress4));
+  EXPECT_FALSE(routing_table_->CreateLinkRoute(kTestDeviceIndex0,
+                                               local_address,
+                                               remote_address));
+}
+
 }  // namespace shill