// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/base/network_change_notifier_fuchsia.h" #include #include #include #include #include #include "base/bind.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/strings/stringprintf.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace net { NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(bool require_wlan) : NetworkChangeNotifierFuchsia(internal::ConnectInterfacesWatcher(), require_wlan) {} NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia( fidl::InterfaceHandle handle, bool require_wlan, SystemDnsConfigChangeNotifier* system_dns_config_notifier) : NetworkChangeNotifier(NetworkChangeCalculatorParams(), system_dns_config_notifier), require_wlan_(require_wlan) { DCHECK(handle); watcher_.set_error_handler(base::LogFidlErrorAndExitProcess( FROM_HERE, "fuchsia.net.interfaces.Watcher")); fuchsia::net::interfaces::WatcherSyncPtr watcher = handle.BindSync(); absl::optional interfaces = internal::GetExistingInterfaces(watcher); if (!interfaces) return; handle = watcher.Unbind(); bool notify_ip_address_changed = false; for (const auto& interface_entry : *interfaces) { notify_ip_address_changed |= CanReachExternalNetwork(interface_entry.second); } interface_cache_ = InterfacePropertiesMap(std::move(*interfaces)); UpdateConnectionType(); if (notify_ip_address_changed) { NotifyObserversOfIPAddressChange(); } // Bind to the dispatcher for the thread's MessagePump. zx_status_t status = watcher_.Bind(std::move(handle)); ZX_CHECK(status == ZX_OK, status) << "Bind()"; watcher_->Watch( fit::bind_member(this, &NetworkChangeNotifierFuchsia::OnInterfacesEvent)); } NetworkChangeNotifierFuchsia::~NetworkChangeNotifierFuchsia() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); ClearGlobalPointer(); } NetworkChangeNotifier::ConnectionType NetworkChangeNotifierFuchsia::GetCurrentConnectionType() const { ConnectionType type = static_cast( base::subtle::Acquire_Load(&cached_connection_type_)); return type; } void NetworkChangeNotifierFuchsia::OnInterfacesEvent( fuchsia::net::interfaces::Event event) { // Immediately trigger the next watch, which will happen asynchronously. If // event processing encounters an error it'll close the watcher channel which // will cancel any pending callbacks. watcher_->Watch( fit::bind_member(this, &NetworkChangeNotifierFuchsia::OnInterfacesEvent)); switch (event.Which()) { case fuchsia::net::interfaces::Event::kAdded: OnInterfaceAdded(std::move(event.added())); break; case fuchsia::net::interfaces::Event::kRemoved: OnInterfaceRemoved(event.removed()); break; case fuchsia::net::interfaces::Event::kChanged: OnInterfaceChanged(std::move(event.changed())); break; case fuchsia::net::interfaces::Event::kExisting: case fuchsia::net::interfaces::Event::kIdle: OnWatcherError(base::StringPrintf( "OnInterfaceEvent: unexpected event %lu.", event.Which())); break; case fuchsia::net::interfaces::Event::Invalid: LOG(WARNING) << "Invalid event received from fuchsia.net.interfaces/Watcher"; break; } } void NetworkChangeNotifierFuchsia::OnInterfaceAdded( fuchsia::net::interfaces::Properties properties) { uint64_t id = properties.id(); absl::optional cache_entry = internal::InterfaceProperties::VerifyAndCreate(std::move(properties)); if (!cache_entry) { OnWatcherError("OnInterfaceAdded: incomplete interface properties."); return; } if (interface_cache_.find(id) != interface_cache_.end()) { OnWatcherError(base::StringPrintf( "OnInterfaceAdded: duplicate interface ID %lu.", id)); return; } const bool can_reach = CanReachExternalNetwork(*cache_entry); interface_cache_.emplace(id, std::move(*cache_entry)); UpdateConnectionType(); if (can_reach) { NotifyObserversOfIPAddressChange(); } } void NetworkChangeNotifierFuchsia::OnInterfaceRemoved(uint64_t interface_id) { InterfacePropertiesMap::iterator cache_entry = interface_cache_.find(interface_id); if (cache_entry == interface_cache_.end()) { OnWatcherError(base::StringPrintf( "OnInterfaceRemoved: unknown interface ID %lu.", interface_id)); return; } const bool can_reach = CanReachExternalNetwork(cache_entry->second); interface_cache_.erase(cache_entry); UpdateConnectionType(); if (can_reach) { NotifyObserversOfIPAddressChange(); } } void NetworkChangeNotifierFuchsia::OnInterfaceChanged( fuchsia::net::interfaces::Properties properties) { if (!properties.has_id()) { OnWatcherError("OnInterfaceChanged: no interface ID."); return; } const uint64_t id = properties.id(); InterfacePropertiesMap::iterator cache_entry = interface_cache_.find(id); if (cache_entry == interface_cache_.end()) { OnWatcherError(base::StringPrintf( "OnInterfaceChanged: unknown interface ID %lu.", id)); return; } const bool old_can_reach = CanReachExternalNetwork(cache_entry->second); const bool has_addresses = properties.has_addresses(); if (!cache_entry->second.Update(std::move(properties))) { OnWatcherError("OnInterfaceChanged: update failed."); return; } UpdateConnectionType(); const bool can_reach = CanReachExternalNetwork(cache_entry->second); if (has_addresses || old_can_reach != can_reach) { NotifyObserversOfIPAddressChange(); } } void NetworkChangeNotifierFuchsia::OnWatcherError( base::StringPiece error_message) { LOG(ERROR) << error_message; watcher_.Unbind(); ResetConnectionType(); } void NetworkChangeNotifierFuchsia::UpdateConnectionType() { ConnectionType connection_type = ConnectionType::CONNECTION_NONE; for (const auto& interface : interface_cache_) { if (CanReachExternalNetwork(interface.second)) { connection_type = GetEffectiveConnectionType(interface.second); break; } } if (connection_type != GetCurrentConnectionType()) { base::subtle::Release_Store(&cached_connection_type_, connection_type); NotifyObserversOfConnectionTypeChange(); } } void NetworkChangeNotifierFuchsia::ResetConnectionType() { base::subtle::Release_Store(&cached_connection_type_, ConnectionType::CONNECTION_UNKNOWN); } NetworkChangeNotifier::ConnectionType NetworkChangeNotifierFuchsia::GetEffectiveConnectionType( const internal::InterfaceProperties& properties) { if (!properties.IsPubliclyRoutable()) return NetworkChangeNotifier::CONNECTION_NONE; NetworkChangeNotifier::ConnectionType connection_type = internal::ConvertConnectionType(properties.device_class()); if (require_wlan_ && connection_type != NetworkChangeNotifier::CONNECTION_WIFI) { return NetworkChangeNotifier::CONNECTION_NONE; } return connection_type; } bool NetworkChangeNotifierFuchsia::CanReachExternalNetwork( const internal::InterfaceProperties& properties) { return GetEffectiveConnectionType(properties) != NetworkChangeNotifier::CONNECTION_NONE; } } // namespace net