network_change_notifier_fuchsia.cc 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/base/network_change_notifier_fuchsia.h"
  5. #include <algorithm>
  6. #include <iterator>
  7. #include <string>
  8. #include <utility>
  9. #include <vector>
  10. #include "base/bind.h"
  11. #include "base/fuchsia/fuchsia_logging.h"
  12. #include "base/strings/stringprintf.h"
  13. #include "third_party/abseil-cpp/absl/types/optional.h"
  14. namespace net {
  15. NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(bool require_wlan)
  16. : NetworkChangeNotifierFuchsia(internal::ConnectInterfacesWatcher(),
  17. require_wlan) {}
  18. NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(
  19. fidl::InterfaceHandle<fuchsia::net::interfaces::Watcher> handle,
  20. bool require_wlan,
  21. SystemDnsConfigChangeNotifier* system_dns_config_notifier)
  22. : NetworkChangeNotifier(NetworkChangeCalculatorParams(),
  23. system_dns_config_notifier),
  24. require_wlan_(require_wlan) {
  25. DCHECK(handle);
  26. watcher_.set_error_handler(base::LogFidlErrorAndExitProcess(
  27. FROM_HERE, "fuchsia.net.interfaces.Watcher"));
  28. fuchsia::net::interfaces::WatcherSyncPtr watcher = handle.BindSync();
  29. absl::optional<internal::ExistingInterfaceProperties> interfaces =
  30. internal::GetExistingInterfaces(watcher);
  31. if (!interfaces)
  32. return;
  33. handle = watcher.Unbind();
  34. bool notify_ip_address_changed = false;
  35. for (const auto& interface_entry : *interfaces) {
  36. notify_ip_address_changed |=
  37. CanReachExternalNetwork(interface_entry.second);
  38. }
  39. interface_cache_ = InterfacePropertiesMap(std::move(*interfaces));
  40. UpdateConnectionType();
  41. if (notify_ip_address_changed) {
  42. NotifyObserversOfIPAddressChange();
  43. }
  44. // Bind to the dispatcher for the thread's MessagePump.
  45. zx_status_t status = watcher_.Bind(std::move(handle));
  46. ZX_CHECK(status == ZX_OK, status) << "Bind()";
  47. watcher_->Watch(
  48. fit::bind_member(this, &NetworkChangeNotifierFuchsia::OnInterfacesEvent));
  49. }
  50. NetworkChangeNotifierFuchsia::~NetworkChangeNotifierFuchsia() {
  51. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  52. ClearGlobalPointer();
  53. }
  54. NetworkChangeNotifier::ConnectionType
  55. NetworkChangeNotifierFuchsia::GetCurrentConnectionType() const {
  56. ConnectionType type = static_cast<ConnectionType>(
  57. base::subtle::Acquire_Load(&cached_connection_type_));
  58. return type;
  59. }
  60. void NetworkChangeNotifierFuchsia::OnInterfacesEvent(
  61. fuchsia::net::interfaces::Event event) {
  62. // Immediately trigger the next watch, which will happen asynchronously. If
  63. // event processing encounters an error it'll close the watcher channel which
  64. // will cancel any pending callbacks.
  65. watcher_->Watch(
  66. fit::bind_member(this, &NetworkChangeNotifierFuchsia::OnInterfacesEvent));
  67. switch (event.Which()) {
  68. case fuchsia::net::interfaces::Event::kAdded:
  69. OnInterfaceAdded(std::move(event.added()));
  70. break;
  71. case fuchsia::net::interfaces::Event::kRemoved:
  72. OnInterfaceRemoved(event.removed());
  73. break;
  74. case fuchsia::net::interfaces::Event::kChanged:
  75. OnInterfaceChanged(std::move(event.changed()));
  76. break;
  77. case fuchsia::net::interfaces::Event::kExisting:
  78. case fuchsia::net::interfaces::Event::kIdle:
  79. OnWatcherError(base::StringPrintf(
  80. "OnInterfaceEvent: unexpected event %lu.", event.Which()));
  81. break;
  82. case fuchsia::net::interfaces::Event::Invalid:
  83. LOG(WARNING)
  84. << "Invalid event received from fuchsia.net.interfaces/Watcher";
  85. break;
  86. }
  87. }
  88. void NetworkChangeNotifierFuchsia::OnInterfaceAdded(
  89. fuchsia::net::interfaces::Properties properties) {
  90. uint64_t id = properties.id();
  91. absl::optional<internal::InterfaceProperties> cache_entry =
  92. internal::InterfaceProperties::VerifyAndCreate(std::move(properties));
  93. if (!cache_entry) {
  94. OnWatcherError("OnInterfaceAdded: incomplete interface properties.");
  95. return;
  96. }
  97. if (interface_cache_.find(id) != interface_cache_.end()) {
  98. OnWatcherError(base::StringPrintf(
  99. "OnInterfaceAdded: duplicate interface ID %lu.", id));
  100. return;
  101. }
  102. const bool can_reach = CanReachExternalNetwork(*cache_entry);
  103. interface_cache_.emplace(id, std::move(*cache_entry));
  104. UpdateConnectionType();
  105. if (can_reach) {
  106. NotifyObserversOfIPAddressChange();
  107. }
  108. }
  109. void NetworkChangeNotifierFuchsia::OnInterfaceRemoved(uint64_t interface_id) {
  110. InterfacePropertiesMap::iterator cache_entry =
  111. interface_cache_.find(interface_id);
  112. if (cache_entry == interface_cache_.end()) {
  113. OnWatcherError(base::StringPrintf(
  114. "OnInterfaceRemoved: unknown interface ID %lu.", interface_id));
  115. return;
  116. }
  117. const bool can_reach = CanReachExternalNetwork(cache_entry->second);
  118. interface_cache_.erase(cache_entry);
  119. UpdateConnectionType();
  120. if (can_reach) {
  121. NotifyObserversOfIPAddressChange();
  122. }
  123. }
  124. void NetworkChangeNotifierFuchsia::OnInterfaceChanged(
  125. fuchsia::net::interfaces::Properties properties) {
  126. if (!properties.has_id()) {
  127. OnWatcherError("OnInterfaceChanged: no interface ID.");
  128. return;
  129. }
  130. const uint64_t id = properties.id();
  131. InterfacePropertiesMap::iterator cache_entry = interface_cache_.find(id);
  132. if (cache_entry == interface_cache_.end()) {
  133. OnWatcherError(base::StringPrintf(
  134. "OnInterfaceChanged: unknown interface ID %lu.", id));
  135. return;
  136. }
  137. const bool old_can_reach = CanReachExternalNetwork(cache_entry->second);
  138. const bool has_addresses = properties.has_addresses();
  139. if (!cache_entry->second.Update(std::move(properties))) {
  140. OnWatcherError("OnInterfaceChanged: update failed.");
  141. return;
  142. }
  143. UpdateConnectionType();
  144. const bool can_reach = CanReachExternalNetwork(cache_entry->second);
  145. if (has_addresses || old_can_reach != can_reach) {
  146. NotifyObserversOfIPAddressChange();
  147. }
  148. }
  149. void NetworkChangeNotifierFuchsia::OnWatcherError(
  150. base::StringPiece error_message) {
  151. LOG(ERROR) << error_message;
  152. watcher_.Unbind();
  153. ResetConnectionType();
  154. }
  155. void NetworkChangeNotifierFuchsia::UpdateConnectionType() {
  156. ConnectionType connection_type = ConnectionType::CONNECTION_NONE;
  157. for (const auto& interface : interface_cache_) {
  158. if (CanReachExternalNetwork(interface.second)) {
  159. connection_type = GetEffectiveConnectionType(interface.second);
  160. break;
  161. }
  162. }
  163. if (connection_type != GetCurrentConnectionType()) {
  164. base::subtle::Release_Store(&cached_connection_type_, connection_type);
  165. NotifyObserversOfConnectionTypeChange();
  166. }
  167. }
  168. void NetworkChangeNotifierFuchsia::ResetConnectionType() {
  169. base::subtle::Release_Store(&cached_connection_type_,
  170. ConnectionType::CONNECTION_UNKNOWN);
  171. }
  172. NetworkChangeNotifier::ConnectionType
  173. NetworkChangeNotifierFuchsia::GetEffectiveConnectionType(
  174. const internal::InterfaceProperties& properties) {
  175. if (!properties.IsPubliclyRoutable())
  176. return NetworkChangeNotifier::CONNECTION_NONE;
  177. NetworkChangeNotifier::ConnectionType connection_type =
  178. internal::ConvertConnectionType(properties.device_class());
  179. if (require_wlan_ &&
  180. connection_type != NetworkChangeNotifier::CONNECTION_WIFI) {
  181. return NetworkChangeNotifier::CONNECTION_NONE;
  182. }
  183. return connection_type;
  184. }
  185. bool NetworkChangeNotifierFuchsia::CanReachExternalNetwork(
  186. const internal::InterfaceProperties& properties) {
  187. return GetEffectiveConnectionType(properties) !=
  188. NetworkChangeNotifier::CONNECTION_NONE;
  189. }
  190. } // namespace net