• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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* ipv4_primary_interface_name, SCDynamicStoreRef)
100                 -> std::string { return *ipv4_primary_interface_name; },
101             &ipv4_primary_interface_name_),
102         base::BindRepeating(
103             [](std::string* ipv6_primary_interface_name, SCDynamicStoreRef)
104                 -> std::string { return *ipv6_primary_interface_name; },
105             &ipv6_primary_interface_name_));
106     run_loop.Run();
107     return notifier;
108   }
109 
SimulateDynamicStoreCallback(NetworkChangeNotifierApple & notifier,CFStringRef entity)110   void SimulateDynamicStoreCallback(NetworkChangeNotifierApple& notifier,
111                                     CFStringRef entity) {
112     base::RunLoop run_loop;
113     notifier.config_watcher_->GetNotifierThreadForTest()
114         ->task_runner()
115         ->PostTask(
116             FROM_HERE, base::BindLambdaForTesting([&]() {
117               base::apple::ScopedCFTypeRef<CFMutableArrayRef> array(
118                   CFArrayCreateMutable(nullptr,
119                                        /*capacity=*/0, &kCFTypeArrayCallBacks));
120               base::apple::ScopedCFTypeRef<CFStringRef> entry_key(
121                   SCDynamicStoreKeyCreateNetworkGlobalEntity(
122                       nullptr, kSCDynamicStoreDomainState, entity));
123               CFArrayAppendValue(array.get(), entry_key.get());
124               notifier.OnNetworkConfigChange(array.get());
125               run_loop.Quit();
126             }));
127     run_loop.Run();
128   }
129 
130  protected:
131   std::optional<NetworkInterfaceList> network_interface_list_ =
132       NetworkInterfaceList();
133   std::string ipv4_primary_interface_name_ = "en0";
134   std::string ipv6_primary_interface_name_ = "en0";
135 
136  private:
137   // Allows us to allocate our own NetworkChangeNotifier for unit testing.
138   NetworkChangeNotifier::DisableForTest disable_for_test_;
139   base::test::ScopedFeatureList feature_list_;
140 };
141 
142 INSTANTIATE_TEST_SUITE_P(
143     All,
144     NetworkChangeNotifierAppleTest,
145     ::testing::Values(true, false),
__anoncef054190602(const testing::TestParamInfo<bool>& info) 146     [](const testing::TestParamInfo<bool>& info) {
147       return info.param ? "ReduceIPAddressChangeNotificationEnabled"
148                         : "ReduceIPAddressChangeNotificationDisabled";
149     });
150 
TEST_P(NetworkChangeNotifierAppleTest,NoInterfaceChange)151 TEST_P(NetworkChangeNotifierAppleTest, NoInterfaceChange) {
152   net::IPAddress ip_address;
153   EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
154   network_interface_list_->push_back(net::NetworkInterface(
155       "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
156       ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
157 
158   std::unique_ptr<NetworkChangeNotifierApple> notifier =
159       CreateNetworkChangeNotifierApple();
160 
161   // Simulate OnNetworkConfigChange callback without any change in
162   // NetworkInterfaceList
163   TestIPAddressObserver observer;
164   SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
165   RunUntilIdle();
166   // When kReduceIPAddressChangeNotification feature is enabled, we ignores
167   // the OnNetworkConfigChange callback without any network interface change.
168   EXPECT_EQ(observer.ip_address_changed(),
169             !ReduceIPAddressChangeNotificationEnabled());
170 }
171 
TEST_P(NetworkChangeNotifierAppleTest,IPv4AddressChange)172 TEST_P(NetworkChangeNotifierAppleTest, IPv4AddressChange) {
173   net::IPAddress ip_address;
174   EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
175   network_interface_list_->push_back(net::NetworkInterface(
176       "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
177       ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
178 
179   std::unique_ptr<NetworkChangeNotifierApple> notifier =
180       CreateNetworkChangeNotifierApple();
181 
182   // Simulate OnNetworkConfigChange callback with IPv4 address change.
183   EXPECT_TRUE((*network_interface_list_)[0].address.AssignFromIPLiteral(
184       kIPv4PrivateAddrString2));
185   TestIPAddressObserver observer;
186   SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
187   RunUntilIdle();
188   EXPECT_TRUE(observer.ip_address_changed());
189 }
190 
TEST_P(NetworkChangeNotifierAppleTest,PublicIPv6AddressChange)191 TEST_P(NetworkChangeNotifierAppleTest, PublicIPv6AddressChange) {
192   net::IPAddress ip_address;
193   EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv6PublicAddrString1));
194   network_interface_list_->push_back(net::NetworkInterface(
195       "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
196       ip_address, 64, net::IP_ADDRESS_ATTRIBUTE_NONE));
197 
198   std::unique_ptr<NetworkChangeNotifierApple> notifier =
199       CreateNetworkChangeNotifierApple();
200 
201   // Simulate OnNetworkConfigChange callback with a public IPv6 address change.
202   EXPECT_TRUE((*network_interface_list_)[0].address.AssignFromIPLiteral(
203       kIPv6PublicAddrString2));
204   TestIPAddressObserver observer;
205   SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv6);
206   RunUntilIdle();
207   EXPECT_TRUE(observer.ip_address_changed());
208 }
209 
TEST_P(NetworkChangeNotifierAppleTest,LinkLocalIPv6AddressChangeOnPrimaryInterface)210 TEST_P(NetworkChangeNotifierAppleTest,
211        LinkLocalIPv6AddressChangeOnPrimaryInterface) {
212   net::IPAddress ip_address;
213   EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv6LinkLocalAddrString1));
214   network_interface_list_->push_back(net::NetworkInterface(
215       "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
216       ip_address, 64, net::IP_ADDRESS_ATTRIBUTE_NONE));
217 
218   std::unique_ptr<NetworkChangeNotifierApple> notifier =
219       CreateNetworkChangeNotifierApple();
220 
221   // Simulate OnNetworkConfigChange callback with a link local IPv6 address
222   // change on the primary interface "en0".
223   EXPECT_TRUE((*network_interface_list_)[0].address.AssignFromIPLiteral(
224       kIPv6LinkLocalAddrString2));
225   TestIPAddressObserver observer;
226   SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
227   RunUntilIdle();
228   EXPECT_TRUE(observer.ip_address_changed());
229 }
230 
TEST_P(NetworkChangeNotifierAppleTest,LinkLocalIPv6AddressChangeOnNonPrimaryInterface)231 TEST_P(NetworkChangeNotifierAppleTest,
232        LinkLocalIPv6AddressChangeOnNonPrimaryInterface) {
233   net::IPAddress ip_address1;
234   EXPECT_TRUE(ip_address1.AssignFromIPLiteral(kIPv4PrivateAddrString1));
235   network_interface_list_->push_back(net::NetworkInterface(
236       "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
237       ip_address1, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
238 
239   net::IPAddress ip_address2;
240   EXPECT_TRUE(ip_address2.AssignFromIPLiteral(kIPv6LinkLocalAddrString1));
241   network_interface_list_->push_back(net::NetworkInterface(
242       "en1", "en1", 2, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
243       ip_address2, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
244 
245   std::unique_ptr<NetworkChangeNotifierApple> notifier =
246       CreateNetworkChangeNotifierApple();
247 
248   // Simulate OnNetworkConfigChange callback with a link local IPv6 address
249   // change on the non-primary interface "en1".
250   EXPECT_TRUE((*network_interface_list_)[1].address.AssignFromIPLiteral(
251       kIPv6LinkLocalAddrString2));
252   TestIPAddressObserver observer;
253   SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
254   RunUntilIdle();
255   // When kReduceIPAddressChangeNotification feature is enabled, we ignores
256   // the link local IPv6 address change on the non-primary interface.
257   EXPECT_EQ(observer.ip_address_changed(),
258             !ReduceIPAddressChangeNotificationEnabled());
259 }
260 
TEST_P(NetworkChangeNotifierAppleTest,NewInterfaceWithIpV4)261 TEST_P(NetworkChangeNotifierAppleTest, NewInterfaceWithIpV4) {
262   net::IPAddress ip_address;
263   EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
264   network_interface_list_->push_back(net::NetworkInterface(
265       "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
266       ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
267 
268   std::unique_ptr<NetworkChangeNotifierApple> notifier =
269       CreateNetworkChangeNotifierApple();
270 
271   // Simulate OnNetworkConfigChange callback with a new interface with a IPv4
272   // address.
273   net::IPAddress ip_address2;
274   EXPECT_TRUE(ip_address2.AssignFromIPLiteral(kIPv4PrivateAddrString2));
275   network_interface_list_->push_back(net::NetworkInterface(
276       "en1", "en1", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
277       ip_address2, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
278 
279   TestIPAddressObserver observer;
280   SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
281   RunUntilIdle();
282   EXPECT_TRUE(observer.ip_address_changed());
283 }
284 
TEST_P(NetworkChangeNotifierAppleTest,NewInterfaceWithLinkLocalIpV6)285 TEST_P(NetworkChangeNotifierAppleTest, NewInterfaceWithLinkLocalIpV6) {
286   net::IPAddress ip_address;
287   EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
288   network_interface_list_->push_back(net::NetworkInterface(
289       "en0", "en0", 2, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
290       ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
291 
292   std::unique_ptr<NetworkChangeNotifierApple> notifier =
293       CreateNetworkChangeNotifierApple();
294 
295   // Simulate OnNetworkConfigChange callback with a new interface with a link
296   // local IPv6 address.
297   net::IPAddress ip_address2;
298   EXPECT_TRUE(ip_address2.AssignFromIPLiteral(kIPv6LinkLocalAddrString1));
299   EXPECT_FALSE(ip_address2.IsPubliclyRoutable());
300   network_interface_list_->push_back(net::NetworkInterface(
301       "en1", "en1", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
302       ip_address2, 64, net::IP_ADDRESS_ATTRIBUTE_NONE));
303 
304   TestIPAddressObserver observer;
305   SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
306   RunUntilIdle();
307   // When kReduceIPAddressChangeNotification feature is enabled, we ignores
308   // the new link local IPv6 interface.
309   EXPECT_EQ(observer.ip_address_changed(),
310             !ReduceIPAddressChangeNotificationEnabled());
311 }
312 
TEST_P(NetworkChangeNotifierAppleTest,NewInterfaceWithPublicIpV6)313 TEST_P(NetworkChangeNotifierAppleTest, NewInterfaceWithPublicIpV6) {
314   net::IPAddress ip_address;
315   EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
316   network_interface_list_->push_back(net::NetworkInterface(
317       "en0", "en0", 2, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
318       ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
319 
320   std::unique_ptr<NetworkChangeNotifierApple> notifier =
321       CreateNetworkChangeNotifierApple();
322 
323   // Simulate OnNetworkConfigChange callback with a new interface with a
324   // public IPv6 address.
325   net::IPAddress ip_address2;
326   EXPECT_TRUE(ip_address2.AssignFromIPLiteral(kIPv6PublicAddrString1));
327   EXPECT_TRUE(ip_address2.IsPubliclyRoutable());
328   network_interface_list_->push_back(net::NetworkInterface(
329       "en1", "en1", 2, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
330       ip_address2, 64, net::IP_ADDRESS_ATTRIBUTE_NONE));
331 
332   TestIPAddressObserver observer;
333   SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
334   RunUntilIdle();
335   EXPECT_TRUE(observer.ip_address_changed());
336 }
337 
TEST_P(NetworkChangeNotifierAppleTest,IPv4PrimaryInterfaceChange)338 TEST_P(NetworkChangeNotifierAppleTest, IPv4PrimaryInterfaceChange) {
339   net::IPAddress ip_address;
340   EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv4PrivateAddrString1));
341   network_interface_list_->push_back(net::NetworkInterface(
342       "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
343       ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
344   net::IPAddress ip_address2;
345   EXPECT_TRUE(ip_address2.AssignFromIPLiteral(kIPv4PrivateAddrString2));
346   network_interface_list_->push_back(net::NetworkInterface(
347       "en1", "en1", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
348       ip_address2, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
349 
350   std::unique_ptr<NetworkChangeNotifierApple> notifier =
351       CreateNetworkChangeNotifierApple();
352 
353   // Simulate OnNetworkConfigChange callback for the IPv4 primary interface
354   // change.
355   TestIPAddressObserver observer;
356   ipv4_primary_interface_name_ = "en1";
357   SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv4);
358   RunUntilIdle();
359   EXPECT_TRUE(observer.ip_address_changed());
360 }
361 
TEST_P(NetworkChangeNotifierAppleTest,IPv6PrimaryInterfaceChange)362 TEST_P(NetworkChangeNotifierAppleTest, IPv6PrimaryInterfaceChange) {
363   net::IPAddress ip_address;
364   EXPECT_TRUE(ip_address.AssignFromIPLiteral(kIPv6PublicAddrString1));
365   network_interface_list_->push_back(net::NetworkInterface(
366       "en0", "en0", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
367       ip_address, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
368   net::IPAddress ip_address2;
369   EXPECT_TRUE(ip_address2.AssignFromIPLiteral(kIPv6PublicAddrString2));
370   network_interface_list_->push_back(net::NetworkInterface(
371       "en1", "en1", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
372       ip_address2, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
373 
374   std::unique_ptr<NetworkChangeNotifierApple> notifier =
375       CreateNetworkChangeNotifierApple();
376 
377   // Simulate OnNetworkConfigChange callback for the IPv6 primary interface
378   // change.
379   TestIPAddressObserver observer;
380   ipv6_primary_interface_name_ = "en1";
381   SimulateDynamicStoreCallback(*notifier, kSCEntNetIPv6);
382   RunUntilIdle();
383   EXPECT_TRUE(observer.ip_address_changed());
384 }
385 
386 }  // namespace net
387