shill: Allow service override of DHCP-derived IP parameters

Create new class StaticIPParameters which encapsulates all of the
property handling for RPC and storage, as well as applying parameters
to an IPConfig::Properties element.  When a DHCP request succeeds
these parameters are used to selectively override values gained from
DHCP with those specified on the service.

BUG=chromium-os:23930
TEST=New unit tests -- manual testing is pending

Change-Id: I3b784f897ec6ffe0c78f2efe615d07d8f8924add
Reviewed-on: https://gerrit.chromium.org/gerrit/21448
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/device.cc b/device.cc
index a5c6ac8..e89c118 100644
--- a/device.cc
+++ b/device.cc
@@ -307,6 +307,8 @@
   ipconfig_ = dhcp_provider_->CreateConfig(link_name_, manager_->GetHostName());
   ipconfig_->RegisterUpdateCallback(Bind(&Device::OnIPConfigUpdated,
                                          weak_ptr_factory_.GetWeakPtr()));
+  dispatcher_->PostTask(Bind(&Device::ConfigureStaticIPTask,
+                             weak_ptr_factory_.GetWeakPtr()));
   return ipconfig_->RequestIP();
 }
 
@@ -337,14 +339,39 @@
           new CustomAccessor<Device, RpcIdentifiers>(this, get, NULL)));
 }
 
+void Device::ConfigureStaticIPTask() {
+  SLOG(Device, 2) << __func__ << " selected_service " << selected_service_.get()
+                  << " ipconfig " << ipconfig_.get();
+
+  if (!selected_service_ || !ipconfig_) {
+    return;
+  }
+
+  const StaticIPParameters &static_ip_parameters =
+      selected_service_->static_ip_parameters();
+  if (static_ip_parameters.ContainsAddress()) {
+    SLOG(Device, 2) << __func__ << " " << " configuring static IP parameters.";
+    // If the parameters contain an IP address, apply them now and bring
+    // the interface up.  When DHCP information arrives, it will supplement
+    // the static information.
+    OnIPConfigUpdated(ipconfig_, true);
+  } else {
+    SLOG(Device, 2) << __func__ << " " << " no static IP address.";
+  }
+}
+
 void Device::OnIPConfigUpdated(const IPConfigRefPtr &ipconfig, bool success) {
   SLOG(Device, 2) << __func__ << " " << " success: " << success;
   if (success) {
     CreateConnection();
+    if (selected_service_) {
+      ipconfig->ApplyStaticIPParameters(
+          selected_service_->static_ip_parameters());
+    }
     connection_->UpdateFromIPConfig(ipconfig);
     // SetConnection must occur after the UpdateFromIPConfig so the
     // service can use the values derived from the connection.
-    if (selected_service_.get()) {
+    if (selected_service_) {
       selected_service_->SetConnection(connection_);
     }
     // The service state change needs to happen last, so that at the