| // Copyright 2020 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. |
| |
| use anyhow::{Context as _, Error}; |
| use net_declare::{fidl_ip, fidl_mac, fidl_subnet}; |
| |
| struct IpLayerConfig { |
| alice_subnet: fidl_fuchsia_net::Subnet, |
| bob_ip: fidl_fuchsia_net::IpAddress, |
| } |
| |
| struct EthernetLayerConfig { |
| alice_mac: fidl_fuchsia_net::MacAddress, |
| bob_mac: fidl_fuchsia_net::MacAddress, |
| ip_layer: IpLayerConfig, |
| } |
| |
| /// The network configuration used for TAP-like example. |
| /// |
| /// Note that the tests in this crate run in parallel against the same Netstack, |
| /// so the IP addresses between tests must be different to avoid interference. |
| const CONFIG_FOR_TAP_LIKE: EthernetLayerConfig = EthernetLayerConfig { |
| alice_mac: fidl_mac!("02:03:04:05:06:07"), |
| bob_mac: fidl_mac!("02:AA:BB:CC:DD:EE"), |
| ip_layer: IpLayerConfig { |
| alice_subnet: fidl_subnet!("192.168.0.1/24"), |
| bob_ip: fidl_ip!("192.168.0.2"), |
| }, |
| }; |
| |
| /// The network configuration used for TUN-like example. |
| /// |
| /// Note that the tests in this crate run in parallel against the same Netstack, |
| /// so the IP addresses between tests must be different to avoid interference. |
| const CONFIG_FOR_TUN_LIKE: IpLayerConfig = IpLayerConfig { |
| alice_subnet: fidl_subnet!("192.168.86.1/24"), |
| bob_ip: fidl_ip!("192.168.86.2"), |
| }; |
| |
| /// Creates a new [`fidl_fuchsia_net_tun::DeviceConfig`] using for examples |
| /// using the provided `frame_type` and `mac`. |
| fn new_device_config( |
| frame_types: Vec<fidl_fuchsia_hardware_network::FrameType>, |
| mac: Option<fidl_fuchsia_net::MacAddress>, |
| ) -> fidl_fuchsia_net_tun::DeviceConfig { |
| let rx_types = frame_types; |
| let tx_types = rx_types |
| .iter() |
| .map(|frame_type| fidl_fuchsia_hardware_network::FrameTypeSupport { |
| type_: *frame_type, |
| features: 0, |
| supported_flags: fidl_fuchsia_hardware_network::TxFlags::empty(), |
| }) |
| .collect(); |
| fidl_fuchsia_net_tun::DeviceConfig { |
| base: Some(fidl_fuchsia_net_tun::BaseConfig { |
| // Device MTU reported to Netstack. |
| mtu: Some(1500), |
| // The frame types we're going to accept. TAP and TUN examples will |
| // request different frame types. |
| rx_types: Some(rx_types), |
| tx_types: Some(tx_types), |
| // Discard frame metadata. It is reported through the `read_frame` |
| // method. |
| report_metadata: Some(false), |
| min_tx_buffer_length: None, |
| ..fidl_fuchsia_net_tun::BaseConfig::EMPTY |
| }), |
| // Create the device with the link online signal. |
| online: Some(true), |
| // Blocking will cause the read and write frame methods to block. See |
| // the documentation on fuchsia.net.tun/DeviceConfig for more details. |
| blocking: Some(true), |
| // Use the MAC requested by the caller. TAP-like devices require a MAC |
| // address, while TUN-like devices don't. |
| mac, |
| ..fidl_fuchsia_net_tun::DeviceConfig::EMPTY |
| } |
| } |
| |
| /// An example of configuring and operating a TAP-like interface using |
| /// fuchsia.net.tun. |
| #[fuchsia_async::run_singlethreaded(test)] |
| async fn tap_like_over_network_tun() -> Result<(), Error> { |
| // Connect to the ambient fuchsia.net.tun/Control service. |
| let tun = |
| fuchsia_component::client::connect_to_service::<fidl_fuchsia_net_tun::ControlMarker>() |
| .context("failed to connect to service")?; |
| |
| let (tun_device, server_end) = |
| fidl::endpoints::create_proxy::<fidl_fuchsia_net_tun::DeviceMarker>() |
| .context("failed to create device endpoints")?; |
| |
| // Define the tun device configuration we want to use. |
| // For a TAP-like device, the frame type must be Ethernet, and we must |
| // provide a MAC address for the virtual interface. |
| let config = new_device_config( |
| vec![fidl_fuchsia_hardware_network::FrameType::Ethernet], |
| Some(CONFIG_FOR_TAP_LIKE.alice_mac), |
| ); |
| |
| // Request device creation. |
| let () = tun.create_device(config, server_end).context("failed to create device")?; |
| |
| // Get the Netstack ends of the tun Ethernet device. |
| let (network_device, netdevice_server_end) = |
| fidl::endpoints::create_endpoints::<fidl_fuchsia_hardware_network::DeviceMarker>() |
| .context("failed to create netdevice endpoints")?; |
| let (mac, mac_server_end) = |
| fidl::endpoints::create_endpoints::<fidl_fuchsia_hardware_network::MacAddressingMarker>() |
| .context("failed to create netdevice endpoints")?; |
| let () = tun_device |
| .connect_protocols(fidl_fuchsia_net_tun::Protocols { |
| network_device: Some(netdevice_server_end), |
| mac_addressing: Some(mac_server_end), |
| ..fidl_fuchsia_net_tun::Protocols::EMPTY |
| }) |
| .context("failed to connect protocols")?; |
| |
| // Connect to Netstack and install the device. |
| let stack = |
| fuchsia_component::client::connect_to_service::<fidl_fuchsia_net_stack::StackMarker>() |
| .context("failed to connect to stack")?; |
| let interface_id = stack |
| .add_interface( |
| // Interface configuration. |
| fidl_fuchsia_net_stack::InterfaceConfig { |
| name: Some("tap-like".to_string()), |
| topopath: Some("/fake-topopath".to_string()), |
| metric: Some(100), |
| ..fidl_fuchsia_net_stack::InterfaceConfig::EMPTY |
| }, |
| // The device definition tells Netstack this is an Ethernet device |
| // and gives it handles to access the data plane and the MAC |
| // addressing control plane. |
| &mut fidl_fuchsia_net_stack::DeviceDefinition::Ethernet( |
| fidl_fuchsia_net_stack::EthernetDeviceDefinition { network_device, mac }, |
| ), |
| ) |
| .await |
| .context("add_interface FIDL error")? |
| .map_err(|e: fidl_fuchsia_net_stack::Error| { |
| anyhow::anyhow!("add_interface failed: {:?}", e) |
| })?; |
| |
| // Enable the interface. |
| let () = stack |
| .enable_interface(interface_id) |
| .await |
| .context("enable_interface FIDL error")? |
| .map_err(|e: fidl_fuchsia_net_stack::Error| { |
| anyhow::anyhow!("enable_interface failed: {:?}", e) |
| })?; |
| |
| // Add an IPv4 address to it. |
| let () = stack |
| .add_interface_address(interface_id, &mut CONFIG_FOR_TAP_LIKE.ip_layer.alice_subnet.clone()) |
| .await |
| .context("add_interface_address FIDL error")? |
| .map_err(|e: fidl_fuchsia_net_stack::Error| { |
| anyhow::anyhow!("add_interface_address failed: {:?}", e) |
| })?; |
| |
| // Wait for Netstack to report the interface online. Netstack may drop |
| // frames if they're sent before the online signal is observed. This is |
| // necessary to prevent this test from flaking since we're only going to |
| // send a single echo request. |
| let () = helpers::wait_interface_online(interface_id) |
| .await |
| .context("failed to observe interface online")?; |
| |
| // Now we can send frames. In this example we'll send an ICMP echo request |
| // and wait for the Netstack to respond. |
| let () = tun_device |
| .write_frame(fidl_fuchsia_net_tun::Frame { |
| frame_type: Some(fidl_fuchsia_hardware_network::FrameType::Ethernet), |
| data: Some(helpers::bob_pings_alice_for_tap_like(&CONFIG_FOR_TAP_LIKE)), |
| meta: None, |
| ..fidl_fuchsia_net_tun::Frame::EMPTY |
| }) |
| .await |
| .context("write_frame FIDL error")? |
| .map_err(fuchsia_zircon::Status::from_raw) |
| .context("write_frame failed")?; |
| |
| // Read frames until we see the echo response. |
| loop { |
| let fidl_fuchsia_net_tun::Frame { |
| // The frame's type will always be Ethernet in this example. If multiple |
| // frame types are supported (like IPv4 and IPv6, for example) that |
| // information will be here. |
| frame_type, |
| // Frame payload. |
| data, |
| // Metadata associated with the frame. |
| meta: _, |
| .. |
| } = tun_device |
| .read_frame() |
| .await |
| .context("read_frame FIDL error")? |
| .map_err(fuchsia_zircon::Status::from_raw) |
| .context("read_frame failed")?; |
| let data = data.ok_or_else(|| anyhow::anyhow!("received Frame with missing data field"))?; |
| assert_eq!(frame_type, Some(fidl_fuchsia_hardware_network::FrameType::Ethernet)); |
| match helpers::get_icmp_response_for_tap_like(&data[..]) |
| .context("failed to parse ICMP response")? |
| { |
| None => (), |
| Some(helpers::ParsedFrame::ArpRequest { target_ip, sender_mac, sender_ip }) => { |
| // When we get an ARP request for bob's IP, we need to send an |
| // ARP response back in order for the ICMP echo response to come |
| // in. |
| if target_ip == CONFIG_FOR_TAP_LIKE.ip_layer.bob_ip { |
| assert_eq!(sender_mac, CONFIG_FOR_TAP_LIKE.alice_mac); |
| assert_eq!(sender_ip, CONFIG_FOR_TAP_LIKE.ip_layer.alice_subnet.addr); |
| let () = tun_device |
| .write_frame(fidl_fuchsia_net_tun::Frame { |
| frame_type: Some(fidl_fuchsia_hardware_network::FrameType::Ethernet), |
| data: Some(helpers::build_bob_arp_response(&CONFIG_FOR_TAP_LIKE)), |
| meta: None, |
| ..fidl_fuchsia_net_tun::Frame::EMPTY |
| }) |
| .await |
| .context("write_frame FIDL error")? |
| .map_err(fuchsia_zircon::Status::from_raw) |
| .context("write_frame failed")?; |
| } |
| } |
| Some(helpers::ParsedFrame::IcmpEchoResponse { src_mac, dst_mac, src_ip, dst_ip }) => { |
| assert_eq!(src_mac, CONFIG_FOR_TAP_LIKE.alice_mac); |
| assert_eq!(dst_mac, CONFIG_FOR_TAP_LIKE.bob_mac); |
| assert_eq!(src_ip, CONFIG_FOR_TAP_LIKE.ip_layer.alice_subnet.addr); |
| assert_eq!(dst_ip, CONFIG_FOR_TAP_LIKE.ip_layer.bob_ip); |
| break Ok(()); |
| } |
| } |
| } |
| } |
| |
| /// An example of configuring and operating a TUN-like interface using |
| /// fuchsia.net.tun. |
| #[fuchsia_async::run_singlethreaded(test)] |
| async fn tun_like_over_network_tun() -> Result<(), Error> { |
| // Connect to the ambient fuchsia.net.tun/Control service. |
| let tun = |
| fuchsia_component::client::connect_to_service::<fidl_fuchsia_net_tun::ControlMarker>() |
| .context("failed to connect to service")?; |
| |
| let (tun_device, server_end) = |
| fidl::endpoints::create_proxy::<fidl_fuchsia_net_tun::DeviceMarker>() |
| .context("failed to create device endpoints")?; |
| |
| // Define the tun device configuration we want to use. For a TUN-like |
| // device, the frame types must be IPv4 and IPv6, and we don't provide a MAC |
| // address. |
| let config = new_device_config( |
| vec![ |
| fidl_fuchsia_hardware_network::FrameType::Ipv4, |
| fidl_fuchsia_hardware_network::FrameType::Ipv6, |
| ], |
| None, |
| ); |
| |
| // Request device creation. |
| let () = tun.create_device(config, server_end).context("failed to create device")?; |
| |
| // Get the Netstack end of the tun IPv4/IPv6 device. |
| let (network_device, netdevice_server_end) = |
| fidl::endpoints::create_endpoints::<fidl_fuchsia_hardware_network::DeviceMarker>() |
| .context("failed to create netdevice endpoints")?; |
| let () = tun_device |
| .connect_protocols(fidl_fuchsia_net_tun::Protocols { |
| network_device: Some(netdevice_server_end), |
| mac_addressing: None, |
| ..fidl_fuchsia_net_tun::Protocols::EMPTY |
| }) |
| .context("failed to connect protocols")?; |
| |
| // Connect to Netstack and install the device. |
| let stack = |
| fuchsia_component::client::connect_to_service::<fidl_fuchsia_net_stack::StackMarker>() |
| .context("failed to connect to stack")?; |
| let interface_id = stack |
| .add_interface( |
| // Interface configuration. |
| fidl_fuchsia_net_stack::InterfaceConfig { |
| name: Some("tun-like".to_string()), |
| topopath: Some("/fake-topopath".to_string()), |
| metric: Some(100), |
| ..fidl_fuchsia_net_stack::InterfaceConfig::EMPTY |
| }, |
| // The device definition tells Netstack this is a TUN-like device |
| // operating at the IP layer and gives it handles to access the data |
| // plane. |
| &mut fidl_fuchsia_net_stack::DeviceDefinition::Ip(network_device), |
| ) |
| .await |
| .context("add_interface FIDL error")? |
| .map_err(|e: fidl_fuchsia_net_stack::Error| { |
| anyhow::anyhow!("add_interface failed: {:?}", e) |
| })?; |
| |
| // Enable the interface. |
| let () = stack |
| .enable_interface(interface_id) |
| .await |
| .context("enable_interface FIDL error")? |
| .map_err(|e: fidl_fuchsia_net_stack::Error| { |
| anyhow::anyhow!("enable_interface failed: {:?}", e) |
| })?; |
| |
| // Add an IPv4 address to it. |
| let () = stack |
| .add_interface_address(interface_id, &mut CONFIG_FOR_TUN_LIKE.alice_subnet.clone()) |
| .await |
| .context("add_interface_address FIDL error")? |
| .map_err(|e: fidl_fuchsia_net_stack::Error| { |
| anyhow::anyhow!("add_interface_address failed: {:?}", e) |
| })?; |
| |
| // Wait for Netstack to report the interface online. Netstack may drop |
| // frames if they're sent before the online signal is observed. This is |
| // necessary to prevent this test from flaking since we're only going to |
| // send a single echo request. |
| let () = helpers::wait_interface_online(interface_id) |
| .await |
| .context("failed to observe interface online")?; |
| |
| // Now we can send frames. In this example we'll send an ICMP echo request |
| // and wait for the Netstack to respond. |
| let () = tun_device |
| .write_frame(fidl_fuchsia_net_tun::Frame { |
| frame_type: Some(fidl_fuchsia_hardware_network::FrameType::Ipv4), |
| data: Some(helpers::bob_pings_alice_for_tun_like(&CONFIG_FOR_TUN_LIKE)), |
| meta: None, |
| ..fidl_fuchsia_net_tun::Frame::EMPTY |
| }) |
| .await |
| .context("write_frame FIDL error")? |
| .map_err(fuchsia_zircon::Status::from_raw) |
| .context("write_frame failed")?; |
| |
| // Read frames until we see the echo response. |
| loop { |
| let fidl_fuchsia_net_tun::Frame { |
| // A TUN-like device can receive IPv4 or IPv6 frames, in this |
| // example we're going to be looking only into IPv4 packets. |
| frame_type, |
| // Frame payload. |
| data, |
| // Metadata associated with the frame. |
| meta: _, |
| .. |
| } = tun_device |
| .read_frame() |
| .await |
| .context("read_frame FIDL error")? |
| .map_err(fuchsia_zircon::Status::from_raw) |
| .context("read_frame failed")?; |
| match frame_type { |
| Some(fidl_fuchsia_hardware_network::FrameType::Ipv4) => (), |
| Some(fidl_fuchsia_hardware_network::FrameType::Ipv6) => continue, |
| unexpected => { |
| break Err(anyhow::anyhow!("received unexpected frame_type: {:?}", unexpected)) |
| } |
| } |
| let data = data.ok_or_else(|| anyhow::anyhow!("received Frame with missing data field"))?; |
| |
| if let Some((src_ip, dst_ip)) = helpers::get_icmp_response_for_tun_like(&data[..]) |
| .context("failed to parse ICMP response")? |
| { |
| assert_eq!(src_ip, CONFIG_FOR_TUN_LIKE.alice_subnet.addr); |
| assert_eq!(dst_ip, CONFIG_FOR_TUN_LIKE.bob_ip); |
| break Ok(()); |
| } |
| } |
| } |
| |
| /// This module contains some helpers to remove noise from the examples in this |
| /// crate. |
| mod helpers { |
| use anyhow::{Context as _, Error}; |
| use net_types::{ |
| ethernet::Mac, |
| ip::{Ipv4, Ipv4Addr}, |
| }; |
| use packet::{InnerPacketBuilder as _, ParsablePacket as _, Serializer as _}; |
| use packet_formats::{ |
| arp::{ArpOp, ArpPacket, ArpPacketBuilder}, |
| ethernet::{EtherType, EthernetFrame, EthernetFrameBuilder, EthernetFrameLengthCheck}, |
| icmp::{IcmpEchoRequest, IcmpPacketBuilder, IcmpParseArgs, IcmpUnusedCode, Icmpv4Packet}, |
| ip::IpProto, |
| ipv4::{Ipv4Header as _, Ipv4Packet, Ipv4PacketBuilder}, |
| }; |
| |
| fn ip_v4(fidl_ip: fidl_fuchsia_net::IpAddress) -> Ipv4Addr { |
| match fidl_ip { |
| fidl_fuchsia_net::IpAddress::Ipv4(ip) => Ipv4Addr::new(ip.addr), |
| fidl_fuchsia_net::IpAddress::Ipv6(v6) => { |
| panic!("can't convert from FIDL IPv6 {:?}", v6) |
| } |
| } |
| } |
| |
| const fn mac(fidl_mac: fidl_fuchsia_net::MacAddress) -> Mac { |
| Mac::new(fidl_mac.octets) |
| } |
| |
| fn fidl_mac(mac: Mac) -> fidl_fuchsia_net::MacAddress { |
| fidl_fuchsia_net::MacAddress { octets: mac.bytes() } |
| } |
| |
| fn fidl_ip(ip_v4: Ipv4Addr) -> fidl_fuchsia_net::IpAddress { |
| fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address { |
| addr: ip_v4.ipv4_bytes(), |
| }) |
| } |
| |
| const ECHO_PAYLOAD: [u8; 4] = [1, 2, 3, 4]; |
| const ICMP_ID: u16 = 1; |
| const ICMP_SEQNUM: u16 = 1; |
| |
| /// Creates the ICMP bytes for bob pinging alice to use in TAP-like |
| /// examples. |
| pub(super) fn bob_pings_alice_for_tap_like(config: &super::EthernetLayerConfig) -> Vec<u8> { |
| packet::Buf::new(&mut bob_pings_alice_for_tun_like(&config.ip_layer)[..], ..) |
| .encapsulate(EthernetFrameBuilder::new( |
| mac(config.bob_mac), |
| mac(config.alice_mac), |
| EtherType::Ipv4, |
| )) |
| .serialize_vec_outer() |
| .expect("serialization failed") |
| .as_ref() |
| .to_vec() |
| } |
| |
| /// Creates the ICMP bytes for bob pinging alice to use in TUN-like |
| /// examples. |
| pub(super) fn bob_pings_alice_for_tun_like(config: &super::IpLayerConfig) -> Vec<u8> { |
| let alice_ip = ip_v4(config.alice_subnet.addr); |
| let bob_ip = ip_v4(config.bob_ip); |
| packet::Buf::new(&mut ECHO_PAYLOAD.to_vec()[..], ..) |
| .encapsulate(IcmpPacketBuilder::<Ipv4, &[u8], _>::new( |
| bob_ip, |
| alice_ip, |
| IcmpUnusedCode, |
| IcmpEchoRequest::new(ICMP_ID, ICMP_SEQNUM), |
| )) |
| .encapsulate(Ipv4PacketBuilder::new(bob_ip, alice_ip, 1, IpProto::Icmp)) |
| .serialize_vec_outer() |
| .expect("serialization failed") |
| .as_ref() |
| .to_vec() |
| } |
| |
| /// A parsed frame for a tap-like interface. |
| pub(super) enum ParsedFrame { |
| /// An ARP request was observed, an ARP response should be sent. |
| ArpRequest { |
| target_ip: fidl_fuchsia_net::IpAddress, |
| sender_mac: fidl_fuchsia_net::MacAddress, |
| sender_ip: fidl_fuchsia_net::IpAddress, |
| }, |
| /// An ICMP echo response was observed. |
| IcmpEchoResponse { |
| src_mac: fidl_fuchsia_net::MacAddress, |
| dst_mac: fidl_fuchsia_net::MacAddress, |
| src_ip: fidl_fuchsia_net::IpAddress, |
| dst_ip: fidl_fuchsia_net::IpAddress, |
| }, |
| } |
| |
| /// Attempts to parse an ICMP echo response in an Ethernet frame contained |
| /// in `data`. |
| /// |
| /// Returns `Err` if the frame can't be parsed, `Ok(None)` if the frame is |
| /// valid but it's not an ICMP response. |
| /// |
| /// Otherwise, returns either a [`ParsedFrame::IcmpEchoResponse`] with the |
| /// echo response or a [`ParsedFrame::ArpRequest`] indicating that an ARP |
| /// response must be sent for LL resolution. |
| pub(super) fn get_icmp_response_for_tap_like( |
| data: &[u8], |
| ) -> Result<Option<ParsedFrame>, Error> { |
| let mut bv = data; |
| let ethernet = EthernetFrame::parse(&mut bv, EthernetFrameLengthCheck::NoCheck) |
| .context("failed to parse Ethernet frame")?; |
| let src_mac = fidl_mac(ethernet.src_mac()); |
| let dst_mac = fidl_mac(ethernet.dst_mac()); |
| match ethernet.ethertype() { |
| Some(EtherType::Ipv4) => get_icmp_response_for_tun_like(bv).map(|r| { |
| r.map(|(src_ip, dst_ip)| ParsedFrame::IcmpEchoResponse { |
| src_mac, |
| dst_mac, |
| src_ip, |
| dst_ip, |
| }) |
| }), |
| Some(EtherType::Arp) => { |
| let arp = ArpPacket::<_, Mac, Ipv4Addr>::parse(&mut bv, ()) |
| .context("failed to parse ARP packet")?; |
| match arp.operation() { |
| ArpOp::Request => Ok(Some(ParsedFrame::ArpRequest { |
| target_ip: fidl_ip(arp.target_protocol_address()), |
| sender_mac: fidl_mac(arp.sender_hardware_address()), |
| sender_ip: fidl_ip(arp.sender_protocol_address()), |
| })), |
| ArpOp::Response | ArpOp::Other(_) => Ok(None), |
| } |
| } |
| _ => Ok(None), |
| } |
| } |
| |
| /// Attempts to parse an ICMP echo response in an Ethernet frame contained |
| /// in `data`. |
| /// |
| /// Returns `Err` if the frame can't be parsed, `Ok(None)` if the frame is |
| /// valid but it's not an ICMP response. |
| /// |
| /// Otherwise, returns the `(src_ip, dst_ip)` addressing tuple. |
| /// |
| /// Note that this function is provided as a support to the examples in this |
| /// crate and it makes assumptions that may not be valid in a production |
| /// environment, such as assuming that no IP fragmentation happens, and the |
| /// ICMP echo responses are not validated. |
| pub(super) fn get_icmp_response_for_tun_like( |
| data: &[u8], |
| ) -> Result<Option<(fidl_fuchsia_net::IpAddress, fidl_fuchsia_net::IpAddress)>, Error> { |
| let mut bv = data; |
| let ipv4 = Ipv4Packet::parse(&mut bv, ()).context("failed to parse IPv4")?; |
| if ipv4.proto() != IpProto::Icmp { |
| return Ok(None); |
| } |
| let src_ip = ipv4.src_ip(); |
| let dst_ip = ipv4.dst_ip(); |
| let icmp = Icmpv4Packet::parse(&mut bv, IcmpParseArgs::new(src_ip, dst_ip)) |
| .context("failed to parse ICMP")?; |
| if let Icmpv4Packet::EchoReply(_echo) = icmp { |
| Ok(Some((fidl_ip(src_ip), fidl_ip(dst_ip)))) |
| } else { |
| Ok(None) |
| } |
| } |
| |
| /// Creates an ARP response from bob to alice for use in tap-like tests. |
| pub(super) fn build_bob_arp_response(config: &super::EthernetLayerConfig) -> Vec<u8> { |
| ArpPacketBuilder::new( |
| ArpOp::Response, |
| mac(config.bob_mac), |
| ip_v4(config.ip_layer.bob_ip), |
| mac(config.alice_mac), |
| ip_v4(config.ip_layer.alice_subnet.addr), |
| ) |
| .into_serializer() |
| .encapsulate(EthernetFrameBuilder::new( |
| mac(config.bob_mac), |
| mac(config.alice_mac), |
| EtherType::Arp, |
| )) |
| .serialize_vec_outer() |
| .expect("serialization failed") |
| .as_ref() |
| .to_vec() |
| } |
| |
| /// Waits for interface with ID `interface_id` to come online. |
| pub(super) async fn wait_interface_online(interface_id: u64) -> Result<(), Error> { |
| let interface_state = fuchsia_component::client::connect_to_service::< |
| fidl_fuchsia_net_interfaces::StateMarker, |
| >() |
| .context("failed to connect to interfaces state service")?; |
| fidl_fuchsia_net_interfaces_ext::wait_interface_with_id( |
| fidl_fuchsia_net_interfaces_ext::event_stream_from_state(&interface_state) |
| .context("failed to create event stream")?, |
| &mut fidl_fuchsia_net_interfaces_ext::InterfaceState::Unknown(interface_id), |
| |&fidl_fuchsia_net_interfaces_ext::Properties { |
| id: _, |
| addresses: _, |
| online, |
| device_class: _, |
| has_default_ipv4_route: _, |
| has_default_ipv6_route: _, |
| name: _, |
| }| { |
| if online { |
| Some(()) |
| } else { |
| None |
| } |
| }, |
| ) |
| .await |
| .context("failed to wait for interface online") |
| } |
| } |