blob: 341be90e699d9dd50b37351f8518d40fcd9c0f64 [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.
#![feature(async_await, await_macro, futures_api)]
use {
failure::{format_err, Error, ResultExt},
fidl::endpoints::ServiceMarker,
fidl_fuchsia_netemul_environment::{
EnvironmentOptions, LaunchService, ManagedEnvironmentMarker, VirtualDevice,
},
fidl_fuchsia_netemul_network::{
DeviceProxy_Marker, EndpointBacking, EndpointConfig, EndpointManagerMarker, EndpointProxy,
NetworkConfig, NetworkContextMarker, NetworkManagerMarker, NetworkProxy,
},
fidl_fuchsia_netstack::NetstackMarker,
fidl_fuchsia_sys::{
ComponentControllerEvent, ComponentControllerMarker, ComponentControllerProxy, LaunchInfo,
LauncherMarker, TerminationReason,
},
fuchsia_app::client,
fuchsia_async::{self as fasync, TimeoutExt},
fuchsia_zircon::{self as zx, DurationNum},
futures::TryStreamExt,
structopt::StructOpt,
};
mod child;
mod common;
const MY_PACKAGE: &str =
"fuchsia-pkg://fuchsia.com/netemul_sandbox_test#meta/netstack_socks_run.cmx";
const NETSTACK_URL: &str = "fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cmx";
const NETWORK_NAME: &str = "test-network";
const TIMEOUT: i64 = 2;
struct Env {
controller: ComponentControllerProxy,
_endpoint: EndpointProxy,
}
struct SpawnOptions {
env_name: &'static str,
ip: &'static str,
remote: Option<&'static str>,
}
async fn spawn_env(network: &NetworkProxy, options: SpawnOptions) -> Result<Env, Error> {
// connect to NetworkContext and ManagedEnvironment services
let netctx = client::connect_to_service::<NetworkContextMarker>()?;
let env = client::connect_to_service::<ManagedEnvironmentMarker>()?;
let env_name = options.env_name;
// get the endpoint manager
let (epm, epm_server_end) = fidl::endpoints::create_proxy::<EndpointManagerMarker>()?;
netctx.get_endpoint_manager(epm_server_end)?;
let mut cfg = EndpointConfig {
backing: EndpointBacking::Ethertap,
mac: None, // let network service create a random mac for us
mtu: 1500,
};
// create a network endpoint
let (_, ep) = await!(epm.create_endpoint(env_name, &mut cfg))?;
let ep = ep.unwrap().into_proxy()?;
let stat = await!(network.attach_endpoint(env_name))?;
let () = zx::Status::ok(stat)?;
await!(ep.set_link_up(true))?;
// get the endpoint proxy to pass to child environment
let (ep_proxy_client, ep_proxy_server) =
fidl::endpoints::create_endpoints::<DeviceProxy_Marker>()?;
ep.get_proxy_(ep_proxy_server)?;
// prepare a child managed environment
let (child_env, child_env_server) =
fidl::endpoints::create_proxy::<ManagedEnvironmentMarker>()?;
let mut env_options = EnvironmentOptions {
name: String::from(env_name),
services: vec![
LaunchService {
name: String::from(NetstackMarker::NAME),
url: String::from(NETSTACK_URL),
arguments: None,
},
LaunchService {
name: String::from("fuchsia.net.SocketProvider"),
url: String::from(NETSTACK_URL),
arguments: None,
},
],
// pass the endpoint's proxy to create a virtual device
devices: vec![VirtualDevice {
path: String::from(format!("class/ethernet/{}", env_name)),
device: ep_proxy_client,
}],
inherit_parent_launch_services: false,
};
// launch the child env
env.create_child_environment(child_env_server, &mut env_options)?;
// launch as a process in the created environment.
let (launcher, launcher_req) = fidl::endpoints::create_proxy::<LauncherMarker>()?;
child_env.get_launcher(launcher_req)?;
let mut arguments = vec![
String::from("-c"),
String::from("-e"),
String::from(env_name),
String::from("-a"),
String::from(options.ip),
];
if let Some(con) = options.remote {
arguments.push(String::from("-r"));
arguments.push(String::from(con));
}
// launch info is our own package
// plus the command line argument to run the child proc
let mut linfo = LaunchInfo {
url: String::from(MY_PACKAGE),
arguments: Some(arguments),
additional_services: None,
directory_request: None,
err: None,
out: None,
flat_namespace: None,
};
let (comp_controller, comp_controller_req) =
fidl::endpoints::create_proxy::<ComponentControllerMarker>()?;
launcher.create_component(&mut linfo, Some(comp_controller_req))?;
Ok(Env { controller: comp_controller, _endpoint: ep })
}
async fn wait_for_component(component: &ComponentControllerProxy) -> Result<(), Error> {
let mut component_events = component.take_event_stream();
// wait for child to exit and mimic the result code
let result = loop {
let event = await!(component_events.try_next())
.context("wait for child component to exit")?
.ok_or_else(|| format_err!("Child didn't exit cleanly"))?;
match event {
ComponentControllerEvent::OnTerminated {
return_code: code,
termination_reason: reason,
} => {
println!("Child exited with code {}, reason {}", code, reason as u32);
if code != 0 || reason != TerminationReason::Exited {
break Err(format_err!(
"Child exited with code {}, reason {}",
code,
reason as u32
));
} else {
break Ok(());
}
}
_ => {
continue;
}
}
};
result
}
async fn create_network() -> Result<NetworkProxy, Error> {
let netctx = client::connect_to_service::<NetworkContextMarker>()?;
let (netm, netm_server_end) = fidl::endpoints::create_proxy::<NetworkManagerMarker>()?;
netctx.get_network_manager(netm_server_end)?;
let config = NetworkConfig { latency: None, packet_loss: None, reorder: None };
let (_, network) = await!(netm.create_network(NETWORK_NAME, config))?;
let network = network.ok_or_else(|| format_err!("can't create network"))?.into_proxy()?;
Ok(network)
}
async fn prepare_env() -> Result<(), Error> {
let net = await!(create_network())?;
let server_ip = "192.168.0.1";
let client_ip = "192.168.0.2";
let bus = common::BusConnection::new("root")?;
println!("Starting server...");
let server =
await!(spawn_env(&net, SpawnOptions { env_name: "server", ip: server_ip, remote: None }))?;
let () = await!(bus
.wait_for_event(common::SERVER_READY)
.on_timeout(TIMEOUT.seconds().after_now(), || Err(format_err!(
"timed out waiting for server"
))))?;
println!("Server ready, starting client...");
let client = await!(spawn_env(
&net,
SpawnOptions { env_name: "client", ip: client_ip, remote: Some(server_ip) }
))?;
let () = await!(wait_for_component(&client.controller)
.on_timeout(TIMEOUT.seconds().after_now(), || Err(format_err!(
"Timed out waiting for client"
))))?;
let () = await!(wait_for_component(&server.controller)
.on_timeout(TIMEOUT.seconds().after_now(), || Err(format_err!(
"Timed out waiting for server"
))))?;
Ok(())
}
#[derive(StructOpt, Debug)]
struct Opt {
#[structopt(short = "c")]
is_child: bool,
#[structopt(short = "e")]
ep_name: Option<String>,
#[structopt(short = "a")]
ip: Option<String>,
#[structopt(short = "r")]
remote: Option<String>,
}
fn main() -> Result<(), Error> {
let opt = Opt::from_args();
let mut executor = fasync::Executor::new().context("Error creating executor")?;
if opt.is_child {
let child_opts = child::ChildOptions {
endpoint: opt.ep_name.unwrap(),
ip: opt.ip.unwrap(),
connect_ip: opt.remote,
};
executor.run_singlethreaded(child::run_child(child_opts))
} else {
executor.run_singlethreaded(prepare_env())
}
}