blob: 42c23dd0d611b6e7d3d02c71f96bc522519a3416 [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::types::AddressDto;
use crate::common_utils::common::macros::{fx_err_and_bail, with_line};
use crate::netstack::types::CustomInterfaceInfo;
use anyhow::{Context as _, Error};
use fidl::endpoints::create_proxy;
use fidl_fuchsia_net::IpAddress;
use fidl_fuchsia_net_interfaces::{
Address, Event, StateMarker, StateProxy, WatcherMarker, WatcherOptions, WatcherProxy,
};
use fidl_fuchsia_net_stack::{StackMarker, StackProxy};
use fuchsia_component as app;
use fuchsia_syslog::{self, fx_log_err, fx_log_info};
use parking_lot::RwLock;
#[derive(Debug)]
struct InnerNestackFacade {
/// The current Netstack Proxy
netstack_proxy: Option<StackProxy>,
/// The proxy to access the lowpan State service.
state_proxy: Option<StateProxy>,
}
/// Perform Netstack operations.
///
/// Note this object is shared among all threads created by server.
///
#[derive(Debug)]
pub struct NetstackFacade {
inner: RwLock<InnerNestackFacade>,
}
impl NetstackFacade {
pub fn new() -> NetstackFacade {
NetstackFacade {
inner: RwLock::new(InnerNestackFacade { netstack_proxy: None, state_proxy: None }),
}
}
/// Creates a Netstack Proxy.
pub fn create_netstack_proxy(&self) -> Result<StackProxy, Error> {
let tag = "NestackFacade::create_netstack_proxy";
match self.inner.read().netstack_proxy.clone() {
Some(netstack_proxy) => {
fx_log_info!(tag: &with_line!(tag), "Current netstack proxy: {:?}", netstack_proxy);
Ok(netstack_proxy)
}
None => {
fx_log_info!(tag: &with_line!(tag), "Setting new netstack proxy");
let netstack_proxy = app::client::connect_to_service::<StackMarker>();
if let Err(err) = netstack_proxy {
fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to create profile server proxy: {}", err)
);
}
netstack_proxy
}
}
}
/// Creates a Netstack Proxy.
pub fn create_state_proxy(&self) -> Result<StateProxy, Error> {
let tag = "NestackFacade::create_state_proxy";
match self.inner.read().state_proxy.clone() {
Some(state_proxy) => {
fx_log_info!(tag: &with_line!(tag), "Current state proxy: {:?}", state_proxy);
Ok(state_proxy)
}
None => {
fx_log_info!(tag: &with_line!(tag), "Setting new state proxy");
let state_proxy = app::client::connect_to_service::<StateMarker>();
if let Err(err) = state_proxy {
fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to create profile server proxy: {}", err)
);
}
state_proxy
}
}
}
pub fn init_netstack_proxy(&self) -> Result<(), Error> {
self.inner.write().netstack_proxy = Some(self.create_netstack_proxy()?);
self.inner.write().state_proxy = Some(self.create_state_proxy()?);
Ok(())
}
/// Returns the PairingStateWatcher proxy provided on instantiation.
fn watcher(&self) -> Result<WatcherProxy, Error> {
let (watching_proxy, watching_server_end) = create_proxy::<WatcherMarker>()?;
match self.inner.read().state_proxy.as_ref() {
Some(state) => {
state.get_watcher(WatcherOptions::EMPTY, watching_server_end)?;
}
None => bail!("State proxy is not set!"),
}
Ok(watching_proxy)
}
pub async fn list_interfaces(&self) -> Result<Vec<CustomInterfaceInfo>, Error> {
let tag = "NestackFacade::list_interfaces";
let raw_interface_list = match &self.inner.read().netstack_proxy {
Some(proxy) => proxy.list_interfaces().await?,
None => fx_err_and_bail!(&with_line!(tag), "No Server Proxy created."),
};
let mut interface_list: Vec<CustomInterfaceInfo> = Vec::new();
for interface in raw_interface_list {
interface_list.push(CustomInterfaceInfo::new(&interface));
}
Ok(interface_list)
}
pub async fn get_interface_info(&self, id: u64) -> Result<CustomInterfaceInfo, Error> {
let tag = "NestackFacade::get_interface_info";
match &self.inner.read().netstack_proxy {
Some(proxy) => {
match proxy.get_interface_info(id).await.context("failed to get interface info")? {
Ok(info) => Ok(CustomInterfaceInfo::new(&info)),
Err(e) => {
let err_msg =
format!("Unable to get interface info for id {:?}: {:?}", id, e);
fx_err_and_bail!(&with_line!(tag), err_msg)
}
}
}
None => fx_err_and_bail!(&with_line!(tag), "No Server Proxy created."),
}
}
pub async fn enable_interface(&self, id: u64) -> Result<(), Error> {
let tag = "NestackFacade::enable_interface";
match &self.inner.read().netstack_proxy {
Some(proxy) => {
let res = proxy.enable_interface(id).await?;
match res {
Ok(()) => (),
Err(e) => {
let err_msg = format!("Unable to enable interface id {:?}: {:?}", id, e);
fx_err_and_bail!(&with_line!(tag), err_msg)
}
}
}
None => fx_err_and_bail!(&with_line!(tag), "No Server Proxy created."),
};
Ok(())
}
pub async fn disable_interface(&self, id: u64) -> Result<(), Error> {
let tag = "NestackFacade::disable_interface";
match &self.inner.read().netstack_proxy {
Some(proxy) => {
let res = proxy.disable_interface(id).await?;
match res {
Ok(()) => (),
Err(e) => {
let err_msg = format!("Unable to disable interface id {:?}: {:?}", id, e);
fx_err_and_bail!(&with_line!(tag), err_msg)
}
}
}
None => fx_err_and_bail!(&with_line!(tag), "No Server Proxy created."),
};
Ok(())
}
/// Returns the ipv6 addresses that are registered with the PairingStateWatcher.
pub async fn get_ipv6_addresses(&self) -> Result<Vec<AddressDto>, Error> {
let mut addresses: Vec<AddressDto> = Vec::new();
let watcher = self.watcher()?;
loop {
match watcher.watch().await {
Ok(Event::Existing(existing)) => {
addresses.append(
&mut existing
.addresses
.unwrap()
.into_iter()
.filter(|addr: &Address| match addr.addr {
Some(subnet) => match subnet.addr {
IpAddress::Ipv6(_ipv6) => true,
_ => false,
},
None => false,
})
.map(|addr: Address| addr.into())
.collect(),
);
}
_ => {
break;
}
}
}
Ok(addresses)
}
/// Returns the ipv6 link local addresses that are registered with the PairingStateWatcher.
pub async fn get_link_local_ipv6_addresses(&self) -> Result<Vec<AddressDto>, Error> {
let mut addresses: Vec<AddressDto> = Vec::new();
let watcher = self.watcher()?;
loop {
match watcher.watch().await {
Ok(Event::Existing(existing)) => {
addresses.append(
&mut existing
.addresses
.unwrap()
.into_iter()
.filter(|addr: &Address| match addr.addr {
Some(subnet) => match subnet.addr {
IpAddress::Ipv6(ipv6) => {
ipv6.addr[0] == 254 && ipv6.addr[1] == 128
}
_ => false,
},
None => false,
})
.map(|addr: Address| addr.into())
.collect(),
);
}
_ => {
break;
}
}
}
Ok(addresses)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::netstack::types::AddressDto;
use fidl::endpoints::create_proxy_and_stream;
use fidl_fuchsia_net::{IpAddress, Ipv4Address, Ipv6Address, Subnet};
use fidl_fuchsia_net_interfaces::{Address, Properties, StateRequest, WatcherRequest};
use fuchsia_async as fasync;
use futures::prelude::*;
struct MockStateTester {
expected_state: Vec<Box<dyn FnOnce(WatcherRequest) + Send + 'static>>,
}
impl MockStateTester {
fn new() -> Self {
Self { expected_state: vec![] }
}
pub fn create_facade_and_serve_state(self) -> (NetstackFacade, impl Future<Output = ()>) {
let (state_proxy, stream_future) = self.build_state_and_watcher();
(
NetstackFacade {
inner: RwLock::new(InnerNestackFacade { netstack_proxy: None, state_proxy }),
},
stream_future,
)
}
fn push_state(mut self, request: impl FnOnce(WatcherRequest) + Send + 'static) -> Self {
self.expected_state.push(Box::new(request));
self
}
fn build_state_and_watcher(self) -> (Option<StateProxy>, impl Future<Output = ()>) {
let (proxy, mut stream) = create_proxy_and_stream::<StateMarker>().unwrap();
let stream_fut = async move {
match stream.next().await {
Some(Ok(StateRequest::GetWatcher { watcher, .. })) => {
let mut into_stream = watcher.into_stream().unwrap();
for expected in self.expected_state {
expected(into_stream.next().await.unwrap().unwrap());
}
}
err => panic!("Error in request handler: {:?}", err),
}
};
(Some(proxy), stream_fut)
}
fn expect_get_ipv6_addresses(self, result: Vec<Address>) -> Self {
self.push_state(move |req| match req {
WatcherRequest::Watch { responder } => {
responder
.send(&mut Event::Existing(Properties {
addresses: Some(result),
..Properties::EMPTY
}))
.unwrap();
}
})
}
}
#[fasync::run_singlethreaded(test)]
async fn test_get_ipv6_addresses() {
let ipv6_address = Address {
addr: Some(Subnet {
addr: IpAddress::Ipv6(Ipv6Address {
addr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
}),
prefix_len: 2,
}),
..Address::EMPTY
};
let ipv4_address = Address {
addr: Some(Subnet {
addr: IpAddress::Ipv4(Ipv4Address { addr: [0, 1, 2, 3] }),
prefix_len: 2,
}),
..Address::EMPTY
};
let ipv6_addresses = [ipv6_address.clone()];
let all_addresses = [ipv6_address.clone(), ipv4_address.clone()];
let (facade, stream_fut) = MockStateTester::new()
.expect_get_ipv6_addresses(all_addresses.to_vec())
.create_facade_and_serve_state();
let facade_fut = async move {
let result_address = facade.get_ipv6_addresses().await.unwrap();
assert_eq!(result_address.len(), ipv6_addresses.len());
assert!(result_address
.into_iter()
.zip(ipv6_addresses.iter())
.all(|a: (AddressDto, &Address)| Into::<Address>::into(a.0) == (*a.1)));
};
future::join(facade_fut, stream_fut).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_get_link_local_ipv6_addresses() {
let ipv6_address = Address {
addr: Some(Subnet {
addr: IpAddress::Ipv6(Ipv6Address {
addr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
}),
prefix_len: 2,
}),
..Address::EMPTY
};
let link_local_ipv6_address = Address {
addr: Some(Subnet {
addr: IpAddress::Ipv6(Ipv6Address {
addr: [254, 128, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
}),
prefix_len: 2,
}),
..Address::EMPTY
};
let ipv4_address = Address {
addr: Some(Subnet {
addr: IpAddress::Ipv4(Ipv4Address { addr: [0, 1, 2, 3] }),
prefix_len: 2,
}),
..Address::EMPTY
};
let link_local_ipv6_addresses = [link_local_ipv6_address.clone()];
let all_addresses =
[ipv6_address.clone(), link_local_ipv6_address.clone(), ipv4_address.clone()];
let (facade, stream_fut) = MockStateTester::new()
.expect_get_ipv6_addresses(all_addresses.to_vec())
.create_facade_and_serve_state();
let facade_fut = async move {
let result_address = facade.get_link_local_ipv6_addresses().await.unwrap();
assert_eq!(result_address.len(), link_local_ipv6_addresses.len());
assert!(result_address
.into_iter()
.zip(link_local_ipv6_addresses.iter())
.all(|a: (AddressDto, &Address)| Into::<Address>::into(a.0) == (*a.1)));
};
future::join(facade_fut, stream_fut).await;
}
}