1 // Copyright 2024 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/base/network_change_notifier_apple.h"
6
7 #include <optional>
8 #include <string>
9
10 #include "base/apple/scoped_cftyperef.h"
11 #include "base/location.h"
12 #include "base/test/bind.h"
13 #include "base/test/scoped_feature_list.h"
14 #include "base/threading/thread.h"
15 #include "net/base/features.h"
16 #include "net/base/network_change_notifier.h"
17 #include "net/base/network_config_watcher_apple.h"
18 #include "net/test/test_with_task_environment.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 namespace net {
23 namespace {
24
25 static const char kIPv4PrivateAddrString1[] = "192.168.0.1";
26 static const char kIPv4PrivateAddrString2[] = "192.168.0.2";
27
28 static const char kIPv6PublicAddrString1[] =
29 "2401:fa00:4:1000:be30:5b30:50e5:c0";
30 static const char kIPv6PublicAddrString2[] =
31 "2401:fa00:4:1000:be30:5b30:50e5:c1";
32 static const char kIPv6LinkLocalAddrString1[] = "fe80::0:1:1:1";
33 static const char kIPv6LinkLocalAddrString2[] = "fe80::0:2:2:2";
34
35 class TestIPAddressObserver : public NetworkChangeNotifier::IPAddressObserver {
36 public:
TestIPAddressObserver()37 TestIPAddressObserver() { NetworkChangeNotifier::AddIPAddressObserver(this); }
38
39 TestIPAddressObserver(const TestIPAddressObserver&) = delete;
40 TestIPAddressObserver& operator=(const TestIPAddressObserver&) = delete;
41
~TestIPAddressObserver()42 ~TestIPAddressObserver() override {
43 NetworkChangeNotifier::RemoveIPAddressObserver(this);
44 }
45
46 // Implements NetworkChangeNotifier::IPAddressObserver:
OnIPAddressChanged()47 void OnIPAddressChanged() override { ip_address_changed_ = true; }
48
ip_address_changed() const49 bool ip_address_changed() const { return ip_address_changed_; }
50
51 private:
52 bool ip_address_changed_ = false;
53 };
54
55 } // namespace
56
57 class NetworkChangeNotifierAppleTest : public WithTaskEnvironment,
58 public ::testing::TestWithParam<bool> {
59 public:
NetworkChangeNotifierAppleTest()60 NetworkChangeNotifierAppleTest() {
61 if (ReduceIPAddressChangeNotificationEnabled()) {
62 feature_list_.InitWithFeatures(
63 /*enabled_features=*/{features::kReduceIPAddressChangeNotification},
64 /*disabled_features=*/{});
65 } else {
66 feature_list_.InitWithFeatures(
67 /*enabled_features=*/{},
68 /*disabled_features=*/{features::kReduceIPAddressChangeNotification});
69 }
70 }
71 NetworkChangeNotifierAppleTest(const NetworkChangeNotifierAppleTest&) =
72 delete;
73 NetworkChangeNotifierAppleTest& operator=(
74 const NetworkChangeNotifierAppleTest&) = delete;
75 ~NetworkChangeNotifierAppleTest() override = default;
76
TearDown()77 void TearDown() override { RunUntilIdle(); }
78
79 protected:
ReduceIPAddressChangeNotificationEnabled() const80 bool ReduceIPAddressChangeNotificationEnabled() const { return GetParam(); }
81
82 std::unique_ptr<NetworkChangeNotifierApple>
CreateNetworkChangeNotifierApple()83 CreateNetworkChangeNotifierApple() {
84 auto notifier = std::make_unique<NetworkChangeNotifierApple>();
85 base::RunLoop run_loop;
86 notifier->SetCallbacksForTest(
87 run_loop.QuitClosure(),
88 base::BindRepeating(
89 [](std::optional<NetworkInterfaceList>* network_interface_list,
90 NetworkInterfaceList* list_out, int) {
91 if (!network_interface_list->has_value()) {
92 return false;
93 }
94 *list_out = **network_interface_list;
95 return true;
96 },
97 &network_interface_list_),
98 base::BindRepeating(
99 [](std::string* ipv6_primary_interface_name, SCDynamicStoreRef)
100 -> std::string { return *ipv6_primary_interface_name; },
101 &ipv6_primary_interface_name_));
102 run_loop.Run();
103 return notifier;
104 }
105
SimulateDynamicStoreCallback(NetworkChangeNotifierApple & notifier,CFStringRef entity)106 void SimulateDynamicStoreCallback(NetworkChangeNotifierApple& notifier,
107 CFStringRef entity) {
108 base::RunLoop run_loop;
109 notifier.config_watcher_->GetNotifierThreadForTest()
110 ->task_runner()
111 ->PostTask(
112 FROM_HERE, base::BindLambdaForTesting([&]() {
113 base::apple::ScopedCFTypeRef<CFMutableArrayRef> array(
114 CFArrayCreateMutable(nullptr,
115 /*capacity=*/0, &kCFTypeArrayCallBacks));
116 base::apple::ScopedCFTypeRef<CFStringRef> entry_key(
117 SCDynamicStoreKeyCreateNetworkGlobalEntity(
118 nullptr, kSCDynamicStoreDomainState, entity));
119 CFArrayAppendValue(array.get(), entry_key.get());
120 notifier.OnNetworkConfigChange(array.get());
121 run_loop.Quit();
122 }));
123 run_loop.Run();
124 }
125
126 protected:
127 std::optional<NetworkInterfaceList> network_interface_list_ =
128 NetworkInterfaceList();
129 std::string ipv6_primary_interface_name_ = "en0";
130
131 private:
132 // Allows us to allocate our own NetworkChangeNotifier for unit testing.
133 NetworkChangeNotifier::DisableForTest disable_for_test_;
134 base::test::ScopedFeatureList feature_list_;
135 };
136
137 INSTANTIATE_TEST_SUITE_P(
138 All,
139 NetworkChangeNotifierAppleTest,
140 ::testing::Values(true, false),
__anone36180530502(const testing::TestParamInfo<bool>& info) 141 [](const testing::TestParamInfo<bool>& info) {
142 return info.param ? "ReduceIPAddressChangeNotificationEnabled"
143 : "ReduceIPAddressChangeNotificationDisabled";
144 });
145
TEST_P(NetworkChangeNotifierAppleTest,NoInterfaceChange)146 TEST_P(NetworkChangeNotifierAppleTest, NoInterfaceChange) {
147 net::IPAddress ip_address;
148 EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
149 network_interface_list_->push_back(net::NetworkInterface(
150 "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
151 ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
152
153 std::unique_ptr<NetworkChangeNotifierApple> notifier =
154 CreateNetworkChangeNotifierApple();
155
156 // Simulate OnNetworkConfigChange callback without any change in
157 // NetworkInterfaceList
158 TestIPAddressObserver observer;
159 SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
160 RunUntilIdle();
161 // When kReduceIPAddressChangeNotification feature is enabled, we ignores
162 // the OnNetworkConfigChange callback without any network interface change.
163 EXPECT_EQ(observer.ip_address_changed(),
164 !ReduceIPAddressChangeNotificationEnabled());
165 }
166
TEST_P(NetworkChangeNotifierAppleTest,IPv4AddressChange)167 TEST_P(NetworkChangeNotifierAppleTest, IPv4AddressChange) {
168 net::IPAddress ip_address;
169 EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
170 network_interface_list_->push_back(net::NetworkInterface(
171 "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
172 ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
173
174 std::unique_ptr<NetworkChangeNotifierApple> notifier =
175 CreateNetworkChangeNotifierApple();
176
177 // Simulate OnNetworkConfigChange callback with IPv4 address change.
178 EXPECT_TRUE((*network_interface_list_)[0].address.AssignFromIPLiteral(
179 kIPv4PrivateAddrString2));
180 TestIPAddressObserver observer;
181 SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
182 RunUntilIdle();
183 EXPECT_TRUE(observer.ip_address_changed());
184 }
185
TEST_P(NetworkChangeNotifierAppleTest,PublicIPv6AddressChange)186 TEST_P(NetworkChangeNotifierAppleTest, PublicIPv6AddressChange) {
187 net::IPAddress ip_address;
188 EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv6PublicAddrString1));
189 network_interface_list_->push_back(net::NetworkInterface(
190 "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
191 ip_address, 64, net::IP_ADDRESS_ATTRIBUTE_NONE));
192
193 std::unique_ptr<NetworkChangeNotifierApple> notifier =
194 CreateNetworkChangeNotifierApple();
195
196 // Simulate OnNetworkConfigChange callback with a public IPv6 address change.
197 EXPECT_TRUE((*network_interface_list_)[0].address.AssignFromIPLiteral(
198 kIPv6PublicAddrString2));
199 TestIPAddressObserver observer;
200 SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv6);
201 RunUntilIdle();
202 EXPECT_TRUE(observer.ip_address_changed());
203 }
204
TEST_P(NetworkChangeNotifierAppleTest,LinkLocalIPv6AddressChangeOnPrimaryInterface)205 TEST_P(NetworkChangeNotifierAppleTest,
206 LinkLocalIPv6AddressChangeOnPrimaryInterface) {
207 net::IPAddress ip_address;
208 EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv6LinkLocalAddrString1));
209 network_interface_list_->push_back(net::NetworkInterface(
210 "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
211 ip_address, 64, net::IP_ADDRESS_ATTRIBUTE_NONE));
212
213 std::unique_ptr<NetworkChangeNotifierApple> notifier =
214 CreateNetworkChangeNotifierApple();
215
216 // Simulate OnNetworkConfigChange callback with a link local IPv6 address
217 // change on the primary interface "en0".
218 EXPECT_TRUE((*network_interface_list_)[0].address.AssignFromIPLiteral(
219 kIPv6LinkLocalAddrString2));
220 TestIPAddressObserver observer;
221 SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
222 RunUntilIdle();
223 EXPECT_TRUE(observer.ip_address_changed());
224 }
225
TEST_P(NetworkChangeNotifierAppleTest,LinkLocalIPv6AddressChangeOnNonPrimaryInterface)226 TEST_P(NetworkChangeNotifierAppleTest,
227 LinkLocalIPv6AddressChangeOnNonPrimaryInterface) {
228 net::IPAddress ip_address1;
229 EXPECT_TRUE(ip_address1.AssignFromIPLiteral(kIPv4PrivateAddrString1));
230 network_interface_list_->push_back(net::NetworkInterface(
231 "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
232 ip_address1, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
233
234 net::IPAddress ip_address2;
235 EXPECT_TRUE(ip_address2.AssignFromIPLiteral(kIPv6LinkLocalAddrString1));
236 network_interface_list_->push_back(net::NetworkInterface(
237 "en1", "en1", 2, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
238 ip_address2, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
239
240 std::unique_ptr<NetworkChangeNotifierApple> notifier =
241 CreateNetworkChangeNotifierApple();
242
243 // Simulate OnNetworkConfigChange callback with a link local IPv6 address
244 // change on the non-primary interface "en1".
245 EXPECT_TRUE((*network_interface_list_)[1].address.AssignFromIPLiteral(
246 kIPv6LinkLocalAddrString2));
247 TestIPAddressObserver observer;
248 SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
249 RunUntilIdle();
250 // When kReduceIPAddressChangeNotification feature is enabled, we ignores
251 // the link local IPv6 address change on the non-primary interface.
252 EXPECT_EQ(observer.ip_address_changed(),
253 !ReduceIPAddressChangeNotificationEnabled());
254 }
255
TEST_P(NetworkChangeNotifierAppleTest,NewInterfaceWithIpV4)256 TEST_P(NetworkChangeNotifierAppleTest, NewInterfaceWithIpV4) {
257 net::IPAddress ip_address;
258 EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
259 network_interface_list_->push_back(net::NetworkInterface(
260 "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
261 ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
262
263 std::unique_ptr<NetworkChangeNotifierApple> notifier =
264 CreateNetworkChangeNotifierApple();
265
266 // Simulate OnNetworkConfigChange callback with a new interface with a IPv4
267 // address.
268 net::IPAddress ip_address2;
269 EXPECT_TRUE(ip_address2.AssignFromIPLiteral(kIPv4PrivateAddrString2));
270 network_interface_list_->push_back(net::NetworkInterface(
271 "en1", "en1", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
272 ip_address2, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
273
274 TestIPAddressObserver observer;
275 SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
276 RunUntilIdle();
277 EXPECT_TRUE(observer.ip_address_changed());
278 }
279
TEST_P(NetworkChangeNotifierAppleTest,NewInterfaceWithLinkLocalIpV6)280 TEST_P(NetworkChangeNotifierAppleTest, NewInterfaceWithLinkLocalIpV6) {
281 net::IPAddress ip_address;
282 EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
283 network_interface_list_->push_back(net::NetworkInterface(
284 "en0", "en0", 2, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
285 ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
286
287 std::unique_ptr<NetworkChangeNotifierApple> notifier =
288 CreateNetworkChangeNotifierApple();
289
290 // Simulate OnNetworkConfigChange callback with a new interface with a link
291 // local IPv6 address.
292 net::IPAddress ip_address2;
293 EXPECT_TRUE(ip_address2.AssignFromIPLiteral(kIPv6LinkLocalAddrString1));
294 EXPECT_FALSE(ip_address2.IsPubliclyRoutable());
295 network_interface_list_->push_back(net::NetworkInterface(
296 "en1", "en1", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
297 ip_address2, 64, net::IP_ADDRESS_ATTRIBUTE_NONE));
298
299 TestIPAddressObserver observer;
300 SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
301 RunUntilIdle();
302 // When kReduceIPAddressChangeNotification feature is enabled, we ignores
303 // the new link local IPv6 interface.
304 EXPECT_EQ(observer.ip_address_changed(),
305 !ReduceIPAddressChangeNotificationEnabled());
306 }
307
TEST_P(NetworkChangeNotifierAppleTest,NewInterfaceWithPublicIpV6)308 TEST_P(NetworkChangeNotifierAppleTest, NewInterfaceWithPublicIpV6) {
309 net::IPAddress ip_address;
310 EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
311 network_interface_list_->push_back(net::NetworkInterface(
312 "en0", "en0", 2, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
313 ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
314
315 std::unique_ptr<NetworkChangeNotifierApple> notifier =
316 CreateNetworkChangeNotifierApple();
317
318 // Simulate OnNetworkConfigChange callback with a new interface with a
319 // public IPv6 address.
320 net::IPAddress ip_address2;
321 EXPECT_TRUE(ip_address2.AssignFromIPLiteral(kIPv6PublicAddrString1));
322 EXPECT_TRUE(ip_address2.IsPubliclyRoutable());
323 network_interface_list_->push_back(net::NetworkInterface(
324 "en1", "en1", 2, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
325 ip_address2, 64, net::IP_ADDRESS_ATTRIBUTE_NONE));
326
327 TestIPAddressObserver observer;
328 SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
329 RunUntilIdle();
330 EXPECT_TRUE(observer.ip_address_changed());
331 }
332
333 } // namespace net
334