| // Copyright 2019 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. |
| |
| #![cfg(test)] |
| #![feature(async_await)] |
| |
| use failure::ResultExt; |
| use std::convert::TryInto; |
| |
| use fidl_fuchsia_net_stack_ext::FidlReturn; |
| |
| 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)) |
| } |
| |
| 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) = endpoint_manager |
| .create_endpoint( |
| name, |
| &mut fidl_fuchsia_netemul_network::EndpointConfig { |
| mtu: 1500, |
| mac: None, |
| backing: fidl_fuchsia_netemul_network::EndpointBacking::Ethertap, |
| }, |
| ) |
| .await |
| .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_net_stack::StackMarker as fidl::endpoints::ServiceMarker>::NAME, |
| <fidl_fuchsia_netstack::NetstackMarker as fidl::endpoints::ServiceMarker>::NAME, |
| <fidl_fuchsia_posix_socket::ProviderMarker 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( |
| ["--sniff", "--verbosity=debug"] |
| // TODO(tamird): use into_iter after |
| // https://github.com/rust-lang/rust/issues/25725. |
| .iter() |
| .map(std::ops::Deref::deref) |
| .map(str::to_string) |
| .collect(), |
| ), |
| }).chain(Some( |
| fidl_fuchsia_netemul_environment::LaunchService { |
| name: <fidl_fuchsia_stash::StoreMarker as fidl::endpoints::ServiceMarker>::NAME.to_string(), |
| url: fuchsia_component::fuchsia_single_component_package_url!("stash").to_string(), |
| arguments: None, |
| } |
| )) |
| .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 = |
| create_endpoint(name, &endpoint_manager).await.context("failed to create endpoint")?; |
| let device = endpoint.get_ethernet_device().await.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")?; |
| async_fn(netstack_proxy, device).await |
| } |
| |
| #[fuchsia_async::run_singlethreaded(test)] |
| async fn get_aggregate_stats() -> Result { |
| let name = stringify!(get_aggregate_stats); |
| |
| 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 netstack_proxy = |
| connect_to_service::<fidl_fuchsia_netstack::NetstackMarker>(&managed_environment) |
| .context("failed to connect to netstack")?; |
| |
| let (client, server) = fidl::endpoints::create_proxy::<fidl_fuchsia_io::DirectoryMarker>() |
| .context("failed to create node proxy")?; |
| let () = netstack_proxy.get_aggregate_stats(server).context("failed to get aggregate stats")?; |
| let dir_entries = files_async::readdir_recursive(&client).await.context("failed to readdir")?; |
| let path_segments: std::collections::hash_set::HashSet<usize> = dir_entries |
| .iter() |
| .map(|dir_entry| 1 + dir_entry.name.matches(std::path::MAIN_SEPARATOR).count()) |
| .collect(); |
| // TODO(tamird): use into_iter after |
| // https://github.com/rust-lang/rust/issues/25725. |
| assert_eq!(path_segments, [1, 2, 3].iter().cloned().collect()); |
| Ok(()) |
| } |
| |
| #[fuchsia_async::run_singlethreaded(test)] |
| async fn add_ethernet_device() -> Result { |
| let name = stringify!(add_ethernet_device); |
| |
| with_netstack_and_device::<_, _, fidl_fuchsia_netstack::NetstackMarker>( |
| name, |
| |netstack, device| { |
| async move { |
| let id = netstack |
| .add_ethernet_device( |
| name, |
| &mut fidl_fuchsia_netstack::InterfaceConfig { |
| name: name.to_string(), |
| filepath: "/fake/filepath/for_test".to_string(), |
| metric: 0, |
| ip_address_config: fidl_fuchsia_netstack::IpAddressConfig::Dhcp(true), |
| }, |
| device, |
| ) |
| .await |
| .context("failed to add ethernet device")?; |
| let interface = netstack |
| .get_interfaces2() |
| .await |
| .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::<(), failure::Error>(()) |
| } |
| }, |
| ) |
| .await |
| } |
| |
| #[fuchsia_async::run_singlethreaded(test)] |
| async fn add_ethernet_interface() -> Result { |
| let name = stringify!(add_ethernet_interface); |
| |
| with_netstack_and_device::<_, _, fidl_fuchsia_net_stack::StackMarker>(name, |stack, device| { |
| async move { |
| let id = stack |
| .add_ethernet_interface(name, device) |
| .await |
| .squash_result() |
| .context("failed to add ethernet interface")?; |
| let interface = stack |
| .list_interfaces() |
| .await |
| .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(()) |
| } |
| }) |
| .await |
| } |
| |
| #[fuchsia_async::run_singlethreaded(test)] |
| async fn add_del_interface_address() -> Result { |
| let name = stringify!(add_del_interface_address); |
| |
| 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 = stack.list_interfaces().await.context("failed to list interfaces")?; |
| let loopback = interfaces |
| .iter() |
| .find(|interface| { |
| interface.properties.features & fidl_fuchsia_hardware_ethernet::INFO_FEATURE_LOOPBACK |
| != 0 |
| }) |
| .ok_or(failure::err_msg("failed to find loopback"))?; |
| let mut interface_address = fidl_fuchsia_net_stack::InterfaceAddress { |
| ip_address: fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address { |
| addr: [1, 1, 1, 1], |
| }), |
| prefix_len: 32, |
| }; |
| let res = stack |
| .add_interface_address(loopback.id, &mut interface_address) |
| .await |
| .context("failed to call add interface address")?; |
| assert_eq!(res, Ok(())); |
| let loopback = stack |
| .get_interface_info(loopback.id) |
| .await |
| .squash_result() |
| .context("failed to get loopback interface")?; |
| |
| assert!( |
| loopback.properties.addresses.iter().find(|addr| *addr == &interface_address).is_some(), |
| "couldn't find {:#?} in {:#?}", |
| interface_address, |
| loopback.properties.addresses |
| ); |
| |
| let res = stack |
| .del_interface_address(loopback.id, &mut interface_address) |
| .await |
| .context("failed to call del interface address")?; |
| assert_eq!(res, Ok(())); |
| let loopback = stack |
| .get_interface_info(loopback.id) |
| .await |
| .squash_result() |
| .context("failed to get loopback interface")?; |
| |
| assert!( |
| loopback.properties.addresses.iter().find(|addr| *addr == &interface_address).is_none(), |
| "did not expect to find {:#?} in {:#?}", |
| interface_address, |
| loopback.properties.addresses |
| ); |
| |
| Ok(()) |
| } |
| |
| #[fuchsia_async::run_singlethreaded(test)] |
| async fn add_remove_interface_address_errors() -> Result { |
| let name = stringify!(add_remove_interface_address_errors); |
| |
| 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 stack")?; |
| let netstack = |
| connect_to_service::<fidl_fuchsia_netstack::NetstackMarker>(&managed_environment) |
| .context("failed to connect to netstack")?; |
| let interfaces = stack.list_interfaces().await.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, |
| }; |
| |
| // Don't crash on interface not found. |
| |
| let error = stack |
| .add_interface_address(max_id + 1, &mut interface_address) |
| .await |
| .context("failed to call add interface address")? |
| .unwrap_err(); |
| assert_eq!(error, fidl_fuchsia_net_stack::Error::NotFound); |
| |
| let error = netstack |
| .remove_interface_address( |
| std::convert::TryInto::try_into(max_id + 1).expect("should fit"), |
| &mut interface_address.ip_address, |
| interface_address.prefix_len, |
| ) |
| .await |
| .context("failed to call add interface address")?; |
| assert_eq!( |
| error, |
| fidl_fuchsia_netstack::NetErr { |
| status: fidl_fuchsia_netstack::Status::UnknownInterface, |
| message: "".to_string(), |
| }, |
| ); |
| |
| // Don't crash on invalid prefix length. |
| |
| interface_address.prefix_len = 43; |
| let error = stack |
| .add_interface_address(max_id, &mut interface_address) |
| .await |
| .context("failed to call add interface address")? |
| .unwrap_err(); |
| assert_eq!(error, fidl_fuchsia_net_stack::Error::InvalidArgs); |
| |
| let error = netstack |
| .remove_interface_address( |
| std::convert::TryInto::try_into(max_id).expect("should fit"), |
| &mut interface_address.ip_address, |
| interface_address.prefix_len, |
| ) |
| .await |
| .context("failed to call add interface address")?; |
| assert_eq!( |
| error, |
| fidl_fuchsia_netstack::NetErr { |
| status: fidl_fuchsia_netstack::Status::ParseError, |
| message: "prefix length exceeds address length".to_string(), |
| }, |
| ); |
| |
| Ok(()) |
| } |
| |
| #[fuchsia_async::run_singlethreaded(test)] |
| async fn get_interface_info_not_found() -> Result { |
| let name = stringify!(get_interface_info_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 = stack.list_interfaces().await.context("failed to list interfaces")?; |
| let max_id = interfaces.iter().map(|interface| interface.id).max().unwrap_or(0); |
| let res = |
| stack.get_interface_info(max_id + 1).await.context("failed to call get interface info")?; |
| assert_eq!(res, Err(fidl_fuchsia_net_stack::Error::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 = stack.list_interfaces().await.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 |
| ); |
| let () = stack |
| .disable_interface(localhost.id) |
| .await |
| .squash_result() |
| .context("failed to disable interface")?; |
| let info = stack |
| .get_interface_info(localhost.id) |
| .await |
| .squash_result() |
| .context("failed to get interface info")?; |
| assert_eq!( |
| info.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 = create_endpoint(server_endpoint_name, &endpoint_manager) |
| .await |
| .context("failed to create endpoint")?; |
| let () = server_endpoint.set_link_up(true).await.context("failed to start server endpoint")?; |
| { |
| let server_device = server_endpoint |
| .get_ethernet_device() |
| .await |
| .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 id = server_stack |
| .add_ethernet_interface(name, server_device) |
| .await |
| .squash_result() |
| .context("failed to add server ethernet interface")?; |
| let () = 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, |
| }, |
| ) |
| .await |
| .squash_result() |
| .context("failed to add interface address")?; |
| let () = server_stack |
| .enable_interface(id) |
| .await |
| .squash_result() |
| .context("failed to enable server interface")?; |
| } |
| 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 = create_endpoint(client_endpoint_name, &endpoint_manager) |
| .await |
| .context("failed to create endpoint")?; |
| let () = client_endpoint.set_link_up(true).await.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) = network_manager |
| .create_network( |
| name, |
| fidl_fuchsia_netemul_network::NetworkConfig { |
| latency: None, |
| packet_loss: None, |
| reorder: None, |
| }, |
| ) |
| .await |
| .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 = network |
| .attach_endpoint(server_endpoint_name) |
| .await |
| .context("failed to attach server endpoint")?; |
| let () = fuchsia_zircon::Status::ok(status).context("failed to attach server endpoint")?; |
| let status = network |
| .attach_endpoint(client_endpoint_name) |
| .await |
| .context("failed to attach client endpoint")?; |
| let () = fuchsia_zircon::Status::ok(status).context("failed to attach client endpoint")?; |
| |
| { |
| let client_device = client_endpoint |
| .get_ethernet_device() |
| .await |
| .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 id = client_stack |
| .add_ethernet_interface(name, client_device) |
| .await |
| .squash_result() |
| .context("failed to add client ethernet interface")?; |
| let () = client_stack |
| .enable_interface(id) |
| .await |
| .squash_result() |
| .context("failed to enable client interface")?; |
| let client_netstack = |
| connect_to_service::<fidl_fuchsia_netstack::NetstackMarker>(&client_environment) |
| .context("failed to connect to client netstack")?; |
| let (dhcp_client, server_end) = |
| fidl::endpoints::create_proxy::<fidl_fuchsia_net_dhcp::ClientMarker>() |
| .context("failed to create endpoints for fuchsia.net.dhcp.Client")?; |
| |
| let () = client_netstack |
| .get_dhcp_client(id.try_into().expect("should fit"), server_end) |
| .await |
| .context("failed to call client_netstack.get_dhcp_client")? |
| .map_err(fuchsia_zircon::Status::from_raw) |
| .context("failed to get dhcp client")?; |
| let () = dhcp_client |
| .start() |
| .await |
| .context("failed to call dhcp_client.start")? |
| .map_err(fuchsia_zircon::Status::from_raw) |
| .context("failed to start dhcp client")?; |
| |
| 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, interface.netmask)) |
| } |
| } |
| 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_async::Time::after(fuchsia_zircon::Duration::from_seconds(60)), |
| || None, |
| ); |
| let (addr, netmask) = address_change |
| .await |
| .ok_or(failure::err_msg("failed to observe DHCP acquisition"))? |
| .context("failed to observe DHCP acquisition")?; |
| assert_eq!( |
| addr, |
| fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address { |
| addr: [192, 168, 0, 2] |
| }) |
| ); |
| assert_eq!( |
| netmask, |
| fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address { |
| addr: [255, 255, 255, 128] |
| }) |
| ); |
| } |
| Ok(()) |
| } |