shill: IPv6 DNS server addresses changed notification.
Setup DeviceInfo to subscribe to RDNSS messages, and notify Device when there
is an update.
BUG=chromium:394010
TEST=unit tests, manual test.
Manual Test:
1. Update code to log the lifetime and DNS server IP addresses in Device's
callback function (OnIPv6DnsServerAddressesChanged).
2. Run network_Ipv6SimpleNegotiation on the DUT.
3. Verify the lifetime and DNS server IP addresses in net.log.
Change-Id: I6ce462388bbb168ed0816db24be349584f3ae51f
Reviewed-on: https://chromium-review.googlesource.com/210039
Reviewed-by: Peter Qiu <zqiu@chromium.org>
Commit-Queue: Peter Qiu <zqiu@chromium.org>
Tested-by: Peter Qiu <zqiu@chromium.org>
diff --git a/device.cc b/device.cc
index f21fc84..1c1f0a8 100644
--- a/device.cc
+++ b/device.cc
@@ -428,6 +428,10 @@
UpdateIPConfigsProperty();
}
+void Device::OnIPv6DnsServerAddressesChanged() {
+ // TODO(zqiu): To be implemented.
+}
+
bool Device::ShouldUseArpGateway() const {
return false;
}
diff --git a/device.h b/device.h
index c5dfbfb..07eb5cb 100644
--- a/device.h
+++ b/device.h
@@ -263,6 +263,10 @@
// IPv6 address from this interface.
virtual void OnIPv6AddressChanged();
+ // Called by DeviceInfo when the kernel receives a update for IPv6 DNS server
+ // addresses from this interface.
+ virtual void OnIPv6DnsServerAddressesChanged();
+
protected:
friend class base::RefCounted<Device>;
friend class DeviceHealthCheckerTest;
diff --git a/device_info.cc b/device_info.cc
index cac5835..a9f53c5 100644
--- a/device_info.cc
+++ b/device_info.cc
@@ -39,11 +39,13 @@
#include "shill/netlink_attribute.h"
#include "shill/netlink_manager.h"
#include "shill/nl80211_message.h"
+#include "shill/ndisc.h"
#include "shill/routing_table.h"
#include "shill/rtnl_handler.h"
#include "shill/rtnl_listener.h"
#include "shill/rtnl_message.h"
#include "shill/service.h"
+#include "shill/shill_time.h"
#include "shill/sockets.h"
#include "shill/virtio_ethernet.h"
#include "shill/vpn_provider.h"
@@ -96,11 +98,13 @@
manager_(manager),
link_callback_(Bind(&DeviceInfo::LinkMsgHandler, Unretained(this))),
address_callback_(Bind(&DeviceInfo::AddressMsgHandler, Unretained(this))),
+ rdnss_callback_(Bind(&DeviceInfo::RdnssMsgHandler, Unretained(this))),
device_info_root_(kDeviceInfoRoot),
routing_table_(RoutingTable::GetInstance()),
rtnl_handler_(RTNLHandler::GetInstance()),
netlink_manager_(NetlinkManager::GetInstance()),
- sockets_(new Sockets()) {
+ sockets_(new Sockets()),
+ time_(Time::GetInstance()) {
}
DeviceInfo::~DeviceInfo() {}
@@ -118,6 +122,8 @@
new RTNLListener(RTNLHandler::kRequestLink, link_callback_));
address_listener_.reset(
new RTNLListener(RTNLHandler::kRequestAddr, address_callback_));
+ rdnss_listener_.reset(
+ new RTNLListener(RTNLHandler::kRequestRdnss, rdnss_callback_));
rtnl_handler_->RequestDump(RTNLHandler::kRequestLink |
RTNLHandler::kRequestAddr);
request_link_statistics_callback_.Reset(
@@ -776,6 +782,34 @@
return has_address;
}
+bool DeviceInfo::GetIPv6DnsServerAddresses(int interface_index,
+ std::vector<IPAddress> *address_list,
+ uint32 *life_time) {
+ const Info *info = GetInfo(interface_index);
+ if (!info || info->ipv6_dns_server_addresses.empty()) {
+ return false;
+ }
+
+ // Determine the remaining DNS server life time.
+ if (info->ipv6_dns_server_lifetime_seconds == ND_OPT_LIFETIME_INFINITY) {
+ *life_time = ND_OPT_LIFETIME_INFINITY;
+ } else {
+ time_t cur_time;
+ if (!time_->GetSecondsBoottime(&cur_time)) {
+ NOTREACHED();
+ }
+ uint32 time_elapsed = static_cast<uint32>(
+ cur_time - info->ipv6_dns_server_received_time_seconds);
+ if (time_elapsed >= info->ipv6_dns_server_lifetime_seconds) {
+ *life_time = 0;
+ } else {
+ *life_time = info->ipv6_dns_server_lifetime_seconds - time_elapsed;
+ }
+ }
+ *address_list = info->ipv6_dns_server_addresses;
+ return true;
+}
+
bool DeviceInfo::HasDirectConnectivityTo(
int interface_index, const IPAddress &address) const {
SLOG(Device, 3) << __func__ << "(" << interface_index << ")";
@@ -926,6 +960,31 @@
}
}
+void DeviceInfo::RdnssMsgHandler(const RTNLMessage &msg) {
+ SLOG(Device, 2) << __func__;
+ DCHECK(msg.type() == RTNLMessage::kTypeRdnss);
+ int interface_index = msg.interface_index();
+ if (!ContainsKey(infos_, interface_index)) {
+ SLOG(Device, 2) << "Got RDNSS option for unknown index "
+ << interface_index;
+ }
+
+ const RTNLMessage::RdnssOption &rdnss_option = msg.rdnss_option();
+ infos_[interface_index].ipv6_dns_server_lifetime_seconds =
+ rdnss_option.lifetime;
+ infos_[interface_index].ipv6_dns_server_addresses = rdnss_option.addresses;
+ if (!time_->GetSecondsBoottime(
+ &infos_[interface_index].ipv6_dns_server_received_time_seconds)) {
+ NOTREACHED();
+ }
+
+ // Notify device of the IPv6 DNS server addresses update.
+ DeviceRefPtr device = GetDevice(interface_index);
+ if (device) {
+ device->OnIPv6DnsServerAddressesChanged();
+ }
+}
+
void DeviceInfo::DelayDeviceCreation(int interface_index) {
delayed_devices_.insert(interface_index);
delayed_devices_callback_.Reset(
diff --git a/device_info.h b/device_info.h
index 2947448..134e32d 100644
--- a/device_info.h
+++ b/device_info.h
@@ -22,6 +22,7 @@
#include "shill/device.h"
#include "shill/ip_address.h"
#include "shill/rtnl_listener.h"
+#include "shill/shill_time.h"
#include "shill/technology.h"
namespace shill {
@@ -111,6 +112,18 @@
// exists. Otherwise it returns false and leaves |address| unmodified.
virtual bool GetPrimaryIPv6Address(int interface_index, IPAddress *address);
+ // Get the IPv6 DNS server addresses for |interface_index|. This method
+ // returns true and sets |address_list| and |life_time_seconds| if the IPv6
+ // DNS server addresses exists. Otherwise, it returns false and leave
+ // |address_list| and |life_time_seconds| unmodified. |life_time_seconds|
+ // indicates the number of the seconds the DNS server is still valid for at
+ // the time of this function call. Value of 0 means the DNS server is not
+ // valid anymore, and value of 0xFFFFFFFF means the DNS server is valid
+ // forever.
+ virtual bool GetIPv6DnsServerAddresses(int interface_index,
+ std::vector<IPAddress> *address_list,
+ uint32 *life_time_seconds);
+
// Returns true if any of the addresses on |interface_index| are on the
// same network prefix as |address|.
virtual bool HasDirectConnectivityTo(
@@ -133,6 +146,7 @@
FRIEND_TEST(DeviceInfoTest, IPv6AddressChanged); // For infos_.
FRIEND_TEST(DeviceInfoTest, RequestLinkStatistics);
FRIEND_TEST(DeviceInfoTest, StartStop);
+ FRIEND_TEST(DeviceInfoTest, IPv6DnsServerAddressesChanged); // For infos_.
struct Info {
Info()
@@ -147,6 +161,9 @@
std::string name;
ByteString mac_address;
std::vector<AddressData> ip_addresses;
+ std::vector<IPAddress> ipv6_dns_server_addresses;
+ uint32 ipv6_dns_server_lifetime_seconds;
+ time_t ipv6_dns_server_received_time_seconds;
unsigned int flags;
uint64 rx_bytes;
uint64 tx_bytes;
@@ -233,6 +250,7 @@
void DelLinkMsgHandler(const RTNLMessage &msg);
void LinkMsgHandler(const RTNLMessage &msg);
void AddressMsgHandler(const RTNLMessage &msg);
+ void RdnssMsgHandler(const RTNLMessage &msg);
const Info *GetInfo(int interface_index) const;
void RemoveInfo(int interface_index);
@@ -257,8 +275,10 @@
base::Callback<void(const RTNLMessage &)> link_callback_;
base::Callback<void(const RTNLMessage &)> address_callback_;
+ base::Callback<void(const RTNLMessage &)> rdnss_callback_;
scoped_ptr<RTNLListener> link_listener_;
scoped_ptr<RTNLListener> address_listener_;
+ scoped_ptr<RTNLListener> rdnss_listener_;
std::set<std::string> black_list_;
base::FilePath device_info_root_;
@@ -277,6 +297,8 @@
// A member of the class so that a mock can be injected for testing.
scoped_ptr<Sockets> sockets_;
+ Time *time_;
+
DISALLOW_COPY_AND_ASSIGN(DeviceInfo);
};
diff --git a/device_info_unittest.cc b/device_info_unittest.cc
index c19bbc6..246b3f7 100644
--- a/device_info_unittest.cc
+++ b/device_info_unittest.cc
@@ -37,6 +37,7 @@
#include "shill/mock_routing_table.h"
#include "shill/mock_rtnl_handler.h"
#include "shill/mock_sockets.h"
+#include "shill/mock_time.h"
#include "shill/mock_vpn_provider.h"
#include "shill/mock_wimax_provider.h"
#include "shill/netlink_attribute.h"
@@ -59,6 +60,7 @@
using testing::Mock;
using testing::NotNull;
using testing::Return;
+using testing::SetArgPointee;
using testing::StrictMock;
using testing::Test;
@@ -89,6 +91,7 @@
device_info_.rtnl_handler_ = &rtnl_handler_;
device_info_.routing_table_ = &routing_table_;
device_info_.netlink_manager_ = &netlink_manager_;
+ device_info_.time_ = &time_;
manager_.set_mock_device_info(&device_info_);
}
@@ -155,6 +158,9 @@
const IPAddress &address,
unsigned char flags,
unsigned char scope);
+ RTNLMessage *BuildRdnssMessage(RTNLMessage::Mode mode,
+ uint32 lifetime,
+ const vector<IPAddress> &dns_servers);
void SendMessageToDeviceInfo(const RTNLMessage &message);
MockGLib glib_;
@@ -167,6 +173,7 @@
MockNetlinkManager netlink_manager_;
StrictMock<MockRTNLHandler> rtnl_handler_;
MockSockets *mock_sockets_; // Owned by DeviceInfo.
+ MockTime time_;
};
const int DeviceInfoTest::kTestDeviceIndex = 123456;
@@ -224,11 +231,28 @@
return message;
}
+RTNLMessage *DeviceInfoTest::BuildRdnssMessage(RTNLMessage::Mode mode,
+ uint32 lifetime, const vector<IPAddress> &dns_servers) {
+ RTNLMessage *message = new RTNLMessage(
+ RTNLMessage::kTypeRdnss,
+ mode,
+ 0,
+ 0,
+ 0,
+ kTestDeviceIndex,
+ IPAddress::kFamilyIPv6);
+ message->set_rdnss_option(
+ RTNLMessage::RdnssOption(lifetime, dns_servers));
+ return message;
+}
+
void DeviceInfoTest::SendMessageToDeviceInfo(const RTNLMessage &message) {
if (message.type() == RTNLMessage::kTypeLink) {
device_info_.LinkMsgHandler(message);
} else if (message.type() == RTNLMessage::kTypeAddress) {
device_info_.AddressMsgHandler(message);
+ } else if (message.type() == RTNLMessage::kTypeRdnss) {
+ device_info_.RdnssMsgHandler(message);
} else {
NOTREACHED();
}
@@ -1147,6 +1171,88 @@
EXPECT_TRUE(address2.Equals(ipv6_address4));
}
+
+TEST_F(DeviceInfoTest, IPv6DnsServerAddressesChanged) {
+ scoped_refptr<MockDevice> device(new MockDevice(
+ &control_interface_, &dispatcher_, &metrics_, &manager_,
+ "null0", "addr0", kTestDeviceIndex));
+ device_info_.time_ = &time_;
+ vector<IPAddress> dns_server_addresses_out;
+ uint32 lifetime_out;
+
+ // Device info entry does not exist.
+ EXPECT_FALSE(device_info_.GetIPv6DnsServerAddresses(
+ kTestDeviceIndex, &dns_server_addresses_out, &lifetime_out));
+
+ device_info_.infos_[kTestDeviceIndex].device = device;
+
+ // Device info entry contains no IPv6 dns server addresses.
+ EXPECT_FALSE(device_info_.GetIPv6DnsServerAddresses(
+ kTestDeviceIndex, &dns_server_addresses_out, &lifetime_out));
+
+ // Setup IPv6 dns server addresses.
+ IPAddress ipv6_address1(IPAddress::kFamilyIPv6);
+ IPAddress ipv6_address2(IPAddress::kFamilyIPv6);
+ EXPECT_TRUE(ipv6_address1.SetAddressFromString(kTestIPAddress1));
+ EXPECT_TRUE(ipv6_address2.SetAddressFromString(kTestIPAddress2));
+ vector<IPAddress> dns_server_addresses_in;
+ dns_server_addresses_in.push_back(ipv6_address1);
+ dns_server_addresses_in.push_back(ipv6_address2);
+
+ // Infinite lifetime
+ const uint32 kInfiniteLifetime = 0xffffffff;
+ auto message(make_scoped_ptr(BuildRdnssMessage(
+ RTNLMessage::kModeAdd, kInfiniteLifetime, dns_server_addresses_in)));
+ EXPECT_CALL(time_, GetSecondsBoottime(_)).
+ WillOnce(DoAll(SetArgPointee<0>(0), Return(true)));
+ EXPECT_CALL(*device, OnIPv6DnsServerAddressesChanged()).Times(1);
+ SendMessageToDeviceInfo(*message);
+ EXPECT_CALL(time_, GetSecondsBoottime(_)).Times(0);
+ EXPECT_TRUE(device_info_.GetIPv6DnsServerAddresses(
+ kTestDeviceIndex, &dns_server_addresses_out, &lifetime_out));
+ // Verify addresses and lifetime.
+ EXPECT_EQ(kInfiniteLifetime, lifetime_out);
+ EXPECT_EQ(2, dns_server_addresses_out.size());
+ EXPECT_EQ(kTestIPAddress1, dns_server_addresses_out.at(0).ToString());
+ EXPECT_EQ(kTestIPAddress2, dns_server_addresses_out.at(1).ToString());
+
+ // Lifetime of 120, retrieve DNS server addresses after 10 seconds.
+ const uint32 kLifetime120 = 120;
+ const uint32 kElapseTime10 = 10;
+ auto message1(make_scoped_ptr(BuildRdnssMessage(
+ RTNLMessage::kModeAdd, kLifetime120, dns_server_addresses_in)));
+ EXPECT_CALL(time_, GetSecondsBoottime(_)).
+ WillOnce(DoAll(SetArgPointee<0>(0), Return(true)));
+ EXPECT_CALL(*device, OnIPv6DnsServerAddressesChanged()).Times(1);
+ SendMessageToDeviceInfo(*message1);
+ // 10 seconds passed when GetIPv6DnsServerAddreses is called.
+ EXPECT_CALL(time_, GetSecondsBoottime(_)).
+ WillOnce(DoAll(SetArgPointee<0>(kElapseTime10), Return(true)));
+ EXPECT_TRUE(device_info_.GetIPv6DnsServerAddresses(
+ kTestDeviceIndex, &dns_server_addresses_out, &lifetime_out));
+ // Verify addresses and lifetime.
+ EXPECT_EQ(kLifetime120 - kElapseTime10, lifetime_out);
+ EXPECT_EQ(2, dns_server_addresses_out.size());
+ EXPECT_EQ(kTestIPAddress1, dns_server_addresses_out.at(0).ToString());
+ EXPECT_EQ(kTestIPAddress2, dns_server_addresses_out.at(1).ToString());
+
+ // Lifetime of 120, retrieve DNS server addresses after lifetime expired.
+ EXPECT_CALL(time_, GetSecondsBoottime(_)).
+ WillOnce(DoAll(SetArgPointee<0>(0), Return(true)));
+ EXPECT_CALL(*device, OnIPv6DnsServerAddressesChanged()).Times(1);
+ SendMessageToDeviceInfo(*message1);
+ // 120 seconds passed when GetIPv6DnsServerAddreses is called.
+ EXPECT_CALL(time_, GetSecondsBoottime(_)).
+ WillOnce(DoAll(SetArgPointee<0>(kLifetime120), Return(true)));
+ EXPECT_TRUE(device_info_.GetIPv6DnsServerAddresses(
+ kTestDeviceIndex, &dns_server_addresses_out, &lifetime_out));
+ // Verify addresses and lifetime.
+ EXPECT_EQ(0, lifetime_out);
+ EXPECT_EQ(2, dns_server_addresses_out.size());
+ EXPECT_EQ(kTestIPAddress1, dns_server_addresses_out.at(0).ToString());
+ EXPECT_EQ(kTestIPAddress2, dns_server_addresses_out.at(1).ToString());
+}
+
class DeviceInfoTechnologyTest : public DeviceInfoTest {
public:
DeviceInfoTechnologyTest()
diff --git a/mock_device.h b/mock_device.h
index 8e622aa..2f2f3b2 100644
--- a/mock_device.h
+++ b/mock_device.h
@@ -55,6 +55,7 @@
MOCK_METHOD0(OnConnectionUpdated, void());
MOCK_METHOD0(OnIPv6AddressChanged, void());
MOCK_CONST_METHOD0(GetGeolocationObjects, std::vector<GeolocationInfo>());
+ MOCK_METHOD0(OnIPv6DnsServerAddressesChanged, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockDevice);
diff --git a/ndisc.h b/ndisc.h
index 2d7c22a..b845b24 100644
--- a/ndisc.h
+++ b/ndisc.h
@@ -26,4 +26,7 @@
#define ND_OPT_RDNSS 25 /* RFC 5006 */
#define ND_OPT_DNSSL 31 /* RFC 6106 */
+// Infinity lifetime.
+#define ND_OPT_LIFETIME_INFINITY 0xFFFFFFFF
+
#endif // SHILL_NDISC_H_