shill: add function to check time till DHCP lease renewal
Add public function IPConfig::TimeToLeaseExpiry to check the time
left before the current DHCP lease is due to be renewed.
BUG=chromium:426657
TEST='P2_TEST_FILTER="shill::*" FEATURES="test" USE="clang asan"
emerge-samus shill' succeeds.
Change-Id: I80a912e86c677a98b48a22494e7bbc36a936cf3b
Reviewed-on: https://chromium-review.googlesource.com/225278
Reviewed-by: Samuel Tan <samueltan@chromium.org>
Commit-Queue: Samuel Tan <samueltan@chromium.org>
Tested-by: Samuel Tan <samueltan@chromium.org>
diff --git a/shill/device.cc b/shill/device.cc
index 8fefc1f..b279e6a 100644
--- a/shill/device.cc
+++ b/shill/device.cc
@@ -484,15 +484,18 @@
addresses_str.push_back(address_str);
}
- // Setup timer to monitor DNS server lifetime if not infinite lifetime.
- if (lifetime != ND_OPT_LIFETIME_INFINITY) {
- StartIPv6DNSServerTimer(lifetime);
- }
-
if (!ip6config_) {
ip6config_ = new IPConfig(control_interface_, link_name_);
}
+ if (lifetime != ND_OPT_LIFETIME_INFINITY) {
+ // Setup timer to monitor DNS server lifetime if not infinite lifetime.
+ StartIPv6DNSServerTimer(lifetime);
+ ip6config_->UpdateLeaseExpirationTime(lifetime);
+ } else {
+ ip6config_->ResetLeaseExpirationTime();
+ }
+
// Done if no change in server addresses.
if (ip6config_->properties().dns_servers == addresses_str) {
SLOG(Device, 2) << __func__ << " IPv6 DNS server list for "
diff --git a/shill/device.h b/shill/device.h
index 8902983..b18f232 100644
--- a/shill/device.h
+++ b/shill/device.h
@@ -324,6 +324,8 @@
FRIEND_TEST(DeviceTest, OnIPv6AddressChanged);
FRIEND_TEST(DeviceTest, OnIPv6ConfigurationCompleted);
FRIEND_TEST(DeviceTest, OnIPv6DnsServerAddressesChanged);
+ FRIEND_TEST(DeviceTest,
+ OnIPv6DnsServerAddressesChanged_LeaseExpirationUpdated);
FRIEND_TEST(DeviceTest, Save);
FRIEND_TEST(DeviceTest, SelectedService);
FRIEND_TEST(DeviceTest, SetEnabledNonPersistent);
diff --git a/shill/device_unittest.cc b/shill/device_unittest.cc
index 6e0599d..58a2e0a 100644
--- a/shill/device_unittest.cc
+++ b/shill/device_unittest.cc
@@ -48,6 +48,7 @@
#include "shill/mock_store.h"
#include "shill/mock_time.h"
#include "shill/mock_traffic_monitor.h"
+#include "shill/ndisc.h"
#include "shill/portal_detector.h"
#include "shill/property_store_unittest.h"
#include "shill/static_ip_parameters.h"
@@ -1029,6 +1030,41 @@
Mock::VerifyAndClearExpectations(&device_info_);
}
+TEST_F(DeviceTest, OnIPv6DnsServerAddressesChanged_LeaseExpirationUpdated) {
+ MockManager manager(control_interface(),
+ dispatcher(),
+ metrics(),
+ glib());
+ manager.set_mock_device_info(&device_info_);
+ SetManager(&manager);
+
+ scoped_refptr<MockIPConfig> ip6config =
+ new MockIPConfig(control_interface(), kDeviceName);
+ device_->ip6config_ = ip6config;
+
+ // Non-infinite lifetime should trigger an update of the current lease
+ // expiration time.
+ const uint32 kExpiredLifetime = 1;
+ EXPECT_CALL(device_info_,
+ GetIPv6DnsServerAddresses(kDeviceInterfaceIndex, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(kExpiredLifetime),
+ Return(true)));
+ EXPECT_CALL(*ip6config, UpdateLeaseExpirationTime(_)).Times(1);
+ EXPECT_CALL(*ip6config, ResetLeaseExpirationTime()).Times(0);
+ device_->OnIPv6DnsServerAddressesChanged();
+
+ // Infinite lifetime should cause a reset of the current lease expiration
+ // time to its default value.
+ const uint32 kExpiredLifetimeInfinity = ND_OPT_LIFETIME_INFINITY;
+ EXPECT_CALL(device_info_,
+ GetIPv6DnsServerAddresses(kDeviceInterfaceIndex, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(kExpiredLifetimeInfinity),
+ Return(true)));
+ EXPECT_CALL(*ip6config, UpdateLeaseExpirationTime(_)).Times(0);
+ EXPECT_CALL(*ip6config, ResetLeaseExpirationTime()).Times(1);
+ device_->OnIPv6DnsServerAddressesChanged();
+}
+
TEST_F(DeviceTest, OnIPv6DnsServerAddressesChanged) {
StrictMock<MockManager> manager(control_interface(),
dispatcher(),
diff --git a/shill/dhcp_config.cc b/shill/dhcp_config.cc
index ecd479d..0bcb5fa 100644
--- a/shill/dhcp_config.cc
+++ b/shill/dhcp_config.cc
@@ -267,9 +267,11 @@
void DHCPConfig::UpdateProperties(const Properties &properties) {
StopAcquisitionTimeout();
if (properties.lease_duration_seconds) {
+ UpdateLeaseExpirationTime(properties.lease_duration_seconds);
StartExpirationTimeout(properties.lease_duration_seconds);
} else {
LOG(WARNING) << "Lease duration is zero; not starting an expiration timer.";
+ ResetLeaseExpirationTime();
StopExpirationTimeout();
}
IPConfig::UpdateProperties(properties);
diff --git a/shill/ipconfig.cc b/shill/ipconfig.cc
index 1f64df1..7af79c0 100644
--- a/shill/ipconfig.cc
+++ b/shill/ipconfig.cc
@@ -4,12 +4,15 @@
#include "shill/ipconfig.h"
+#include <sys/time.h>
+
#include <chromeos/dbus/service_constants.h>
#include "shill/adaptor_interfaces.h"
#include "shill/control_interface.h"
#include "shill/error.h"
#include "shill/logging.h"
+#include "shill/shill_time.h"
#include "shill/static_ip_parameters.h"
using base::Callback;
@@ -17,8 +20,15 @@
namespace shill {
+namespace {
+
+const time_t kDefaultLeaseExpirationTime = LONG_MAX;
+
+} // namespace
+
// static
const char IPConfig::kType[] = "ip";
+
// static
uint IPConfig::global_serial_ = 0;
@@ -58,6 +68,8 @@
&properties_.vendor_encapsulated_options);
store_.RegisterConstString(kWebProxyAutoDiscoveryUrlProperty,
&properties_.web_proxy_auto_discovery);
+ time_ = Time::GetInstance();
+ current_lease_expiration_time_ = {kDefaultLeaseExpirationTime, 0};
SLOG(Inet, 2) << __func__ << " device: " << device_name();
}
@@ -100,6 +112,34 @@
EmitChanges();
}
+void IPConfig::UpdateLeaseExpirationTime(uint32_t new_lease_duration) {
+ struct timeval new_expiration_time;
+ time_->GetTimeBoottime(&new_expiration_time);
+ new_expiration_time.tv_sec += new_lease_duration;
+ current_lease_expiration_time_ = new_expiration_time;
+}
+
+void IPConfig::ResetLeaseExpirationTime() {
+ current_lease_expiration_time_ = {kDefaultLeaseExpirationTime, 0};
+}
+
+bool IPConfig::TimeToLeaseExpiry(uint32_t *time_left) {
+ if (current_lease_expiration_time_.tv_sec == kDefaultLeaseExpirationTime) {
+ LOG(ERROR) << __func__ << ": "
+ << "No current DHCP lease";
+ return false;
+ }
+ struct timeval now;
+ time_->GetTimeBoottime(&now);
+ if (now.tv_sec > current_lease_expiration_time_.tv_sec) {
+ LOG(ERROR) << __func__ << ": "
+ << "Current DHCP lease has already expired";
+ return false;
+ }
+ *time_left = current_lease_expiration_time_.tv_sec - now.tv_sec;
+ return true;
+}
+
void IPConfig::UpdateProperties(const Properties &properties) {
// Take a reference of this instance to make sure we don't get destroyed in
// the middle of this call. (The |update_callback_| may cause a reference
diff --git a/shill/ipconfig.h b/shill/ipconfig.h
index 97ecff8..cda8bcd 100644
--- a/shill/ipconfig.h
+++ b/shill/ipconfig.h
@@ -7,6 +7,7 @@
#include <memory>
#include <string>
+#include <sys/time.h>
#include <vector>
#include <base/callback.h>
@@ -23,6 +24,7 @@
class Error;
class IPConfigAdaptorInterface;
class StaticIPParameters;
+class Time;
// IPConfig superclass. Individual IP configuration types will inherit from this
// class.
@@ -152,6 +154,18 @@
// static IP parameters were previously applied.
void RestoreSavedIPParameters(StaticIPParameters *static_ip_parameters);
+ // Updates |current_lease_expiration_time_| by adding |new_lease_duration| to
+ // the current time.
+ virtual void UpdateLeaseExpirationTime(uint32_t new_lease_duration);
+
+ // Resets |current_lease_expiration_time_| to its default value.
+ virtual void ResetLeaseExpirationTime();
+
+ // Returns the time left (in seconds) till the current DHCP lease is to be
+ // renewed in |time_left|. Returns false if an error occurs (i.e. current
+ // lease has already expired or no current DHCP lease), true otherwise.
+ bool TimeToLeaseExpiry(uint32_t *time_left);
+
protected:
// Inform RPC listeners of changes to our properties. MAY emit
// changes even on unchanged properties.
@@ -178,6 +192,10 @@
FRIEND_TEST(DeviceTest, OnIPConfigExpired);
FRIEND_TEST(IPConfigTest, UpdateCallback);
FRIEND_TEST(IPConfigTest, UpdateProperties);
+ FRIEND_TEST(IPConfigTest, UpdateLeaseExpirationTime);
+ FRIEND_TEST(IPConfigTest, TimeToLeaseExpiry_NoDHCPLease);
+ FRIEND_TEST(IPConfigTest, TimeToLeaseExpiry_CurrentLeaseExpired);
+ FRIEND_TEST(IPConfigTest, TimeToLeaseExpiry_Success);
FRIEND_TEST(ResolverTest, Empty);
FRIEND_TEST(ResolverTest, NonEmpty);
FRIEND_TEST(RoutingTableTest, ConfigureRoutes);
@@ -199,6 +217,8 @@
Callback failure_callback_;
Callback refresh_callback_;
Callback expire_callback_;
+ struct timeval current_lease_expiration_time_;
+ Time *time_;
DISALLOW_COPY_AND_ASSIGN(IPConfig);
};
diff --git a/shill/ipconfig_unittest.cc b/shill/ipconfig_unittest.cc
index 31b5248..0aa4469 100644
--- a/shill/ipconfig_unittest.cc
+++ b/shill/ipconfig_unittest.cc
@@ -4,23 +4,31 @@
#include "shill/ipconfig.h"
+#include <sys/time.h>
+
#include <base/bind.h>
#include <chromeos/dbus/service_constants.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include "shill/logging.h"
#include "shill/mock_adaptors.h"
#include "shill/mock_control.h"
+#include "shill/mock_log.h"
#include "shill/mock_store.h"
+#include "shill/mock_time.h"
#include "shill/static_ip_parameters.h"
using base::Bind;
using base::Unretained;
using std::string;
using testing::_;
+using testing::EndsWith;
+using testing::DoAll;
using testing::Mock;
using testing::Return;
using testing::SaveArg;
+using testing::SetArgPointee;
using testing::SetArgumentPointee;
using testing::StrictMock;
using testing::Test;
@@ -29,11 +37,14 @@
namespace {
const char kDeviceName[] = "testdevice";
+const uint32_t kTimeNow = 10;
} // namespace
class IPConfigTest : public Test {
public:
- IPConfigTest() : ipconfig_(new IPConfig(&control_, kDeviceName)) {}
+ IPConfigTest() : ipconfig_(new IPConfig(&control_, kDeviceName)) {
+ ipconfig_->time_ = &time_;
+ }
void DropRef(const IPConfigRefPtr &/*ipconfig*/) {
ipconfig_ = nullptr;
}
@@ -90,6 +101,7 @@
}
MockControl control_;
+ MockTime time_;
IPConfigRefPtr ipconfig_;
};
@@ -226,4 +238,53 @@
Mock::VerifyAndClearExpectations(adaptor);
}
+TEST_F(IPConfigTest, UpdateLeaseExpirationTime) {
+ const struct timeval expected_time_now = {kTimeNow , 0};
+ uint32_t lease_duration = 1;
+ EXPECT_CALL(time_, GetTimeBoottime(_))
+ .WillOnce(DoAll(SetArgPointee<0>(expected_time_now), Return(0)));
+ ipconfig_->UpdateLeaseExpirationTime(lease_duration);
+ EXPECT_EQ(kTimeNow + lease_duration,
+ ipconfig_->current_lease_expiration_time_.tv_sec);
+}
+
+TEST_F(IPConfigTest, TimeToLeaseExpiry_NoDHCPLease) {
+ ScopedMockLog log;
+ uint32_t time_left = 0;
+ // |current_lease_expiration_time_| has not been set, so expect an error.
+ EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
+ EndsWith("No current DHCP lease")));
+ EXPECT_FALSE(ipconfig_->TimeToLeaseExpiry(&time_left));
+ EXPECT_EQ(0, time_left);
+}
+
+TEST_F(IPConfigTest, TimeToLeaseExpiry_CurrentLeaseExpired) {
+ ScopedMockLog log;
+ const struct timeval time_now = {kTimeNow, 0};
+ uint32_t time_left = 0;
+ // Set |current_lease_expiration_time_| so it is expired (i.e. earlier than
+ // current time).
+ ipconfig_->current_lease_expiration_time_ = {kTimeNow - 1, 0};
+ EXPECT_CALL(time_, GetTimeBoottime(_))
+ .WillOnce(DoAll(SetArgPointee<0>(time_now), Return(0)));
+ EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
+ EndsWith("Current DHCP lease has already expired")));
+ EXPECT_FALSE(ipconfig_->TimeToLeaseExpiry(&time_left));
+ EXPECT_EQ(0, time_left);
+}
+
+TEST_F(IPConfigTest, TimeToLeaseExpiry_Success) {
+ const uint32_t expected_time_to_expiry = 10;
+ const struct timeval time_now = {kTimeNow, 0};
+ uint32_t time_left;
+ // Set |current_lease_expiration_time_| so it appears like we already
+ // have obtained a DHCP lease before.
+ ipconfig_->current_lease_expiration_time_ = {
+ kTimeNow + expected_time_to_expiry, 0};
+ EXPECT_CALL(time_, GetTimeBoottime(_))
+ .WillOnce(DoAll(SetArgPointee<0>(time_now), Return(0)));
+ EXPECT_TRUE(ipconfig_->TimeToLeaseExpiry(&time_left));
+ EXPECT_EQ(expected_time_to_expiry, time_left);
+}
+
} // namespace shill
diff --git a/shill/mock_ipconfig.h b/shill/mock_ipconfig.h
index d35bbcc..0d97b57 100644
--- a/shill/mock_ipconfig.h
+++ b/shill/mock_ipconfig.h
@@ -29,6 +29,8 @@
MOCK_METHOD0(EmitChanges, void(void));
MOCK_METHOD1(UpdateDNSServers,
void(const std::vector<std::string> &dns_servers));
+ MOCK_METHOD1(UpdateLeaseExpirationTime, void(uint32_t new_lease_duration));
+ MOCK_METHOD0(ResetLeaseExpirationTime, void(void));
private:
const Properties &real_properties() {