blob: 553c70b2d819484f437df93dadc4e0044124af81 [file] [log] [blame]
// 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.
use {
crate::common::{BusConnection, SERVER_READY},
failure::{format_err, Error, ResultExt},
fidl_fuchsia_netemul_network::{EndpointManagerMarker, NetworkContextMarker},
fidl_fuchsia_netstack::{InterfaceConfig, NetstackMarker},
fuchsia_app::client,
futures::TryStreamExt,
std::io::{Read, Write},
std::net::{SocketAddr, TcpListener, TcpStream},
std::str::FromStr,
};
const PORT: i32 = 8080;
const HELLO_MSG_REQ: &str = "Hello World from Client!";
const HELLO_MSG_RSP: &str = "Hello World from Server!";
pub struct ChildOptions {
pub endpoint: String,
pub ip: String,
pub connect_ip: Option<String>,
}
fn publish_server_ready() -> Result<(), Error> {
let bc = BusConnection::new("server")?;
bc.publish_code(SERVER_READY)
}
fn run_server(ip: &str) -> Result<(), Error> {
let listener =
TcpListener::bind(&format!("{}:{}", ip, PORT)).context("Can't bind to address")?;
println!("Waiting for connections...");
let () = publish_server_ready()?;
let (mut stream, remote) = listener.accept().context("Accept failed")?;
println!("Accepted connection from {}", remote);
let mut buffer = [0; 512];
let rd = stream.read(&mut buffer).context("read failed")?;
let req = String::from_utf8_lossy(&buffer[0..rd]);
if req != HELLO_MSG_REQ {
return Err(format_err!("Got unexpected request from client: {}", req));
}
println!("Got request {}", req);
stream
.write(HELLO_MSG_RSP.as_bytes())
.context("write failed")?;
stream.flush().context("flush failed")?;
Ok(())
}
fn run_client(server_ip: &str) -> Result<(), Error> {
println!("Connecting to server...");
let addr = SocketAddr::from_str(&format!("{}:{}", server_ip, PORT))?;
let mut stream = TcpStream::connect(&addr).context("Tcp connection failed")?;
let request = HELLO_MSG_REQ.as_bytes();
stream.write(request)?;
stream.flush()?;
let mut buffer = [0; 512];
let rd = stream.read(&mut buffer)?;
let rsp = String::from_utf8_lossy(&buffer[0..rd]);
println!("Got response {}", rsp);
if rsp != HELLO_MSG_RSP {
return Err(format_err!("Got unexpected echo from server: {}", rsp));
}
Ok(())
}
pub async fn run_child(opt: ChildOptions) -> Result<(), Error> {
println!("Running child with endpoint '{}'", opt.endpoint);
// get the network context service:
let netctx = client::connect_to_service::<NetworkContextMarker>()?;
// get the endpoint manager
let (epm, epmch) = fidl::endpoints::create_proxy::<EndpointManagerMarker>()?;
netctx.get_endpoint_manager(epmch)?;
// retrieve the created endpoint:
let ep = await!(epm.get_endpoint(&opt.endpoint))?;
let ep = ep
.ok_or_else(|| format_err!("can't find endpoint {}", opt.endpoint))?
.into_proxy()?;
// and the ethernet device:
let eth = await!(ep.get_ethernet_device())?;
let if_name = format!("eth-{}", opt.endpoint);
// connect to netstack:
let netstack = client::connect_to_service::<NetstackMarker>()?;
let mut cfg = InterfaceConfig {
name: if_name.to_string(),
ip_address_config: fidl_fuchsia_netstack_ext::IpAddressConfig::StaticIp(
opt.ip
.parse::<fidl_fuchsia_net_ext::Subnet>()
.expect("must be able to parse ip"),
)
.into(),
};
let mut if_changed = netstack.take_event_stream().try_filter_map(
|fidl_fuchsia_netstack::NetstackEvent::OnInterfacesChanged { interfaces }| {
let iface = interfaces
.iter()
.filter(|iface| iface.name == if_name)
.next();
match iface {
None => futures::future::ok(None),
Some(a) => futures::future::ok(Some((a.id, a.hwaddr.clone()))),
}
},
);
let _nicid =
await!(netstack.add_ethernet_device(&format!("/vdev/{}", opt.endpoint), &mut cfg, eth))
.context("can't add ethernet device")?;
let (if_id, hwaddr) = await!(if_changed.try_next())
.context("wait for interfaces")?
.ok_or_else(|| format_err!("interface added"))?;
println!("Found ethernet with id {} {:?}", if_id, hwaddr);
if let Some(remote) = opt.connect_ip {
run_client(&remote)
} else {
run_server(&opt.ip)
}
}