Reland "[fuchsia] Migrate to fuchsia.net.interfaces/State"
This is a reland of 022c5b0e929855e1422d8dea776d72ada41020f0.
Consumers of WebEngine have been prepared for a soft transition, and
runners_integration_tests.test-cmx is now updated.
Original change's description:
> [fuchsia] Migrate to fuchsia.net.interfaces/State
>
> Fixed: fuchsia:21155
> Change-Id: I85b388be33d4c9697ba866cf11a143493b2b3239
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2380329
> Reviewed-by: Robert Sesek <[email protected]>
> Reviewed-by: Adam Langley <[email protected]>
> Reviewed-by: Sergey Volk <[email protected]>
> Reviewed-by: Wez <[email protected]>
> Commit-Queue: Marina Ciocea <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#828229}
[email protected]
Change-Id: I7c63e2ab88ce8b087411de97bc4ee2eceebc7c3a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2554857
Commit-Queue: Marina Ciocea <[email protected]>
Reviewed-by: David Dorwin <[email protected]>
Reviewed-by: Robert Sesek <[email protected]>
Reviewed-by: Wez <[email protected]>
Reviewed-by: Marina Ciocea <[email protected]>
Reviewed-by: Adam Langley <[email protected]>
Cr-Commit-Position: refs/heads/master@{#833397}
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 076568a..bc07d101 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1458,7 +1458,8 @@
if (is_fuchsia) {
deps += [ "//third_party/fuchsia-sdk/sdk/pkg/async-loop-cpp" ]
- public_deps += [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.netstack" ]
+ public_deps +=
+ [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.net.interfaces" ]
sources += [
"base/network_change_notifier_fuchsia.cc",
"base/network_change_notifier_fuchsia.h",
@@ -1710,8 +1711,8 @@
if (is_fuchsia) {
public_deps += [
- "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.hardware.ethernet",
- "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.netstack",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.hardware.network",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.net.interfaces",
"//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
]
}
@@ -4630,7 +4631,7 @@
if (is_fuchsia) {
use_test_server = true
deps += [
- "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.netstack",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.net.interfaces",
"//third_party/fuchsia-sdk/sdk/pkg/fidl_cpp",
]
sources += [ "base/network_change_notifier_fuchsia_unittest.cc" ]
diff --git a/net/base/network_change_notifier.cc b/net/base/network_change_notifier.cc
index 1bf82ef..d786914 100644
--- a/net/base/network_change_notifier.cc
+++ b/net/base/network_change_notifier.cc
@@ -251,7 +251,7 @@
return std::make_unique<NetworkChangeNotifierMac>();
#elif defined(OS_FUCHSIA)
return std::make_unique<NetworkChangeNotifierFuchsia>(
- fuchsia::hardware::ethernet::Features());
+ /*require_wlan=*/false);
#else
NOTIMPLEMENTED();
return NULL;
diff --git a/net/base/network_change_notifier_fuchsia.cc b/net/base/network_change_notifier_fuchsia.cc
index 75d66bc..3c10a11 100644
--- a/net/base/network_change_notifier_fuchsia.cc
+++ b/net/base/network_change_notifier_fuchsia.cc
@@ -4,63 +4,63 @@
#include "net/base/network_change_notifier_fuchsia.h"
-#include <lib/async-loop/cpp/loop.h>
-#include <lib/sys/cpp/component_context.h>
-
#include <algorithm>
+#include <iterator>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/fuchsia/fuchsia_logging.h"
-#include "base/fuchsia/process_context.h"
#include "base/optional.h"
-#include "base/run_loop.h"
-#include "net/base/network_interfaces.h"
-#include "net/base/network_interfaces_fuchsia.h"
+#include "base/strings/stringprintf.h"
namespace net {
-NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(
- fuchsia::hardware::ethernet::Features required_features)
- : NetworkChangeNotifierFuchsia(base::ComponentContextForProcess()
- ->svc()
- ->Connect<fuchsia::netstack::Netstack>(),
- required_features) {}
+NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(bool require_wlan)
+ : NetworkChangeNotifierFuchsia(internal::ConnectInterfacesWatcher(),
+ require_wlan) {}
NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(
- fidl::InterfaceHandle<fuchsia::netstack::Netstack> netstack,
- fuchsia::hardware::ethernet::Features required_features,
+ fidl::InterfaceHandle<fuchsia::net::interfaces::Watcher> handle,
+ bool require_wlan,
SystemDnsConfigChangeNotifier* system_dns_config_notifier)
: NetworkChangeNotifier(NetworkChangeCalculatorParams(),
system_dns_config_notifier),
- required_features_(required_features) {
- DCHECK(netstack);
+ require_wlan_(require_wlan) {
+ DCHECK(handle);
- netstack_.set_error_handler([](zx_status_t status) {
- ZX_LOG(FATAL, status) << "Lost connection to netstack.";
- });
+ watcher_.set_error_handler(
+ [](zx_status_t status) {
+ ZX_LOG(FATAL, status)
+ << "Lost connection to fuchsia.net.interfaces/Watcher.";
+ });
- netstack_.events().OnInterfacesChanged = fit::bind_member(
- this, &NetworkChangeNotifierFuchsia::ProcessInterfaceList);
+ fuchsia::net::interfaces::WatcherSyncPtr watcher = handle.BindSync();
+ base::Optional<internal::ExistingInterfaceProperties> interfaces =
+ internal::GetExistingInterfaces(watcher);
+ if (!interfaces) {
+ ZX_LOG(ERROR, ZX_ERR_INVALID_ARGS) << "Failed to load existing 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));
- // Temporarily bind to a local dispatcher so we can synchronously wait for the
- // synthetic event to populate the initial state.
- async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
- zx_status_t status = netstack_.Bind(std::move(netstack), loop.dispatcher());
- ZX_CHECK(status == ZX_OK, status) << "Bind()";
- on_initial_interfaces_received_ =
- base::BindOnce(&async::Loop::Quit, base::Unretained(&loop));
- status = loop.Run();
- ZX_CHECK(status == ZX_ERR_CANCELED, status) << "loop.Run()";
+ UpdateConnectionType();
+ if (notify_ip_address_changed) {
+ NotifyObserversOfIPAddressChange();
+ }
// Bind to the dispatcher for the thread's MessagePump.
- //
- // Note this must be done before |loop| is destroyed, since that would close
- // the interface handle underlying |netstack_|.
- status = netstack_.Bind(netstack_.Unbind());
+ 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() {
@@ -75,80 +75,146 @@
return type;
}
-void NetworkChangeNotifierFuchsia::ProcessInterfaceList(
- std::vector<fuchsia::netstack::NetInterface> interfaces) {
- netstack_->GetRouteTable(
- [this, interfaces = std::move(interfaces)](
- std::vector<fuchsia::netstack::RouteTableEntry> route_table) mutable {
- OnRouteTableReceived(std::move(interfaces), std::move(route_table));
- });
+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::OnRouteTableReceived(
- std::vector<fuchsia::netstack::NetInterface> interfaces,
- std::vector<fuchsia::netstack::RouteTableEntry> route_table) {
- // Create a set of NICs that have default routes (ie 0.0.0.0).
- base::flat_set<uint32_t> default_route_ids;
- for (const auto& route : route_table) {
- if (MaskPrefixLength(
- internal::FuchsiaIpAddressToIPAddress(route.netmask)) == 0) {
- default_route_ids.insert(route.nicid);
- }
+void NetworkChangeNotifierFuchsia::OnInterfaceAdded(
+ fuchsia::net::interfaces::Properties properties) {
+ base::Optional<internal::InterfaceProperties> cache_entry =
+ internal::InterfaceProperties::VerifyAndCreate(std::move(properties));
+ if (!cache_entry) {
+ OnWatcherError("OnInterfaceAdded: incomplete interface properties.");
+ return;
}
-
- ConnectionType connection_type = CONNECTION_NONE;
- base::flat_set<IPAddress> addresses;
- for (auto& interface : interfaces) {
- // Filter out loopback and invalid connection types.
- if ((internal::ConvertConnectionType(interface) ==
- NetworkChangeNotifier::CONNECTION_NONE) ||
- (interface.features &
- fuchsia::hardware::ethernet::Features::LOOPBACK) ==
- fuchsia::hardware::ethernet::Features::LOOPBACK) {
- continue;
- }
-
- // Filter out interfaces that do not meet the |required_features_|.
- if ((interface.features & required_features_) != required_features_) {
- continue;
- }
-
- // Filter out interfaces with non-default routes.
- if (!default_route_ids.contains(interface.id)) {
- continue;
- }
-
- std::vector<NetworkInterface> flattened_interfaces =
- internal::NetInterfaceToNetworkInterfaces(interface);
- if (flattened_interfaces.empty()) {
- continue;
- }
-
- // Add the addresses from this interface to the list of all addresses.
- std::transform(
- flattened_interfaces.begin(), flattened_interfaces.end(),
- std::inserter(addresses, addresses.begin()),
- [](const NetworkInterface& interface) { return interface.address; });
-
- // Set the default connection to the first interface connection found.
- if (connection_type == CONNECTION_NONE) {
- connection_type = flattened_interfaces.front().type;
- }
+ uint64_t id = properties.id();
+ if (interface_cache_.find(id) != interface_cache_.end()) {
+ OnWatcherError(base::StringPrintf(
+ "OnInterfaceAdded: duplicate interface ID %lu.", id));
+ return;
}
-
- if (addresses != cached_addresses_) {
- std::swap(cached_addresses_, addresses);
+ const bool can_reach = CanReachExternalNetwork(*cache_entry);
+ interface_cache_.emplace(id, std::move(*cache_entry));
+ UpdateConnectionType();
+ if (can_reach) {
NotifyObserversOfIPAddressChange();
}
+}
- if (connection_type != cached_connection_type_) {
+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();
}
+}
- if (on_initial_interfaces_received_) {
- std::move(on_initial_interfaces_received_).Run();
+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
diff --git a/net/base/network_change_notifier_fuchsia.h b/net/base/network_change_notifier_fuchsia.h
index 99dc60c..cb5e002 100644
--- a/net/base/network_change_notifier_fuchsia.h
+++ b/net/base/network_change_notifier_fuchsia.h
@@ -5,19 +5,21 @@
#ifndef NET_BASE_NETWORK_CHANGE_NOTIFIER_FUCHSIA_H_
#define NET_BASE_NETWORK_CHANGE_NOTIFIER_FUCHSIA_H_
-#include <fuchsia/netstack/cpp/fidl.h>
+#include <fuchsia/net/interfaces/cpp/fidl.h>
#include <lib/fidl/cpp/binding.h>
#include <vector>
#include "base/atomicops.h"
#include "base/callback.h"
-#include "base/containers/flat_set.h"
+#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
+#include "base/strings/string_piece.h"
#include "base/threading/thread_checker.h"
#include "net/base/net_export.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_interfaces.h"
+#include "net/base/network_interfaces_fuchsia.h"
namespace net {
@@ -25,9 +27,8 @@
: public NetworkChangeNotifier {
public:
// Registers for asynchronous notifications of changes to network interfaces.
- // Interfaces are filtered by |required_features|.
- explicit NetworkChangeNotifierFuchsia(
- fuchsia::hardware::ethernet::Features required_features);
+ // Only WLAN interfaces are observed if |require_wlan| is requested.
+ explicit NetworkChangeNotifierFuchsia(bool require_wlan);
NetworkChangeNotifierFuchsia(const NetworkChangeNotifierFuchsia&) = delete;
NetworkChangeNotifierFuchsia& operator=(const NetworkChangeNotifierFuchsia&) =
delete;
@@ -37,42 +38,55 @@
ConnectionType GetCurrentConnectionType() const override;
private:
+ using InterfacePropertiesMap =
+ base::flat_map<uint64_t, internal::InterfaceProperties>;
friend class NetworkChangeNotifierFuchsiaTest;
- // For testing purposes. Receives a |netstack| pointer for easy mocking.
- // Interfaces are filtered by |required_features|.
NetworkChangeNotifierFuchsia(
- fidl::InterfaceHandle<fuchsia::netstack::Netstack> netstack,
- fuchsia::hardware::ethernet::Features required_features,
+ fidl::InterfaceHandle<fuchsia::net::interfaces::Watcher> watcher,
+ bool require_wlan,
SystemDnsConfigChangeNotifier* system_dns_config_notifier = nullptr);
- // Forwards the network interface list along with the result of
- // GetRouteTable() to OnRouteTableReceived().
- void ProcessInterfaceList(
- std::vector<fuchsia::netstack::NetInterface> interfaces);
+ // Processes events from the watcher for interface addition, change, or
+ // removal.
+ void OnInterfacesEvent(fuchsia::net::interfaces::Event event);
- // Computes network change notification state change from the list of
- // interfaces and routing table data, sending observer events if IP or
- // connection type changes are detected.
- void OnRouteTableReceived(
- std::vector<fuchsia::netstack::NetInterface> interfaces,
- std::vector<fuchsia::netstack::RouteTableEntry> table);
+ // Handlers for the interface change events. Listeners are notified of changes
+ // that affect them. |watcher_| is closed if an event is malformed in some
+ // way.
+ void OnInterfaceAdded(fuchsia::net::interfaces::Properties properties);
+ void OnInterfaceRemoved(uint64_t interface_id);
+ void OnInterfaceChanged(fuchsia::net::interfaces::Properties properties);
- // Required features for an interface to be taken into account.
- const fuchsia::hardware::ethernet::Features required_features_;
+ // Unbinds the watcher, reset the connection type and logs |error_message|.
+ void OnWatcherError(base::StringPiece error_message);
- fuchsia::netstack::NetstackPtr netstack_;
+ // Updates the connection type from |interface_cache_| and notifies observers
+ // of changes.
+ void UpdateConnectionType();
- // Used to allow the constructor to block until the initial state is received
- // from |netstack_|.
- base::OnceClosure on_initial_interfaces_received_;
+ // Resets the connection type to CONNECTION_UNKNOWN.
+ void ResetConnectionType();
+
+ // Returns the ConnectionType converted from |properties|' device_class.
+ // Returns CONNECTION_NONE if the interface is not publicly routable, taking
+ // into account the |requires_wlan_| setting.
+ ConnectionType GetEffectiveConnectionType(
+ const internal::InterfaceProperties& properties);
+
+ // Returns true if the effective connection type is not CONNECTION_NONE.
+ bool CanReachExternalNetwork(const internal::InterfaceProperties& properties);
+
+ // Whether only WLAN interfaces should be taken into account.
+ const bool require_wlan_;
+
+ fuchsia::net::interfaces::WatcherPtr watcher_;
// The ConnectionType of the default network interface, stored as an atomic
// 32-bit int for safe concurrent access.
base::subtle::Atomic32 cached_connection_type_ = CONNECTION_UNKNOWN;
- // Set of addresses from the previous query/update for the default interface.
- base::flat_set<IPAddress> cached_addresses_;
+ InterfacePropertiesMap interface_cache_;
THREAD_CHECKER(thread_checker_);
};
diff --git a/net/base/network_change_notifier_fuchsia_unittest.cc b/net/base/network_change_notifier_fuchsia_unittest.cc
index cac2360..b2a9444 100644
--- a/net/base/network_change_notifier_fuchsia_unittest.cc
+++ b/net/base/network_change_notifier_fuchsia_unittest.cc
@@ -4,8 +4,7 @@
#include "net/base/network_change_notifier_fuchsia.h"
-#include <fuchsia/hardware/ethernet/cpp/fidl.h>
-#include <fuchsia/netstack/cpp/fidl_test_base.h>
+#include <fuchsia/net/interfaces/cpp/fidl_test_base.h>
#include <memory>
#include <string>
#include <utility>
@@ -32,18 +31,15 @@
using IPv4Octets = std::array<uint8_t, 4>;
using IPv6Octets = std::array<uint8_t, 16>;
-constexpr IPv4Octets kIPv4DefaultGatewayNetmask = {0, 0, 0, 0};
-constexpr IPv4Octets kIPv4DefaultGatewayAddress = {192, 168, 0, 1};
-
constexpr IPv4Octets kDefaultIPv4Address = {192, 168, 0, 2};
-constexpr IPv4Octets kDefaultIPv4Netmask = {255, 255, 0, 0};
+constexpr uint8_t kDefaultIPv4Prefix = 16;
constexpr IPv4Octets kSecondaryIPv4Address = {10, 0, 0, 1};
-constexpr IPv4Octets kSecondaryIPv4Netmask = {255, 0, 0, 0};
+constexpr uint8_t kSecondaryIPv4Prefix = 8;
-constexpr IPv6Octets kDefaultIPv6Address = {0xfe, 0x80, 0x01};
-constexpr IPv6Octets kDefaultIPv6Netmask = {0xfe, 0x80};
-constexpr IPv6Octets kSecondaryIPv6Address = {0xfe, 0x80, 0x02};
-constexpr IPv6Octets kSecondaryIPv6Netmask = {0xfe, 0x80};
+constexpr IPv6Octets kDefaultIPv6Address = {0x20, 0x01, 0x01};
+constexpr uint8_t kDefaultIPv6Prefix = 16;
+constexpr IPv6Octets kSecondaryIPv6Address = {0x20, 0x01, 0x02};
+constexpr uint8_t kSecondaryIPv6Prefix = 16;
fuchsia::net::IpAddress IpAddressFrom(IPv4Octets octets) {
fuchsia::net::IpAddress output;
@@ -57,130 +53,165 @@
return output;
}
-fuchsia::net::Subnet SubnetFrom(IPv6Octets octets, uint8_t prefix) {
+template <typename T>
+fuchsia::net::Subnet SubnetFrom(T octets, uint8_t prefix) {
fuchsia::net::Subnet output;
output.addr = IpAddressFrom(octets);
output.prefix_len = prefix;
return output;
}
-fuchsia::netstack::NetInterface DefaultNetInterface() {
- // For most tests a live interface with an IPv4 address and no |features| set
+template <typename T>
+fuchsia::net::interfaces::Address InterfaceAddressFrom(T octets,
+ uint8_t prefix) {
+ fuchsia::net::interfaces::Address addr;
+ addr.set_addr(SubnetFrom(octets, prefix));
+ return addr;
+}
+
+template <typename T>
+std::vector<T> MakeSingleItemVec(T item) {
+ std::vector<T> vec;
+ vec.push_back(std::move(item));
+ return vec;
+}
+
+fuchsia::net::interfaces::Properties DefaultInterfaceProperties(
+ fuchsia::hardware::network::DeviceClass device_class =
+ fuchsia::hardware::network::DeviceClass::UNKNOWN) {
+ // For most tests a live interface with an IPv4 address and an unknown class
// is sufficient.
- fuchsia::netstack::NetInterface interface;
- interface.id = kDefaultInterfaceId;
- interface.flags = fuchsia::netstack::Flags::UP;
- interface.features = {};
- interface.addr = IpAddressFrom(kDefaultIPv4Address);
- interface.netmask = IpAddressFrom(kDefaultIPv4Netmask);
- interface.broadaddr = IpAddressFrom(kDefaultIPv4Address);
+ fuchsia::net::interfaces::Properties interface;
+ interface.set_id(kDefaultInterfaceId);
+ interface.set_online(true);
+ interface.set_has_default_ipv4_route(true);
+ interface.set_has_default_ipv6_route(true);
+ interface.set_device_class(fuchsia::net::interfaces::DeviceClass::WithDevice(
+ std::move(device_class)));
+ interface.set_addresses(MakeSingleItemVec(
+ InterfaceAddressFrom(kDefaultIPv4Address, kDefaultIPv4Prefix)));
return interface;
}
-fuchsia::netstack::NetInterface SecondaryNetInterface() {
- // For most tests a live interface with an IPv4 address and no |features| set
+fuchsia::net::interfaces::Properties SecondaryInterfaceProperties() {
+ // For most tests a live interface with an IPv4 address and an unknown class
// is sufficient.
- fuchsia::netstack::NetInterface interface;
- interface.id = kSecondaryInterfaceId;
- interface.flags = fuchsia::netstack::Flags::UP;
- interface.features = {};
- interface.addr = IpAddressFrom(kSecondaryIPv4Address);
- interface.netmask = IpAddressFrom(kSecondaryIPv4Netmask);
- interface.broadaddr = IpAddressFrom(kSecondaryIPv4Address);
+ fuchsia::net::interfaces::Properties interface;
+ interface.set_id(kSecondaryInterfaceId);
+ interface.set_online(true);
+ interface.set_has_default_ipv4_route(false);
+ interface.set_has_default_ipv6_route(false);
+ interface.set_device_class(fuchsia::net::interfaces::DeviceClass::WithDevice(
+ fuchsia::hardware::network::DeviceClass::UNKNOWN));
+ interface.set_addresses(MakeSingleItemVec(
+ InterfaceAddressFrom(kSecondaryIPv4Address, kSecondaryIPv4Prefix)));
return interface;
}
-std::vector<fuchsia::netstack::NetInterface> CloneNetInterfaces(
- const std::vector<fuchsia::netstack::NetInterface>& interfaces) {
- std::vector<fuchsia::netstack::NetInterface> interfaces_copy(
- interfaces.size());
- for (size_t i = 0; i < interfaces.size(); ++i) {
- CHECK_EQ(ZX_OK, interfaces[i].Clone(&interfaces_copy[i]));
- }
- return interfaces_copy;
+template <typename F>
+fuchsia::net::interfaces::Event MakeChangeEvent(uint64_t interface_id, F fn) {
+ fuchsia::net::interfaces::Properties props;
+ props.set_id(interface_id);
+ fn(&props);
+ return fuchsia::net::interfaces::Event::WithChanged(std::move(props));
}
-// Partial fake implementation of a Netstack.
-class FakeNetstack : public fuchsia::netstack::testing::Netstack_TestBase {
+// Partial fake implementation of a fuchsia.net.interfaces/Watcher.
+class FakeWatcher : public fuchsia::net::interfaces::testing::Watcher_TestBase {
public:
- FakeNetstack() = default;
- FakeNetstack(const FakeNetstack&) = delete;
- FakeNetstack& operator=(const FakeNetstack&) = delete;
- ~FakeNetstack() override = default;
+ explicit FakeWatcher() : binding_(this) {
+ // Always create the watcher with an empty set of interfaces.
+ // Callers can override the initial set of events with SetInitial.
+ pending_.push(fuchsia::net::interfaces::Event::WithIdle(
+ fuchsia::net::interfaces::Empty{}));
+ }
+ FakeWatcher(const FakeWatcher&) = delete;
+ FakeWatcher& operator=(const FakeWatcher&) = delete;
+ ~FakeWatcher() override = default;
- void Bind(
- fidl::InterfaceRequest<fuchsia::netstack::Netstack> netstack_request) {
- CHECK_EQ(ZX_OK, binding_.Bind(std::move(netstack_request)));
- binding_.events().OnInterfacesChanged(CloneNetInterfaces(interfaces_));
+ void Bind(fidl::InterfaceRequest<fuchsia::net::interfaces::Watcher> request) {
+ CHECK_EQ(ZX_OK, binding_.Bind(std::move(request)));
}
- // Sets the interfaces reported by the fake Netstack and sends an
- // OnInterfacesChanged() event to the client.
- void SetInterfaces(std::vector<fuchsia::netstack::NetInterface> interfaces) {
- interfaces_ = std::move(interfaces);
- if (binding_.is_bound()) {
- binding_.events().OnInterfacesChanged(CloneNetInterfaces(interfaces_));
+ void PushEvent(fuchsia::net::interfaces::Event event) {
+ if (pending_callback_) {
+ pending_callback_(std::move(event));
+ pending_callback_ = nullptr;
+ } else {
+ pending_.push(std::move(event));
}
}
+ void SetInitial(std::vector<fuchsia::net::interfaces::Properties> props) {
+ // Discard any pending events.
+ pending_ = std::queue<fuchsia::net::interfaces::Event>();
+ for (auto& prop : props) {
+ pending_.push(
+ fuchsia::net::interfaces::Event::WithExisting(std::move(prop)));
+ }
+ pending_.push(fuchsia::net::interfaces::Event::WithIdle(
+ fuchsia::net::interfaces::Empty{}));
+ // We should not have a pending callback already when setting initial state.
+ CHECK(!pending_callback_);
+ }
+
private:
- void GetRouteTable(GetRouteTableCallback callback) override {
- CHECK(binding_.is_bound());
- std::vector<fuchsia::netstack::RouteTableEntry> table(2);
-
- table[0].nicid = kDefaultInterfaceId;
- table[0].netmask = IpAddressFrom(kIPv4DefaultGatewayNetmask);
- table[0].destination = IpAddressFrom(kDefaultIPv4Address);
- table[0].gateway = IpAddressFrom(kIPv4DefaultGatewayAddress);
-
- table[1].nicid = kSecondaryInterfaceId;
- table[1].netmask = IpAddressFrom(kSecondaryIPv4Netmask);
- table[1].destination = IpAddressFrom(kSecondaryIPv4Address);
- table[1].gateway = IpAddressFrom(kSecondaryIPv4Address);
-
- callback(std::move(table));
+ void Watch(WatchCallback callback) override {
+ ASSERT_FALSE(pending_callback_);
+ if (pending_.empty()) {
+ pending_callback_ = std::move(callback);
+ } else {
+ callback(std::move(pending_.front()));
+ pending_.pop();
+ }
}
void NotImplemented_(const std::string& name) override {
LOG(FATAL) << "Unimplemented function called: " << name;
}
- std::vector<fuchsia::netstack::NetInterface> interfaces_;
- fidl::Binding<fuchsia::netstack::Netstack> binding_{this};
+ std::queue<fuchsia::net::interfaces::Event> pending_;
+ fidl::Binding<fuchsia::net::interfaces::Watcher> binding_;
+ WatchCallback pending_callback_ = nullptr;
};
-class FakeNetstackAsync {
+class FakeWatcherAsync {
public:
- FakeNetstackAsync() : thread_("Netstack Thread") {
+ explicit FakeWatcherAsync() {
base::Thread::Options options(base::MessagePumpType::IO, 0);
CHECK(thread_.StartWithOptions(options));
- netstack_ = base::SequenceBound<FakeNetstack>(thread_.task_runner());
+ watcher_ = base::SequenceBound<FakeWatcher>(thread_.task_runner());
}
- FakeNetstackAsync(const FakeNetstackAsync&) = delete;
- FakeNetstackAsync& operator=(const FakeNetstackAsync&) = delete;
- ~FakeNetstackAsync() = default;
+ FakeWatcherAsync(const FakeWatcherAsync&) = delete;
+ FakeWatcherAsync& operator=(const FakeWatcherAsync&) = delete;
+ ~FakeWatcherAsync() = default;
- void Bind(
- fidl::InterfaceRequest<fuchsia::netstack::Netstack> netstack_request) {
- netstack_.Post(FROM_HERE, &FakeNetstack::Bind, std::move(netstack_request));
+ void Bind(fidl::InterfaceRequest<fuchsia::net::interfaces::Watcher> request) {
+ watcher_.Post(FROM_HERE, &FakeWatcher::Bind, std::move(request));
}
- // Asynchronously update the state of the netstack.
- void SetInterfaces(
- const std::vector<fuchsia::netstack::NetInterface>& interfaces) {
- netstack_.Post(FROM_HERE, &FakeNetstack::SetInterfaces,
- CloneNetInterfaces(interfaces));
+ // Asynchronously push an event to the watcher.
+ void PushEvent(fuchsia::net::interfaces::Event event) {
+ watcher_.Post(FROM_HERE, &FakeWatcher::PushEvent, std::move(event));
}
- // Ensures that any SetInterfaces() or SendOnInterfacesChanged() calls have
+ // Asynchronously push an initial set of interfaces to the watcher.
+ void SetInitial(std::vector<fuchsia::net::interfaces::Properties> props) {
+ watcher_.Post(FROM_HERE, &FakeWatcher::SetInitial, std::move(props));
+ }
+
+ // Asynchronously push an initial single intface to the watcher.
+ void SetInitial(fuchsia::net::interfaces::Properties prop) {
+ SetInitial(MakeSingleItemVec(std::move(prop)));
+ }
+
+ // Ensures that any PushEvent() or SetInitial() calls have
// been processed.
- void FlushNetstackThread() {
- thread_.FlushForTesting();
- }
+ void FlushThread() { thread_.FlushForTesting(); }
private:
- base::Thread thread_;
- base::SequenceBound<FakeNetstack> netstack_;
+ base::Thread thread_{"Watcher Thread"};
+ base::SequenceBound<FakeWatcher> watcher_;
};
template <class T>
@@ -302,25 +333,22 @@
const NetworkChangeNotifierFuchsiaTest&) = delete;
~NetworkChangeNotifierFuchsiaTest() override = default;
- // Creates a NetworkChangeNotifier and spins the MessageLoop to allow it to
- // populate from the list of interfaces which have already been added to
- // |netstack_|. |observer_| is registered last, so that tests need only
- // express expectations on changes they make themselves.
- void CreateNotifier(
- fuchsia::hardware::ethernet::Features required_features = {}) {
- // Ensure that the Netstack internal state is up-to-date before the
+ // Creates a NetworkChangeNotifier that binds to |watcher_|.
+ // |observer_| is registered last, so that tests need only express
+ // expectations on changes they make themselves.
+ void CreateNotifier(bool requires_wlan = false) {
+ // Ensure that internal state is up-to-date before the
// notifier queries it.
- netstack_.FlushNetstackThread();
+ watcher_.FlushThread();
- CHECK(!netstack_handle_);
- netstack_.Bind(netstack_handle_.NewRequest());
+ CHECK(!watcher_handle_);
+ watcher_.Bind(watcher_handle_.NewRequest());
// Use a noop DNS notifier.
dns_config_notifier_ = std::make_unique<SystemDnsConfigChangeNotifier>(
nullptr /* task_runner */, nullptr /* dns_config_service */);
notifier_.reset(new NetworkChangeNotifierFuchsia(
- std::move(netstack_handle_), required_features,
- dns_config_notifier_.get()));
+ std::move(watcher_handle_), requires_wlan, dns_config_notifier_.get()));
type_observer_ = std::make_unique<FakeConnectionTypeObserver>();
ip_observer_ = std::make_unique<FakeIPAddressObserver>();
@@ -328,7 +356,7 @@
void TearDown() override {
// Spin the loops to catch any unintended notifications.
- netstack_.FlushNetstackThread();
+ watcher_.FlushThread();
base::RunLoop().RunUntilIdle();
}
@@ -336,8 +364,8 @@
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
- fidl::InterfaceHandle<fuchsia::netstack::Netstack> netstack_handle_;
- FakeNetstackAsync netstack_;
+ fidl::InterfaceHandle<fuchsia::net::interfaces::Watcher> watcher_handle_;
+ FakeWatcherAsync watcher_;
// Allows us to allocate our own NetworkChangeNotifier for unit testing.
NetworkChangeNotifier::DisableForTest disable_for_test_;
@@ -356,19 +384,19 @@
TEST_F(NetworkChangeNotifierFuchsiaTest, NotifyNetworkChangeOnInitialIPChange) {
// Set a live interface with an IP address and create the notifier.
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
- interfaces[0].features = fuchsia::hardware::ethernet::Features::WLAN;
-
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(DefaultInterfaceProperties(
+ fuchsia::hardware::network::DeviceClass::WLAN));
CreateNotifier();
// Add the NetworkChangeNotifier, and change the IP address. This should
- // trigger a network change notification, since the IP address is out-of-sync.
+ // trigger a network change notification.
FakeNetworkChangeObserver network_change_observer;
- interfaces[0].addr = IpAddressFrom(kSecondaryIPv4Address);
- netstack_.SetInterfaces(interfaces);
+ watcher_.PushEvent(MakeChangeEvent(
+ kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) {
+ props->set_addresses(MakeSingleItemVec(
+ InterfaceAddressFrom(kSecondaryIPv4Address, kSecondaryIPv4Prefix)));
+ }));
EXPECT_TRUE(network_change_observer.RunAndExpectNetworkChanges(
{NetworkChangeNotifier::CONNECTION_NONE,
@@ -378,139 +406,137 @@
TEST_F(NetworkChangeNotifierFuchsiaTest, NoChange) {
// Set a live interface with an IP address and create the notifier.
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
-
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(DefaultInterfaceProperties());
CreateNotifier();
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
notifier_->GetCurrentConnectionType());
-
- // Leave the set of interfaces unchanged, but re-send OnInterfacesChanged.
- netstack_.SetInterfaces(interfaces);
+ // Push an event with no side-effects.
+ watcher_.PushEvent(MakeChangeEvent(kDefaultInterfaceId, [](auto*) {}));
}
TEST_F(NetworkChangeNotifierFuchsiaTest, NoChangeV6) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
- interfaces[0].addr = IpAddressFrom(kDefaultIPv6Address);
- interfaces[0].netmask = IpAddressFrom(kDefaultIPv6Netmask);
-
- netstack_.SetInterfaces(interfaces);
+ auto initial = DefaultInterfaceProperties();
+ initial.set_addresses(MakeSingleItemVec(
+ InterfaceAddressFrom(kDefaultIPv6Address, kDefaultIPv6Prefix)));
+ watcher_.SetInitial(std::move(initial));
CreateNotifier();
-
- // Leave the set of interfaces unchanged, but re-send OnInterfacesChanged.
- netstack_.SetInterfaces(interfaces);
+ // Push an event with no side-effects.
+ watcher_.PushEvent(MakeChangeEvent(kDefaultInterfaceId, [](auto*) {}));
}
TEST_F(NetworkChangeNotifierFuchsiaTest, MultiInterfaceNoChange) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(2);
- interfaces[0] = DefaultNetInterface();
- interfaces[1] = SecondaryNetInterface();
-
- netstack_.SetInterfaces(interfaces);
+ std::vector<fuchsia::net::interfaces::Properties> props;
+ props.push_back(DefaultInterfaceProperties());
+ props.push_back(SecondaryInterfaceProperties());
+ watcher_.SetInitial(std::move(props));
CreateNotifier();
-
- // Leave the set of interfaces unchanged, but re-send OnInterfacesChanged.
- netstack_.SetInterfaces(interfaces);
+ // Push an event with no side-effects.
+ watcher_.PushEvent(MakeChangeEvent(kDefaultInterfaceId, [](auto*) {}));
}
TEST_F(NetworkChangeNotifierFuchsiaTest, MultiV6IPNoChange) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
- interfaces[0].ipv6addrs.push_back(SubnetFrom(kDefaultIPv6Address, 2));
+ auto props = DefaultInterfaceProperties();
+ props.mutable_addresses()->push_back(
+ InterfaceAddressFrom(kDefaultIPv6Address, kDefaultIPv6Prefix));
+ props.mutable_addresses()->push_back(
+ InterfaceAddressFrom(kSecondaryIPv6Address, kSecondaryIPv6Prefix));
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(std::move(props));
CreateNotifier();
- // Leave the set of interfaces unchanged, but re-send OnInterfacesChanged.
- netstack_.SetInterfaces(interfaces);
+ // Push an event with no side-effects.
+ watcher_.PushEvent(MakeChangeEvent(kDefaultInterfaceId, [](auto*) {}));
}
TEST_F(NetworkChangeNotifierFuchsiaTest, IpChange) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
-
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(DefaultInterfaceProperties());
CreateNotifier();
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
notifier_->GetCurrentConnectionType());
- interfaces[0].addr = IpAddressFrom(kSecondaryIPv4Address);
- netstack_.SetInterfaces(interfaces);
+ watcher_.PushEvent(MakeChangeEvent(
+ kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) {
+ props->set_addresses(MakeSingleItemVec(
+ InterfaceAddressFrom(kSecondaryIPv4Address, kSecondaryIPv4Prefix)));
+ }));
// Expect a single OnIPAddressChanged() notification.
EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1));
}
TEST_F(NetworkChangeNotifierFuchsiaTest, IpChangeV6) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
- interfaces[0].addr = IpAddressFrom(kDefaultIPv6Address);
- interfaces[0].netmask = IpAddressFrom(kDefaultIPv6Netmask);
- interfaces[0].broadaddr = IpAddressFrom(kDefaultIPv6Address);
-
- netstack_.SetInterfaces(interfaces);
+ auto props = DefaultInterfaceProperties();
+ props.set_addresses(MakeSingleItemVec(
+ InterfaceAddressFrom(kDefaultIPv6Address, kDefaultIPv6Prefix)));
+ watcher_.SetInitial(std::move(props));
CreateNotifier();
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
notifier_->GetCurrentConnectionType());
- interfaces[0].addr = IpAddressFrom(kSecondaryIPv6Address);
- interfaces[0].netmask = IpAddressFrom(kSecondaryIPv6Netmask);
- interfaces[0].broadaddr = IpAddressFrom(kSecondaryIPv6Address);
- netstack_.SetInterfaces(interfaces);
+ watcher_.PushEvent(MakeChangeEvent(
+ kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) {
+ props->set_addresses(MakeSingleItemVec(
+ InterfaceAddressFrom(kSecondaryIPv6Address, kSecondaryIPv6Prefix)));
+ }));
// Expect a single OnIPAddressChanged() notification.
EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1));
}
TEST_F(NetworkChangeNotifierFuchsiaTest, MultiV6IPChanged) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
- interfaces[0].ipv6addrs.push_back(SubnetFrom(kDefaultIPv6Address, 2));
+ auto props = DefaultInterfaceProperties();
+ props.mutable_addresses()->push_back(
+ InterfaceAddressFrom(kDefaultIPv6Address, kDefaultIPv6Prefix));
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(std::move(props));
CreateNotifier();
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
notifier_->GetCurrentConnectionType());
- interfaces[0].addr = IpAddressFrom(kSecondaryIPv4Address);
- interfaces[0].netmask = IpAddressFrom(kSecondaryIPv4Netmask);
- interfaces[0].broadaddr = IpAddressFrom(kSecondaryIPv4Address);
- interfaces[0].ipv6addrs[0] = SubnetFrom(kSecondaryIPv6Address, 2);
- netstack_.SetInterfaces(interfaces);
+ watcher_.PushEvent(MakeChangeEvent(
+ kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) {
+ std::vector<fuchsia::net::interfaces::Address> addrs;
+ addrs.push_back(
+ InterfaceAddressFrom(kSecondaryIPv4Address, kSecondaryIPv4Prefix));
+ addrs.push_back(
+ InterfaceAddressFrom(kSecondaryIPv6Address, kSecondaryIPv6Prefix));
+ props->set_addresses(std::move(addrs));
+ }));
// Expect a single OnIPAddressChanged() notification.
EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1));
}
TEST_F(NetworkChangeNotifierFuchsiaTest, Ipv6AdditionalIpChange) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
-
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(DefaultInterfaceProperties());
CreateNotifier();
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
notifier_->GetCurrentConnectionType());
- interfaces[0].ipv6addrs.push_back(SubnetFrom(kDefaultIPv6Address, 2));
- netstack_.SetInterfaces(interfaces);
+ watcher_.PushEvent(MakeChangeEvent(
+ kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) {
+ // Add the initial default address + a new IPv6 one. Address changes are
+ // always sent as the entire new list of addresses.
+ props->mutable_addresses()->push_back(
+ InterfaceAddressFrom(kDefaultIPv4Address, kDefaultIPv4Prefix));
+ props->mutable_addresses()->push_back(
+ InterfaceAddressFrom(kDefaultIPv6Address, kDefaultIPv6Prefix));
+ }));
// Expect a single OnIPAddressChanged() notification.
EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1));
}
TEST_F(NetworkChangeNotifierFuchsiaTest, InterfaceDown) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
-
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(DefaultInterfaceProperties());
CreateNotifier();
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
notifier_->GetCurrentConnectionType());
- interfaces[0].flags = {};
- netstack_.SetInterfaces(interfaces);
+ watcher_.PushEvent(MakeChangeEvent(
+ kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) {
+ props->set_online(false);
+ }));
EXPECT_TRUE(type_observer_->RunAndExpectConnectionTypes(
{NetworkChangeNotifier::ConnectionType::CONNECTION_NONE}));
@@ -518,17 +544,17 @@
}
TEST_F(NetworkChangeNotifierFuchsiaTest, InterfaceUp) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
- interfaces[0].flags = {};
-
- netstack_.SetInterfaces(interfaces);
+ auto props = DefaultInterfaceProperties();
+ props.set_online(false);
+ watcher_.SetInitial(std::move(props));
CreateNotifier();
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_NONE,
notifier_->GetCurrentConnectionType());
- interfaces[0].flags = fuchsia::netstack::Flags::UP;
- netstack_.SetInterfaces(interfaces);
+ watcher_.PushEvent(MakeChangeEvent(
+ kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) {
+ props->set_online(true);
+ }));
EXPECT_TRUE(type_observer_->RunAndExpectConnectionTypes(
{NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN}));
@@ -536,15 +562,13 @@
}
TEST_F(NetworkChangeNotifierFuchsiaTest, InterfaceDeleted) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
-
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(DefaultInterfaceProperties());
CreateNotifier();
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
notifier_->GetCurrentConnectionType());
- netstack_.SetInterfaces({});
+ watcher_.PushEvent(
+ fuchsia::net::interfaces::Event::WithRemoved(kDefaultInterfaceId));
EXPECT_TRUE(type_observer_->RunAndExpectConnectionTypes(
{NetworkChangeNotifier::ConnectionType::CONNECTION_NONE}));
@@ -557,11 +581,9 @@
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_NONE,
notifier_->GetCurrentConnectionType());
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
- interfaces[0].features = fuchsia::hardware::ethernet::Features::WLAN;
-
- netstack_.SetInterfaces(interfaces);
+ watcher_.PushEvent(
+ fuchsia::net::interfaces::Event::WithAdded(DefaultInterfaceProperties(
+ fuchsia::hardware::network::DeviceClass::WLAN)));
EXPECT_TRUE(type_observer_->RunAndExpectConnectionTypes(
{NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI}));
@@ -569,56 +591,44 @@
}
TEST_F(NetworkChangeNotifierFuchsiaTest, SecondaryInterfaceAddedNoop) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
-
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(DefaultInterfaceProperties());
CreateNotifier();
- interfaces.push_back(SecondaryNetInterface());
- netstack_.SetInterfaces(interfaces);
+ watcher_.PushEvent(fuchsia::net::interfaces::Event::WithAdded(
+ SecondaryInterfaceProperties()));
}
TEST_F(NetworkChangeNotifierFuchsiaTest, SecondaryInterfaceDeletedNoop) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(2);
- interfaces[0] = DefaultNetInterface();
- interfaces[1] = SecondaryNetInterface();
+ std::vector<fuchsia::net::interfaces::Properties> interfaces;
+ interfaces.push_back(DefaultInterfaceProperties());
+ interfaces.push_back(SecondaryInterfaceProperties());
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(std::move(interfaces));
CreateNotifier();
- interfaces.pop_back();
- netstack_.SetInterfaces(interfaces);
+ watcher_.PushEvent(
+ fuchsia::net::interfaces::Event::WithRemoved(kSecondaryInterfaceId));
}
TEST_F(NetworkChangeNotifierFuchsiaTest, FoundWiFi) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
- interfaces[0].features = fuchsia::hardware::ethernet::Features::WLAN;
-
- netstack_.SetInterfaces(interfaces);
+ watcher_.SetInitial(DefaultInterfaceProperties(
+ fuchsia::hardware::network::DeviceClass::WLAN));
CreateNotifier();
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI,
notifier_->GetCurrentConnectionType());
}
-TEST_F(NetworkChangeNotifierFuchsiaTest, FindsInterfaceWithRequiredFeature) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
- interfaces[0].features = fuchsia::hardware::ethernet::Features::WLAN;
-
- netstack_.SetInterfaces(interfaces);
- CreateNotifier(fuchsia::hardware::ethernet::Features::WLAN);
+TEST_F(NetworkChangeNotifierFuchsiaTest, FindsInterfaceWithRequiredWlan) {
+ watcher_.SetInitial(DefaultInterfaceProperties(
+ fuchsia::hardware::network::DeviceClass::WLAN));
+ CreateNotifier(/*require_wlan=*/true);
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI,
notifier_->GetCurrentConnectionType());
}
-TEST_F(NetworkChangeNotifierFuchsiaTest, IgnoresInterfaceWithMissingFeature) {
- std::vector<fuchsia::netstack::NetInterface> interfaces(1);
- interfaces[0] = DefaultNetInterface();
-
- netstack_.SetInterfaces(interfaces);
- CreateNotifier(fuchsia::hardware::ethernet::Features::WLAN);
+TEST_F(NetworkChangeNotifierFuchsiaTest, IgnoresNonWlanInterface) {
+ watcher_.SetInitial(DefaultInterfaceProperties());
+ CreateNotifier(/*require_wlan=*/true);
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_NONE,
notifier_->GetCurrentConnectionType());
}
diff --git a/net/base/network_interfaces_fuchsia.cc b/net/base/network_interfaces_fuchsia.cc
index 47aa4dc..e04c349 100644
--- a/net/base/network_interfaces_fuchsia.cc
+++ b/net/base/network_interfaces_fuchsia.cc
@@ -4,9 +4,6 @@
#include "net/base/network_interfaces_fuchsia.h"
-#include <fuchsia/hardware/ethernet/cpp/fidl.h>
-#include <fuchsia/net/cpp/fidl.h>
-#include <fuchsia/netstack/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include <string>
@@ -16,124 +13,222 @@
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/process_context.h"
#include "base/strings/stringprintf.h"
-#include "net/base/ip_endpoint.h"
#include "net/base/network_interfaces.h"
namespace net {
namespace internal {
namespace {
-using ConnectionType = NetworkChangeNotifier::ConnectionType;
-
-// Converts a Netstack NetInterface |interface| to a Chrome NetworkInterface.
-// NetInterfaces may be bound to multiple IPv6 addresses. |address_index| is
-// used to specify which address to use for the conversion.
-// address_index = 0: Uses NetInterface::addr, NetInterface::netmask.
-// address_index >= 1: Uses NetInterface::ipv6addrs[], with the array index
-// offset by one.
-NetworkInterface NetworkInterfaceFromAddress(
- const fuchsia::netstack::NetInterface& interface,
- size_t address_index) {
- // TODO(crbug.com/1131220): attributes field is used to return address state
- // for IPv6 addresses. Currently Netstack doesn't provide this information.
- const int attributes = 0;
-
- IPAddress address;
- uint8_t prefix_length;
- if (address_index == 0) {
- address = FuchsiaIpAddressToIPAddress(interface.addr);
- prefix_length =
- MaskPrefixLength(FuchsiaIpAddressToIPAddress(interface.netmask));
- } else {
- CHECK_LE(address_index, interface.ipv6addrs.size());
- address = FuchsiaIpAddressToIPAddress(
- interface.ipv6addrs.at(address_index - 1).addr);
- prefix_length = interface.ipv6addrs.at(address_index - 1).prefix_len;
+IPAddress FuchsiaIpAddressToIPAddress(const fuchsia::net::IpAddress& address) {
+ switch (address.Which()) {
+ case fuchsia::net::IpAddress::kIpv4:
+ return IPAddress(address.ipv4().addr.data(), address.ipv4().addr.size());
+ case fuchsia::net::IpAddress::kIpv6:
+ return IPAddress(address.ipv6().addr.data(), address.ipv6().addr.size());
+ default:
+ return IPAddress();
}
-
- return NetworkInterface(interface.name, interface.name, interface.id,
- ConvertConnectionType(interface), address,
- prefix_length, attributes);
}
} // namespace
+// static
+base::Optional<InterfaceProperties> InterfaceProperties::VerifyAndCreate(
+ fuchsia::net::interfaces::Properties properties) {
+ if (!internal::VerifyCompleteInterfaceProperties(properties))
+ return base::nullopt;
+ return base::make_optional(InterfaceProperties(std::move(properties)));
+}
+
+InterfaceProperties::InterfaceProperties(
+ fuchsia::net::interfaces::Properties properties)
+ : properties_(std::move(properties)) {}
+
+InterfaceProperties::InterfaceProperties(InterfaceProperties&& interface) =
+ default;
+
+InterfaceProperties& InterfaceProperties::operator=(
+ InterfaceProperties&& interface) = default;
+
+InterfaceProperties::~InterfaceProperties() = default;
+
+bool InterfaceProperties::Update(
+ fuchsia::net::interfaces::Properties properties) {
+ if (!properties.has_id() || properties_.id() != properties.id()) {
+ LOG(WARNING) << "Update failed: invalid properties.";
+ return false;
+ }
+
+ if (properties.has_addresses()) {
+ for (const auto& fidl_address : properties.addresses()) {
+ if (!fidl_address.has_addr()) {
+ LOG(WARNING) << "Update failed: invalid properties.";
+ return false;
+ }
+ }
+ properties_.set_addresses(std::move(*properties.mutable_addresses()));
+ }
+
+ if (properties.has_online())
+ properties_.set_online(properties.online());
+ if (properties.has_has_default_ipv4_route())
+ properties_.set_has_default_ipv4_route(properties.has_default_ipv4_route());
+ if (properties.has_has_default_ipv6_route())
+ properties_.set_has_default_ipv6_route(properties.has_default_ipv6_route());
+
+ return true;
+}
+
+void InterfaceProperties::AppendNetworkInterfaces(
+ NetworkInterfaceList* interfaces) const {
+ uint32_t address_counter = 1;
+ for (const auto& fidl_address : properties_.addresses()) {
+ IPAddress address = FuchsiaIpAddressToIPAddress(fidl_address.addr().addr);
+ if (address.empty()) {
+ LOG(WARNING) << "Unknown fuchsia.net/IpAddress variant "
+ << fidl_address.addr().addr.Which();
+ continue;
+ }
+
+ // TODO(crbug.com/1131220): Set correct attributes once available in
+ // fuchsia::net::interfaces::Properties.
+ const int kAttributes = 0;
+ std::string interface_name = base::StringPrintf(
+ "%s-%d", properties_.name().c_str(), address_counter);
+ address_counter++;
+ interfaces->emplace_back(
+ interface_name, interface_name, properties_.id(),
+ internal::ConvertConnectionType(properties_.device_class()),
+ std::move(address), fidl_address.addr().prefix_len, kAttributes);
+ }
+}
+
+bool InterfaceProperties::IsPubliclyRoutable() const {
+ if (!properties_.online())
+ return false;
+
+ for (const auto& fidl_address : properties_.addresses()) {
+ const IPAddress address =
+ FuchsiaIpAddressToIPAddress(fidl_address.addr().addr);
+ if ((address.IsIPv4() && properties_.has_default_ipv4_route() &&
+ !address.IsLinkLocal()) ||
+ (address.IsIPv6() && properties_.has_default_ipv6_route() &&
+ address.IsPubliclyRoutable())) {
+ return true;
+ }
+ }
+ return false;
+}
+
NetworkChangeNotifier::ConnectionType ConvertConnectionType(
- const fuchsia::netstack::NetInterface& iface) {
- if ((iface.flags & fuchsia::netstack::Flags::UP) !=
- fuchsia::netstack::Flags::UP) {
- return NetworkChangeNotifier::CONNECTION_NONE;
- } else if ((iface.features & fuchsia::hardware::ethernet::Features::WLAN) ==
- fuchsia::hardware::ethernet::Features::WLAN) {
- return NetworkChangeNotifier::CONNECTION_WIFI;
+ const fuchsia::net::interfaces::DeviceClass& device_class) {
+ switch (device_class.Which()) {
+ case fuchsia::net::interfaces::DeviceClass::kLoopback:
+ return NetworkChangeNotifier::CONNECTION_NONE;
+ case fuchsia::net::interfaces::DeviceClass::kDevice:
+ switch (device_class.device()) {
+ case fuchsia::hardware::network::DeviceClass::WLAN:
+ return NetworkChangeNotifier::CONNECTION_WIFI;
+ case fuchsia::hardware::network::DeviceClass::ETHERNET:
+ return NetworkChangeNotifier::CONNECTION_ETHERNET;
+ default:
+ return NetworkChangeNotifier::CONNECTION_UNKNOWN;
+ }
+ default:
+ LOG(WARNING) << "Received unknown fuchsia.net.interfaces/DeviceClass "
+ << device_class.Which();
+ return NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
- return NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
-IPAddress FuchsiaIpAddressToIPAddress(const fuchsia::net::IpAddress& addr) {
- if (addr.is_ipv4()) {
- return IPAddress(addr.ipv4().addr.data(), addr.ipv4().addr.size());
- }
- if (addr.is_ipv6()) {
- return IPAddress(addr.ipv6().addr.data(), addr.ipv6().addr.size());
- }
- return IPAddress();
+fuchsia::net::interfaces::WatcherHandle ConnectInterfacesWatcher() {
+ fuchsia::net::interfaces::StateSyncPtr state;
+ zx_status_t status =
+ base::ComponentContextForProcess()->svc()->Connect(state.NewRequest());
+ ZX_CHECK(status == ZX_OK, status) << "Connect()";
+
+ fuchsia::net::interfaces::WatcherHandle watcher;
+ state->GetWatcher({} /*options*/, watcher.NewRequest());
+ return watcher;
}
-std::vector<NetworkInterface> NetInterfaceToNetworkInterfaces(
- const fuchsia::netstack::NetInterface& iface_in) {
- std::vector<NetworkInterface> output;
-
- // If the interface is not currently up then there are no addresses to return.
- if (internal::ConvertConnectionType(iface_in) ==
- NetworkChangeNotifier::CONNECTION_NONE) {
- return output;
+bool VerifyCompleteInterfaceProperties(
+ const fuchsia::net::interfaces::Properties& properties) {
+ if (!properties.has_id())
+ return false;
+ if (!properties.has_addresses())
+ return false;
+ for (const auto& fidl_address : properties.addresses()) {
+ if (!fidl_address.has_addr())
+ return false;
}
+ if (!properties.has_online())
+ return false;
+ if (!properties.has_device_class())
+ return false;
+ if (!properties.has_has_default_ipv4_route())
+ return false;
+ if (!properties.has_has_default_ipv6_route())
+ return false;
+ return true;
+}
- // It is possible for the interface not to have an IPv4 address.
- NetworkInterface ipv4_interface = NetworkInterfaceFromAddress(iface_in, 0);
- if (!ipv4_interface.address.IsZero())
- output.push_back(std::move(ipv4_interface));
+base::Optional<ExistingInterfaceProperties> GetExistingInterfaces(
+ const fuchsia::net::interfaces::WatcherSyncPtr& watcher) {
+ ExistingInterfaceProperties existing_interfaces;
+ for (;;) {
+ fuchsia::net::interfaces::Event event;
+ zx_status_t status = watcher->Watch(&event);
+ if (status != ZX_OK)
+ return base::nullopt;
- // Append interface entries for all additional IPv6 addresses.
- for (size_t i = 0; i < iface_in.ipv6addrs.size(); ++i) {
- output.push_back(NetworkInterfaceFromAddress(iface_in, i + 1));
+ switch (event.Which()) {
+ case fuchsia::net::interfaces::Event::Tag::kExisting: {
+ base::Optional<InterfaceProperties> interface =
+ InterfaceProperties::VerifyAndCreate(std::move(event.existing()));
+ if (!interface) {
+ return base::nullopt;
+ }
+ uint64_t id = interface->id();
+ existing_interfaces.emplace_back(id, std::move(*interface));
+ break;
+ }
+ case fuchsia::net::interfaces::Event::Tag::kIdle:
+ // Idle means we've listed all the existing interfaces. We can stop
+ // fetching events.
+ return existing_interfaces;
+ default:
+ return base::nullopt;
+ }
}
-
- return output;
}
} // namespace internal
bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
DCHECK(networks);
-
- fuchsia::netstack::NetstackSyncPtr netstack;
- base::ComponentContextForProcess()->svc()->Connect(netstack.NewRequest());
+ fuchsia::net::interfaces::WatcherHandle handle =
+ internal::ConnectInterfacesWatcher();
+ fuchsia::net::interfaces::WatcherSyncPtr watcher = handle.BindSync();
// TODO(crbug.com/1131238): Use NetworkChangeNotifier's cached interface
// list.
- std::vector<fuchsia::netstack::NetInterface> interfaces;
- zx_status_t status = netstack->GetInterfaces(&interfaces);
- if (status != ZX_OK) {
- ZX_LOG(ERROR, status) << "fuchsia::netstack::GetInterfaces()";
+ base::Optional<internal::ExistingInterfaceProperties> existing_interfaces =
+ internal::GetExistingInterfaces(watcher);
+ if (!existing_interfaces)
return false;
- }
-
- for (auto& interface : interfaces) {
- if ((internal::ConvertConnectionType(interface) ==
- NetworkChangeNotifier::CONNECTION_NONE) ||
- (interface.features &
- fuchsia::hardware::ethernet::Features::LOOPBACK) ==
- fuchsia::hardware::ethernet::Features::LOOPBACK) {
+ handle = watcher.Unbind();
+ for (const auto& interface_entry : *existing_interfaces) {
+ if (!interface_entry.second.online()) {
+ // GetNetworkList() only returns online interfaces.
continue;
}
-
- auto converted = internal::NetInterfaceToNetworkInterfaces(interface);
- std::move(converted.begin(), converted.end(),
- std::back_inserter(*networks));
+ if (interface_entry.second.device_class().is_loopback()) {
+ // GetNetworkList() returns all interfaces except loopback.
+ continue;
+ }
+ interface_entry.second.AppendNetworkInterfaces(networks);
}
-
return true;
}
diff --git a/net/base/network_interfaces_fuchsia.h b/net/base/network_interfaces_fuchsia.h
index b9dcf9c..38ced90 100644
--- a/net/base/network_interfaces_fuchsia.h
+++ b/net/base/network_interfaces_fuchsia.h
@@ -5,41 +5,81 @@
#ifndef NET_BASE_NETWORK_INTERFACES_FUCHSIA_H_
#define NET_BASE_NETWORK_INTERFACES_FUCHSIA_H_
+#include <fuchsia/net/interfaces/cpp/fidl.h>
+
#include <vector>
#include "net/base/network_change_notifier.h"
-
-namespace fuchsia {
-namespace net {
-class IpAddress;
-}
-namespace netstack {
-class NetInterface;
-} // namespace netstack
-} // namespace fuchsia
+#include "net/base/network_interfaces.h"
namespace net {
-
-class IPAddress;
-struct NetworkInterface;
-
namespace internal {
+// Move-only wrapper for fuchsia::net::interface::Properties that guarantees
+// that its inner |properties_| are valid and complete properties as reported by
+// |VerifyCompleteInterfaceProperties|.
+class InterfaceProperties final {
+ public:
+ // Creates an |InterfaceProperties| if |properties| are valid complete
+ // properties as reported by |VerifyCompleteInterfaceProperties|.
+ static base::Optional<InterfaceProperties> VerifyAndCreate(
+ fuchsia::net::interfaces::Properties properties);
+ InterfaceProperties(InterfaceProperties&& interface);
+ InterfaceProperties& operator=(InterfaceProperties&& interface);
+ ~InterfaceProperties();
+
+ // Updates this instance with the values set in |properties|.
+ // Fields not set in |properties| retain their previous values.
+ // Returns false if the |properties| has a missing or mismatched |id| field,
+ // or if any field set in |properties| has an invalid value (e.g. addresses of
+ // unknown types).
+ bool Update(fuchsia::net::interfaces::Properties properties);
+
+ // Appends the NetworkInterfaces for this interface to |interfaces|.
+ void AppendNetworkInterfaces(NetworkInterfaceList* interfaces) const;
+
+ // Returns true if the interface is online and it has either an IPv4 default
+ // route and a non-link-local address, or an IPv6 default route and a global
+ // address.
+ bool IsPubliclyRoutable() const;
+
+ bool HasAddresses() const { return !properties_.addresses().empty(); }
+ uint64_t id() const { return properties_.id(); }
+ bool online() const { return properties_.online(); }
+ const fuchsia::net::interfaces::DeviceClass& device_class() const {
+ return properties_.device_class();
+ }
+
+ private:
+ explicit InterfaceProperties(fuchsia::net::interfaces::Properties properties);
+
+ fuchsia::net::interfaces::Properties properties_;
+};
+
+using ExistingInterfaceProperties =
+ std::vector<std::pair<uint64_t, InterfaceProperties>>;
+
// Returns the //net ConnectionType for the supplied netstack interface
-// description. Returns ConnectionType::CONNECTION_NONE if the interface is not
-// "up".
+// description. Returns CONNECTION_NONE for loopback interfaces.
NetworkChangeNotifier::ConnectionType ConvertConnectionType(
- const fuchsia::netstack::NetInterface& iface);
+ const fuchsia::net::interfaces::DeviceClass& device_class);
-// Converts a Fuchsia Netstack NetInterface object to NetworkInterface objects.
-// Interfaces with more than one IPv6 address will yield multiple
-// NetworkInterface objects, with friendly names to distinguish the different
-// IPs (e.g. "wlan" with three IPv6 IPs yields wlan-0, wlan-1, wlan-2).
-std::vector<NetworkInterface> NetInterfaceToNetworkInterfaces(
- const fuchsia::netstack::NetInterface& iface_in);
+// Connects to the service via the process' ComponentContext, and connects the
+// Watcher to the service.
+fuchsia::net::interfaces::WatcherHandle ConnectInterfacesWatcher();
-// Converts a Fuchsia IPv4/IPv6 address to a Chromium IPAddress.
-IPAddress FuchsiaIpAddressToIPAddress(const fuchsia::net::IpAddress& addr);
+// Validates that |properties| contains all the required fields, returning
+// |true| if so.
+bool VerifyCompleteInterfaceProperties(
+ const fuchsia::net::interfaces::Properties& properties);
+
+// Consumes events describing existing interfaces from |watcher| and
+// returns a vector of interface Id & properties pairs. |watcher| must
+// be a newly-connected Watcher channel.
+// Returns base::nullopt if any protocol error is encountered, e.g.
+// |watcher| supplies an invalid event, or disconnects.
+base::Optional<internal::ExistingInterfaceProperties> GetExistingInterfaces(
+ const fuchsia::net::interfaces::WatcherSyncPtr& watcher);
} // namespace internal
} // namespace net