|  | // Copyright 2012 The Chromium Authors | 
|  | // 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 <linux/rtnetlink.h> | 
|  | #include <sched.h> | 
|  |  | 
|  | #include <array> | 
|  | #include <memory> | 
|  | #include <unordered_set> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/command_line.h" | 
|  | #include "base/containers/span.h" | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/functional/callback_helpers.h" | 
|  | #include "base/memory/raw_ptr.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/synchronization/waitable_event.h" | 
|  | #include "base/test/bind.h" | 
|  | #include "base/test/multiprocess_test.h" | 
|  | #include "base/test/spin_wait.h" | 
|  | #include "base/test/task_environment.h" | 
|  | #include "base/test/test_simple_task_runner.h" | 
|  | #include "base/threading/simple_thread.h" | 
|  | #include "build/build_config.h" | 
|  | #include "net/base/address_map_cache_linux.h" | 
|  | #include "net/base/address_map_linux.h" | 
|  | #include "net/base/address_tracker_linux_test_util.h" | 
|  | #include "net/base/ip_address.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "testing/multiprocess_func_list.h" | 
|  |  | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | #include "base/android/android_info.h" | 
|  | #endif | 
|  |  | 
|  | #ifndef IFA_F_HOMEADDRESS | 
|  | #define IFA_F_HOMEADDRESS 0x10 | 
|  | #endif | 
|  |  | 
|  | using net::internal::AddressTrackerLinux; | 
|  |  | 
|  | namespace net::test { | 
|  | namespace { | 
|  |  | 
|  | const int kTestInterfaceEth = 1; | 
|  | const int kTestInterfaceWifi = 2; | 
|  | const int kTestInterfaceTun = 123; | 
|  | const int kTestInterfaceAp = 456; | 
|  |  | 
|  | const char kIgnoredInterfaceName[] = "uap0"; | 
|  |  | 
|  | std::string TestGetInterfaceName(int interface_index) { | 
|  | switch (interface_index) { | 
|  | case kTestInterfaceEth: | 
|  | return "eth0"; | 
|  | case kTestInterfaceTun: | 
|  | return "tun0"; | 
|  | case kTestInterfaceAp: | 
|  | return kIgnoredInterfaceName; | 
|  | default: | 
|  | return std::string(); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class AddressTrackerLinuxTest : public testing::Test { | 
|  | protected: | 
|  | AddressTrackerLinuxTest() = default; | 
|  |  | 
|  | void InitializeAddressTracker(bool tracking) { | 
|  | tracking_ = tracking; | 
|  | if (tracking) { | 
|  | tracker_ = std::make_unique<AddressTrackerLinux>( | 
|  | base::DoNothing(), base::DoNothing(), base::DoNothing(), | 
|  | ignored_interfaces_); | 
|  | #if BUILDFLAG(IS_LINUX) | 
|  | const auto& [address_map, online_links] = | 
|  | tracker_->GetInitialDataAndStartRecordingDiffs(); | 
|  | address_map_cache_.SetCachedInfo(address_map, online_links); | 
|  | #endif  // BUILDFLAG(IS_LINUX) | 
|  | } else { | 
|  | tracker_ = std::make_unique<AddressTrackerLinux>(); | 
|  | } | 
|  | original_get_interface_name_ = tracker_->get_interface_name_; | 
|  | tracker_->get_interface_name_ = TestGetInterfaceName; | 
|  | } | 
|  |  | 
|  | bool HandleAddressMessage(const NetlinkBuffer& buf) { | 
|  | NetlinkBuffer 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); | 
|  | UpdateCache(); | 
|  | EXPECT_FALSE(link_changed); | 
|  | return address_changed; | 
|  | } | 
|  |  | 
|  | bool HandleLinkMessage(const NetlinkBuffer& buf) { | 
|  | NetlinkBuffer 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); | 
|  | UpdateCache(); | 
|  | EXPECT_FALSE(address_changed); | 
|  | return link_changed; | 
|  | } | 
|  |  | 
|  | bool HandleTunnelMessage(const NetlinkBuffer& buf) { | 
|  | NetlinkBuffer writable_buf = buf; | 
|  | bool address_changed = false; | 
|  | bool link_changed = false; | 
|  | bool tunnel_changed = false; | 
|  | AddressMapOwnerLinux::AddressMapDiff address_map_diff_; | 
|  | AddressMapOwnerLinux::OnlineLinksDiff online_links_diff_; | 
|  | tracker_->HandleMessage(&writable_buf[0], buf.size(), &address_changed, | 
|  | &link_changed, &tunnel_changed); | 
|  | UpdateCache(); | 
|  | EXPECT_FALSE(address_changed); | 
|  | return tunnel_changed; | 
|  | } | 
|  |  | 
|  | AddressTrackerLinux::AddressMap GetAddressMap() { | 
|  | return tracker_->GetAddressMap(); | 
|  | } | 
|  |  | 
|  | const std::unordered_set<int> GetOnlineLinks() const { | 
|  | return tracker_->GetOnlineLinks(); | 
|  | } | 
|  |  | 
|  | void IgnoreInterface(const std::string& interface_name) { | 
|  | ignored_interfaces_.insert(interface_name); | 
|  | } | 
|  |  | 
|  | int GetThreadsWaitingForConnectionTypeInit() { | 
|  | return tracker_->GetThreadsWaitingForConnectionTypeInitForTesting(); | 
|  | } | 
|  |  | 
|  | std::unordered_set<std::string> ignored_interfaces_; | 
|  | std::unique_ptr<AddressTrackerLinux> tracker_; | 
|  | AddressTrackerLinux::GetInterfaceNameFunction original_get_interface_name_; | 
|  |  | 
|  | private: | 
|  | // Checks that applying the generated diff to `address_map_cache_` results in | 
|  | // the same AddressMap and set of online links that `tracker_` maintains. | 
|  | void UpdateCache() { | 
|  | if (!tracking_) { | 
|  | return; | 
|  | } | 
|  | #if BUILDFLAG(IS_LINUX) | 
|  | address_map_cache_.ApplyDiffs(tracker_->address_map_diff_for_testing(), | 
|  | tracker_->online_links_diff_for_testing()); | 
|  | EXPECT_EQ(address_map_cache_.GetAddressMap(), tracker_->GetAddressMap()); | 
|  | EXPECT_EQ(address_map_cache_.GetOnlineLinks(), tracker_->GetOnlineLinks()); | 
|  | tracker_->address_map_diff_for_testing().clear(); | 
|  | tracker_->online_links_diff_for_testing().clear(); | 
|  | #endif  // BUILDFLAG(IS_LINUX) | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(IS_LINUX) | 
|  | AddressMapCacheLinux address_map_cache_; | 
|  | #endif | 
|  | bool tracking_; | 
|  | }; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | 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 IPAddress kEmpty; | 
|  | const IPAddress kAddr0(kAddress0); | 
|  | const IPAddress kAddr1(kAddress1); | 
|  | const IPAddress kAddr2(kAddress2); | 
|  | const IPAddress kAddr3(kAddress3); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  | MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kTestInterfaceEth, | 
|  | 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, kTestInterfaceEth, | 
|  | 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, kTestInterfaceEth, 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 IPAddress kEmpty; | 
|  | const IPAddress kAddr0(kAddress0); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  | MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kTestInterfaceEth, | 
|  | 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, kTestInterfaceEth, | 
|  | 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, kTestInterfaceEth, | 
|  | kAddr0, kEmpty, &buffer); | 
|  | MakeAddrMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kTestInterfaceEth, | 
|  | 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 IPAddress kAddr0(kAddress0); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  | MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kTestInterfaceEth, | 
|  | 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 IPAddress kEmpty; | 
|  | const IPAddress kAddr0(kAddress0); | 
|  | const IPAddress kAddr1(kAddress1); | 
|  | const IPAddress kAddr2(kAddress2); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  | MakeAddrMessage(RTM_NEWADDR, 0, AF_INET, kTestInterfaceEth, kAddr0, kEmpty, | 
|  | &buffer); | 
|  | MakeAddrMessage(RTM_NEWADDR, 0, AF_INET, kTestInterfaceEth, kAddr1, kAddr2, | 
|  | &buffer); | 
|  | EXPECT_TRUE(HandleAddressMessage(buffer)); | 
|  | AddressTrackerLinux::AddressMap map = GetAddressMap(); | 
|  | EXPECT_EQ(2u, map.size()); | 
|  |  | 
|  | buffer.clear(); | 
|  | MakeAddrMessage(RTM_DELADDR, 0, AF_INET, kTestInterfaceEth, 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, kTestInterfaceEth, 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, kTestInterfaceEth, kAddr2, kEmpty, | 
|  | &buffer); | 
|  | EXPECT_TRUE(HandleAddressMessage(buffer)); | 
|  | map = GetAddressMap(); | 
|  | EXPECT_EQ(0u, map.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(AddressTrackerLinuxTest, DeprecatedLifetime) { | 
|  | InitializeAddressTracker(true); | 
|  |  | 
|  | const IPAddress kEmpty; | 
|  | const IPAddress kAddr3(kAddress3); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  | MakeAddrMessage(RTM_NEWADDR, 0, AF_INET6, kTestInterfaceEth, 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, kTestInterfaceEth, | 
|  | 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, | 
|  | kTestInterfaceEth, 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, kTestInterfaceEth, | 
|  | 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 IPAddress kEmpty; | 
|  | const IPAddress kAddr0(kAddress0); | 
|  | const IPAddress kAddr3(kAddress3); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  | // Ignored family. | 
|  | MakeAddrMessage(RTM_NEWADDR, 0, AF_UNSPEC, kTestInterfaceEth, kAddr3, kAddr0, | 
|  | &buffer); | 
|  | // No address. | 
|  | MakeAddrMessage(RTM_NEWADDR, 0, AF_INET, kTestInterfaceEth, kEmpty, kEmpty, | 
|  | &buffer); | 
|  | // Ignored type. | 
|  | MakeAddrMessage(RTM_DELROUTE, 0, AF_INET6, kTestInterfaceEth, kAddr3, kEmpty, | 
|  | &buffer); | 
|  | EXPECT_FALSE(HandleAddressMessage(buffer)); | 
|  | EXPECT_TRUE(GetAddressMap().empty()); | 
|  |  | 
|  | // Valid message after ignored messages. | 
|  | NetlinkMessage nlmsg(RTM_NEWADDR); | 
|  | ifaddrmsg msg = {}; | 
|  | msg.ifa_family = AF_INET; | 
|  | nlmsg.AddPayload(base::byte_span_from_ref(msg)); | 
|  | // Ignored attribute. | 
|  | ifa_cacheinfo cache_info = {}; | 
|  | nlmsg.AddAttribute(IFA_CACHEINFO, base::byte_span_from_ref(cache_info)); | 
|  | nlmsg.AddAttribute(IFA_ADDRESS, kAddr0.bytes().span()); | 
|  | nlmsg.AppendTo(&buffer); | 
|  |  | 
|  | EXPECT_TRUE(HandleAddressMessage(buffer)); | 
|  | EXPECT_EQ(1u, GetAddressMap().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(AddressTrackerLinuxTest, AddInterface) { | 
|  | InitializeAddressTracker(true); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  |  | 
|  | // Ignores loopback. | 
|  | MakeLinkMessage(RTM_NEWLINK, | 
|  | IFF_LOOPBACK | IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceEth, &buffer); | 
|  | EXPECT_FALSE(HandleLinkMessage(buffer)); | 
|  | EXPECT_TRUE(GetOnlineLinks().empty()); | 
|  |  | 
|  | // Ignores not IFF_LOWER_UP. | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, kTestInterfaceEth, | 
|  | &buffer); | 
|  | EXPECT_FALSE(HandleLinkMessage(buffer)); | 
|  | EXPECT_TRUE(GetOnlineLinks().empty()); | 
|  |  | 
|  | // Ignores deletion. | 
|  | MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceEth, &buffer); | 
|  | EXPECT_FALSE(HandleLinkMessage(buffer)); | 
|  | EXPECT_TRUE(GetOnlineLinks().empty()); | 
|  |  | 
|  | // Verify success. | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceEth, &buffer); | 
|  | EXPECT_TRUE(HandleLinkMessage(buffer)); | 
|  | EXPECT_EQ(1u, GetOnlineLinks().count(kTestInterfaceEth)); | 
|  | EXPECT_EQ(1u, GetOnlineLinks().size()); | 
|  |  | 
|  | // Ignores redundant enables. | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceEth, &buffer); | 
|  | EXPECT_FALSE(HandleLinkMessage(buffer)); | 
|  | EXPECT_EQ(1u, GetOnlineLinks().count(kTestInterfaceEth)); | 
|  | EXPECT_EQ(1u, GetOnlineLinks().size()); | 
|  |  | 
|  | // Ignores messages from wireless_send_event. | 
|  | MakeWirelessLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceWifi, &buffer); | 
|  | EXPECT_FALSE(HandleLinkMessage(buffer)); | 
|  | EXPECT_EQ(0u, GetOnlineLinks().count(kTestInterfaceWifi)); | 
|  | 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, 2, &buffer); | 
|  | EXPECT_TRUE(HandleLinkMessage(buffer)); | 
|  | EXPECT_EQ(1u, GetOnlineLinks().count(kTestInterfaceEth)); | 
|  | EXPECT_EQ(1u, GetOnlineLinks().count(2)); | 
|  | EXPECT_EQ(2u, GetOnlineLinks().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(AddressTrackerLinuxTest, RemoveInterface) { | 
|  | InitializeAddressTracker(true); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  |  | 
|  | // Should disappear when not IFF_LOWER_UP. | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceEth, &buffer); | 
|  | EXPECT_TRUE(HandleLinkMessage(buffer)); | 
|  | EXPECT_FALSE(GetOnlineLinks().empty()); | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, kTestInterfaceEth, | 
|  | &buffer); | 
|  | EXPECT_TRUE(HandleLinkMessage(buffer)); | 
|  | EXPECT_TRUE(GetOnlineLinks().empty()); | 
|  |  | 
|  | // Ignores redundant disables. | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, kTestInterfaceEth, | 
|  | &buffer); | 
|  | EXPECT_FALSE(HandleLinkMessage(buffer)); | 
|  | EXPECT_TRUE(GetOnlineLinks().empty()); | 
|  |  | 
|  | // Ignores deleting down interfaces. | 
|  | MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_RUNNING, kTestInterfaceEth, | 
|  | &buffer); | 
|  | EXPECT_FALSE(HandleLinkMessage(buffer)); | 
|  | EXPECT_TRUE(GetOnlineLinks().empty()); | 
|  |  | 
|  | // Should disappear when deleted. | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceEth, &buffer); | 
|  | EXPECT_TRUE(HandleLinkMessage(buffer)); | 
|  | EXPECT_FALSE(GetOnlineLinks().empty()); | 
|  | MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceEth, &buffer); | 
|  | EXPECT_TRUE(HandleLinkMessage(buffer)); | 
|  | EXPECT_TRUE(GetOnlineLinks().empty()); | 
|  |  | 
|  | // Ignores messages from wireless_send_event. | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceWifi, &buffer); | 
|  | EXPECT_TRUE(HandleLinkMessage(buffer)); | 
|  | EXPECT_FALSE(GetOnlineLinks().empty()); | 
|  | MakeWirelessLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP, | 
|  | kTestInterfaceWifi, &buffer); | 
|  | EXPECT_FALSE(HandleLinkMessage(buffer)); | 
|  | EXPECT_FALSE(GetOnlineLinks().empty()); | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, kTestInterfaceWifi, | 
|  | &buffer); | 
|  | EXPECT_TRUE(HandleLinkMessage(buffer)); | 
|  | EXPECT_TRUE(GetOnlineLinks().empty()); | 
|  | } | 
|  |  | 
|  | TEST_F(AddressTrackerLinuxTest, IgnoreInterface) { | 
|  | IgnoreInterface(kIgnoredInterfaceName); | 
|  | InitializeAddressTracker(true); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  | const IPAddress kEmpty; | 
|  | const IPAddress kAddr0(kAddress0); | 
|  |  | 
|  | // Verify online links and address map has been not been updated | 
|  | MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kTestInterfaceAp, | 
|  | kAddr0, kEmpty, &buffer); | 
|  | EXPECT_FALSE(HandleAddressMessage(buffer)); | 
|  | AddressTrackerLinux::AddressMap map = GetAddressMap(); | 
|  | EXPECT_EQ(0u, map.size()); | 
|  | EXPECT_EQ(0u, map.count(kAddr0)); | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceAp, &buffer); | 
|  | EXPECT_FALSE(HandleLinkMessage(buffer)); | 
|  | EXPECT_EQ(0u, GetOnlineLinks().count(kTestInterfaceAp)); | 
|  | EXPECT_EQ(0u, GetOnlineLinks().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(AddressTrackerLinuxTest, IgnoreInterface_NonIgnoredInterface) { | 
|  | IgnoreInterface(kIgnoredInterfaceName); | 
|  | InitializeAddressTracker(true); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  | const IPAddress kEmpty; | 
|  | const IPAddress kAddr0(kAddress0); | 
|  |  | 
|  | // Verify eth0 is not ignored when only uap0 is ignored | 
|  | MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kTestInterfaceEth, | 
|  | kAddr0, kEmpty, &buffer); | 
|  | EXPECT_TRUE(HandleAddressMessage(buffer)); | 
|  | AddressTrackerLinux::AddressMap map = GetAddressMap(); | 
|  | EXPECT_EQ(1u, map.size()); | 
|  | EXPECT_EQ(1u, map.count(kAddr0)); | 
|  | MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, | 
|  | kTestInterfaceEth, &buffer); | 
|  | EXPECT_TRUE(HandleLinkMessage(buffer)); | 
|  | EXPECT_EQ(1u, GetOnlineLinks().count(kTestInterfaceEth)); | 
|  | EXPECT_EQ(1u, GetOnlineLinks().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(AddressTrackerLinuxTest, TunnelInterface) { | 
|  | InitializeAddressTracker(true); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  |  | 
|  | // Ignores without "tun" prefixed name. | 
|  | MakeLinkMessage(RTM_NEWLINK, | 
|  | IFF_UP | IFF_LOWER_UP | IFF_RUNNING | IFF_POINTOPOINT, | 
|  | kTestInterfaceEth, &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. | 
|  | TEST_F(AddressTrackerLinuxTest, GetInterfaceName) { | 
|  | InitializeAddressTracker(true); | 
|  |  | 
|  | for (int i = 0; i < 10; i++) { | 
|  | original_get_interface_name_(i); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(AddressTrackerLinuxTest, NonTrackingMode) { | 
|  | InitializeAddressTracker(false); | 
|  |  | 
|  | const IPAddress kEmpty; | 
|  | const IPAddress kAddr0(kAddress0); | 
|  |  | 
|  | NetlinkBuffer buffer; | 
|  | MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kTestInterfaceEth, | 
|  | 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, 1, &buffer); | 
|  | EXPECT_TRUE(HandleLinkMessage(buffer)); | 
|  | EXPECT_EQ(1u, GetOnlineLinks().count(1)); | 
|  | EXPECT_EQ(1u, GetOnlineLinks().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(AddressTrackerLinuxTest, NonTrackingModeInit) { | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | // Calling Init() on Android P+ isn't supported. | 
|  | if (base::android::android_info::sdk_int() >= | 
|  | base::android::android_info::SDK_VERSION_P) { | 
|  | return; | 
|  | } | 
|  | #endif | 
|  | AddressTrackerLinux tracker; | 
|  | tracker.Init(); | 
|  | } | 
|  |  | 
|  | class GetCurrentConnectionTypeRunner | 
|  | : public base::DelegateSimpleThread::Delegate { | 
|  | public: | 
|  | explicit GetCurrentConnectionTypeRunner(AddressTrackerLinux* tracker, | 
|  | const std::string& thread_name) | 
|  | : tracker_(tracker), | 
|  | done_(base::WaitableEvent::ResetPolicy::MANUAL, | 
|  | base::WaitableEvent::InitialState::NOT_SIGNALED), | 
|  | thread_(this, thread_name) {} | 
|  | ~GetCurrentConnectionTypeRunner() override = default; | 
|  |  | 
|  | void Run() override { | 
|  | tracker_->GetCurrentConnectionType(); | 
|  | done_.Signal(); | 
|  | } | 
|  |  | 
|  | void Start() { | 
|  | thread_.Start(); | 
|  | } | 
|  |  | 
|  | void VerifyCompletes() { | 
|  | EXPECT_TRUE(done_.TimedWait(base::Seconds(5))); | 
|  | thread_.Join(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | const raw_ptr<AddressTrackerLinux> tracker_; | 
|  | base::WaitableEvent done_; | 
|  | base::DelegateSimpleThread thread_; | 
|  | }; | 
|  |  | 
|  | TEST_F(AddressTrackerLinuxTest, BroadcastInit) { | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | // Calling Init() on Android P+ isn't supported. | 
|  | if (base::android::android_info::sdk_int() >= | 
|  | base::android::android_info::SDK_VERSION_P) { | 
|  | return; | 
|  | } | 
|  | #endif | 
|  | base::test::TaskEnvironment task_environment( | 
|  | base::test::TaskEnvironment::MainThreadType::IO); | 
|  | InitializeAddressTracker(true); | 
|  |  | 
|  | GetCurrentConnectionTypeRunner runner1(tracker_.get(), "waiter_thread_1"); | 
|  | GetCurrentConnectionTypeRunner runner2(tracker_.get(), "waiter_thread_2"); | 
|  |  | 
|  | runner1.Start(); | 
|  | runner2.Start(); | 
|  |  | 
|  | SPIN_FOR_1_SECOND_OR_UNTIL_TRUE( | 
|  | GetThreadsWaitingForConnectionTypeInit() == 2); | 
|  |  | 
|  | tracker_->Init(); | 
|  |  | 
|  | runner1.VerifyCompletes(); | 
|  | runner2.VerifyCompletes(); | 
|  | } | 
|  |  | 
|  | TEST_F(AddressTrackerLinuxTest, TunnelInterfaceName) { | 
|  | EXPECT_TRUE(AddressTrackerLinux::IsTunnelInterfaceName("tun0")); | 
|  | EXPECT_FALSE(AddressTrackerLinux::IsTunnelInterfaceName("wlan0")); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace net::test | 
|  |  | 
|  | namespace net::internal { | 
|  |  | 
|  | // This is a regression test for https://crbug.com/1224428. | 
|  | // | 
|  | // This test initializes two instances of `AddressTrackerLinux` in the same | 
|  | // process. The test will fail if the implementation reuses the value of | 
|  | // `sockaddr_nl::nl_pid`. | 
|  | // | 
|  | // Note: consumers generally should not need to create two tracking instances of | 
|  | // `AddressTrackerLinux` in the same process. | 
|  | TEST(AddressTrackerLinuxNetlinkTest, TestInitializeTwoTrackers) { | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | // Calling Init() on Android P+ isn't supported. | 
|  | if (base::android::android_info::sdk_int() >= | 
|  | base::android::android_info::SDK_VERSION_P) { | 
|  | return; | 
|  | } | 
|  | #endif | 
|  | base::test::TaskEnvironment task_env( | 
|  | base::test::TaskEnvironment::MainThreadType::IO); | 
|  | AddressTrackerLinux tracker1(base::DoNothing(), base::DoNothing(), | 
|  | base::DoNothing(), {}); | 
|  | AddressTrackerLinux tracker2(base::DoNothing(), base::DoNothing(), | 
|  | base::DoNothing(), {}); | 
|  | tracker1.Init(); | 
|  | tracker2.Init(); | 
|  | EXPECT_TRUE(tracker1.DidTrackingInitSucceedForTesting()); | 
|  | EXPECT_TRUE(tracker2.DidTrackingInitSucceedForTesting()); | 
|  | } | 
|  |  | 
|  | // These tests use `base::LaunchOptions::clone_flags` for fine-grained control | 
|  | // over the clone syscall, but the field is only defined on Linux and ChromeOS. | 
|  | // Unfortunately, this means these tests do not have coverage on Android. | 
|  | #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | 
|  | // These tests require specific flag values defined in <sched.h>. | 
|  | #if defined(CLONE_NEWUSER) && defined(CLONE_NEWPID) | 
|  |  | 
|  | namespace { | 
|  | const char* const kSwitchParentWriteFd = "addresstrackerlinux_parent_write_fd"; | 
|  | const char* const kSwitchReadFd = "addresstrackerlinux_read_fd"; | 
|  |  | 
|  | enum IPCMessage { | 
|  | // Sent from child to parent once the child has initialized its tracker. | 
|  | kChildInitializedAndWaiting, | 
|  | // Sent from child to parent when it was unable to initialize its tracker. | 
|  | kChildFailed, | 
|  | // Sent from parent to child when all children are permitted to exit. | 
|  | kChildMayExit, | 
|  | }; | 
|  |  | 
|  | base::File GetSwitchValueFile(const base::CommandLine* command_line, | 
|  | std::string_view name) { | 
|  | std::string value = command_line->GetSwitchValueASCII(name); | 
|  | int fd; | 
|  | CHECK(base::StringToInt(value, &fd)); | 
|  | return base::File(fd); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | // This is a regression test for https://crbug.com/1224428. | 
|  | // | 
|  | // This test creates multiple concurrent `AddressTrackerLinux` instances in | 
|  | // separate processes, each in their own PID namespaces. | 
|  | TEST(AddressTrackerLinuxNetlinkTest, TestInitializeTwoTrackersInPidNamespaces) { | 
|  | // This test initializes `kNumChildren` instances of `AddressTrackerLinux` in | 
|  | // tracking mode, each in their own child process running in a PID namespace. | 
|  | // The test will fail if the implementation reuses the value of | 
|  | // `sockaddr_nl::nl_pid`. | 
|  | // | 
|  | // The child processes use pipes to synchronize. Each child initializes a | 
|  | // tracker, sends a message to the parent, and waits for the parent to | 
|  | // respond, indicating that all children are done setting up. This ensures | 
|  | // that the tracker objects have overlapping lifetimes, and thus that the | 
|  | // underlying netlink sockets have overlapping lifetimes. This coexistence is | 
|  | // necessary, but not sufficient, for a `sockaddr_nl::nl_pid` value collision. | 
|  | constexpr size_t kNumChildren = 2; | 
|  |  | 
|  | base::ScopedFD parent_read_fd, parent_write_fd; | 
|  | ASSERT_TRUE(base::CreatePipe(&parent_read_fd, &parent_write_fd)); | 
|  |  | 
|  | struct Child { | 
|  | base::ScopedFD read_fd; | 
|  | base::ScopedFD write_fd; | 
|  | base::Process process; | 
|  | } children[kNumChildren]; | 
|  |  | 
|  | for (Child& child : children) { | 
|  | ASSERT_TRUE(base::CreatePipe(&child.read_fd, &child.write_fd)); | 
|  |  | 
|  | // Since the child process will wipe its address space by calling execvp, we | 
|  | // must share the file descriptors via its command line. | 
|  | base::CommandLine command_line( | 
|  | base::GetMultiProcessTestChildBaseCommandLine()); | 
|  | command_line.AppendSwitchASCII(kSwitchParentWriteFd, | 
|  | base::NumberToString(parent_write_fd.get())); | 
|  | command_line.AppendSwitchASCII(kSwitchReadFd, | 
|  | base::NumberToString(child.read_fd.get())); | 
|  |  | 
|  | base::LaunchOptions options; | 
|  | // Indicate that the child process requires these file descriptors. | 
|  | // Otherwise, they will be closed. See `base::CloseSuperfluousFds`. | 
|  | options.fds_to_remap = {{child.read_fd.get(), child.read_fd.get()}, | 
|  | {parent_write_fd.get(), parent_write_fd.get()}}; | 
|  | // Clone into a new PID namespace. Making it a new user namespace as well to | 
|  | // skirt the CAP_SYS_ADMIN requirement. | 
|  | options.clone_flags = CLONE_NEWPID | CLONE_NEWUSER; | 
|  |  | 
|  | child.process = base::SpawnMultiProcessTestChild( | 
|  | "ChildProcessInitializeTrackerForTesting", command_line, options); | 
|  | } | 
|  |  | 
|  | // Wait for all children to finish initializing their tracking | 
|  | // AddressTrackerLinuxes. | 
|  | base::File parent_reader(std::move(parent_read_fd)); | 
|  | for (const Child& child : children) { | 
|  | ASSERT_TRUE(child.process.IsValid()); | 
|  |  | 
|  | auto message = std::to_array<uint8_t>({0}); | 
|  | ASSERT_TRUE(parent_reader.ReadAtCurrentPosAndCheck(message)); | 
|  | ASSERT_EQ(message[0], kChildInitializedAndWaiting); | 
|  | } | 
|  |  | 
|  | // Tell children to exit and wait for them to exit. | 
|  | for (Child& child : children) { | 
|  | base::File child_writer(std::move(child.write_fd)); | 
|  | const uint8_t kMessage[] = {kChildMayExit}; | 
|  | ASSERT_TRUE(child_writer.WriteAtCurrentPosAndCheck(kMessage)); | 
|  |  | 
|  | int exit_code = 0; | 
|  | ASSERT_TRUE(child.process.WaitForExit(&exit_code)); | 
|  | ASSERT_EQ(exit_code, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | MULTIPROCESS_TEST_MAIN(ChildProcessInitializeTrackerForTesting) { | 
|  | base::test::TaskEnvironment task_env( | 
|  | base::test::TaskEnvironment::MainThreadType::IO); | 
|  |  | 
|  | const base::CommandLine* command_line = | 
|  | base::CommandLine::ForCurrentProcess(); | 
|  | base::File reader = GetSwitchValueFile(command_line, kSwitchReadFd); | 
|  | base::File parent_writer = | 
|  | GetSwitchValueFile(command_line, kSwitchParentWriteFd); | 
|  |  | 
|  | // Initialize an `AddressTrackerLinux` in tracking mode and ensure that it | 
|  | // created a netlink socket. | 
|  | AddressTrackerLinux tracker(base::DoNothing(), base::DoNothing(), | 
|  | base::DoNothing(), {}); | 
|  | tracker.Init(); | 
|  | if (!tracker.DidTrackingInitSucceedForTesting()) { | 
|  | const uint8_t kMessage[] = {kChildFailed}; | 
|  | parent_writer.WriteAtCurrentPosAndCheck(kMessage); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | // Signal to the parent that we have initialized the tracker. | 
|  | const uint8_t kMessage[] = {kChildInitializedAndWaiting}; | 
|  | if (!parent_writer.WriteAtCurrentPosAndCheck(kMessage)) | 
|  | return 1; | 
|  |  | 
|  | // Block until the parent says all children have initialized their trackers. | 
|  | auto message = std::to_array<uint8_t>({0}); | 
|  | if (!reader.ReadAtCurrentPosAndCheck(message) || message[0] != kChildMayExit) | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | 
|  | #endif  // defined(CLONE_NEWUSER) && defined(CLONE_NEWPID) | 
|  |  | 
|  | }  // namespace net::internal |