Fuchsia: Implement NetworkChangeNotifierFuchsia with FIDL APIs.

This code adds Fuchsia platform implementation code for broadcasting
events on major network interface change events (IP
address changes, connectivity change, interface up/down changes, etc.)

Modified network_interfaces_fuchsia.cc to support adapters w/multiple
IPv6 addresses.

Added unit tests.


Bug: 843443
Change-Id: Id7edc944ceef439d9c360e5d31966807f306489c
Reviewed-on: https://chromium-review.googlesource.com/1066074
Commit-Queue: Kevin Marshall <[email protected]>
Reviewed-by: Paul Jensen <[email protected]>
Reviewed-by: Wez <[email protected]>
Reviewed-by: Sergey Ulanov <[email protected]>
Cr-Commit-Position: refs/heads/master@{#564669}
diff --git a/net/base/network_interfaces_fuchsia.cc b/net/base/network_interfaces_fuchsia.cc
index 1c03e968..9d7a83d 100644
--- a/net/base/network_interfaces_fuchsia.cc
+++ b/net/base/network_interfaces_fuchsia.cc
@@ -2,14 +2,64 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "net/base/network_interfaces.h"
+#include "net/base/network_interfaces_fuchsia.h"
 
 #include <fuchsia/netstack/cpp/fidl.h>
 
+#include <string>
+#include <utility>
+
+#include "base/format_macros.h"
 #include "base/fuchsia/component_context.h"
+#include "base/strings/stringprintf.h"
 #include "net/base/ip_endpoint.h"
+#include "net/base/network_change_notifier.h"
+#include "net/base/network_interfaces.h"
 
 namespace net {
+namespace internal {
+namespace {
+
+using ConnectionType = NetworkChangeNotifier::ConnectionType;
+
+ConnectionType ConvertConnectionType(
+    const fuchsia::netstack::NetInterface& iface) {
+  return iface.features & fuchsia::netstack::interfaceFeatureWlan
+             ? NetworkChangeNotifier::CONNECTION_WIFI
+             : NetworkChangeNotifier::CONNECTION_UNKNOWN;
+}
+
+// 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(sergeyu): 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 = NetAddressToIPAddress(interface.addr);
+    prefix_length = MaskPrefixLength(NetAddressToIPAddress(interface.netmask));
+  } else {
+    CHECK_LE(address_index, interface.ipv6addrs->size());
+    address =
+        NetAddressToIPAddress(interface.ipv6addrs->at(address_index - 1).addr);
+    prefix_length = interface.ipv6addrs->at(address_index - 1).prefix_len;
+  }
+
+  return NetworkInterface(*interface.name, interface.name, interface.id,
+                          ConvertConnectionType(interface), address,
+                          prefix_length, attributes);
+}
+
+}  // namespace
 
 IPAddress NetAddressToIPAddress(const fuchsia::netstack::NetAddress& addr) {
   if (addr.ipv4) {
@@ -21,38 +71,46 @@
   return IPAddress();
 }
 
+std::vector<NetworkInterface> NetInterfaceToNetworkInterfaces(
+    const fuchsia::netstack::NetInterface& iface_in) {
+  std::vector<NetworkInterface> output;
+
+  // Check if the interface is up.
+  if (!(iface_in.flags & fuchsia::netstack::NetInterfaceFlagUp))
+    return output;
+
+  // Skip loopback.
+  if (iface_in.features & fuchsia::netstack::interfaceFeatureLoopback)
+    return output;
+
+  output.push_back(NetworkInterfaceFromAddress(iface_in, 0));
+
+  // 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));
+  }
+
+  return output;
+}
+
+}  // namespace internal
+
 bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
+  DCHECK(networks);
+
   fuchsia::netstack::NetstackSyncPtr netstack =
       base::fuchsia::ComponentContext::GetDefault()
           ->ConnectToServiceSync<fuchsia::netstack::Netstack>();
 
+  // TODO(kmarshall): Use NetworkChangeNotifier's cached interface list.
   fidl::VectorPtr<fuchsia::netstack::NetInterface> interfaces;
   if (!netstack->GetInterfaces(&interfaces))
     return false;
 
   for (auto& interface : interfaces.get()) {
-    // Check if the interface is up.
-    if (!(interface.flags & fuchsia::netstack::NetInterfaceFlagUp))
-      continue;
-
-    // Skip loopback.
-    if (interface.features & fuchsia::netstack::interfaceFeatureLoopback)
-      continue;
-
-    NetworkChangeNotifier::ConnectionType connection_type =
-        (interface.features & fuchsia::netstack::interfaceFeatureWlan)
-            ? NetworkChangeNotifier::CONNECTION_WIFI
-            : NetworkChangeNotifier::CONNECTION_UNKNOWN;
-
-    // TODO(sergeyu): attributes field is used to return address state for IPv6
-    // addresses. Currently Netstack doesn't provide this information.
-    int attributes = 0;
-
-    networks->push_back(NetworkInterface(
-        *interface.name, *interface.name, interface.id, connection_type,
-        NetAddressToIPAddress(interface.addr),
-        MaskPrefixLength(NetAddressToIPAddress(interface.netmask)),
-        attributes));
+    auto converted = internal::NetInterfaceToNetworkInterfaces(interface);
+    std::move(converted.begin(), converted.end(),
+              std::back_inserter(*networks));
   }
 
   return true;