// Copyright 2023 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/dns/loopback_only.h" #include #include #include "base/test/bind.h" #include "base/test/task_environment.h" #include "base/threading/thread_restrictions.h" #include "net/base/mock_network_change_notifier.h" #include "net/base/network_change_notifier.h" #include "net/base/test_completion_callback.h" #include "testing/gtest/include/gtest/gtest.h" #if BUILDFLAG(IS_LINUX) #include #include #include #include "net/base/address_map_linux.h" #endif // BUILDFLAG(IS_LINUX) namespace net { #if BUILDFLAG(IS_LINUX) namespace { constexpr uint8_t kIpv4LoopbackBytes[] = {127, 0, 0, 1}; constexpr uint8_t kIpv4PrivateAddressBytes[] = {10, 0, 0, 1}; constexpr uint8_t kIpv6LoopbackBytes[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; constexpr uint8_t kIpv6AddressBytes[] = {0xFE, 0xDC, 0xBA, 0x98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; constexpr uint8_t kIpv4LinkLocalBytes[] = {169, 254, 0, 0}; constexpr uint8_t kIpv4InIpv6LinkLocalBytes[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 169, 254, 0, 0}; constexpr uint8_t kIpv6LinkLocalBytes[] = {0xFE, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; class StubAddressMapOwnerLinux : public AddressMapOwnerLinux { public: AddressMap GetAddressMap() const override { return address_map_; } std::unordered_set GetOnlineLinks() const override { return online_links_; } AddressMap& address_map() { return address_map_; } std::unordered_set& online_links() { return online_links_; } private: AddressMap address_map_; std::unordered_set online_links_; }; bool GetResultOfRunHaveOnlyLoopbackAddressesJob() { bool result = false; net::TestClosure completion; { base::ScopedDisallowBlocking disallow_blocking; RunHaveOnlyLoopbackAddressesJob( base::BindLambdaForTesting([&](bool loopback_result) { result = loopback_result; completion.closure().Run(); })); } completion.WaitForResult(); return result; } } // namespace class LoopbackOnlyTest : public ::testing::Test { public: static constexpr int kTestInterfaceEth = 1; static constexpr int kTestInterfaceLoopback = 2; static inline const net::IPAddress kIpv4Loopback{kIpv4LoopbackBytes}; static inline const net::IPAddress kIpv4PrivateAddress{ kIpv4PrivateAddressBytes}; static inline const net::IPAddress kIpv6Loopback{kIpv6LoopbackBytes}; static inline const net::IPAddress kIpv6Address{kIpv6AddressBytes}; static inline const net::IPAddress kIpv4LinkLocal{kIpv4LinkLocalBytes}; static inline const net::IPAddress kIpv4InIpv6LinkLocal{ kIpv4InIpv6LinkLocalBytes}; static inline const net::IPAddress kIpv6LinkLocal{kIpv6LinkLocalBytes}; LoopbackOnlyTest() { mock_notifier_.mock_network_change_notifier()->SetAddressMapOwnerLinux( &stub_address_map_owner_); } ~LoopbackOnlyTest() override = default; protected: base::test::TaskEnvironment task_environment_; test::ScopedMockNetworkChangeNotifier mock_notifier_; StubAddressMapOwnerLinux stub_address_map_owner_; }; TEST_F(LoopbackOnlyTest, HasOnlyLoopbackIpv4) { // Include only a loopback interface. stub_address_map_owner_.address_map() = { {kIpv4Loopback, ifaddrmsg{ .ifa_family = AF_INET, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = kTestInterfaceLoopback, }}}; // AddressTrackerLinux does not insert loopback interfaces into // `online_links`. stub_address_map_owner_.online_links() = {}; EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob()); } TEST_F(LoopbackOnlyTest, HasActiveIPv4Connection) { stub_address_map_owner_.address_map() = { {kIpv4Loopback, ifaddrmsg{.ifa_family = AF_INET, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = kTestInterfaceLoopback}}, {kIpv4PrivateAddress, ifaddrmsg{.ifa_family = AF_INET, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = kTestInterfaceEth}}}; // `online_links` includes kTestInterfaceEth so that kIpv4PrivateAddress is // the active IPv4 connection. Also, AddressTrackerLinux does not insert // loopback interfaces into `online_links`. stub_address_map_owner_.online_links() = {kTestInterfaceEth}; EXPECT_FALSE(GetResultOfRunHaveOnlyLoopbackAddressesJob()); } TEST_F(LoopbackOnlyTest, HasInactiveIPv4Connection) { stub_address_map_owner_.address_map() = { {kIpv4Loopback, ifaddrmsg{.ifa_family = AF_INET, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = kTestInterfaceLoopback}}, {kIpv4PrivateAddress, ifaddrmsg{.ifa_family = AF_INET, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = kTestInterfaceEth}}}; // `online_links` does not include kTestInterfaceEth so that // kIpv4PrivateAddress is the inactive IPv4 connection. Also, // AddressTrackerLinux does not insert loopback interfaces into // `online_links`. stub_address_map_owner_.online_links() = {}; EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob()); } TEST_F(LoopbackOnlyTest, HasOnlyLoopbackIpv6) { // Include only a loopback interface. stub_address_map_owner_.address_map() = { {kIpv6Loopback, ifaddrmsg{ .ifa_family = AF_INET6, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = kTestInterfaceLoopback, }}}; // AddressTrackerLinux does not insert loopback interfaces into // `online_links`. stub_address_map_owner_.online_links() = {}; EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob()); } TEST_F(LoopbackOnlyTest, HasActiveIPv6Connection) { stub_address_map_owner_.address_map() = { {kIpv6Loopback, ifaddrmsg{.ifa_family = AF_INET6, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = kTestInterfaceLoopback}}, {kIpv6Address, ifaddrmsg{.ifa_family = AF_INET6, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = kTestInterfaceEth}}}; // `online_links` includes kTestInterfaceEth so that kIpv6Address is the // active IPv6 connection. Also, AddressTrackerLinux does not insert loopback // interfaces into `online_links`. stub_address_map_owner_.online_links() = {kTestInterfaceEth}; EXPECT_FALSE(GetResultOfRunHaveOnlyLoopbackAddressesJob()); } TEST_F(LoopbackOnlyTest, HasInactiveIPv6Connection) { stub_address_map_owner_.address_map() = { {kIpv6Loopback, ifaddrmsg{.ifa_family = AF_INET6, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = kTestInterfaceLoopback}}, {kIpv6Address, ifaddrmsg{.ifa_family = AF_INET6, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = kTestInterfaceEth}}}; // `online_links` does not include kTestInterfaceEth so that kIpv6Address is // the inactive IPv6 connection. Also, AddressTrackerLinux does not insert // loopback interfaces into `online_links`. stub_address_map_owner_.online_links() = {}; EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob()); } TEST_F(LoopbackOnlyTest, IPv6LinkLocal) { // Include only IPv6 link-local interfaces. stub_address_map_owner_.address_map() = { {kIpv6LinkLocal, ifaddrmsg{ .ifa_family = AF_INET6, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = 3, }}}; // Mark the IPv6 link-local interface as online. stub_address_map_owner_.online_links() = {3}; EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob()); } TEST_F(LoopbackOnlyTest, ExtraOnlineLinks) { // Include only IPv6 link-local interfaces. stub_address_map_owner_.address_map() = { {kIpv6LinkLocal, ifaddrmsg{ .ifa_family = AF_INET6, .ifa_flags = IFA_F_TEMPORARY, .ifa_index = 3, }}}; // AddressTrackerLinux should not give us online links other than the ones // listed in the AddressMap. However, it's better if this code is resilient to // a mismatch if there is a bug (for example if the kernel truncates the // messages or the buffer the AddressTrackerLinux provides to the kernel is // too small). And if this code runs on a different thread from the // AddressMapOwnerLinux, AddressMap and online links are updated separately, // and so it is possible they can be inconsistent with each other. stub_address_map_owner_.online_links() = {1, 2, 3}; EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob()); } // TODO(crbug.com/1450403): Test HaveOnlyLoopbackAddressesUsingGetifaddrs(). #endif // BUILDFLAG(IS_LINUX) } // namespace net