blob: 667df801526a127818b28403d2480bfb5d833a67 [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::encoding::OutOfLine,
fidl_fuchsia_net_policy::{InterfaceInfo, ObserverRequest, ObserverRequestStream},
fidl_fuchsia_net_stack::StackProxy,
fuchsia_syslog::fx_log_err,
fuchsia_zircon as zx,
futures::lock::Mutex,
futures::prelude::*,
std::collections::HashMap,
std::sync::Arc,
};
const EMPTY: &str = "";
pub async fn serve_fidl_requests(
stack: StackProxy, stream: ObserverRequestStream,
interface_ids: Arc<Mutex<HashMap<String, u64>>>,
) -> Result<(), fidl::Error> {
await!(stream.try_for_each(|req| async {
let interface_ids_lock = await!(interface_ids.lock());
match req {
ObserverRequest::ListInterfaces { responder } => {
let (mut infos, status) = if let Ok(infos) = await!(stack.list_interfaces()) {
let infos = infos
.into_iter()
.filter_map(|info| {
match interface_ids_lock
.iter()
.find(|(_name, id)| &&info.id == id)
{
Some((name, _)) => Some(InterfaceInfo {
name: name.to_owned(),
properties: info.properties,
}),
None => {
fx_log_err!("faild to find nic {}", info.id,);
Some(InterfaceInfo {
name: EMPTY.to_owned(),
properties: info.properties,
})
}
}
})
.collect::<Vec<InterfaceInfo>>();
(Some(infos), zx::sys::ZX_OK)
} else {
fx_log_err!("failed to get response from netstack: list_interface");
(None, zx::sys::ZX_ERR_INTERNAL)
};
responder.send(
infos
.as_mut()
.map(|x| x.iter_mut())
.as_mut()
.map(|x| x as &mut ExactSizeIterator<Item = &mut InterfaceInfo>),
status,
)
}
ObserverRequest::GetInterfaceInfo { name, responder } => {
let (mut info, status) = if let Some(&id) = interface_ids_lock.get(&name) {
if let Ok((info, None)) = await!(stack.get_interface_info(id)) {
(
Some(InterfaceInfo {
name,
properties: info.unwrap().properties,
}),
zx::sys::ZX_OK,
)
} else {
(None, zx::sys::ZX_ERR_INTERNAL)
}
} else {
(None, zx::sys::ZX_ERR_NOT_FOUND)
};
responder.send(info.as_mut().map(OutOfLine), status)
}
}
}))
}
#[cfg(test)]
mod tests {
use super::*;
use {
fidl::endpoints::create_proxy,
fidl_fuchsia_net_policy::{ObserverMarker, ObserverProxy},
fidl_fuchsia_net_stack::{
EnablementStatus, InterfaceAddress, PhysicalStatus, StackMarker, StackRequest,
StackRequestStream,
},
fuchsia_async as fasync,
futures::task::Poll,
pin_utils::pin_mut,
std::boxed::Box,
};
const ID1: u64 = 1;
const ID2: u64 = 2;
const ID3: u64 = 3; // No match in interface_ids
const NAME1: &str = "lo";
const NAME2: &str = "eth";
const UNKNOWN: &str = "";
fn build_interface_properties() -> fidl_fuchsia_net_stack::InterfaceProperties {
fidl_fuchsia_net_stack::InterfaceProperties {
path: "/all/the/way/home".to_owned(),
mac: Some(Box::new(fidl_fuchsia_hardware_ethernet::MacAddress {
octets: [0, 1, 2, 255, 254, 253],
})),
mtu: 1500,
features: 2,
enablement_status: EnablementStatus::Enabled,
physical_status: PhysicalStatus::Up,
addresses: vec![InterfaceAddress {
ip_address: fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::IPv4Address {
addr: [1, 1, 1, 1],
}),
prefix_len: 4,
}],
}
}
fn build_interface_info() -> fidl_fuchsia_net_stack::InterfaceInfo {
fidl_fuchsia_net_stack::InterfaceInfo {
id: ID2,
properties: build_interface_properties(),
}
}
fn build_no_match_interface_info() -> fidl_fuchsia_net_stack::InterfaceInfo {
fidl_fuchsia_net_stack::InterfaceInfo {
id: ID3,
properties: build_interface_properties(),
}
}
fn expect_interface_info() -> fidl_fuchsia_net_policy::InterfaceInfo {
fidl_fuchsia_net_policy::InterfaceInfo {
name: NAME2.to_owned(),
properties: build_interface_properties(),
}
}
fn expect_interface_info_no_match() -> fidl_fuchsia_net_policy::InterfaceInfo {
fidl_fuchsia_net_policy::InterfaceInfo {
name: UNKNOWN.to_owned(),
properties: build_interface_properties(),
}
}
fn setup_interface_tests() -> (
fasync::Executor,
impl Future,
ObserverProxy,
StackRequestStream,
) {
let exec = fasync::Executor::new().expect("failed to create an executor");
// Set up mock stack fidl server.
let (stack_proxy, stack_server) =
create_proxy::<StackMarker>().expect("failed to create stack fidl");
let stack_stream = stack_server
.into_stream()
.expect("failed to create a stack request stream.");
// Set up real Observer fidl server and client.
let (observer_proxy, observer_server) =
create_proxy::<ObserverMarker>().expect("failed to create observer fidl");
let observer_stream = observer_server
.into_stream()
.expect("failed to create an observer request stream.");
// Create and initialize interface_ids
let mut interface_ids = HashMap::new();
interface_ids.insert(NAME1.to_owned(), ID1);
interface_ids.insert(NAME2.to_owned(), ID2);
let interface_ids = Arc::new(Mutex::new(interface_ids));
let observer_service_task =
serve_fidl_requests(stack_proxy, observer_stream, interface_ids)
.unwrap_or_else(|e| fx_log_err!("failed to serve observer FIDL call: {}", e));
(exec, observer_service_task, observer_proxy, stack_stream)
}
#[test]
fn list_interfaces_test() {
let (mut exec, observer_service_task, observer_proxy, mut stack_stream) =
setup_interface_tests();
// Call observer FIDL call.
let client_fut = observer_proxy.list_interfaces();
// Let observer client run to stall.
pin_mut!(client_fut);
assert!(exec.run_until_stalled(&mut client_fut).is_pending());
// Let observer server run to stall.
pin_mut!(observer_service_task);
assert!(exec
.run_until_stalled(&mut observer_service_task)
.is_pending());
// Let stack server run to stall and check that we got an appropriate FIDL call.
let event = match exec.run_until_stalled(&mut stack_stream.next()) {
Poll::Ready(Some(Ok(req))) => req,
_ => panic!("Expected a stack fidl call, but there is none!"),
};
match event {
StackRequest::ListInterfaces { responder } => {
responder
.send(&mut vec![build_interface_info()].iter_mut())
.expect("failed to send list interface response");
}
_ => panic!("Unexpected stack call!"),
};
assert!(exec
.run_until_stalled(&mut observer_service_task)
.is_pending());
// Let observer client run until ready, and check that we got an expected response.
let (infos, status) = match exec.run_until_stalled(&mut client_fut) {
Poll::Ready(Ok(req)) => req,
other => panic!(
"Expected a response from observer fidl call, but got {:?}!",
other
),
};
assert_eq!(zx::sys::ZX_OK, status);
assert_eq!(vec![expect_interface_info()], infos.unwrap());
}
#[test]
fn list_interfaces_no_match_test() {
let (mut exec, observer_service_task, observer_proxy, mut stack_stream) =
setup_interface_tests();
// Call observer FIDL call.
let client_fut = observer_proxy.list_interfaces();
// Let observer client run to stall.
pin_mut!(client_fut);
assert!(exec.run_until_stalled(&mut client_fut).is_pending());
// Let observer server run to stall.
pin_mut!(observer_service_task);
assert!(exec
.run_until_stalled(&mut observer_service_task)
.is_pending());
// Let stack server run to stall and check that we got an appropriate FIDL call.
let event = match exec.run_until_stalled(&mut stack_stream.next()) {
Poll::Ready(Some(Ok(req))) => req,
_ => panic!("Expected a stack fidl call, but there is none!"),
};
match event {
StackRequest::ListInterfaces { responder } => {
responder
.send(
&mut vec![build_interface_info(), build_no_match_interface_info()]
.iter_mut(),
)
.expect("failed to send list interface response");
}
_ => panic!("Unexpected stack call!"),
};
assert!(exec
.run_until_stalled(&mut observer_service_task)
.is_pending());
// Let observer client run until ready, and check that we got an expected response.
let (infos, status) = match exec.run_until_stalled(&mut client_fut) {
Poll::Ready(Ok(req)) => req,
other => panic!(
"Expected a response from observer fidl call, but got {:?}!",
other
),
};
assert_eq!(zx::sys::ZX_OK, status);
assert_eq!(
vec![expect_interface_info(), expect_interface_info_no_match()],
infos.unwrap()
);
}
#[test]
fn get_interface_info_test() {
let (mut exec, observer_service_task, observer_proxy, mut stack_stream) =
setup_interface_tests();
// Call observer FIDL call.
let client_fut = observer_proxy.get_interface_info(NAME2);
// Let observer client run to stall.
pin_mut!(client_fut);
assert!(exec.run_until_stalled(&mut client_fut).is_pending());
// Let observer server run to stall.
pin_mut!(observer_service_task);
assert!(exec
.run_until_stalled(&mut observer_service_task)
.is_pending());
// Let stack server run to stall and check that we got an appropriate FIDL call.
let event = match exec.run_until_stalled(&mut stack_stream.next()) {
Poll::Ready(Some(Ok(req))) => req,
_ => panic!("Expected a stack fidl call, but there is none!"),
};
match event {
StackRequest::GetInterfaceInfo { id: _, responder } => {
responder
.send(Some(OutOfLine(&mut build_interface_info())), None)
.expect("failed to send list interface response");
}
_ => panic!("Unexpected stack call!"),
};
assert!(exec
.run_until_stalled(&mut observer_service_task)
.is_pending());
// Let observer client run until ready, and check that we got an expected response.
let response = match exec.run_until_stalled(&mut client_fut) {
Poll::Ready(Ok(req)) => req,
other => panic!(
"Expected a response from observer fidl call, but got {:?}",
other
),
};
assert_eq!(expect_interface_info(), *response.0.unwrap());
}
#[test]
fn get_interface_info_not_found_test() {
let (mut exec, observer_service_task, observer_proxy, mut _stack_stream) =
setup_interface_tests();
// Call observer FIDL call.
let client_fut = observer_proxy.get_interface_info("unknown");
// Let observer client run to stall.
pin_mut!(client_fut);
assert!(exec.run_until_stalled(&mut client_fut).is_pending());
// Let observer server run to stall.
pin_mut!(observer_service_task);
assert!(exec
.run_until_stalled(&mut observer_service_task)
.is_pending());
// Let observer client run to stall and check that we got an expected response.
let response = match exec.run_until_stalled(&mut client_fut) {
Poll::Ready(Ok(req)) => req,
other => panic!(
"Expected a response from observer fidl call, but got {:?}",
other
),
};
assert_eq!(zx::Status::NOT_FOUND.into_raw(), response.1);
}
}