// 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 "base/memory/scoped_ptr.h" #include "net/base/address_tracker_linux.h" #include #include #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; char* TestGetInterfaceName(int interface_index, char* buf) { if (interface_index == kTestInterfaceTun) return strncpy(buf, "tun0", IFNAMSIZ); return strncpy(buf, "eth0", IFNAMSIZ); } } // namespace typedef std::vector Buffer; class AddressTrackerLinuxTest : public testing::Test { protected: AddressTrackerLinuxTest() {} void InitializeAddressTracker(bool tracking) { if (tracking) { tracker_.reset(new AddressTrackerLinux(base::Bind(&base::DoNothing), base::Bind(&base::DoNothing), base::Bind(&base::DoNothing))); } else { tracker_.reset(new AddressTrackerLinux()); } 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 GetOnlineLinks() const { return tracker_->GetOnlineLinks(); } scoped_ptr 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(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(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(&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) { InitializeAddressTracker(true); 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) { InitializeAddressTracker(true); 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) { InitializeAddressTracker(true); 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) { InitializeAddressTracker(true); 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) { InitializeAddressTracker(true); 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) { InitializeAddressTracker(true); 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) { InitializeAddressTracker(true); 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) { InitializeAddressTracker(true); 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) { InitializeAddressTracker(true); 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) { InitializeAddressTracker(true); for (int i = 0; i < 10; i++) { char buf[IFNAMSIZ] = {0}; EXPECT_NE((const char*)NULL, original_get_interface_name_(i, buf)); } } TEST_F(AddressTrackerLinuxTest, NonTrackingMode) { InitializeAddressTracker(false); 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); 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()); } TEST_F(AddressTrackerLinuxTest, NonTrackingModeInit) { AddressTrackerLinux tracker; tracker.Init(); } } // namespace } // namespace internal } // namespace net