shill: Make EthernetService behave more like a Service

Add Connect() and Disconnect() calls to EthernetService so that they
behave more like a Service and less like strange extensions of the
Ethernet object.  This has the nice side effect that the Manager can
autoconnect them, which adds a certain amount of fault tolerance to the
L3 configuration.  This in turn fixes a bug where DHCP negotiation
timing out over ethernet is incorrectly reported in shill.

BUG=chromium-os:35603
TEST=Unit tests pass, I can hand connect/disconnect to ethernet, DHCP
autotests for basic negotiation success and renewal timeouts pass.
Added another simple unit test.

Change-Id: I35aa2f5d2d8155bf0d24a80c7da9ddf41348ab03
Reviewed-on: https://gerrit.chromium.org/gerrit/36625
Tested-by: Christopher Wiley <wiley@chromium.org>
Commit-Ready: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
diff --git a/ethernet.cc b/ethernet.cc
index 7f4a445..fc4b8fe 100644
--- a/ethernet.cc
+++ b/ethernet.cc
@@ -41,7 +41,6 @@
              address,
              interface_index,
              Technology::kEthernet),
-      service_registered_(false),
       link_up_(false) {
   SLOG(Ethernet, 2) << "Ethernet device " << link_name << " initialized.";
 }
@@ -77,18 +76,11 @@
 void Ethernet::LinkEvent(unsigned int flags, unsigned int change) {
   Device::LinkEvent(flags, change);
   if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
-    LOG(INFO) << link_name() << " is up; should start L3!";
     link_up_ = true;
     if (service_) {
+      LOG(INFO) << "Registering " << link_name() << " with manager.";
+      // Manager will bring up L3 for us.
       manager()->RegisterService(service_);
-      if (service_->auto_connect()) {
-        if (AcquireIPConfigWithLeaseName(service_->GetStorageIdentifier())) {
-          SelectService(service_);
-          SetServiceState(Service::kStateConfiguring);
-        } else {
-          LOG(ERROR) << "Unable to acquire DHCP config.";
-        }
-      }
     }
   } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
     link_up_ = false;
@@ -98,4 +90,22 @@
   }
 }
 
+void Ethernet::ConnectTo(EthernetService *service) {
+  CHECK(service == service_.get()) << "Ethernet was asked to connect the "
+                                   << "wrong service?";
+  if (AcquireIPConfigWithLeaseName(service->GetStorageIdentifier())) {
+    SelectService(service);
+    SetServiceState(Service::kStateConfiguring);
+  } else {
+    LOG(ERROR) << "Unable to acquire DHCP config.";
+    SetServiceState(Service::kStateFailure);
+  }
+}
+
+void Ethernet::DisconnectFrom(EthernetService *service) {
+  CHECK(service == service_.get()) << "Ethernet was asked to disconnect the "
+                                   << "wrong service?";
+  DropConnection();
+}
+
 }  // namespace shill