| // 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; |
| } |
| } |