DHCPV4: support persistent lease

This allows the DHCP client to save lease to persistent storage
when it enters bound state, and read lease when it starts.

Bug: 25642025
TEST=compile, unittest, and run it.

Change-Id: I5d30893f23fa8b7472f552e4b107d6d5c421e934
diff --git a/dhcpv4.cc b/dhcpv4.cc
index dce3191..6f4a21b 100644
--- a/dhcpv4.cc
+++ b/dhcpv4.cc
@@ -33,6 +33,7 @@
 
 #include "dhcp_client/dhcp_message.h"
 #include "dhcp_client/dhcp_options.h"
+#include "dhcp_client/file_io.h"
 
 using base::Bind;
 using base::Unretained;
@@ -45,9 +46,11 @@
 // UDP port numbers for DHCP.
 const uint16_t kDHCPServerPort = 67;
 const uint16_t kDHCPClientPort = 68;
-/* Renewal time in terms of lease duration percentage. */
+// Max length of a DHCP message.
+const size_t kDHCPMessageMaxLength = 548;
+// Renewal time in terms of lease duration percentage.
 const uint32_t kRenewalTimePercentage = 50;
-/* Rebinding time in terms of lease duration percentage. */
+// Rebinding time in terms of lease duration percentage.
 const uint32_t kRebindTimePercentage = 87;
 const int kInvalidSocketDescriptor = -1;
 // RFC 791: the minimum value for a correct header is 20 octets.
@@ -81,6 +84,10 @@
 
 const int dhcp_bpf_filter_len =
     sizeof(dhcp_bpf_filter) / sizeof(dhcp_bpf_filter[0]);
+
+// TODO(nywang): find a place for the lease file.
+const char kIPV4LeaseFilePathFormat[] =
+      "/tmp/lease-ipv4-%s.conf";
 }  // namespace
 
 DHCPV4::DHCPV4(const std::string& interface_name,
@@ -167,6 +174,20 @@
   }
 }
 
+bool DHCPV4::WriteLease() {
+    std::string lease_file =
+        base::StringPrintf(kIPV4LeaseFilePathFormat, network_id_.c_str());
+    LOG(INFO) << "Write lease to file";
+    shill::ByteString lease =
+        ByteString(reinterpret_cast<unsigned char*>(&offered_ip_address_),
+                   sizeof(uint32_t));
+    if (!FileIO::GetInstance()->Write(lease_file, lease)) {
+      LOG(ERROR) << "Failed to write lease to file";
+      return false;
+    }
+    return true;
+}
+
 void DHCPV4::OnReadError(const std::string& error_msg) {
   LOG(INFO) << __func__;
 }
@@ -179,13 +200,41 @@
       socket_,
       Bind(&DHCPV4::ParseRawPacket, Unretained(this)),
       Bind(&DHCPV4::OnReadError, Unretained(this))));
-  // TODO(nywang): use existing configuration from persistent storage
+  if (!network_id_.empty() && ReadLease()) {
+    state_ = State::INIT_REBOOT;
+    LOG(INFO) << "Start from INIT_REBOOT state";
+    if (!SendRequest()) {
+      ResetStateVariables();
+      return false;
+    }
+    state_ = State::REBOOT;
+    return true;
+  }
   if (!SendDiscover()) {
     return false;
   }
   return true;
 }
 
+bool DHCPV4::ReadLease() {
+  std::string lease_file =
+      base::StringPrintf(kIPV4LeaseFilePathFormat, network_id_.c_str());
+  if (!FileIO::GetInstance()->PathExists(lease_file)) {
+    LOG(INFO) << "Lease file does not exist";
+    return false;
+  }
+  ByteString config;
+  if (!FileIO::GetInstance()->Read(lease_file, &config)) {
+    LOG(ERROR) << "Failed to read lease file";
+  }
+  transaction_id_ = static_cast<uint32_t>(
+      std::uniform_int_distribution<unsigned int>()
+      (random_engine_) % UINT32_MAX + 1);
+  offered_ip_address_ =
+      *reinterpret_cast<const uint32_t*>(config.GetConstData());
+  return true;
+}
+
 void DHCPV4::Stop() {
   input_handler_.reset();
   if (!SendRelease()) {
@@ -296,6 +345,9 @@
     LOG(ERROR) << "Option server identifier is missing";
     return;
   }
+  if (state_ == State::REBOOT) {
+    server_identifier_ = msg.server_identifier();
+  }
   if (msg.server_identifier() != server_identifier_) {
     LOG(ERROR) << "Option server doesn't match";
     return;
@@ -340,6 +392,10 @@
   state_ = State::BOUND;
   client_ip_ = offered_ip_address_;
   server_ip_ = server_identifier_;
+  // Write lease to persistent stotrage.
+  if (!network_id_.empty()) {
+    WriteLease();
+  }
   // TODO(nywang): Notify shill to configure the ip, gateway and DNS server.
   // TODO(nywang): Setup a udp socket for future unicast, so that kernel can
   // fill the ethernet header with gateway mac address for us.
diff --git a/dhcpv4.h b/dhcpv4.h
index 78903d7..df76070 100644
--- a/dhcpv4.h
+++ b/dhcpv4.h
@@ -55,6 +55,8 @@
   bool MakeRawPacket(const DHCPMessage& message, shill::ByteString* buffer);
   void OnReadError(const std::string& error_msg);
   void ParseRawPacket(shill::InputData* data);
+  bool ReadLease();
+  bool WriteLease();
   bool SendDiscover();
   bool SendRequest();
   bool SendRelease();