1 // Copyright 2012 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_linux.h"
6
7 #include <string>
8
9 #include "base/compiler_specific.h"
10 #include "base/functional/bind.h"
11 #include "base/functional/callback_helpers.h"
12 #include "base/task/sequenced_task_runner.h"
13 #include "base/task/task_traits.h"
14 #include "base/task/thread_pool.h"
15 #include "base/threading/thread.h"
16 #include "net/base/address_tracker_linux.h"
17 #include "net/dns/dns_config_service_posix.h"
18
19 namespace net {
20
21 // A collection of objects that live on blocking threads.
22 class NetworkChangeNotifierLinux::BlockingThreadObjects {
23 public:
24 explicit BlockingThreadObjects(
25 const std::unordered_set<std::string>& ignored_interfaces,
26 scoped_refptr<base::SequencedTaskRunner> blocking_thread_runner);
27 BlockingThreadObjects(const BlockingThreadObjects&) = delete;
28 BlockingThreadObjects& operator=(const BlockingThreadObjects&) = delete;
29
30 // Plumbing for NetworkChangeNotifier::GetCurrentConnectionType.
31 // Safe to call from any thread.
GetCurrentConnectionType()32 NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() {
33 return address_tracker_.GetCurrentConnectionType();
34 }
35
address_tracker()36 internal::AddressTrackerLinux* address_tracker() { return &address_tracker_; }
37
38 // Begin watching for netlink changes.
39 void Init();
40
41 void InitForTesting(base::ScopedFD netlink_fd); // IN-TEST
42
43 private:
44 void OnIPAddressChanged();
45 void OnLinkChanged();
46 // Used to detect online/offline state and IP address changes.
47 internal::AddressTrackerLinux address_tracker_;
48 NetworkChangeNotifier::ConnectionType last_type_ =
49 NetworkChangeNotifier::CONNECTION_NONE;
50 };
51
BlockingThreadObjects(const std::unordered_set<std::string> & ignored_interfaces,scoped_refptr<base::SequencedTaskRunner> blocking_thread_runner)52 NetworkChangeNotifierLinux::BlockingThreadObjects::BlockingThreadObjects(
53 const std::unordered_set<std::string>& ignored_interfaces,
54 scoped_refptr<base::SequencedTaskRunner> blocking_thread_runner)
55 : address_tracker_(
56 base::BindRepeating(&NetworkChangeNotifierLinux::
57 BlockingThreadObjects::OnIPAddressChanged,
58 base::Unretained(this)),
59 base::BindRepeating(
60 &NetworkChangeNotifierLinux::BlockingThreadObjects::OnLinkChanged,
61 base::Unretained(this)),
62 base::DoNothing(),
63 ignored_interfaces,
64 std::move(blocking_thread_runner)) {}
65
Init()66 void NetworkChangeNotifierLinux::BlockingThreadObjects::Init() {
67 address_tracker_.Init();
68 last_type_ = GetCurrentConnectionType();
69 }
70
InitForTesting(base::ScopedFD netlink_fd)71 void NetworkChangeNotifierLinux::BlockingThreadObjects::InitForTesting(
72 base::ScopedFD netlink_fd) {
73 address_tracker_.InitWithFdForTesting(std::move(netlink_fd)); // IN-TEST
74 last_type_ = GetCurrentConnectionType();
75 }
76
OnIPAddressChanged()77 void NetworkChangeNotifierLinux::BlockingThreadObjects::OnIPAddressChanged() {
78 NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
79 // When the IP address of a network interface is added/deleted, the
80 // connection type may have changed.
81 OnLinkChanged();
82 }
83
OnLinkChanged()84 void NetworkChangeNotifierLinux::BlockingThreadObjects::OnLinkChanged() {
85 if (last_type_ != GetCurrentConnectionType()) {
86 NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
87 last_type_ = GetCurrentConnectionType();
88 double max_bandwidth_mbps =
89 NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
90 last_type_ == CONNECTION_NONE ? SUBTYPE_NONE : SUBTYPE_UNKNOWN);
91 NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChange(
92 max_bandwidth_mbps, last_type_);
93 }
94 }
95
96 // static
97 std::unique_ptr<NetworkChangeNotifierLinux>
CreateWithSocketForTesting(const std::unordered_set<std::string> & ignored_interfaces,base::ScopedFD netlink_fd)98 NetworkChangeNotifierLinux::CreateWithSocketForTesting(
99 const std::unordered_set<std::string>& ignored_interfaces,
100 base::ScopedFD netlink_fd) {
101 auto ncn_linux = std::make_unique<NetworkChangeNotifierLinux>(
102 ignored_interfaces, /*initialize_blocking_thread_objects=*/false,
103 base::PassKey<NetworkChangeNotifierLinux>());
104 ncn_linux->InitBlockingThreadObjectsForTesting( // IN-TEST
105 std::move(netlink_fd));
106 return ncn_linux;
107 }
108
NetworkChangeNotifierLinux(const std::unordered_set<std::string> & ignored_interfaces)109 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(
110 const std::unordered_set<std::string>& ignored_interfaces)
111 : NetworkChangeNotifierLinux(ignored_interfaces,
112 /*initialize_blocking_thread_objects*/ true,
113 base::PassKey<NetworkChangeNotifierLinux>()) {}
114
NetworkChangeNotifierLinux(const std::unordered_set<std::string> & ignored_interfaces,bool initialize_blocking_thread_objects,base::PassKey<NetworkChangeNotifierLinux>)115 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(
116 const std::unordered_set<std::string>& ignored_interfaces,
117 bool initialize_blocking_thread_objects,
118 base::PassKey<NetworkChangeNotifierLinux>)
119 : NetworkChangeNotifier(NetworkChangeCalculatorParamsLinux()),
120 blocking_thread_runner_(
121 base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})),
122 blocking_thread_objects_(
123 new BlockingThreadObjects(ignored_interfaces,
124 blocking_thread_runner_),
125 // Ensure |blocking_thread_objects_| lives on
126 // |blocking_thread_runner_| to prevent races where
127 // NetworkChangeNotifierLinux outlives
128 // TaskEnvironment. https://crbug.com/938126
129 base::OnTaskRunnerDeleter(blocking_thread_runner_)) {
130 if (initialize_blocking_thread_objects) {
131 blocking_thread_runner_->PostTask(
132 FROM_HERE,
133 base::BindOnce(&NetworkChangeNotifierLinux::BlockingThreadObjects::Init,
134 // The Unretained pointer is safe here because it's
135 // posted before the deleter can post.
136 base::Unretained(blocking_thread_objects_.get())));
137 }
138 }
139
~NetworkChangeNotifierLinux()140 NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {
141 ClearGlobalPointer();
142 }
143
144 // static
145 NetworkChangeNotifier::NetworkChangeCalculatorParams
NetworkChangeCalculatorParamsLinux()146 NetworkChangeNotifierLinux::NetworkChangeCalculatorParamsLinux() {
147 NetworkChangeCalculatorParams params;
148 // Delay values arrived at by simple experimentation and adjusted so as to
149 // produce a single signal when switching between network connections.
150 params.ip_address_offline_delay_ = base::Milliseconds(2000);
151 params.ip_address_online_delay_ = base::Milliseconds(2000);
152 params.connection_type_offline_delay_ = base::Milliseconds(1500);
153 params.connection_type_online_delay_ = base::Milliseconds(500);
154 return params;
155 }
156
InitBlockingThreadObjectsForTesting(base::ScopedFD netlink_fd)157 void NetworkChangeNotifierLinux::InitBlockingThreadObjectsForTesting(
158 base::ScopedFD netlink_fd) {
159 DCHECK(blocking_thread_objects_);
160 blocking_thread_runner_->PostTask(
161 FROM_HERE,
162 base::BindOnce(
163 &NetworkChangeNotifierLinux::BlockingThreadObjects::InitForTesting,
164 // The Unretained pointer is safe here because it's
165 // posted before the deleter can post.
166 base::Unretained(blocking_thread_objects_.get()),
167 std::move(netlink_fd)));
168 }
169
170 NetworkChangeNotifier::ConnectionType
GetCurrentConnectionType() const171 NetworkChangeNotifierLinux::GetCurrentConnectionType() const {
172 return blocking_thread_objects_->GetCurrentConnectionType();
173 }
174
GetAddressMapOwnerInternal()175 AddressMapOwnerLinux* NetworkChangeNotifierLinux::GetAddressMapOwnerInternal() {
176 return blocking_thread_objects_->address_tracker();
177 }
178
179 } // namespace net
180