blob: 615ff6eb21b29f3f883ad92b1e3c71f08f51ced7 [file] [log] [blame]
// 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.
use fidl_fuchsia_net_ext::MacAddress;
use fuchsia_async::TimeoutExt as _;
use fuchsia_zircon as zx;
use futures::{FutureExt as _, StreamExt as _, TryStreamExt as _};
use std::convert::TryInto as _;
use std::str::FromStr;
use structopt::StructOpt;
const NETDEV_DIRECTORY: &str = "/dev/class/network";
#[derive(StructOpt, Debug)]
struct Config {
send_byte: u8,
receive_byte: u8,
length: usize,
mac: String,
}
async fn find_network_device(
mac: MacAddress,
) -> (netdevice_client::Client, netdevice_client::Port) {
let (directory, directory_server) =
fidl::endpoints::create_proxy::<fidl_fuchsia_io::DirectoryMarker>().expect("create proxy");
fdio::service_connect(NETDEV_DIRECTORY, directory_server.into_channel().into())
.expect("connect to netdevice devfs");
let devices =
fuchsia_fs::directory::readdir(&directory).await.expect("readdir failed").into_iter().map(
|file| {
let filepath = std::path::Path::new(NETDEV_DIRECTORY).join(&file.name);
let filepath = filepath
.to_str()
.unwrap_or_else(|| panic!("{} failed to convert to str", filepath.display()));
let (netdevice, netdevice_server) = fidl::endpoints::create_proxy::<
fidl_fuchsia_hardware_network::DeviceInstanceMarker,
>()
.expect("create proxy");
fdio::service_connect(filepath, netdevice_server.into_channel())
.expect("connect to service");
netdevice
},
);
let results = futures::stream::iter(devices).filter_map(|netdev_device| async move {
let (device_proxy, device_server) =
fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::DeviceMarker>()
.expect("create proxy");
netdev_device.get_device(device_server).expect("get device");
let device_proxy = &device_proxy;
let client = netdevice_client::Client::new(Clone::clone(device_proxy));
let port_id = match client
.device_port_event_stream()
.expect("failed to get port event stream")
.try_next()
.await
.expect("error observing ports")
.expect("port stream ended unexpectedly")
{
fidl_fuchsia_hardware_network::DevicePortEvent::Existing(port_id) => port_id,
e @ fidl_fuchsia_hardware_network::DevicePortEvent::Removed(_)
| e @ fidl_fuchsia_hardware_network::DevicePortEvent::Idle(_)
| e @ fidl_fuchsia_hardware_network::DevicePortEvent::Added(_) => {
unreachable!("unexpected event: {:?}", e);
}
};
let (port, port_server) =
fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::PortMarker>()
.expect("failed to create proxy");
device_proxy.get_port(&port_id, port_server).expect("failed to get port");
let (mac_addressing, mac_addressing_server) =
fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::MacAddressingMarker>()
.expect("failed to create proxy");
port.get_mac(mac_addressing_server).expect("failed to get mac addressing");
let addr = mac_addressing.get_unicast_address().await.expect("failed to get address");
(addr.octets == mac.octets).then(move || (client, port_id.try_into().expect("bad port id")))
});
futures::pin_mut!(results);
results.next().await.unwrap_or_else(|| panic!("netdevice with mac {} not found", mac))
}
async fn network_device_send(
client: netdevice_client::Client,
port: netdevice_client::Port,
config: Config,
) {
let info = client.device_info().await.expect("get device info");
let (session, task) = client
.primary_session("test", info.base_info.max_buffer_length.unwrap().get() as usize)
.await
.expect("open primary session");
let _task_handle = fuchsia_async::Task::spawn(task.map(|r| r.expect("session task failed")));
session
.attach(port, &[fidl_fuchsia_hardware_network::FrameType::Ethernet])
.await
.expect("attach port");
// It is possible that the device does not yet have rx buffers ready to receive frames, so we
// loop to ensure that we try sending after the device has its rx buffers ready.
let recv_buf = loop {
let mut buffer = session.alloc_tx_buffer(config.length).await.expect("allocate tx buffer");
buffer.set_frame_type(fidl_fuchsia_hardware_network::FrameType::Ethernet);
buffer.set_port(port);
let write_scratch = vec![config.send_byte; config.length];
buffer.write_at(0, &write_scratch).expect("write message");
session.send(buffer).expect("send");
let recv_result = session
.recv()
.map(|result| Some(result.expect("recv failed")))
.on_timeout(fuchsia_async::Time::after(zx::Duration::from_seconds(1)), || None)
.await;
match recv_result {
Some(received_buffer) => {
break received_buffer;
}
None => println!("didn't receive any data, trying to send again..."),
}
};
let mut scratch = vec![0; config.length];
recv_buf.read_at(0, &mut scratch).expect("read from buffer");
assert!(
scratch.iter().all(|b| *b == config.receive_byte),
"expected entire buffer to be {:x}, found {:x?}",
config.receive_byte,
scratch,
);
}
#[fuchsia::main(logging_minimum_severity = "debug")]
async fn main() -> Result<(), anyhow::Error> {
let config = Config::from_args();
let (client, port) = find_network_device(MacAddress::from_str(&config.mac)?).await;
network_device_send(client, port, config).await;
// Test output requires this print, do not remove.
println!("PASS");
Ok(())
}