blob: 9c4a1497b97cc5156abf664035f322c934ce53f8 [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 {
anyhow::{format_err, Context as _, Error},
ethernet,
fidl_fuchsia_netemul_network::{
EndpointManagerMarker, FakeEndpointMarker, NetworkContextMarker, NetworkManagerMarker,
},
fidl_fuchsia_netemul_sync::{BusMarker, BusProxy, SyncManagerMarker},
fidl_fuchsia_netstack::{InterfaceConfig, NetstackMarker},
fuchsia_async as fasync,
fuchsia_component::client,
fuchsia_zircon as zx,
futures::{self, TryStreamExt},
std::{str, task::Poll},
structopt::StructOpt,
};
#[derive(StructOpt, Debug)]
struct Opt {
#[structopt(long = "mock_guest")]
is_mock_guest: bool,
#[structopt(long = "server")]
is_server: bool,
#[structopt(long)]
network_name: Option<String>,
#[structopt(long)]
endpoint_name: Option<String>,
#[structopt(long)]
server_name: Option<String>,
}
const DEFAULT_METRIC: u32 = 100;
const BUS_NAME: &'static str = "netstack-itm-bus";
fn open_bus(cli_name: &str) -> Result<BusProxy, Error> {
let syncm = client::connect_to_service::<SyncManagerMarker>()?;
let (bus, bus_server_end) = fidl::endpoints::create_proxy::<BusMarker>()?;
syncm.bus_subscribe(BUS_NAME, cli_name, bus_server_end)?;
Ok(bus)
}
async fn run_mock_guest(
network_name: String,
ep_name: String,
server_name: String,
) -> Result<(), Error> {
// Create an ethertap client and an associated ethernet device.
let ctx = client::connect_to_service::<NetworkContextMarker>()?;
let (epm, epm_server_end) = fidl::endpoints::create_proxy::<EndpointManagerMarker>()?;
ctx.get_endpoint_manager(epm_server_end)?;
let (netm, netm_server_end) = fidl::endpoints::create_proxy::<NetworkManagerMarker>()?;
ctx.get_network_manager(netm_server_end)?;
let ep = epm.get_endpoint(&ep_name).await?.unwrap().into_proxy()?;
let net = netm.get_network(&network_name).await?.unwrap().into_proxy()?;
let (fake_ep, fake_ep_server_end) = fidl::endpoints::create_proxy::<FakeEndpointMarker>()?;
net.create_fake_endpoint(fake_ep_server_end)?;
let netstack = client::connect_to_service::<NetstackMarker>()?;
let mut cfg = InterfaceConfig {
name: "eth-test".to_string(),
filepath: "[TBD]".to_string(),
metric: DEFAULT_METRIC,
};
let _nicid = match ep.get_device().await? {
fidl_fuchsia_netemul_network::DeviceConnection::Ethernet(eth_device) => {
netstack.add_ethernet_device(&format!("/{}", ep_name), &mut cfg, eth_device).await?
}
fidl_fuchsia_netemul_network::DeviceConnection::NetworkDevice(netdevice) => {
todo!("(48860) Support and test for NetworkDevice connections. Got unexpected NetworkDevice {:?}", netdevice)
}
};
// Send a message to the server and expect it to be echoed back.
let echo_string = String::from("hello");
let bus = open_bus(&ep_name)?;
let (success, absent) =
bus.wait_for_clients(&mut vec![server_name.as_str()].drain(..), 0).await?;
assert!(success);
assert_eq!(absent, None);
fake_ep.write(echo_string.as_bytes()).await.context("write failed")?;
println!("To Server: {}", echo_string);
let (data, dropped_frames) = fake_ep.read().await.context("read failed")?;
assert_eq!(dropped_frames, 0);
let server_string = str::from_utf8(&data)?;
assert!(
echo_string == server_string,
"Server reply ({}) did not match client message ({})",
server_string,
echo_string
);
println!("From Server: {}", server_string);
Ok(())
}
async fn run_echo_server_ethernet(
ep_name: String,
eth_dev: fidl::endpoints::ClientEnd<fidl_fuchsia_hardware_ethernet::DeviceMarker>,
) -> Result<(), Error> {
// Create an EthernetClient to wrap around the Endpoint's ethernet device.
let vmo = zx::Vmo::create(256 * ethernet::DEFAULT_BUFFER_SIZE as u64)?;
let eth_proxy = match eth_dev.into_proxy() {
Ok(proxy) => proxy,
_ => return Err(format_err!("Could not get ethernet proxy")),
};
let mut eth_client =
ethernet::Client::new(eth_proxy, vmo, ethernet::DEFAULT_BUFFER_SIZE, &ep_name).await?;
eth_client.start().await?;
// Listen for a receive event from the client, echo back the client's
// message, and then exit.
let mut eth_events = eth_client.get_stream();
let mut sent_response = false;
// Before connecting to the message bus to notify the client of the server's existence, poll
// for events. Buffers will not be allocated until polling is performed so this ensures that
// there will be buffers to receive the client's message.
loop {
match futures::poll!(eth_events.try_next()) {
Poll::Pending => break,
Poll::Ready(result) => match result {
Ok(result) => match result {
Some(_) => continue,
None => panic!("event stream produced empty event"),
},
Err(e) => panic!("event stream returned an error: {}", e),
},
}
}
// get on bus to unlock mock_guest part of test
let _bus = open_bus(&ep_name)?;
while let Some(event) = eth_events.try_next().await? {
match event {
ethernet::Event::Receive(rx, _flags) => {
if !sent_response {
let mut data: [u8; 100] = [0; 100];
let sz = rx.read(&mut data);
let user_message =
str::from_utf8(&data[0..sz]).expect("failed to parse string");
println!("From client: {}", user_message);
let () = eth_client.send(&data[0..sz]);
sent_response = true;
// Start listening for the server's response to be
// transmitted to the guest.
eth_client.tx_listen_start().await?;
} else {
// The mock guest will not send anything to the server
// beyond its initial request. After the server has echoed
// the response, the next received message will be the
// server's own output since it is listening for its own
// Tx messages.
break;
}
}
_ => {
continue;
}
}
}
Ok(())
}
async fn run_echo_server(ep_name: String) -> Result<(), Error> {
// Get the Endpoint that was created in the server's environment.
let netctx = client::connect_to_service::<NetworkContextMarker>()?;
let (epm, epm_server_end) = fidl::endpoints::create_proxy::<EndpointManagerMarker>()?;
netctx.get_endpoint_manager(epm_server_end)?;
let ep = epm.get_endpoint(&ep_name).await?;
let ep = match ep {
Some(ep) => ep.into_proxy()?,
None => return Err(format_err!("Can't find endpoint {}", &ep_name)),
};
match ep.get_device().await? {
fidl_fuchsia_netemul_network::DeviceConnection::Ethernet(e) => {
run_echo_server_ethernet(ep_name, e).await
}
fidl_fuchsia_netemul_network::DeviceConnection::NetworkDevice(netdevice) => {
todo!("(48860) Support and test for NetworkDevice connections. Got unexpected NetworkDevice {:?}", netdevice)
}
}
}
#[fasync::run_singlethreaded]
async fn main() -> Result<(), Error> {
let opt = Opt::from_args();
if opt.is_mock_guest {
if opt.network_name == None || opt.endpoint_name == None || opt.server_name == None {
return Err(format_err!(
"Must provide network_name, endpoint_name, and server_name for mock guests"
));
}
run_mock_guest(
opt.network_name.unwrap(),
opt.endpoint_name.unwrap(),
opt.server_name.unwrap(),
)
.await?;
} else if opt.is_server {
match opt.endpoint_name {
Some(endpoint_name) => {
run_echo_server(endpoint_name).await?;
}
None => {
return Err(format_err!("Must provide endpoint_name for server"));
}
}
}
Ok(())
}