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();