blob: e03fdabc11cdd3e45a630d8be79253e5baf31b52 [file] [log] [blame]
Avi Drissman64595482022-09-14 20:52:291// Copyright 2018 The Chromium Authors
Kevin Marshall3e89fd72018-06-05 21:29:102// 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_fuchsia.h"
6
Kevin Marshall3e89fd72018-06-05 21:29:107#include <algorithm>
Marina Cioceac05c3e32020-12-03 19:36:588#include <iterator>
Kevin Marshall3e89fd72018-06-05 21:29:109#include <string>
10#include <utility>
11#include <vector>
12
Jeremy Manson18f6ea32018-11-13 21:12:2713#include "base/fuchsia/fuchsia_logging.h"
Avi Drissman41c4a412023-01-11 22:45:3714#include "base/functional/bind.h"
Marina Cioceac05c3e32020-12-03 19:36:5815#include "base/strings/stringprintf.h"
Anton Bikineev068d2912021-05-15 20:43:5216#include "third_party/abseil-cpp/absl/types/optional.h"
Kevin Marshall3e89fd72018-06-05 21:29:1017
18namespace net {
Kevin Marshall3e89fd72018-06-05 21:29:1019
Marina Cioceac05c3e32020-12-03 19:36:5820NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(bool require_wlan)
21 : NetworkChangeNotifierFuchsia(internal::ConnectInterfacesWatcher(),
22 require_wlan) {}
Kevin Marshall3e89fd72018-06-05 21:29:1023
24NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(
Marina Cioceac05c3e32020-12-03 19:36:5825 fidl::InterfaceHandle<fuchsia::net::interfaces::Watcher> handle,
26 bool require_wlan,
Eric Orthc398f1e2019-07-09 21:54:5527 SystemDnsConfigChangeNotifier* system_dns_config_notifier)
28 : NetworkChangeNotifier(NetworkChangeCalculatorParams(),
29 system_dns_config_notifier),
Marina Cioceac05c3e32020-12-03 19:36:5830 require_wlan_(require_wlan) {
31 DCHECK(handle);
Kevin Marshall3e89fd72018-06-05 21:29:1032
Wezf1e023e2021-10-26 08:42:5833 watcher_.set_error_handler(base::LogFidlErrorAndExitProcess(
34 FROM_HERE, "fuchsia.net.interfaces.Watcher"));
Tamir Dubersteinbf0a6a1d2020-08-03 18:44:1135
Marina Cioceac05c3e32020-12-03 19:36:5836 fuchsia::net::interfaces::WatcherSyncPtr watcher = handle.BindSync();
Anton Bikineev068d2912021-05-15 20:43:5237 absl::optional<internal::ExistingInterfaceProperties> interfaces =
Marina Cioceac05c3e32020-12-03 19:36:5838 internal::GetExistingInterfaces(watcher);
Wezf9992b7e2021-01-13 17:53:1839 if (!interfaces)
Marina Cioceac05c3e32020-12-03 19:36:5840 return;
Wezf9992b7e2021-01-13 17:53:1841
Marina Cioceac05c3e32020-12-03 19:36:5842 handle = watcher.Unbind();
43 bool notify_ip_address_changed = false;
44 for (const auto& interface_entry : *interfaces) {
45 notify_ip_address_changed |=
46 CanReachExternalNetwork(interface_entry.second);
47 }
48 interface_cache_ = InterfacePropertiesMap(std::move(*interfaces));
Tamir Dubersteinbf0a6a1d2020-08-03 18:44:1149
Marina Cioceac05c3e32020-12-03 19:36:5850 UpdateConnectionType();
51 if (notify_ip_address_changed) {
52 NotifyObserversOfIPAddressChange();
53 }
Tamir Dubersteinbf0a6a1d2020-08-03 18:44:1154
55 // Bind to the dispatcher for the thread's MessagePump.
Marina Cioceac05c3e32020-12-03 19:36:5856 zx_status_t status = watcher_.Bind(std::move(handle));
Tamir Dubersteinbf0a6a1d2020-08-03 18:44:1157 ZX_CHECK(status == ZX_OK, status) << "Bind()";
Marina Cioceac05c3e32020-12-03 19:36:5858 watcher_->Watch(
59 fit::bind_member(this, &NetworkChangeNotifierFuchsia::OnInterfacesEvent));
Kevin Marshall3e89fd72018-06-05 21:29:1060}
61
62NetworkChangeNotifierFuchsia::~NetworkChangeNotifierFuchsia() {
63 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Paul Jensen61a51a12019-05-23 23:12:4964 ClearGlobalPointer();
Kevin Marshall3e89fd72018-06-05 21:29:1065}
66
67NetworkChangeNotifier::ConnectionType
68NetworkChangeNotifierFuchsia::GetCurrentConnectionType() const {
69 ConnectionType type = static_cast<ConnectionType>(
70 base::subtle::Acquire_Load(&cached_connection_type_));
71 return type;
72}
73
Marina Cioceac05c3e32020-12-03 19:36:5874void NetworkChangeNotifierFuchsia::OnInterfacesEvent(
75 fuchsia::net::interfaces::Event event) {
76 // Immediately trigger the next watch, which will happen asynchronously. If
77 // event processing encounters an error it'll close the watcher channel which
78 // will cancel any pending callbacks.
79 watcher_->Watch(
80 fit::bind_member(this, &NetworkChangeNotifierFuchsia::OnInterfacesEvent));
81
82 switch (event.Which()) {
83 case fuchsia::net::interfaces::Event::kAdded:
84 OnInterfaceAdded(std::move(event.added()));
85 break;
86 case fuchsia::net::interfaces::Event::kRemoved:
87 OnInterfaceRemoved(event.removed());
88 break;
89 case fuchsia::net::interfaces::Event::kChanged:
90 OnInterfaceChanged(std::move(event.changed()));
91 break;
92 case fuchsia::net::interfaces::Event::kExisting:
93 case fuchsia::net::interfaces::Event::kIdle:
94 OnWatcherError(base::StringPrintf(
95 "OnInterfaceEvent: unexpected event %lu.", event.Which()));
96 break;
97 case fuchsia::net::interfaces::Event::Invalid:
98 LOG(WARNING)
99 << "Invalid event received from fuchsia.net.interfaces/Watcher";
100 break;
101 }
Kevin Marshall3e89fd72018-06-05 21:29:10102}
103
Marina Cioceac05c3e32020-12-03 19:36:58104void NetworkChangeNotifierFuchsia::OnInterfaceAdded(
105 fuchsia::net::interfaces::Properties properties) {
Maksim Ivanovf478c332020-12-11 12:01:14106 uint64_t id = properties.id();
Anton Bikineev068d2912021-05-15 20:43:52107 absl::optional<internal::InterfaceProperties> cache_entry =
Marina Cioceac05c3e32020-12-03 19:36:58108 internal::InterfaceProperties::VerifyAndCreate(std::move(properties));
109 if (!cache_entry) {
110 OnWatcherError("OnInterfaceAdded: incomplete interface properties.");
111 return;
Marina Ciocea022c5b0e2020-11-17 16:58:20112 }
Marina Cioceac05c3e32020-12-03 19:36:58113 if (interface_cache_.find(id) != interface_cache_.end()) {
114 OnWatcherError(base::StringPrintf(
115 "OnInterfaceAdded: duplicate interface ID %lu.", id));
116 return;
Stephen Roeb9b97022020-11-20 03:57:23117 }
Marina Cioceac05c3e32020-12-03 19:36:58118 const bool can_reach = CanReachExternalNetwork(*cache_entry);
119 interface_cache_.emplace(id, std::move(*cache_entry));
120 UpdateConnectionType();
121 if (can_reach) {
Stephen Roeb9b97022020-11-20 03:57:23122 NotifyObserversOfIPAddressChange();
123 }
Marina Cioceac05c3e32020-12-03 19:36:58124}
Stephen Roeb9b97022020-11-20 03:57:23125
Marina Cioceac05c3e32020-12-03 19:36:58126void NetworkChangeNotifierFuchsia::OnInterfaceRemoved(uint64_t interface_id) {
127 InterfacePropertiesMap::iterator cache_entry =
128 interface_cache_.find(interface_id);
129 if (cache_entry == interface_cache_.end()) {
130 OnWatcherError(base::StringPrintf(
131 "OnInterfaceRemoved: unknown interface ID %lu.", interface_id));
132 return;
133 }
134 const bool can_reach = CanReachExternalNetwork(cache_entry->second);
135 interface_cache_.erase(cache_entry);
136 UpdateConnectionType();
137 if (can_reach) {
138 NotifyObserversOfIPAddressChange();
139 }
140}
141
142void NetworkChangeNotifierFuchsia::OnInterfaceChanged(
143 fuchsia::net::interfaces::Properties properties) {
144 if (!properties.has_id()) {
145 OnWatcherError("OnInterfaceChanged: no interface ID.");
146 return;
147 }
148 const uint64_t id = properties.id();
149 InterfacePropertiesMap::iterator cache_entry = interface_cache_.find(id);
150 if (cache_entry == interface_cache_.end()) {
151 OnWatcherError(base::StringPrintf(
152 "OnInterfaceChanged: unknown interface ID %lu.", id));
153 return;
154 }
155 const bool old_can_reach = CanReachExternalNetwork(cache_entry->second);
156 const bool has_addresses = properties.has_addresses();
157 if (!cache_entry->second.Update(std::move(properties))) {
158 OnWatcherError("OnInterfaceChanged: update failed.");
159 return;
160 }
161
162 UpdateConnectionType();
163 const bool can_reach = CanReachExternalNetwork(cache_entry->second);
164 if (has_addresses || old_can_reach != can_reach) {
165 NotifyObserversOfIPAddressChange();
166 }
167}
168
169void NetworkChangeNotifierFuchsia::OnWatcherError(
170 base::StringPiece error_message) {
171 LOG(ERROR) << error_message;
172 watcher_.Unbind();
173 ResetConnectionType();
174}
175
176void NetworkChangeNotifierFuchsia::UpdateConnectionType() {
177 ConnectionType connection_type = ConnectionType::CONNECTION_NONE;
178 for (const auto& interface : interface_cache_) {
179 if (CanReachExternalNetwork(interface.second)) {
180 connection_type = GetEffectiveConnectionType(interface.second);
181 break;
182 }
183 }
184 if (connection_type != GetCurrentConnectionType()) {
Wezec6e7f12019-05-17 23:02:09185 base::subtle::Release_Store(&cached_connection_type_, connection_type);
Haines Sy2eb17bcb2019-10-26 17:48:18186 NotifyObserversOfConnectionTypeChange();
Kevin Marshall3e89fd72018-06-05 21:29:10187 }
Marina Cioceac05c3e32020-12-03 19:36:58188}
Tamir Dubersteinbf0a6a1d2020-08-03 18:44:11189
Marina Cioceac05c3e32020-12-03 19:36:58190void NetworkChangeNotifierFuchsia::ResetConnectionType() {
191 base::subtle::Release_Store(&cached_connection_type_,
192 ConnectionType::CONNECTION_UNKNOWN);
193}
194
195NetworkChangeNotifier::ConnectionType
196NetworkChangeNotifierFuchsia::GetEffectiveConnectionType(
197 const internal::InterfaceProperties& properties) {
198 if (!properties.IsPubliclyRoutable())
199 return NetworkChangeNotifier::CONNECTION_NONE;
200
201 NetworkChangeNotifier::ConnectionType connection_type =
202 internal::ConvertConnectionType(properties.device_class());
203 if (require_wlan_ &&
204 connection_type != NetworkChangeNotifier::CONNECTION_WIFI) {
205 return NetworkChangeNotifier::CONNECTION_NONE;
Tamir Dubersteinbf0a6a1d2020-08-03 18:44:11206 }
Marina Cioceac05c3e32020-12-03 19:36:58207 return connection_type;
208}
209
210bool NetworkChangeNotifierFuchsia::CanReachExternalNetwork(
211 const internal::InterfaceProperties& properties) {
212 return GetEffectiveConnectionType(properties) !=
213 NetworkChangeNotifier::CONNECTION_NONE;
Kevin Marshall3e89fd72018-06-05 21:29:10214}
215
216} // namespace net