root/net/base/address_tracker_linux_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. TestGetInterfaceName
  2. original_get_interface_name_
  3. HandleAddressMessage
  4. HandleLinkMessage
  5. HandleTunnelMessage
  6. GetAddressMap
  7. GetOnlineLinks
  8. AddPayload
  9. AddAttribute
  10. AppendTo
  11. Append
  12. Align
  13. header
  14. MakeAddrMessageWithCacheInfo
  15. MakeAddrMessage
  16. MakeLinkMessage
  17. TEST_F
  18. TEST_F
  19. TEST_F
  20. TEST_F
  21. TEST_F
  22. TEST_F
  23. TEST_F
  24. TEST_F
  25. TEST_F
  26. TEST_F

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/address_tracker_linux.h"

#include <linux/if.h>

#include <vector>

#include "base/bind.h"
#include "testing/gtest/include/gtest/gtest.h"

#ifndef IFA_F_HOMEADDRESS
#define IFA_F_HOMEADDRESS 0x10
#endif

namespace net {
namespace internal {
namespace {

const int kTestInterfaceTun = 123;

const char* TestGetInterfaceName(int interface_index) {
  if (interface_index == kTestInterfaceTun)
    return "tun0";
  return "eth0";
}

}  // namespace

typedef std::vector<char> Buffer;

class AddressTrackerLinuxTest : public testing::Test {
 protected:
  AddressTrackerLinuxTest()
      : tracker_(base::Bind(&base::DoNothing),
                 base::Bind(&base::DoNothing),
                 base::Bind(&base::DoNothing)),
        original_get_interface_name_(tracker_.get_interface_name_) {
    tracker_.get_interface_name_ = TestGetInterfaceName;
  }

  bool HandleAddressMessage(const Buffer& buf) {
    Buffer writable_buf = buf;
    bool address_changed = false;
    bool link_changed = false;
    bool tunnel_changed = false;
    tracker_.HandleMessage(&writable_buf[0], buf.size(),
                           &address_changed, &link_changed, &tunnel_changed);
    EXPECT_FALSE(link_changed);
    return address_changed;
  }

  bool HandleLinkMessage(const Buffer& buf) {
    Buffer writable_buf = buf;
    bool address_changed = false;
    bool link_changed = false;
    bool tunnel_changed = false;
    tracker_.HandleMessage(&writable_buf[0], buf.size(),
                           &address_changed, &link_changed, &tunnel_changed);
    EXPECT_FALSE(address_changed);
    return link_changed;
  }

  bool HandleTunnelMessage(const Buffer& buf) {
    Buffer writable_buf = buf;
    bool address_changed = false;
    bool link_changed = false;
    bool tunnel_changed = false;
    tracker_.HandleMessage(&writable_buf[0], buf.size(),
                           &address_changed, &link_changed, &tunnel_changed);
    EXPECT_FALSE(address_changed);
    return tunnel_changed;
  }

  AddressTrackerLinux::AddressMap GetAddressMap() {
    return tracker_.GetAddressMap();
  }

  const base::hash_set<int>* GetOnlineLinks() const {
    return &tracker_.online_links_;
  }

  AddressTrackerLinux tracker_;
  AddressTrackerLinux::GetInterfaceNameFunction original_get_interface_name_;
};

namespace {

class NetlinkMessage {
 public:
  explicit NetlinkMessage(uint16 type) : buffer_(NLMSG_HDRLEN) {
    header()->nlmsg_type = type;
    Align();
  }

  void AddPayload(const void* data, size_t length) {
    CHECK_EQ(static_cast<size_t>(NLMSG_HDRLEN),
             buffer_.size()) << "Payload must be added first";
    Append(data, length);
    Align();
  }

  void AddAttribute(uint16 type, const void* data, size_t length) {
    struct nlattr attr;
    attr.nla_len = NLA_HDRLEN + length;
    attr.nla_type = type;
    Append(&attr, sizeof(attr));
    Align();
    Append(data, length);
    Align();
  }

  void AppendTo(Buffer* output) const {
    CHECK_EQ(NLMSG_ALIGN(output->size()), output->size());
    output->reserve(output->size() + NLMSG_LENGTH(buffer_.size()));
    output->insert(output->end(), buffer_.begin(), buffer_.end());
  }

 private:
  void Append(const void* data, size_t length) {
    const char* chardata = reinterpret_cast<const char*>(data);
    buffer_.insert(buffer_.end(), chardata, chardata + length);
  }

  void Align() {
    header()->nlmsg_len = buffer_.size();
    buffer_.insert(buffer_.end(), NLMSG_ALIGN(buffer_.size()) - buffer_.size(),
                   0);
    CHECK(NLMSG_OK(header(), buffer_.size()));
  }

  struct nlmsghdr* header() {
    return reinterpret_cast<struct nlmsghdr*>(&buffer_[0]);
  }

  Buffer buffer_;
};

#define INFINITY_LIFE_TIME 0xFFFFFFFF

void MakeAddrMessageWithCacheInfo(uint16 type,
                                  uint8 flags,
                                  uint8 family,
                                  const IPAddressNumber& address,
                                  const IPAddressNumber& local,
                                  uint32 preferred_lifetime,
                                  Buffer* output) {
  NetlinkMessage nlmsg(type);
  struct ifaddrmsg msg = {};
  msg.ifa_family = family;
  msg.ifa_flags = flags;
  nlmsg.AddPayload(&msg, sizeof(msg));
  if (address.size())
    nlmsg.AddAttribute(IFA_ADDRESS, &address[0], address.size());
  if (local.size())
    nlmsg.AddAttribute(IFA_LOCAL, &local[0], local.size());
  struct ifa_cacheinfo cache_info = {};
  cache_info.ifa_prefered = preferred_lifetime;
  cache_info.ifa_valid = INFINITY_LIFE_TIME;
  nlmsg.AddAttribute(IFA_CACHEINFO, &cache_info, sizeof(cache_info));
  nlmsg.AppendTo(output);
}

void MakeAddrMessage(uint16 type,
                     uint8 flags,
                     uint8 family,
                     const IPAddressNumber& address,
                     const IPAddressNumber& local,
                     Buffer* output) {
  MakeAddrMessageWithCacheInfo(type, flags, family, address, local,
                               INFINITY_LIFE_TIME, output);
}

void MakeLinkMessage(uint16 type, uint32 flags, uint32 index, Buffer* output) {
  NetlinkMessage nlmsg(type);
  struct ifinfomsg msg = {};
  msg.ifi_index = index;
  msg.ifi_flags = flags;
  nlmsg.AddPayload(&msg, sizeof(msg));
  output->clear();
  nlmsg.AppendTo(output);
}

const unsigned char kAddress0[] = { 127, 0, 0, 1 };
const unsigned char kAddress1[] = { 10, 0, 0, 1 };
const unsigned char kAddress2[] = { 192, 168, 0, 1 };
const unsigned char kAddress3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 1 };

TEST_F(AddressTrackerLinuxTest, NewAddress) {
  const IPAddressNumber kEmpty;
  const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
  const IPAddressNumber kAddr1(kAddress1, kAddress1 + arraysize(kAddress1));
  const IPAddressNumber kAddr2(kAddress2, kAddress2 + arraysize(kAddress2));
  const IPAddressNumber kAddr3(kAddress3, kAddress3 + arraysize(kAddress3));

  Buffer buffer;
  MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty,
                  &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  AddressTrackerLinux::AddressMap map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(1u, map.count(kAddr0));
  EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags);

  buffer.clear();
  MakeAddrMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr1, kAddr2,
                  &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(2u, map.size());
  EXPECT_EQ(1u, map.count(kAddr0));
  EXPECT_EQ(1u, map.count(kAddr2));
  EXPECT_EQ(IFA_F_HOMEADDRESS, map[kAddr2].ifa_flags);

  buffer.clear();
  MakeAddrMessage(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(3u, map.size());
  EXPECT_EQ(1u, map.count(kAddr3));
}

TEST_F(AddressTrackerLinuxTest, NewAddressChange) {
  const IPAddressNumber kEmpty;
  const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));

  Buffer buffer;
  MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty,
                  &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  AddressTrackerLinux::AddressMap map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(1u, map.count(kAddr0));
  EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags);

  buffer.clear();
  MakeAddrMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr0, kEmpty,
                  &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(1u, map.count(kAddr0));
  EXPECT_EQ(IFA_F_HOMEADDRESS, map[kAddr0].ifa_flags);

  // Both messages in one buffer.
  buffer.clear();
  MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty,
                  &buffer);
  MakeAddrMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr0, kEmpty,
                  &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(IFA_F_HOMEADDRESS, map[kAddr0].ifa_flags);
}

TEST_F(AddressTrackerLinuxTest, NewAddressDuplicate) {
  const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));

  Buffer buffer;
  MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kAddr0,
                  &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  AddressTrackerLinux::AddressMap map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(1u, map.count(kAddr0));
  EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags);

  EXPECT_FALSE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags);
}

TEST_F(AddressTrackerLinuxTest, DeleteAddress) {
  const IPAddressNumber kEmpty;
  const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
  const IPAddressNumber kAddr1(kAddress1, kAddress1 + arraysize(kAddress1));
  const IPAddressNumber kAddr2(kAddress2, kAddress2 + arraysize(kAddress2));

  Buffer buffer;
  MakeAddrMessage(RTM_NEWADDR, 0, AF_INET, kAddr0, kEmpty, &buffer);
  MakeAddrMessage(RTM_NEWADDR, 0, AF_INET, kAddr1, kAddr2, &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  AddressTrackerLinux::AddressMap map = GetAddressMap();
  EXPECT_EQ(2u, map.size());

  buffer.clear();
  MakeAddrMessage(RTM_DELADDR, 0, AF_INET, kEmpty, kAddr0, &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(0u, map.count(kAddr0));
  EXPECT_EQ(1u, map.count(kAddr2));

  buffer.clear();
  MakeAddrMessage(RTM_DELADDR, 0, AF_INET, kAddr2, kAddr1, &buffer);
  // kAddr1 does not exist in the map.
  EXPECT_FALSE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(1u, map.size());

  buffer.clear();
  MakeAddrMessage(RTM_DELADDR, 0, AF_INET, kAddr2, kEmpty, &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(0u, map.size());
}

TEST_F(AddressTrackerLinuxTest, DeprecatedLifetime) {
  const IPAddressNumber kEmpty;
  const IPAddressNumber kAddr3(kAddress3, kAddress3 + arraysize(kAddress3));

  Buffer buffer;
  MakeAddrMessage(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  AddressTrackerLinux::AddressMap map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(1u, map.count(kAddr3));
  EXPECT_EQ(0, map[kAddr3].ifa_flags);

  // Verify 0 preferred lifetime implies deprecated.
  buffer.clear();
  MakeAddrMessageWithCacheInfo(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, 0,
                               &buffer);
  EXPECT_TRUE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(IFA_F_DEPRECATED, map[kAddr3].ifa_flags);

  // Verify properly flagged message doesn't imply change.
  buffer.clear();
  MakeAddrMessageWithCacheInfo(RTM_NEWADDR, IFA_F_DEPRECATED, AF_INET6, kEmpty,
                               kAddr3, 0, &buffer);
  EXPECT_FALSE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(IFA_F_DEPRECATED, map[kAddr3].ifa_flags);

  // Verify implied deprecated doesn't imply change.
  buffer.clear();
  MakeAddrMessageWithCacheInfo(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, 0,
                               &buffer);
  EXPECT_FALSE(HandleAddressMessage(buffer));
  map = GetAddressMap();
  EXPECT_EQ(1u, map.size());
  EXPECT_EQ(IFA_F_DEPRECATED, map[kAddr3].ifa_flags);
}

TEST_F(AddressTrackerLinuxTest, IgnoredMessage) {
  const IPAddressNumber kEmpty;
  const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
  const IPAddressNumber kAddr3(kAddress3, kAddress3 + arraysize(kAddress3));

  Buffer buffer;
  // Ignored family.
  MakeAddrMessage(RTM_NEWADDR, 0, AF_UNSPEC, kAddr3, kAddr0, &buffer);
  // No address.
  MakeAddrMessage(RTM_NEWADDR, 0, AF_INET, kEmpty, kEmpty, &buffer);
  // Ignored type.
  MakeAddrMessage(RTM_DELROUTE, 0, AF_INET6, kAddr3, kEmpty, &buffer);
  EXPECT_FALSE(HandleAddressMessage(buffer));
  EXPECT_TRUE(GetAddressMap().empty());

  // Valid message after ignored messages.
  NetlinkMessage nlmsg(RTM_NEWADDR);
  struct ifaddrmsg msg = {};
  msg.ifa_family = AF_INET;
  nlmsg.AddPayload(&msg, sizeof(msg));
  // Ignored attribute.
  struct ifa_cacheinfo cache_info = {};
  nlmsg.AddAttribute(IFA_CACHEINFO, &cache_info, sizeof(cache_info));
  nlmsg.AddAttribute(IFA_ADDRESS, &kAddr0[0], kAddr0.size());
  nlmsg.AppendTo(&buffer);

  EXPECT_TRUE(HandleAddressMessage(buffer));
  EXPECT_EQ(1u, GetAddressMap().size());
}

TEST_F(AddressTrackerLinuxTest, AddInterface) {
  Buffer buffer;

  // Ignores loopback.
  MakeLinkMessage(RTM_NEWLINK,
                  IFF_LOOPBACK | IFF_UP | IFF_LOWER_UP | IFF_RUNNING,
                  0, &buffer);
  EXPECT_FALSE(HandleLinkMessage(buffer));
  EXPECT_TRUE(GetOnlineLinks()->empty());

  // Ignores not IFF_LOWER_UP.
  MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, 0, &buffer);
  EXPECT_FALSE(HandleLinkMessage(buffer));
  EXPECT_TRUE(GetOnlineLinks()->empty());

  // Ignores deletion.
  MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
  EXPECT_FALSE(HandleLinkMessage(buffer));
  EXPECT_TRUE(GetOnlineLinks()->empty());

  // Verify success.
  MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
  EXPECT_TRUE(HandleLinkMessage(buffer));
  EXPECT_EQ(1u, GetOnlineLinks()->count(0));
  EXPECT_EQ(1u, GetOnlineLinks()->size());

  // Ignores redundant enables.
  MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
  EXPECT_FALSE(HandleLinkMessage(buffer));
  EXPECT_EQ(1u, GetOnlineLinks()->count(0));
  EXPECT_EQ(1u, GetOnlineLinks()->size());

  // Verify adding another online device (e.g. VPN) is considered a change.
  MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 1, &buffer);
  EXPECT_TRUE(HandleLinkMessage(buffer));
  EXPECT_EQ(1u, GetOnlineLinks()->count(0));
  EXPECT_EQ(1u, GetOnlineLinks()->count(1));
  EXPECT_EQ(2u, GetOnlineLinks()->size());
}

TEST_F(AddressTrackerLinuxTest, RemoveInterface) {
  Buffer buffer;

  // Should disappear when not IFF_LOWER_UP.
  MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
  EXPECT_TRUE(HandleLinkMessage(buffer));
  EXPECT_FALSE(GetOnlineLinks()->empty());
  MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, 0, &buffer);
  EXPECT_TRUE(HandleLinkMessage(buffer));
  EXPECT_TRUE(GetOnlineLinks()->empty());

  // Ignores redundant disables.
  MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, 0, &buffer);
  EXPECT_FALSE(HandleLinkMessage(buffer));
  EXPECT_TRUE(GetOnlineLinks()->empty());

  // Ignores deleting down interfaces.
  MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_RUNNING, 0, &buffer);
  EXPECT_FALSE(HandleLinkMessage(buffer));
  EXPECT_TRUE(GetOnlineLinks()->empty());

  // Should disappear when deleted.
  MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
  EXPECT_TRUE(HandleLinkMessage(buffer));
  EXPECT_FALSE(GetOnlineLinks()->empty());
  MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
  EXPECT_TRUE(HandleLinkMessage(buffer));
  EXPECT_TRUE(GetOnlineLinks()->empty());
}

TEST_F(AddressTrackerLinuxTest, TunnelInterface) {
  Buffer buffer;

  // Ignores without "tun" prefixed name.
  MakeLinkMessage(RTM_NEWLINK,
                  IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
                  0, &buffer);
  EXPECT_FALSE(HandleTunnelMessage(buffer));

  // Verify success.
  MakeLinkMessage(RTM_NEWLINK,
                  IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
                  kTestInterfaceTun, &buffer);
  EXPECT_TRUE(HandleTunnelMessage(buffer));

  // Ignores redundant enables.
  MakeLinkMessage(RTM_NEWLINK,
                  IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
                  kTestInterfaceTun, &buffer);
  EXPECT_FALSE(HandleTunnelMessage(buffer));

  // Ignores deleting without "tun" prefixed name.
  MakeLinkMessage(RTM_DELLINK,
                  IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
                  0, &buffer);
  EXPECT_FALSE(HandleTunnelMessage(buffer));

  // Verify successful deletion
  MakeLinkMessage(RTM_DELLINK,
                  IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
                  kTestInterfaceTun, &buffer);
  EXPECT_TRUE(HandleTunnelMessage(buffer));

  // Ignores redundant deletions.
  MakeLinkMessage(RTM_DELLINK,
                  IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT,
                  kTestInterfaceTun, &buffer);
  EXPECT_FALSE(HandleTunnelMessage(buffer));
}

// Check AddressTrackerLinux::get_interface_name_ original implementation
// doesn't crash or return NULL.
TEST_F(AddressTrackerLinuxTest, GetInterfaceName) {
  for (int i = 0; i < 10; i++)
    EXPECT_NE((const char*)NULL, original_get_interface_name_(i));
}

}  // namespace

}  // namespace internal
}  // namespace net

/* [<][>][^][v][top][bottom][index][help] */