[network/tests] add DHCP integration test
...after porting similar tests to Rust.
Also harden netcfg against disappearing devices.
NET-2115 #done
Change-Id: Id7ed5d148d6f2930aae50c6e133c0087b753e8d6
diff --git a/garnet/bin/dhcpd/data/test_server_config.json b/garnet/bin/dhcpd/data/test_server_config.json
index 0308cbf..e7b45b3 100644
--- a/garnet/bin/dhcpd/data/test_server_config.json
+++ b/garnet/bin/dhcpd/data/test_server_config.json
@@ -1,5 +1,5 @@
{
- "server_ip": "127.0.0.1",
+ "server_ip": "192.168.0.1",
"default_lease_time": 86400,
"subnet_mask": 24,
"managed_addrs": [
diff --git a/garnet/bin/netcfg/src/main.rs b/garnet/bin/netcfg/src/main.rs
index ed2c8cf..37217a9 100644
--- a/garnet/bin/netcfg/src/main.rs
+++ b/garnet/bin/netcfg/src/main.rs
@@ -248,75 +248,80 @@
fidl_fuchsia_hardware_ethernet::DeviceMarker,
>::new(client)
.into_proxy()?;
- let device_info = await!(device.get_info())?;
- let device_info: fidl_fuchsia_hardware_ethernet_ext::EthernetInfo =
- device_info.into();
- if device_info.features.is_physical() {
- let client = device
- .into_channel()
- .map_err(|fidl_fuchsia_hardware_ethernet::DeviceProxy { .. }| {
- failure::err_msg("failed to convert device proxy into channel")
- })?
- .into_zx_channel();
+ if let Ok(device_info) = await!(device.get_info()) {
+ let device_info: fidl_fuchsia_hardware_ethernet_ext::EthernetInfo =
+ device_info.into();
- let name = persisted_interface_config.get_stable_name(
- topological_path.clone(), /* TODO(tamird): we can probably do
- * better with std::borrow::Cow. */
- device_info.mac,
- device_info.features.contains(
+ if device_info.features.is_physical() {
+ let client = device
+ .into_channel()
+ .map_err(
+ |fidl_fuchsia_hardware_ethernet::DeviceProxy { .. }| {
+ failure::err_msg(
+ "failed to convert device proxy into channel",
+ )
+ },
+ )?
+ .into_zx_channel();
+
+ let name = persisted_interface_config.get_stable_name(
+ topological_path.clone(), /* TODO(tamird): we can probably do
+ * better with std::borrow::Cow. */
+ device_info.mac,
+ device_info.features.contains(
+ fidl_fuchsia_hardware_ethernet_ext::EthernetFeatures::WLAN,
+ ),
+ )?;
+
+ // Hardcode the interface metric. Eventually this should
+ // be part of the config file.
+ let metric: u32 = match device_info.features.contains(
fidl_fuchsia_hardware_ethernet_ext::EthernetFeatures::WLAN,
- ),
- )?;
+ ) {
+ true => INTF_METRIC_WLAN,
+ false => INTF_METRIC_ETH,
+ };
+ let mut derived_interface_config = matchers::config_for_device(
+ &device_info,
+ name.to_string(),
+ &topological_path,
+ metric,
+ &default_config_rules,
+ );
+ let nic_id = await!(netstack.add_ethernet_device(
+ &topological_path,
+ &mut derived_interface_config,
+ fidl::endpoints::ClientEnd::<
+ fidl_fuchsia_hardware_ethernet::DeviceMarker,
+ >::new(client),
+ ))
+ .with_context(|_| {
+ format!(
+ "fidl_netstack::Netstack::add_ethernet_device({})",
+ filename.display()
+ )
+ })?;
- // Hardcode the interface metric. Eventually this should
- // be part of the config file.
- let metric: u32 = match device_info
- .features
- .contains(fidl_fuchsia_hardware_ethernet_ext::EthernetFeatures::WLAN)
- {
- true => INTF_METRIC_WLAN,
- false => INTF_METRIC_ETH,
- };
- let mut derived_interface_config = matchers::config_for_device(
- &device_info,
- name.to_string(),
- &topological_path,
- metric,
- &default_config_rules,
- );
- let nic_id = await!(netstack.add_ethernet_device(
- &topological_path,
- &mut derived_interface_config,
- fidl::endpoints::ClientEnd::<
- fidl_fuchsia_hardware_ethernet::DeviceMarker,
- >::new(client),
- ))
- .with_context(|_| {
- format!(
- "fidl_netstack::Netstack::add_ethernet_device({})",
- filename.display()
- )
- })?;
+ await!(match derived_interface_config.ip_address_config {
+ fidl_fuchsia_netstack::IpAddressConfig::Dhcp(_) => {
+ netstack.set_dhcp_client_status(nic_id as u32, true)
+ }
+ fidl_fuchsia_netstack::IpAddressConfig::StaticIp(
+ fidl_fuchsia_net::Subnet { addr: mut address, prefix_len },
+ ) => netstack.set_interface_address(
+ nic_id as u32,
+ &mut address,
+ prefix_len
+ ),
+ })?;
+ let () = netstack.set_interface_status(nic_id as u32, true)?;
- await!(match derived_interface_config.ip_address_config {
- fidl_fuchsia_netstack::IpAddressConfig::Dhcp(_) => {
- netstack.set_dhcp_client_status(nic_id as u32, true)
- }
- fidl_fuchsia_netstack::IpAddressConfig::StaticIp(
- fidl_fuchsia_net::Subnet { addr: mut address, prefix_len },
- ) => netstack.set_interface_address(
- nic_id as u32,
- &mut address,
- prefix_len
- ),
- })?;
- let () = netstack.set_interface_status(nic_id as u32, true)?;
-
- // TODO(chunyingw): when netcfg switches to stack.add_ethernet_interface,
- // remove casting nic_id to u64.
- await!(interface_ids.lock())
- .insert(derived_interface_config.name, nic_id as u64);
+ // TODO(chunyingw): when netcfg switches to stack.add_ethernet_interface,
+ // remove casting nic_id to u64.
+ await!(interface_ids.lock())
+ .insert(derived_interface_config.name, nic_id as u64);
+ }
}
}
diff --git a/garnet/packages/tests/BUILD.gn b/garnet/packages/tests/BUILD.gn
index 7d22d0f..1073c42 100644
--- a/garnet/packages/tests/BUILD.gn
+++ b/garnet/packages/tests/BUILD.gn
@@ -478,9 +478,19 @@
]
}
+group("netstack_integration_tests") {
+ testonly = true
+ public_deps = [
+ "//garnet/bin/dhcpd",
+ "//src/connectivity/network/testing/netemul",
+ "//src/connectivity/network/tests:netstack_integration_tests",
+ ]
+}
+
group("netstack") {
testonly = true
public_deps = [
+ ":netstack_integration_tests",
"//garnet/packages/prod:netstack",
"//src/connectivity/network/netstack:netstack_gotests",
"//src/connectivity/network/netstack:netstack_non_component_gotests",
@@ -489,9 +499,7 @@
"//src/connectivity/network/netstack/routes:netstack_routes_gotests",
"//src/connectivity/network/netstack/tests:netstack_manual_tests",
"//src/connectivity/network/netstack/util:netstack_util_test($host_toolchain)",
- "//src/connectivity/network/tests:netstack_bsdsocket_c_api_test($host_toolchain)",
"//src/connectivity/network/tests:netstack_c_api_tests",
- "//src/connectivity/network/tests:netstack_integration_tests",
"//src/connectivity/network/tests/test_filter_client",
"//src/connectivity/network/tests/test_ioctl_client",
"//src/connectivity/network/tests/test_no_network_client",
diff --git a/src/connectivity/network/tests/BUILD.gn b/src/connectivity/network/tests/BUILD.gn
index 29d04f4..24ecf1c 100644
--- a/src/connectivity/network/tests/BUILD.gn
+++ b/src/connectivity/network/tests/BUILD.gn
@@ -11,7 +11,6 @@
testonly = true
sources = [
- "netstack_add_eth_test.cc",
"netstack_filter_test.cc",
"netstack_ioctl_test.cc",
"netstack_no_network_test.cc",
@@ -37,9 +36,16 @@
edition = "2018"
deps = [
+ "//garnet/public/lib/fidl/rust/fidl",
"//garnet/public/rust/fuchsia-async",
"//garnet/public/rust/fuchsia-component",
"//garnet/public/rust/fuchsia-zircon",
+ "//sdk/fidl/fuchsia.netstack:fuchsia.netstack-rustc",
+ "//sdk/fidl/fuchsia.sys:fuchsia.sys-rustc",
+ "//src/connectivity/network/testing/netemul/lib/fidl:environment-rustc",
+ "//src/connectivity/network/testing/netemul/lib/fidl:network-rustc",
+ "//src/connectivity/network/testing/netemul/lib/fidl:sandbox-rustc",
+ "//third_party/rust_crates:failure",
"//third_party/rust_crates:futures-preview",
"//zircon/public/fidl/fuchsia-hardware-ethernet:fuchsia-hardware-ethernet-rustc",
"//zircon/public/fidl/fuchsia-net:fuchsia-net-rustc",
diff --git a/src/connectivity/network/tests/meta/netstack_fidl_integration_lib_test.cmx b/src/connectivity/network/tests/meta/netstack_fidl_integration_lib_test.cmx
index d2c35390..f9c4c8c 100644
--- a/src/connectivity/network/tests/meta/netstack_fidl_integration_lib_test.cmx
+++ b/src/connectivity/network/tests/meta/netstack_fidl_integration_lib_test.cmx
@@ -2,7 +2,7 @@
"facets": {
"fuchsia.test": {
"injected-services": {
- "fuchsia.net.stack.Stack": "fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cmx"
+ "fuchsia.netemul.sandbox.Sandbox": "fuchsia-pkg://fuchsia.com/netemul_sandbox#meta/netemul_sandbox.cmx"
}
}
},
@@ -11,7 +11,7 @@
},
"sandbox": {
"services": [
- "fuchsia.net.stack.Stack"
+ "fuchsia.netemul.sandbox.Sandbox"
]
}
}
diff --git a/src/connectivity/network/tests/netstack_add_eth_test.cc b/src/connectivity/network/tests/netstack_add_eth_test.cc
deleted file mode 100644
index 1b1b659..0000000
--- a/src/connectivity/network/tests/netstack_add_eth_test.cc
+++ /dev/null
@@ -1,262 +0,0 @@
-// Copyright 2018 The Fuchsia 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 <fuchsia/hardware/ethernet/cpp/fidl.h>
-#include <fuchsia/net/stack/cpp/fidl.h>
-#include <fuchsia/netstack/cpp/fidl.h>
-#include <lib/sys/cpp/file_descriptor.h>
-#include <lib/sys/cpp/testing/test_with_environment.h>
-#include <src/connectivity/network/testing/netemul/lib/network/ethernet_client.h>
-#include <src/connectivity/network/testing/netemul/lib/network/ethertap_client.h>
-#include <zircon/status.h>
-
-#include "gtest/gtest.h"
-
-namespace {
-class NetstackLaunchTest : public sys::testing::TestWithEnvironment {};
-
-const char kNetstackUrl[] =
- "fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cmx";
-
-TEST_F(NetstackLaunchTest, AddEthernetInterface) {
- auto services = CreateServices();
-
- // TODO(NET-1818): parameterize this over multiple netstack implementations
- fuchsia::sys::LaunchInfo launch_info;
- launch_info.url = kNetstackUrl;
- launch_info.out = sys::CloneFileDescriptor(1);
- launch_info.err = sys::CloneFileDescriptor(2);
- services->AddServiceWithLaunchInfo(std::move(launch_info),
- fuchsia::net::stack::Stack::Name_);
-
- auto env = CreateNewEnclosingEnvironment("NetstackLaunchTest_AddEth",
- std::move(services));
- ASSERT_TRUE(WaitForEnclosingEnvToStart(env.get()));
-
- auto eth_config = netemul::EthertapConfig("AddEthernetInterface");
- auto tap = netemul::EthertapClient::Create(eth_config);
- ASSERT_TRUE(tap) << "failed to create ethertap device";
-
- netemul::EthernetClientFactory eth_factory;
- auto eth = eth_factory.RetrieveWithMAC(eth_config.tap_cfg.mac);
- ASSERT_TRUE(eth) << "failed to retrieve ethernet client";
-
- bool list_ifs = false;
- fuchsia::net::stack::StackPtr stack;
- env->ConnectToService(stack.NewRequest());
- stack->ListInterfaces(
- [&](::std::vector<::fuchsia::net::stack::InterfaceInfo> interfaces) {
- for (const auto& iface : interfaces) {
- ASSERT_TRUE(iface.properties.features &
- ::fuchsia::hardware::ethernet::INFO_FEATURE_LOOPBACK);
- }
- list_ifs = true;
- });
- ASSERT_TRUE(RunLoopWithTimeoutOrUntil([&] { return list_ifs; }, zx::sec(5)));
-
- uint64_t eth_id = 0;
- fidl::StringPtr topo_path = "/fake/device";
- stack->AddEthernetInterface(
- std::move(topo_path), std::move(eth->device()),
- [&](std::unique_ptr<::fuchsia::net::stack::Error> err, uint64_t id) {
- if (err != nullptr) {
- fprintf(stderr, "error adding ethernet interface: %u\n",
- static_cast<uint32_t>(err->type));
- } else {
- eth_id = id;
- }
- });
- ASSERT_TRUE(
- RunLoopWithTimeoutOrUntil([&] { return eth_id > 0; }, zx::sec(5)));
-
- list_ifs = false;
- stack->ListInterfaces(
- [&](::std::vector<::fuchsia::net::stack::InterfaceInfo> interfaces) {
- for (const auto& iface : interfaces) {
- if (iface.properties.features &
- ::fuchsia::hardware::ethernet::INFO_FEATURE_LOOPBACK) {
- continue;
- }
- ASSERT_EQ(eth_id, iface.id);
- // tap device is created with link down, so we expect physical status
- // to be DOWN.
- EXPECT_EQ(iface.properties.physicalStatus,
- fuchsia::net::stack::PhysicalStatus::DOWN);
- }
- list_ifs = true;
- });
- ASSERT_TRUE(RunLoopWithTimeoutOrUntil([&] { return list_ifs; }, zx::sec(5)));
-}
-
-TEST_F(NetstackLaunchTest, AddEthernetDevice) {
- auto services = CreateServices();
-
- // TODO(NET-1818): parameterize this over multiple netstack implementations
- fuchsia::sys::LaunchInfo launch_info;
- launch_info.url = kNetstackUrl;
- launch_info.out = sys::CloneFileDescriptor(1);
- launch_info.err = sys::CloneFileDescriptor(2);
- services->AddServiceWithLaunchInfo(std::move(launch_info),
- fuchsia::netstack::Netstack::Name_);
-
- auto env = CreateNewEnclosingEnvironment("NetstackLaunchTest_AddEth",
- std::move(services));
- ASSERT_TRUE(WaitForEnclosingEnvToStart(env.get()));
-
- auto eth_config = netemul::EthertapConfig("AddEthernetDevice");
- auto tap = netemul::EthertapClient::Create(eth_config);
- ASSERT_TRUE(tap) << "failed to create ethertap device";
-
- netemul::EthernetClientFactory eth_factory;
- auto eth = eth_factory.RetrieveWithMAC(eth_config.tap_cfg.mac);
- ASSERT_TRUE(eth) << "failed to retrieve ethernet client";
-
- bool list_ifs = false;
- fuchsia::netstack::NetstackPtr netstack;
- env->ConnectToService(netstack.NewRequest());
- fidl::StringPtr topo_path = "/fake/device";
- fidl::StringPtr interface_name = "en0";
- fuchsia::netstack::InterfaceConfig config =
- fuchsia::netstack::InterfaceConfig{};
- config.name = interface_name;
- config.ip_address_config.set_dhcp(true);
- netstack->GetInterfaces(
- [&](::std::vector<::fuchsia::netstack::NetInterface> interfaces) {
- for (const auto& iface : interfaces) {
- ASSERT_TRUE(iface.features &
- ::fuchsia::hardware::ethernet::INFO_FEATURE_LOOPBACK);
- }
- list_ifs = true;
- });
- ASSERT_TRUE(RunLoopWithTimeoutOrUntil([&] { return list_ifs; }, zx::sec(5)));
-
- uint32_t eth_id = 0;
- netstack->AddEthernetDevice(std::move(topo_path), std::move(config),
- std::move(eth->device()),
- [&](uint32_t id) { eth_id = id; });
- ASSERT_TRUE(
- RunLoopWithTimeoutOrUntil([&] { return eth_id > 0; }, zx::sec(5)));
-
- list_ifs = false;
- netstack->GetInterfaces(
- [&](::std::vector<::fuchsia::netstack::NetInterface> interfaces) {
- for (const auto& iface : interfaces) {
- if (iface.features &
- ::fuchsia::hardware::ethernet::INFO_FEATURE_LOOPBACK) {
- continue;
- }
- ASSERT_EQ(eth_id, iface.id);
- // tap device is created with link down, so we expect physical status
- // to be DOWN.
- EXPECT_EQ(iface.flags & fuchsia::netstack::NetInterfaceFlagUp, 0u);
- }
- list_ifs = true;
- });
- ASSERT_TRUE(RunLoopWithTimeoutOrUntil([&] { return list_ifs; }, zx::sec(5)));
-}
-
-TEST_F(NetstackLaunchTest, DHCPRequestSent) {
- auto services = CreateServices();
-
- // TODO(NET-1818): parameterize this over multiple netstack implementations
- fuchsia::sys::LaunchInfo launch_info;
- launch_info.url = kNetstackUrl;
- launch_info.out = sys::CloneFileDescriptor(1);
- launch_info.err = sys::CloneFileDescriptor(2);
- zx_status_t status = services->AddServiceWithLaunchInfo(
- std::move(launch_info), fuchsia::netstack::Netstack::Name_);
- ASSERT_EQ(status, ZX_OK) << zx_status_get_string(status);
-
- auto env = CreateNewEnclosingEnvironment("NetstackDHCPTest_RequestSent",
- std::move(services));
- ASSERT_TRUE(WaitForEnclosingEnvToStart(env.get()));
-
- auto eth_config = netemul::EthertapConfig("DHCPRequestSent");
- auto tap = netemul::EthertapClient::Create(eth_config);
- ASSERT_TRUE(tap) << "failed to create ethertap device";
- tap->SetLinkUp(true);
-
- netemul::EthernetClientFactory eth_factory;
- auto eth = eth_factory.RetrieveWithMAC(eth_config.tap_cfg.mac);
- ASSERT_TRUE(eth) << "failed to retrieve ethernet client";
-
- fuchsia::netstack::NetstackPtr netstack;
- env->ConnectToService(netstack.NewRequest());
- fidl::StringPtr topo_path = "/fake/device";
-
- fidl::StringPtr interface_name = "dhcp_test_interface";
- fuchsia::netstack::InterfaceConfig config =
- fuchsia::netstack::InterfaceConfig{};
- config.name = interface_name;
- config.ip_address_config.set_dhcp(true);
-
- bool data_callback_run = false;
- auto f = [&data_callback_run](std::vector<uint8_t> data) {
- auto len = data.size();
- const std::byte* ethbuf = reinterpret_cast<const std::byte*>(&data[0]);
- size_t expected_len = 302;
- size_t parsed = 0;
-
- EXPECT_EQ(len, (size_t)expected_len)
- << "got " << len << " bytes of " << expected_len << " requested\n";
-
- const std::byte ethertype = ethbuf[12];
- EXPECT_EQ((int)ethertype, 0x08);
-
- // TODO(stijlist): add an ETH_FRAME_MIN_HDR_SIZE to ddk's ethernet.h
- size_t eth_frame_min_hdr_size = 14;
- const std::byte* ip = ðbuf[eth_frame_min_hdr_size];
- parsed += eth_frame_min_hdr_size;
- const std::byte protocol_number = ip[9];
- EXPECT_EQ((int)protocol_number, 17);
-
- size_t ihl = (size_t)(ip[0] & (std::byte)0x0f);
- size_t ip_bytes = (ihl * 32u) / 8u;
-
- const std::byte* udp = &ip[ip_bytes];
- parsed += ip_bytes;
-
- uint16_t src_port = (uint16_t)udp[0] << 8 | (uint8_t)udp[1];
- uint16_t dst_port = (uint16_t)udp[2] << 8 | (uint8_t)udp[3];
-
- // DHCP requests from netstack should come from port 68 (DHCP client) to
- // port 67 (DHCP server).
- EXPECT_EQ(src_port, 68u);
- EXPECT_EQ(dst_port, 67u);
-
- const std::byte* dhcp = &udp[8];
- // Assert the DHCP op type is DHCP request.
- const std::byte dhcp_op_type = dhcp[0];
- EXPECT_EQ((int)dhcp_op_type, 0x01);
-
- data_callback_run = true;
- };
-
- tap->SetPacketCallback(f);
-
- uint32_t nicid = 0;
- // TODO(NET-1864): migrate to fuchsia.net.stack.AddEthernetInterface when we
- // migrate netcfg to use AddEthernetInterface.
- netstack->AddEthernetDevice(std::move(topo_path), std::move(config),
- std::move(eth->device()),
- [&nicid](uint32_t id) { nicid = id; });
-
- ASSERT_TRUE(
- RunLoopWithTimeoutOrUntil([&] { return nicid != 0; }, zx::sec(5)));
-
- netstack->SetInterfaceStatus(nicid, true);
- fuchsia::netstack::Status net_status =
- fuchsia::netstack::Status::UNKNOWN_ERROR;
- netstack->SetDhcpClientStatus(
- nicid, true, [&net_status](fuchsia::netstack::NetErr result) {
- net_status = result.status;
- });
-
- ASSERT_TRUE(RunLoopWithTimeoutOrUntil(
- [&] { return net_status == fuchsia::netstack::Status::OK; }, zx::sec(5)));
-
- ASSERT_TRUE(RunLoopWithTimeoutOrUntil(
- [&data_callback_run] { return data_callback_run; }, zx::sec(5)));
-}
-} // namespace
diff --git a/src/connectivity/network/tests/src/lib.rs b/src/connectivity/network/tests/src/lib.rs
index ce1446a..cd807cf 100644
--- a/src/connectivity/network/tests/src/lib.rs
+++ b/src/connectivity/network/tests/src/lib.rs
@@ -2,70 +2,416 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#![cfg(test)]
#![deny(warnings)]
#![feature(async_await, await_macro, futures_api)]
-#[test]
-fn add_interface_address_not_found() {
- let mut executor = fuchsia_async::Executor::new().expect("failed to create an executor");
- let stack =
- fuchsia_component::client::connect_to_service::<fidl_fuchsia_net_stack::StackMarker>()
- .expect("failed to connect to stack");
- let () = executor.run_singlethreaded(
- async {
- let interfaces = await!(stack.list_interfaces()).expect("failed to list interfaces");
- let max_id = interfaces.iter().map(|interface| interface.id).max().unwrap_or(0);
- let mut interface_address = fidl_fuchsia_net_stack::InterfaceAddress {
- ip_address: fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
- addr: [0, 0, 0, 0],
- }),
- prefix_len: 0,
- };
- let error = await!(stack.add_interface_address(max_id + 1, &mut interface_address,))
- .expect("failed to add interface address")
- .expect("failed to get add interface address response");
- assert_eq!(
- error.as_ref(),
- &fidl_fuchsia_net_stack::Error {
- type_: fidl_fuchsia_net_stack::ErrorType::NotFound
- }
- )
- },
- );
+use failure::ResultExt;
+
+type Result = std::result::Result<(), failure::Error>;
+
+fn connect_to_service<S: fidl::endpoints::ServiceMarker>(
+ managed_environment: &fidl_fuchsia_netemul_environment::ManagedEnvironmentProxy,
+) -> std::result::Result<S::Proxy, failure::Error> {
+ let (proxy, server) = fuchsia_zircon::Channel::create()?;
+ let () = managed_environment.connect_to_service(S::NAME, server)?;
+ let proxy = fuchsia_async::Channel::from_channel(proxy)?;
+ Ok(<S::Proxy as fidl::endpoints::Proxy>::from_channel(proxy))
}
-#[test]
-fn disable_interface_loopback() {
- let mut executor = fuchsia_async::Executor::new().expect("failed to create an executor");
- let stack =
- fuchsia_component::client::connect_to_service::<fidl_fuchsia_net_stack::StackMarker>()
- .expect("failed to connect to stack");
- let () = executor.run_singlethreaded(
- async {
- let interfaces = await!(stack.list_interfaces()).expect("failed to list interfaces");
- let localhost = interfaces
- .iter()
- .find(|interface| {
- interface.properties.features
- & fidl_fuchsia_hardware_ethernet::INFO_FEATURE_LOOPBACK
- != 0
- })
- .expect("failed to find loopback interface");
- assert_eq!(
- localhost.properties.administrative_status,
- fidl_fuchsia_net_stack::AdministrativeStatus::Enabled
- );
- assert_eq!(
- await!(stack.disable_interface(localhost.id)).expect("failed to disable interface"),
- None
- );
- let (info, error) = await!(stack.get_interface_info(localhost.id))
- .expect("failed to get interface info");
- assert_eq!(error, None);
- assert_eq!(
- info.expect("expected interface info to be present").properties.administrative_status,
- fidl_fuchsia_net_stack::AdministrativeStatus::Disabled
- );
+fn get_network_context(
+ sandbox: &fidl_fuchsia_netemul_sandbox::SandboxProxy,
+) -> std::result::Result<fidl_fuchsia_netemul_network::NetworkContextProxy, failure::Error> {
+ let (client, server) =
+ fidl::endpoints::create_proxy::<fidl_fuchsia_netemul_network::NetworkContextMarker>()
+ .context("failed to create network context proxy")?;
+ let () = sandbox.get_network_context(server).context("failed to get network context")?;
+ Ok(client)
+}
+
+fn get_endpoint_manager(
+ network_context: &fidl_fuchsia_netemul_network::NetworkContextProxy,
+) -> std::result::Result<fidl_fuchsia_netemul_network::EndpointManagerProxy, failure::Error> {
+ let (client, server) =
+ fidl::endpoints::create_proxy::<fidl_fuchsia_netemul_network::EndpointManagerMarker>()
+ .context("failed to create endpoint manager proxy")?;
+ let () =
+ network_context.get_endpoint_manager(server).context("failed to get endpoint manager")?;
+ Ok(client)
+}
+
+async fn create_endpoint<'a>(
+ name: &'static str,
+ endpoint_manager: &'a fidl_fuchsia_netemul_network::EndpointManagerProxy,
+) -> std::result::Result<fidl_fuchsia_netemul_network::EndpointProxy, failure::Error> {
+ let (status, endpoint) = await!(endpoint_manager.create_endpoint(
+ name,
+ &mut fidl_fuchsia_netemul_network::EndpointConfig {
+ mtu: 1500,
+ mac: None,
+ backing: fidl_fuchsia_netemul_network::EndpointBacking::Ethertap,
},
+ ))
+ .context("failed to create endpoint")?;
+ let () = fuchsia_zircon::Status::ok(status).context("failed to create endpoint")?;
+ let endpoint = endpoint
+ .ok_or(failure::err_msg("failed to create endpoint"))?
+ .into_proxy()
+ .context("failed to get endpoint proxy")?;
+ Ok(endpoint)
+}
+
+fn create_netstack_environment(
+ sandbox: &fidl_fuchsia_netemul_sandbox::SandboxProxy,
+ name: String,
+) -> std::result::Result<fidl_fuchsia_netemul_environment::ManagedEnvironmentProxy, failure::Error>
+{
+ let (client, server) = fidl::endpoints::create_proxy::<
+ fidl_fuchsia_netemul_environment::ManagedEnvironmentMarker,
+ >()
+ .context("failed to create managed environment proxy")?;
+ let () = sandbox
+ .create_environment(
+ server,
+ fidl_fuchsia_netemul_environment::EnvironmentOptions {
+ name: Some(name),
+ services: Some([
+ <fidl_fuchsia_netstack::NetstackMarker as fidl::endpoints::ServiceMarker>::NAME,
+ <fidl_fuchsia_net::SocketProviderMarker as fidl::endpoints::ServiceMarker>::NAME,
+ <fidl_fuchsia_net_stack::StackMarker as fidl::endpoints::ServiceMarker>::NAME,
+ ]
+ // TODO(tamird): use into_iter after
+ // https://github.com/rust-lang/rust/issues/25725.
+ .iter()
+ .map(std::ops::Deref::deref)
+ .map(str::to_string)
+ .map(|name| fidl_fuchsia_netemul_environment::LaunchService {
+ name,
+ url: fuchsia_component::fuchsia_single_component_package_url!("netstack")
+ .to_string(),
+ arguments: Some(vec!["--sniff".to_string()]),
+ })
+ .collect()),
+ devices: None,
+ inherit_parent_launch_services: None,
+ logger_options: Some(fidl_fuchsia_netemul_environment::LoggerOptions {
+ enabled: Some(true),
+ klogs_enabled: None,
+ filter_options: None,
+ syslog_output: Some(true),
+ }),
+ },
+ )
+ .context("failed to create environment")?;
+ Ok(client)
+}
+
+async fn with_netstack_and_device<F, T, S>(name: &'static str, async_fn: T) -> Result
+where
+ F: futures::Future<Output = Result>,
+ T: FnOnce(
+ S::Proxy,
+ fidl::endpoints::ClientEnd<fidl_fuchsia_hardware_ethernet::DeviceMarker>,
+ ) -> F,
+ S: fidl::endpoints::ServiceMarker,
+{
+ let sandbox = fuchsia_component::client::connect_to_service::<
+ fidl_fuchsia_netemul_sandbox::SandboxMarker,
+ >()
+ .context("failed to connect to sandbox")?;
+ let network_context = get_network_context(&sandbox).context("failed to get network context")?;
+ let endpoint_manager =
+ get_endpoint_manager(&network_context).context("failed to get endpoint manager")?;
+ let endpoint =
+ await!(create_endpoint(name, &endpoint_manager)).context("failed to create endpoint")?;
+ let device = await!(endpoint.get_ethernet_device()).context("failed to get ethernet device")?;
+ let managed_environment = create_netstack_environment(&sandbox, name.to_string())
+ .context("failed to create netstack environment")?;
+ let netstack_proxy =
+ connect_to_service::<S>(&managed_environment).context("failed to connect to netstack")?;
+ await!(async_fn(netstack_proxy, device))
+}
+
+#[fuchsia_async::run_singlethreaded(test)]
+async fn add_ethernet_device() -> Result {
+ let name = stringify!(add_ethernet_device);
+
+ await!(with_netstack_and_device::<_, _, fidl_fuchsia_netstack::NetstackMarker>(
+ name,
+ async move |netstack, device| -> Result {
+ let id = await!(netstack.add_ethernet_device(
+ name,
+ &mut fidl_fuchsia_netstack::InterfaceConfig {
+ name: name.to_string(),
+ metric: 0,
+ ip_address_config: fidl_fuchsia_netstack::IpAddressConfig::Dhcp(true),
+ },
+ device,
+ ))
+ .context("failed to add ethernet device")?;
+ let interface = await!(netstack.get_interfaces2())
+ .context("failed to get interfaces")?
+ .into_iter()
+ .find(|interface| interface.id == id)
+ .ok_or(failure::err_msg("failed to find added ethernet device"))?;
+ assert_eq!(
+ interface.features & fidl_fuchsia_hardware_ethernet::INFO_FEATURE_LOOPBACK,
+ 0
+ );
+ assert_eq!(interface.flags & fidl_fuchsia_netstack::NET_INTERFACE_FLAG_UP, 0,);
+ Ok(())
+ },
+ ))
+}
+
+#[fuchsia_async::run_singlethreaded(test)]
+async fn add_ethernet_interface() -> Result {
+ let name = stringify!(add_ethernet_interface);
+
+ await!(with_netstack_and_device::<_, _, fidl_fuchsia_net_stack::StackMarker>(
+ name,
+ async move |stack, device| -> Result {
+ let (error, id) = await!(stack.add_ethernet_interface(name, device))
+ .context("failed to add ethernet interface")?;
+ assert_eq!(error, None);
+ let interface = await!(stack.list_interfaces())
+ .context("failed to list interfaces")?
+ .into_iter()
+ .find(|interface| interface.id == id)
+ .ok_or(failure::err_msg("failed to find added ethernet interface"))?;
+ assert_eq!(
+ interface.properties.features
+ & fidl_fuchsia_hardware_ethernet::INFO_FEATURE_LOOPBACK,
+ 0
+ );
+ assert_eq!(
+ interface.properties.physical_status,
+ fidl_fuchsia_net_stack::PhysicalStatus::Down
+ );
+ Ok(())
+ },
+ ))
+}
+
+#[fuchsia_async::run_singlethreaded(test)]
+async fn add_interface_address_not_found() -> Result {
+ let name = stringify!(add_interface_address_not_found);
+
+ let sandbox = fuchsia_component::client::connect_to_service::<
+ fidl_fuchsia_netemul_sandbox::SandboxMarker,
+ >()
+ .context("failed to connect to sandbox")?;
+ let managed_environment = create_netstack_environment(&sandbox, name.to_string())
+ .context("failed to create netstack environment")?;
+ let stack = connect_to_service::<fidl_fuchsia_net_stack::StackMarker>(&managed_environment)
+ .context("failed to connect to netstack")?;
+ let interfaces = await!(stack.list_interfaces()).context("failed to list interfaces")?;
+ let max_id = interfaces.iter().map(|interface| interface.id).max().unwrap_or(0);
+ let mut interface_address = fidl_fuchsia_net_stack::InterfaceAddress {
+ ip_address: fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
+ addr: [0, 0, 0, 0],
+ }),
+ prefix_len: 0,
+ };
+ let error = await!(stack.add_interface_address(max_id + 1, &mut interface_address,))
+ .context("failed to add interface address")?
+ .ok_or(failure::err_msg("failed to get add interface address response"))?;
+ assert_eq!(
+ error.as_ref(),
+ &fidl_fuchsia_net_stack::Error { type_: fidl_fuchsia_net_stack::ErrorType::NotFound }
);
+ Ok(())
+}
+
+#[fuchsia_async::run_singlethreaded(test)]
+async fn disable_interface_loopback() -> Result {
+ let name = stringify!(disable_interface_loopback);
+
+ let sandbox = fuchsia_component::client::connect_to_service::<
+ fidl_fuchsia_netemul_sandbox::SandboxMarker,
+ >()
+ .context("failed to connect to sandbox")?;
+ let managed_environment = create_netstack_environment(&sandbox, name.to_string())
+ .context("failed to create netstack environment")?;
+ let stack = connect_to_service::<fidl_fuchsia_net_stack::StackMarker>(&managed_environment)
+ .context("failed to connect to netstack")?;
+ let interfaces = await!(stack.list_interfaces()).context("failed to list interfaces")?;
+ let localhost = interfaces
+ .iter()
+ .find(|interface| {
+ interface.properties.features & fidl_fuchsia_hardware_ethernet::INFO_FEATURE_LOOPBACK
+ != 0
+ })
+ .ok_or(failure::err_msg("failed to find loopback interface"))?;
+ assert_eq!(
+ localhost.properties.administrative_status,
+ fidl_fuchsia_net_stack::AdministrativeStatus::Enabled
+ );
+ assert_eq!(
+ await!(stack.disable_interface(localhost.id)).context("failed to disable interface")?,
+ None
+ );
+ let (info, error) =
+ await!(stack.get_interface_info(localhost.id)).context("failed to get interface info")?;
+ assert_eq!(error, None);
+ assert_eq!(
+ info.ok_or(failure::err_msg("expected interface info to be present"))?
+ .properties
+ .administrative_status,
+ fidl_fuchsia_net_stack::AdministrativeStatus::Disabled
+ );
+ Ok(())
+}
+
+// TODO(tamird): could this be done with a single stack and bridged interfaces?
+#[fuchsia_async::run_singlethreaded(test)]
+async fn acquire_dhcp() -> Result {
+ let name = stringify!(acquire_dhcp);
+
+ let sandbox = fuchsia_component::client::connect_to_service::<
+ fidl_fuchsia_netemul_sandbox::SandboxMarker,
+ >()
+ .context("failed to connect to sandbox")?;
+ let network_context = get_network_context(&sandbox).context("failed to get network context")?;
+ let endpoint_manager =
+ get_endpoint_manager(&network_context).context("failed to get endpoint manager")?;
+ let server_environment = create_netstack_environment(&sandbox, format!("{}_server", name))
+ .context("failed to create server environment")?;
+ let server_endpoint_name = "server";
+ let server_endpoint = await!(create_endpoint(server_endpoint_name, &endpoint_manager))
+ .context("failed to create endpoint")?;
+ let () =
+ await!(server_endpoint.set_link_up(true)).context("failed to start server endpoint")?;
+ {
+ let server_device = await!(server_endpoint.get_ethernet_device())
+ .context("failed to get server ethernet device")?;
+ let server_stack =
+ connect_to_service::<fidl_fuchsia_net_stack::StackMarker>(&server_environment)
+ .context("failed to connect to server stack")?;
+ let (error, id) = await!(server_stack.add_ethernet_interface(name, server_device))
+ .context("failed to add server ethernet interface")?;
+ assert_eq!(error, None);
+ let error = await!(server_stack.add_interface_address(
+ id,
+ &mut fidl_fuchsia_net_stack::InterfaceAddress {
+ ip_address: fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
+ addr: [192, 168, 0, 1]
+ }),
+ prefix_len: 24,
+ }
+ ))
+ .context("failed to add interface address")?;
+ assert_eq!(error, None);
+ let error = await!(server_stack.enable_interface(id))
+ .context("failed to enable server interface")?;
+ assert_eq!(error, None);
+ }
+ let launcher = {
+ let (client, server) = fidl::endpoints::create_proxy::<fidl_fuchsia_sys::LauncherMarker>()
+ .context("failed to create launcher proxy")?;
+ let () = server_environment.get_launcher(server).context("failed to get launcher")?;
+ client
+ };
+ let dhcpd = fuchsia_component::client::launch(
+ &launcher,
+ fuchsia_component::fuchsia_single_component_package_url!("dhcpd").to_string(),
+ None,
+ )
+ .context("failed to start dhcpd")?;
+ let client_environment = create_netstack_environment(&sandbox, format!("{}_client", name))
+ .context("failed to create client environment")?;
+ let client_endpoint_name = "client";
+ let client_endpoint = await!(create_endpoint(client_endpoint_name, &endpoint_manager))
+ .context("failed to create endpoint")?;
+ let () =
+ await!(client_endpoint.set_link_up(true)).context("failed to start client endpoint")?;
+
+ let network_manager = {
+ let (client, server) =
+ fidl::endpoints::create_proxy::<fidl_fuchsia_netemul_network::NetworkManagerMarker>()
+ .context("failed to create network manager proxy")?;
+ let () =
+ network_context.get_network_manager(server).context("failed to get network manager")?;
+ client
+ };
+
+ let (status, network) = await!(network_manager.create_network(
+ name,
+ fidl_fuchsia_netemul_network::NetworkConfig {
+ latency: None,
+ packet_loss: None,
+ reorder: None,
+ },
+ ))
+ .context("failed to create network")?;
+ let network = network
+ .ok_or(failure::err_msg("failed to create network"))?
+ .into_proxy()
+ .context("failed to get network proxy")?;
+ let () = fuchsia_zircon::Status::ok(status).context("failed to create network")?;
+ let status = await!(network.attach_endpoint(server_endpoint_name))
+ .context("failed to attach server endpoint")?;
+ let () = fuchsia_zircon::Status::ok(status).context("failed to attach server endpoint")?;
+ let status = await!(network.attach_endpoint(client_endpoint_name))
+ .context("failed to attach client endpoint")?;
+ let () = fuchsia_zircon::Status::ok(status).context("failed to attach client endpoint")?;
+
+ {
+ let client_device = await!(client_endpoint.get_ethernet_device())
+ .context("failed to get client ethernet device")?;
+ let client_stack =
+ connect_to_service::<fidl_fuchsia_net_stack::StackMarker>(&client_environment)
+ .context("failed to connect to client stack")?;
+ let (error, id) = await!(client_stack.add_ethernet_interface(name, client_device))
+ .context("failed to add client ethernet interface")?;
+ assert_eq!(error, None);
+ let error = await!(client_stack.enable_interface(id))
+ .context("failed to enable client interface")?;
+ assert_eq!(error, None);
+ let client_netstack =
+ connect_to_service::<fidl_fuchsia_netstack::NetstackMarker>(&client_environment)
+ .context("failed to connect to client netstack")?;
+ let error = await!(client_netstack.set_dhcp_client_status(id as u32, true))
+ .context("failed to set DHCP client status")?;
+ assert_eq!(error.status, fidl_fuchsia_netstack::Status::Ok, "{}", error.message);
+
+ let mut address_change_stream = futures::TryStreamExt::try_filter_map(
+ client_netstack.take_event_stream(),
+ |fidl_fuchsia_netstack::NetstackEvent::OnInterfacesChanged { interfaces }| {
+ futures::future::ok(
+ interfaces.into_iter().find(|interface| interface.id as u64 == id).and_then(
+ |interface| match interface.addr {
+ fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
+ addr,
+ }) => {
+ if addr == std::net::Ipv4Addr::UNSPECIFIED.octets() {
+ None
+ } else {
+ Some(interface.addr)
+ }
+ }
+ fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
+ ..
+ }) => None,
+ },
+ ),
+ )
+ },
+ );
+ let address_change = futures::StreamExt::next(&mut address_change_stream);
+ let address_change = fuchsia_async::TimeoutExt::on_timeout(
+ address_change,
+ // Netstack's DHCP client retries every 3 seconds. At the time of writing, dhcpd loses
+ // the race here and only starts after the first request from the DHCP client, which
+ // results in a 3 second toll. This test typically takes ~4.5 seconds; we apply a large
+ // multiple to be safe.
+ fuchsia_zircon::Time::after(fuchsia_zircon::Duration::from_seconds(60)),
+ || None,
+ );
+ let _: fidl_fuchsia_net::IpAddress = await!(address_change)
+ .ok_or(failure::err_msg("failed to observe DHCP acquisition"))?
+ .context("failed to observe DHCP acquisition")?;
+ }
+ drop(dhcpd);
+ Ok(())
}