1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/dns/loopback_only.h"
6
7 #include <optional>
8 #include <unordered_set>
9
10 #include "base/test/bind.h"
11 #include "base/test/task_environment.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "net/base/mock_network_change_notifier.h"
14 #include "net/base/network_change_notifier.h"
15 #include "net/base/test_completion_callback.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 #if BUILDFLAG(IS_LINUX)
19 #include <linux/if.h>
20 #include <linux/netlink.h>
21 #include <linux/rtnetlink.h>
22
23 #include "net/base/address_map_linux.h"
24 #endif // BUILDFLAG(IS_LINUX)
25
26 namespace net {
27
28 #if BUILDFLAG(IS_LINUX)
29
30 namespace {
31
32 constexpr uint8_t kIpv4LoopbackBytes[] = {127, 0, 0, 1};
33 constexpr uint8_t kIpv4PrivateAddressBytes[] = {10, 0, 0, 1};
34 constexpr uint8_t kIpv6LoopbackBytes[] = {0, 0, 0, 0, 0, 0, 0, 0,
35 0, 0, 0, 0, 0, 0, 0, 1};
36 constexpr uint8_t kIpv6AddressBytes[] = {0xFE, 0xDC, 0xBA, 0x98, 0, 0, 0, 0,
37 0, 0, 0, 0, 0, 0, 0, 0};
38
39 constexpr uint8_t kIpv4LinkLocalBytes[] = {169, 254, 0, 0};
40 constexpr uint8_t kIpv4InIpv6LinkLocalBytes[] = {
41 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 169, 254, 0, 0};
42 constexpr uint8_t kIpv6LinkLocalBytes[] = {0xFE, 0x80, 0, 0, 0, 0, 0, 0,
43 0, 0, 0, 0, 0, 0, 0, 0};
44
45 class StubAddressMapOwnerLinux : public AddressMapOwnerLinux {
46 public:
GetAddressMap() const47 AddressMap GetAddressMap() const override { return address_map_; }
GetOnlineLinks() const48 std::unordered_set<int> GetOnlineLinks() const override {
49 return online_links_;
50 }
51
address_map()52 AddressMap& address_map() { return address_map_; }
online_links()53 std::unordered_set<int>& online_links() { return online_links_; }
54
55 private:
56 AddressMap address_map_;
57 std::unordered_set<int> online_links_;
58 };
59
GetResultOfRunHaveOnlyLoopbackAddressesJob()60 bool GetResultOfRunHaveOnlyLoopbackAddressesJob() {
61 bool result = false;
62 net::TestClosure completion;
63 {
64 base::ScopedDisallowBlocking disallow_blocking;
65 RunHaveOnlyLoopbackAddressesJob(
66 base::BindLambdaForTesting([&](bool loopback_result) {
67 result = loopback_result;
68 completion.closure().Run();
69 }));
70 }
71 completion.WaitForResult();
72 return result;
73 }
74
75 } // namespace
76
77 class LoopbackOnlyTest : public ::testing::Test {
78 public:
79 static constexpr int kTestInterfaceEth = 1;
80 static constexpr int kTestInterfaceLoopback = 2;
81
82 static inline const net::IPAddress kIpv4Loopback{kIpv4LoopbackBytes};
83 static inline const net::IPAddress kIpv4PrivateAddress{
84 kIpv4PrivateAddressBytes};
85
86 static inline const net::IPAddress kIpv6Loopback{kIpv6LoopbackBytes};
87 static inline const net::IPAddress kIpv6Address{kIpv6AddressBytes};
88
89 static inline const net::IPAddress kIpv4LinkLocal{kIpv4LinkLocalBytes};
90 static inline const net::IPAddress kIpv4InIpv6LinkLocal{
91 kIpv4InIpv6LinkLocalBytes};
92 static inline const net::IPAddress kIpv6LinkLocal{kIpv6LinkLocalBytes};
93
LoopbackOnlyTest()94 LoopbackOnlyTest() {
95 mock_notifier_.mock_network_change_notifier()->SetAddressMapOwnerLinux(
96 &stub_address_map_owner_);
97 }
98 ~LoopbackOnlyTest() override = default;
99
100 protected:
101 base::test::TaskEnvironment task_environment_;
102 test::ScopedMockNetworkChangeNotifier mock_notifier_;
103 StubAddressMapOwnerLinux stub_address_map_owner_;
104 };
105
TEST_F(LoopbackOnlyTest,HasOnlyLoopbackIpv4)106 TEST_F(LoopbackOnlyTest, HasOnlyLoopbackIpv4) {
107 // Include only a loopback interface.
108 stub_address_map_owner_.address_map() = {
109 {kIpv4Loopback, ifaddrmsg{
110 .ifa_family = AF_INET,
111 .ifa_flags = IFA_F_TEMPORARY,
112 .ifa_index = kTestInterfaceLoopback,
113 }}};
114 // AddressTrackerLinux does not insert loopback interfaces into
115 // `online_links`.
116 stub_address_map_owner_.online_links() = {};
117
118 EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob());
119 }
120
TEST_F(LoopbackOnlyTest,HasActiveIPv4Connection)121 TEST_F(LoopbackOnlyTest, HasActiveIPv4Connection) {
122 stub_address_map_owner_.address_map() = {
123 {kIpv4Loopback, ifaddrmsg{.ifa_family = AF_INET,
124 .ifa_flags = IFA_F_TEMPORARY,
125 .ifa_index = kTestInterfaceLoopback}},
126 {kIpv4PrivateAddress, ifaddrmsg{.ifa_family = AF_INET,
127 .ifa_flags = IFA_F_TEMPORARY,
128 .ifa_index = kTestInterfaceEth}}};
129 // `online_links` includes kTestInterfaceEth so that kIpv4PrivateAddress is
130 // the active IPv4 connection. Also, AddressTrackerLinux does not insert
131 // loopback interfaces into `online_links`.
132 stub_address_map_owner_.online_links() = {kTestInterfaceEth};
133
134 EXPECT_FALSE(GetResultOfRunHaveOnlyLoopbackAddressesJob());
135 }
136
TEST_F(LoopbackOnlyTest,HasInactiveIPv4Connection)137 TEST_F(LoopbackOnlyTest, HasInactiveIPv4Connection) {
138 stub_address_map_owner_.address_map() = {
139 {kIpv4Loopback, ifaddrmsg{.ifa_family = AF_INET,
140 .ifa_flags = IFA_F_TEMPORARY,
141 .ifa_index = kTestInterfaceLoopback}},
142 {kIpv4PrivateAddress, ifaddrmsg{.ifa_family = AF_INET,
143 .ifa_flags = IFA_F_TEMPORARY,
144 .ifa_index = kTestInterfaceEth}}};
145 // `online_links` does not include kTestInterfaceEth so that
146 // kIpv4PrivateAddress is the inactive IPv4 connection. Also,
147 // AddressTrackerLinux does not insert loopback interfaces into
148 // `online_links`.
149 stub_address_map_owner_.online_links() = {};
150
151 EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob());
152 }
153
TEST_F(LoopbackOnlyTest,HasOnlyLoopbackIpv6)154 TEST_F(LoopbackOnlyTest, HasOnlyLoopbackIpv6) {
155 // Include only a loopback interface.
156 stub_address_map_owner_.address_map() = {
157 {kIpv6Loopback, ifaddrmsg{
158 .ifa_family = AF_INET6,
159 .ifa_flags = IFA_F_TEMPORARY,
160 .ifa_index = kTestInterfaceLoopback,
161 }}};
162 // AddressTrackerLinux does not insert loopback interfaces into
163 // `online_links`.
164 stub_address_map_owner_.online_links() = {};
165
166 EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob());
167 }
168
TEST_F(LoopbackOnlyTest,HasActiveIPv6Connection)169 TEST_F(LoopbackOnlyTest, HasActiveIPv6Connection) {
170 stub_address_map_owner_.address_map() = {
171 {kIpv6Loopback, ifaddrmsg{.ifa_family = AF_INET6,
172 .ifa_flags = IFA_F_TEMPORARY,
173 .ifa_index = kTestInterfaceLoopback}},
174 {kIpv6Address, ifaddrmsg{.ifa_family = AF_INET6,
175 .ifa_flags = IFA_F_TEMPORARY,
176 .ifa_index = kTestInterfaceEth}}};
177 // `online_links` includes kTestInterfaceEth so that kIpv6Address is the
178 // active IPv6 connection. Also, AddressTrackerLinux does not insert loopback
179 // interfaces into `online_links`.
180 stub_address_map_owner_.online_links() = {kTestInterfaceEth};
181
182 EXPECT_FALSE(GetResultOfRunHaveOnlyLoopbackAddressesJob());
183 }
184
TEST_F(LoopbackOnlyTest,HasInactiveIPv6Connection)185 TEST_F(LoopbackOnlyTest, HasInactiveIPv6Connection) {
186 stub_address_map_owner_.address_map() = {
187 {kIpv6Loopback, ifaddrmsg{.ifa_family = AF_INET6,
188 .ifa_flags = IFA_F_TEMPORARY,
189 .ifa_index = kTestInterfaceLoopback}},
190 {kIpv6Address, ifaddrmsg{.ifa_family = AF_INET6,
191 .ifa_flags = IFA_F_TEMPORARY,
192 .ifa_index = kTestInterfaceEth}}};
193 // `online_links` does not include kTestInterfaceEth so that kIpv6Address is
194 // the inactive IPv6 connection. Also, AddressTrackerLinux does not insert
195 // loopback interfaces into `online_links`.
196 stub_address_map_owner_.online_links() = {};
197
198 EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob());
199 }
200
TEST_F(LoopbackOnlyTest,IPv6LinkLocal)201 TEST_F(LoopbackOnlyTest, IPv6LinkLocal) {
202 // Include only IPv6 link-local interfaces.
203 stub_address_map_owner_.address_map() = {
204 {kIpv6LinkLocal, ifaddrmsg{
205 .ifa_family = AF_INET6,
206 .ifa_flags = IFA_F_TEMPORARY,
207 .ifa_index = 3,
208 }}};
209 // Mark the IPv6 link-local interface as online.
210 stub_address_map_owner_.online_links() = {3};
211
212 EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob());
213 }
214
TEST_F(LoopbackOnlyTest,ExtraOnlineLinks)215 TEST_F(LoopbackOnlyTest, ExtraOnlineLinks) {
216 // Include only IPv6 link-local interfaces.
217 stub_address_map_owner_.address_map() = {
218 {kIpv6LinkLocal, ifaddrmsg{
219 .ifa_family = AF_INET6,
220 .ifa_flags = IFA_F_TEMPORARY,
221 .ifa_index = 3,
222 }}};
223 // AddressTrackerLinux should not give us online links other than the ones
224 // listed in the AddressMap. However, it's better if this code is resilient to
225 // a mismatch if there is a bug (for example if the kernel truncates the
226 // messages or the buffer the AddressTrackerLinux provides to the kernel is
227 // too small). And if this code runs on a different thread from the
228 // AddressMapOwnerLinux, AddressMap and online links are updated separately,
229 // and so it is possible they can be inconsistent with each other.
230 stub_address_map_owner_.online_links() = {1, 2, 3};
231
232 EXPECT_TRUE(GetResultOfRunHaveOnlyLoopbackAddressesJob());
233 }
234
235 // TODO(crbug.com/1450403): Test HaveOnlyLoopbackAddressesUsingGetifaddrs().
236
237 #endif // BUILDFLAG(IS_LINUX)
238
239 } // namespace net
240