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_