blob: 619c1a57ac8b278bae28ab040a21912b0c057aff [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 super::*;
use crate::eventloop::util::{FidlCompatible, IntoFidlExt};
use failure::{format_err, Error};
use fidl::encoding::Decodable;
use fidl_fuchsia_netemul_network as net;
use fidl_fuchsia_netemul_sandbox as sandbox;
use fuchsia_async as fasync;
use fuchsia_component::client;
use netstack3_core::{AddrSubnetEither, IpAddr, Ipv4Addr};
use std::convert::TryFrom;
use zx;
struct TestStack {
event_loop: EventLoop,
event_sender: futures::channel::mpsc::UnboundedSender<Event>,
}
impl TestStack {
fn connect_stack(&self) -> Result<fidl_fuchsia_net_stack::StackProxy, Error> {
let (stack, rs) =
fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_stack::StackMarker>()?;
let events =
self.event_sender.clone().sink_map_err(|e| panic!("event sender error: {}", e));
fasync::spawn_local(
rs.map_ok(Event::FidlStackEvent).map_err(|_| ()).forward(events).map(|_| ()),
);
Ok(stack)
}
}
struct TestSetup {
sandbox: sandbox::SandboxProxy,
_nets: fidl::endpoints::ClientEnd<net::SetupHandleMarker>,
stacks: Vec<TestStack>,
}
impl TestSetup {
fn get(&mut self, i: usize) -> &mut TestStack {
&mut self.stacks[i]
}
fn ctx(&mut self, i: usize) -> &mut Context<EventLoopInner> {
&mut self.get(i).event_loop.ctx
}
}
fn new_endpoint_setup(name: String) -> net::EndpointSetup {
net::EndpointSetup { config: None, link_up: true, name }
}
#[derive(Clone)]
struct StackConfig {
ep_name: String,
static_addr: AddrSubnetEither,
}
impl StackConfig {
fn new_ipv4<S: Into<String>>(ep_name: S, ip: [u8; 4], prefix: u8) -> Self {
Self {
ep_name: ep_name.into(),
static_addr: AddrSubnetEither::new(IpAddr::V4(Ipv4Addr::from(ip)), prefix).unwrap(),
}
}
}
async fn configure_stack(
cli: fidl_fuchsia_net_stack::StackProxy,
endpoint: fidl::endpoints::ClientEnd<fidl_fuchsia_hardware_ethernet::DeviceMarker>,
cfg: StackConfig,
) -> Result<u64, Error> {
// add interface:
let if_id = match await!(cli.add_ethernet_interface("fake_topo_path", endpoint))? {
(None, id) => id,
(Some(err), _) => {
return Err(format_err!("Error adding interface: {:?}", err));
}
};
// add address:
if let Some(err) = await!(cli.add_interface_address(if_id, &mut cfg.static_addr.into_fidl()))? {
return Err(format_err!("Error adding address: {:?}", err));
}
// add route:
let (_, subnet) = AddrSubnetEither::try_from(cfg.static_addr)
.expect("Invalid test subnet configuration")
.into_addr_subnet();
if let Some(err) =
await!(cli.add_forwarding_entry(&mut fidl_fuchsia_net_stack::ForwardingEntry {
subnet: cfg.static_addr.into_addr_subnet().1.into_fidl(),
destination: fidl_fuchsia_net_stack::ForwardingDestination::DeviceId(if_id),
}))?
{
return Err(format_err!("Error adding forwarding rule: {:?}", err));
}
Ok(if_id)
}
impl TestSetup {
async fn get_endpoint<'a>(
&'a self,
ep_name: &'a str,
) -> Result<fidl::endpoints::ClientEnd<fidl_fuchsia_hardware_ethernet::DeviceMarker>, Error>
{
let (net_ctx, net_ctx_server) =
fidl::endpoints::create_proxy::<net::NetworkContextMarker>()?;
self.sandbox.get_network_context(net_ctx_server)?;
let (epm, epm_server) = fidl::endpoints::create_proxy::<net::EndpointManagerMarker>()?;
net_ctx.get_endpoint_manager(epm_server)?;
let ep = match await!(epm.get_endpoint(ep_name))? {
Some(ep) => ep.into_proxy()?,
None => {
return Err(format_err!("Failed to retrieve endpoint {}", ep_name));
}
};
Ok(await!(ep.get_ethernet_device())?)
}
async fn new_simple_network<N: Iterator<Item = StackConfig> + Clone>(
stacks: N,
) -> Result<TestSetup, Error> {
let sandbox = client::connect_to_service::<sandbox::SandboxMarker>()?;
let (net_ctx, net_ctx_server) =
fidl::endpoints::create_proxy::<net::NetworkContextMarker>()?;
sandbox.get_network_context(net_ctx_server)?;
let (status, handle) = await!(net_ctx.setup(
&mut vec![&mut net::NetworkSetup {
name: "test_net".to_owned(),
config: net::NetworkConfig::new_empty(),
endpoints: stacks.clone().map(|s| new_endpoint_setup(s.ep_name)).collect(),
}]
.into_iter()
))?;
let handle = match handle {
Some(handle) => handle,
None => {
return Err(format_err!("Create network failed: {}", status));
}
};
println!("Created base network");
let mut test_setup = TestSetup { sandbox, _nets: handle, stacks: Vec::new() };
for cfg in stacks {
await!(test_setup.new_stack(cfg));
}
Ok(test_setup)
}
async fn new_stack<'a>(&'a mut self, cfg: StackConfig) -> Result<(), Error> {
// get the endpoint from the sandbox config:
let endpoint = await!(self.get_endpoint(&cfg.ep_name))?;
let (event_sender, evt_rcv) = futures::channel::mpsc::unbounded();
let event_loop = EventLoop::new_with_channels(event_sender.clone(), evt_rcv);
let mut stack = TestStack { event_loop, event_sender };
let cli = stack.connect_stack()?;
let (mut signal_sender, mut signal_rcv) =
futures::channel::mpsc::unbounded::<Result<u64, Error>>();
fasync::spawn_local(async move {
signal_sender.unbounded_send(await!(configure_stack(cli, endpoint, cfg))).unwrap();
});
let if_id = await!(stack.event_loop.run_until(&mut signal_rcv))??;
// check that we actually have what was transmitted over fidl:
assert!(stack
.event_loop
.ctx
.dispatcher()
.devices
.iter()
.find(|d| d.id.id() == if_id)
.is_some());
self.stacks.push(stack);
Ok(())
}
}
#[fasync::run_singlethreaded]
#[test]
async fn test_ping() {
// simple test to ping between two stacks:
let _t = await!(TestSetup::new_simple_network(
vec![StackConfig::new_ipv4("bob", [192, 168, 0, 1], 24)].into_iter()
))
.expect("Test Setup succeeds");
// TODO(brunodalbo): for now, we're just checking that we can build stacks
// with the helper functions in this mod. Come back to this test and
// actually create two stacks that ping
}