shill: DHCPConfig: Enable and accept "GATEWAY-ARP" messages

Speed up the authentication process by enabling dhcpcd to probe
for the default gateway.  This provides a quick means for
verifying that we are on the same network as before.  When this
succeeds, re-configure using our saved lease parameters, but
continue our timeout so that a failed DHCP renew will cause the
network to enter the failed state.

A side effect of this change is that we don't ask dhcpcd to
release our lease on disconnect.  This is not as big a change
as one might think -- in many cases were are already disconnected
from the network by the time this code hits, which means we were
just removing our own memory of the lease instead of actually
freeing the remote resource.

BUG=chromium-os:25717
TEST=Unit tests; manual: switch between two known networks
Change-Id: Ib219033e688aa8c31eb86b45e9dd27a607bb93c9
Reviewed-on: https://gerrit.chromium.org/gerrit/23906
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/dhcp_config.cc b/dhcp_config.cc
index ad7a4a8..7e879e4 100644
--- a/dhcp_config.cc
+++ b/dhcp_config.cc
@@ -47,6 +47,7 @@
 const int DHCPConfig::kMinMTU = 576;
 const char DHCPConfig::kReasonBound[] = "BOUND";
 const char DHCPConfig::kReasonFail[] = "FAIL";
+const char DHCPConfig::kReasonGatewayArp[] = "GATEWAY-ARP";
 const char DHCPConfig::kReasonRebind[] = "REBIND";
 const char DHCPConfig::kReasonReboot[] = "REBOOT";
 const char DHCPConfig::kReasonRenew[] = "RENEW";
@@ -119,7 +120,9 @@
   if (!pid_) {
     return true;
   }
-  if (proxy_.get()) {
+  // If we are using gateway unicast ARP to speed up re-connect, don't
+  // give up our leases when we disconnect.
+  if (!arp_gateway_ && proxy_.get()) {
     proxy_->Release(device_name());
   }
   Stop();
@@ -144,13 +147,23 @@
   if (reason != kReasonBound &&
       reason != kReasonRebind &&
       reason != kReasonReboot &&
-      reason != kReasonRenew) {
+      reason != kReasonRenew &&
+      reason != kReasonGatewayArp) {
     LOG(WARNING) << "Event ignored.";
     return;
   }
   IPConfig::Properties properties;
   CHECK(ParseConfiguration(configuration, &properties));
-  UpdateProperties(properties, true);
+  if (reason == kReasonGatewayArp) {
+    // This is a non-authoritative confirmation that we or on the same
+    // network as the one we received a lease on previously.  The DHCP
+    // client is still running, so we should not cancel the timeout
+    // until that completes.  In the meantime, however, we can tentatively
+    // configure our network in anticipation of successful completion.
+    IPConfig::UpdateProperties(properties, true);
+  } else {
+    UpdateProperties(properties, true);
+  }
 }
 
 void DHCPConfig::UpdateProperties(const Properties &properties, bool success) {
@@ -171,6 +184,7 @@
   }
   if (arp_gateway_) {
     args.push_back(const_cast<char *>("-R"));  // ARP for default gateway.
+    args.push_back(const_cast<char *>("-U"));  // Enable unicast ARP on renew.
   }
   string interface_arg(device_name());
   if (lease_file_suffix_ != device_name()) {