| // 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 <algorithm> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/fuchsia/component_context.h" |
| #include "base/optional.h" |
| #include "base/run_loop.h" |
| #include "net/base/network_interfaces.h" |
| #include "net/base/network_interfaces_fuchsia.h" |
| |
| namespace net { |
| namespace { |
| |
| using ConnectionType = NetworkChangeNotifier::ConnectionType; |
| |
| // Adapts a base::RepeatingCallback to a std::function object. |
| // Useful when binding callbacks to asynchronous FIDL calls, because |
| // it allows the caller to reference in-scope move-only objects as well as use |
| // Chromium's ownership signifiers such as base::Passed, base::Unretained, etc. |
| // |
| // Note that the function takes a RepeatingCallback because it is copyable, but |
| // in practice the callback will only be executed once by the FIDL system. |
| template <typename R, typename... Args> |
| std::function<R(Args...)> WrapCallbackAsFunction( |
| base::RepeatingCallback<R(Args...)> callback) { |
| return |
| [callback](Args... args) { callback.Run(std::forward<Args>(args)...); }; |
| } |
| |
| } // namespace |
| |
| NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia() |
| : NetworkChangeNotifierFuchsia( |
| base::fuchsia::ComponentContext::GetDefault() |
| ->ConnectToService<fuchsia::netstack::Netstack>()) {} |
| |
| NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia( |
| fuchsia::netstack::NetstackPtr netstack) |
| : netstack_(std::move(netstack)) { |
| DCHECK(netstack_); |
| |
| netstack_.set_error_handler( |
| [this]() { LOG(ERROR) << "Lost connection to netstack."; }); |
| netstack_.events().OnInterfacesChanged = |
| [this](fidl::VectorPtr<fuchsia::netstack::NetInterface> interfaces) { |
| ProcessInterfaceList(base::OnceClosure(), std::move(interfaces)); |
| }; |
| |
| // Fetch the interface list synchronously, so that an initial ConnectionType |
| // is available before we return. |
| base::RunLoop wait_for_interfaces; |
| netstack_->GetInterfaces([ |
| this, quit_closure = wait_for_interfaces.QuitClosure() |
| ](fidl::VectorPtr<fuchsia::netstack::NetInterface> interfaces) { |
| ProcessInterfaceList(quit_closure, std::move(interfaces)); |
| }); |
| wait_for_interfaces.Run(); |
| } |
| |
| NetworkChangeNotifierFuchsia::~NetworkChangeNotifierFuchsia() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| } |
| |
| NetworkChangeNotifier::ConnectionType |
| NetworkChangeNotifierFuchsia::GetCurrentConnectionType() const { |
| ConnectionType type = static_cast<ConnectionType>( |
| base::subtle::Acquire_Load(&cached_connection_type_)); |
| return type; |
| } |
| |
| void NetworkChangeNotifierFuchsia::ProcessInterfaceList( |
| base::OnceClosure on_initialized_cb, |
| fidl::VectorPtr<fuchsia::netstack::NetInterface> interfaces) { |
| netstack_->GetRouteTable(WrapCallbackAsFunction(base::BindRepeating( |
| &NetworkChangeNotifierFuchsia::OnRouteTableReceived, |
| base::Unretained(this), base::Passed(std::move(on_initialized_cb)), |
| base::Passed(std::move(interfaces))))); |
| } |
| |
| void NetworkChangeNotifierFuchsia::OnRouteTableReceived( |
| base::OnceClosure on_initialized_cb, |
| fidl::VectorPtr<fuchsia::netstack::NetInterface> interfaces, |
| fidl::VectorPtr<fuchsia::netstack::RouteTableEntry> route_table) { |
| // Find the default interface in the routing table. |
| auto default_route_interface = std::find_if( |
| route_table->begin(), route_table->end(), |
| [](const fuchsia::netstack::RouteTableEntry& rt) { |
| return MaskPrefixLength(internal::NetAddressToIPAddress(rt.netmask)) == |
| 0; |
| }); |
| |
| // Find the default interface in the NetInterface list. |
| const fuchsia::netstack::NetInterface* default_interface = nullptr; |
| if (default_route_interface != route_table->end()) { |
| for (const auto& cur_interface : *interfaces) { |
| if (cur_interface.id == default_route_interface->nicid) { |
| default_interface = &cur_interface; |
| } |
| } |
| } |
| |
| base::flat_set<IPAddress> addresses; |
| std::string ssid; |
| ConnectionType connection_type = CONNECTION_NONE; |
| if (default_interface) { |
| std::vector<NetworkInterface> flattened_interfaces = |
| internal::NetInterfaceToNetworkInterfaces(*default_interface); |
| std::transform( |
| flattened_interfaces.begin(), flattened_interfaces.end(), |
| std::inserter(addresses, addresses.begin()), |
| [](const NetworkInterface& interface) { return interface.address; }); |
| if (!flattened_interfaces.empty()) { |
| connection_type = flattened_interfaces.front().type; |
| } |
| |
| // TODO(https://crbug.com/848355): Treat SSID changes as IP address changes. |
| } |
| |
| bool connection_type_changed = false; |
| if (connection_type != cached_connection_type_) { |
| base::subtle::Release_Store(&cached_connection_type_, connection_type); |
| connection_type_changed = true; |
| } |
| |
| if (addresses != cached_addresses_) { |
| std::swap(cached_addresses_, addresses); |
| if (on_initialized_cb.is_null()) { |
| NotifyObserversOfIPAddressChange(); |
| } |
| connection_type_changed = true; |
| } |
| |
| if (on_initialized_cb.is_null() && connection_type_changed) { |
| NotifyObserversOfConnectionTypeChange(); |
| } |
| |
| if (!on_initialized_cb.is_null()) { |
| std::move(on_initialized_cb).Run(); |
| } |
| } |
| |
| } // namespace net |