blob: 9e854a9396d68c4a6c7af74f4ecdd7a95a181200 [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.
use {
anyhow::{format_err, Error},
fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_mlme as fidl_mlme,
fidl_fuchsia_wlan_sme as fidl_sme,
fuchsia_inspect_contrib::{auto_persist, inspect_log},
futures::{channel::mpsc, future::Future},
log::info,
parking_lot::Mutex,
std::{collections::HashMap, sync::Arc},
wlan_inspect,
wlan_sme::serve::{create_sme, SmeServer},
};
use crate::{inspect, ServiceCfg};
pub type ShutdownSender = mpsc::Sender<()>;
/// Iface's PHY information.
#[derive(Debug, PartialEq)]
pub struct PhyOwnership {
// Iface's global PHY ID.
pub phy_id: u16,
// Local ID assigned by this iface's PHY.
pub phy_assigned_id: u16,
}
#[derive(Debug)]
pub struct NewIface {
// Global, unique iface ID.
pub id: u16,
// Information about this iface's PHY.
pub phy_ownership: PhyOwnership,
// A proxy to communicate with the iface's underlying MLME.
pub mlme_proxy: fidl_mlme::MlmeProxy,
}
pub struct IfaceDevice {
pub phy_ownership: PhyOwnership,
pub sme_server: SmeServer,
pub mlme_proxy: fidl_mlme::MlmeProxy,
pub device_info: fidl_mlme::DeviceInfo,
pub shutdown_sender: ShutdownSender,
}
pub struct IfaceMap {
inner: Mutex<Arc<HashMap<u16, Arc<IfaceDevice>>>>,
}
impl IfaceMap {
pub fn new() -> Self {
IfaceMap { inner: Mutex::new(Arc::new(HashMap::new())) }
}
pub fn insert(&self, iface_id: u16, device: IfaceDevice) {
let mut inner = self.inner.lock();
let hash_map = Arc::make_mut(&mut inner);
hash_map.insert(iface_id, Arc::new(device));
}
pub fn remove(&self, iface_id: &u16) {
let mut inner = self.inner.lock();
let hash_map = Arc::make_mut(&mut inner);
hash_map.remove(iface_id);
}
pub fn get_snapshot(&self) -> Arc<HashMap<u16, Arc<IfaceDevice>>> {
self.inner.lock().clone()
}
pub fn get(&self, iface_id: &u16) -> Option<Arc<IfaceDevice>> {
self.inner.lock().get(iface_id).map(|v| v.clone())
}
}
pub fn create_and_serve_sme(
cfg: ServiceCfg,
id: u16,
phy_ownership: PhyOwnership,
mlme_proxy: fidl_mlme::MlmeProxy,
ifaces: Arc<IfaceMap>,
inspect_tree: Arc<inspect::WlanstackTree>,
iface_tree_holder: Arc<wlan_inspect::iface_mgr::IfaceTreeHolder>,
device_info: fidl_mlme::DeviceInfo,
mac_sublayer_support: fidl_common::MacSublayerSupport,
security_support: fidl_common::SecuritySupport,
spectrum_management_support: fidl_common::SpectrumManagementSupport,
dev_monitor_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
persistence_req_sender: auto_persist::PersistenceReqSender,
generic_sme: fidl::endpoints::ServerEnd<fidl_sme::GenericSmeMarker>,
) -> Result<impl Future<Output = Result<(), Error>>, Error> {
let (shutdown_sender, shutdown_receiver) = mpsc::channel(1);
let (sme, sme_fut) = create_sme(
cfg.into(),
mlme_proxy.clone(),
&device_info,
mac_sublayer_support,
security_support,
spectrum_management_support,
iface_tree_holder.clone(),
inspect_tree.hasher.clone(),
persistence_req_sender,
shutdown_receiver,
generic_sme,
);
info!("new iface #{} with role '{:?}'", id, device_info.role);
inspect_log!(inspect_tree.device_events.lock().get_mut(), {
msg: format!("new iface #{} with role '{:?}'", id, device_info.role)
});
if let fidl_common::WlanMacRole::Client = device_info.role {
inspect_tree.mark_active_client_iface(id);
}
// For testing only: currently, all synthetic devices are softmac devices.
// This may change in the future.
if mac_sublayer_support.device.is_synthetic
&& mac_sublayer_support.device.mac_implementation_type
!= fidl_common::MacImplementationType::Softmac
{
return Err(format_err!("Synthetic devices must be SoftMAC"));
}
ifaces.insert(
id,
IfaceDevice { phy_ownership, sme_server: sme, mlme_proxy, device_info, shutdown_sender },
);
Ok(async move {
let result = sme_fut.await.map_err(|e| format_err!("error while serving SME: {}", e));
inspect_log!(
inspect_tree.device_events.lock().get_mut(),
msg: format!("iface removed: #{}", id)
);
inspect_tree.unmark_active_client_iface(id);
ifaces.remove(&id);
// Upon any error associated with the iface in wlanstack, the iface should be destroyed
// since it can no longer being managed by wlanstack. This includes either the associated
// MLME or SME FIDL channels being closed.
let mut req = fidl_fuchsia_wlan_device_service::DestroyIfaceRequest { iface_id: id };
if let Err(e) = dev_monitor_proxy.destroy_iface(&mut req).await {
info!("unable to inform DeviceMonitor of interface removal: {:?}", e);
}
result
})
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::test_helper,
fidl::endpoints::create_proxy,
fidl_mlme::MlmeMarker,
fuchsia_async as fasync,
fuchsia_inspect::assert_data_tree,
futures::channel::mpsc,
futures::future::join,
futures::sink::SinkExt,
futures::task::Poll,
futures::StreamExt,
pin_utils::pin_mut,
wlan_common::{
assert_variant, test_utils::fake_features::fake_spectrum_management_support_empty,
},
};
fn fake_device_info() -> fidl_mlme::DeviceInfo {
fidl_mlme::DeviceInfo {
role: fidl_common::WlanMacRole::Client,
bands: vec![],
sta_addr: [0xAC; 6],
softmac_hardware_capability: 0,
qos_capable: false,
}
}
fn fake_mac_sublayer_support() -> fidl_common::MacSublayerSupport {
fidl_common::MacSublayerSupport {
rate_selection_offload: fidl_common::RateSelectionOffloadExtension { supported: false },
data_plane: fidl_common::DataPlaneExtension {
data_plane_type: fidl_common::DataPlaneType::EthernetDevice,
},
device: fidl_common::DeviceExtension {
is_synthetic: false,
mac_implementation_type: fidl_common::MacImplementationType::Fullmac,
tx_status_report_supported: false,
},
}
}
fn fake_security_support() -> fidl_common::SecuritySupport {
fidl_common::SecuritySupport {
mfp: fidl_common::MfpFeature { supported: false },
sae: fidl_common::SaeFeature {
driver_handler_supported: false,
sme_handler_supported: false,
},
}
}
#[test]
fn query_serve_with_sme_channel() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (mlme_proxy, _mlme_server) =
create_proxy::<MlmeMarker>().expect("failed to create MlmeProxy");
let iface_map = IfaceMap::new();
let iface_map = Arc::new(iface_map);
let (inspect_tree, _persistence_stream) = test_helper::fake_inspect_tree();
let iface_tree_holder = inspect_tree.create_iface_child(1);
let (dev_monitor_proxy, _) =
create_proxy::<fidl_fuchsia_wlan_device_service::DeviceMonitorMarker>()
.expect("failed to create DeviceMonitor proxy");
let (persistence_req_sender, _persistence_stream) =
test_helper::create_inspect_persistence_channel();
let (generic_sme_proxy, generic_sme_server) =
create_proxy::<fidl_sme::GenericSmeMarker>().expect("failed to create MlmeProxy");
// Assert that the IfaceMap is initially empty.
assert!(iface_map.get(&5).is_none());
let serve_fut = create_and_serve_sme(
ServiceCfg::default(),
5,
PhyOwnership { phy_id: 1, phy_assigned_id: 2 },
mlme_proxy,
iface_map.clone(),
inspect_tree.clone(),
iface_tree_holder,
fake_device_info(),
fake_mac_sublayer_support(),
fake_security_support(),
fake_spectrum_management_support_empty(),
dev_monitor_proxy,
persistence_req_sender,
generic_sme_server,
)
.expect("failed to create SME");
// Assert that the IfaceMap now has an entry for the new iface.
assert!(iface_map.get(&5).is_some());
pin_mut!(serve_fut);
// Progress to cause SME creation and serving.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Retrieve SME instance and close SME (iface must be acquired).
let mut iface = iface_map.get(&5).expect("expected iface");
iface_map.remove(&5);
let mut_iface = Arc::get_mut(&mut iface).expect("error yielding iface");
let sme = assert_variant!(
mut_iface.sme_server,
SmeServer::Client(ref mut sme) => sme,
"expected Client SME to be spawned"
);
let close_fut = sme.close();
pin_mut!(close_fut);
let fut_result = exec.run_until_stalled(&mut close_fut);
assert_variant!(fut_result, Poll::Ready(_), "expected closing SME to succeed");
drop(generic_sme_proxy);
// Insert iface back into map.
let (mlme_proxy, _) = create_proxy::<MlmeMarker>().expect("failed to create MlmeProxy");
let (sender, _) = mpsc::unbounded();
let (shutdown_sender, _) = mpsc::channel(1);
iface_map.insert(
5,
IfaceDevice {
phy_ownership: PhyOwnership { phy_id: 0, phy_assigned_id: 0 },
sme_server: SmeServer::Client(sender),
mlme_proxy,
device_info: fake_device_info(),
shutdown_sender,
},
);
iface_map.get(&5).expect("expected iface");
// Progress SME serving to completion and verify iface was deleted
let fut_result = exec.run_until_stalled(&mut serve_fut);
assert_variant!(fut_result, Poll::Ready(_), "expected SME serving to be terminated");
assert!(iface_map.get(&5).is_none());
assert_data_tree!(inspect_tree.inspector, root: contains {
device_events: {
"0": contains { msg: "new iface #5 with role 'Client'" },
"1": contains { msg: "iface removed: #5" },
},
});
}
#[test]
fn sme_shutdown_signal() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (mlme_proxy, _mlme_server) =
create_proxy::<MlmeMarker>().expect("failed to create MlmeProxy");
let iface_map = IfaceMap::new();
let iface_map = Arc::new(iface_map);
let (inspect_tree, _persistence_stream) = test_helper::fake_inspect_tree();
let iface_tree_holder = inspect_tree.create_iface_child(1);
let (dev_monitor_proxy, dev_monitor_server) =
create_proxy::<fidl_fuchsia_wlan_device_service::DeviceMonitorMarker>()
.expect("failed to create DeviceMonitor proxy");
let mut dev_monitor_stream = dev_monitor_server
.into_stream()
.expect("failed to create DeviceMonitor request stream");
let (persistence_req_sender, _persistence_stream) =
test_helper::create_inspect_persistence_channel();
let (_generic_sme_proxy, generic_sme_server) =
create_proxy::<fidl_sme::GenericSmeMarker>().expect("failed to create MlmeProxy");
// Assert that the IfaceMap is initially empty.
assert!(iface_map.get(&5).is_none());
let serve_fut = create_and_serve_sme(
ServiceCfg::default(),
5,
PhyOwnership { phy_id: 1, phy_assigned_id: 2 },
mlme_proxy,
iface_map.clone(),
inspect_tree.clone(),
iface_tree_holder,
fake_device_info(),
fake_mac_sublayer_support(),
fake_security_support(),
fake_spectrum_management_support_empty(),
dev_monitor_proxy,
persistence_req_sender,
generic_sme_server,
)
.expect("failed to create SME");
// Assert that the IfaceMap now has an entry for the new iface.
assert!(iface_map.get(&5).is_some());
pin_mut!(serve_fut);
// Progress to cause SME creation and serving.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// SME closes on shutdown.
let mut shutdown_sender = iface_map.get(&5).unwrap().shutdown_sender.clone();
let shutdown_signal = shutdown_sender.send(());
let mut shutdown_fut = join(serve_fut, shutdown_signal);
assert_variant!(exec.run_until_stalled(&mut shutdown_fut), Poll::Pending);
// Verify that a notification is sent to DeviceMonitor.
assert_variant!(
exec.run_until_stalled(&mut dev_monitor_stream.next()),
Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::DestroyIface {
req,
responder,
}))) => {
assert_eq!(req, fidl_fuchsia_wlan_device_service::DestroyIfaceRequest { iface_id: 5 });
responder.send(0).expect("failed to send result to SME fut.");
});
// The SME future should complete successfully.
assert_variant!(exec.run_until_stalled(&mut shutdown_fut), Poll::Ready((Ok(()), Ok(()))));
}
#[test]
fn test_new_iface_map() {
let _exec = fasync::TestExecutor::new().expect("failed to create an executor");
let iface_map = IfaceMap::new();
let hashmap = iface_map.inner.lock();
assert!(hashmap.is_empty());
}
#[test]
fn test_iface_map_insert_remove() {
let _exec = fasync::TestExecutor::new().expect("failed to create an executor");
let iface_map = IfaceMap::new();
let (mlme_proxy, _) = create_proxy::<MlmeMarker>().expect("failed to create MlmeProxy");
let (sender, _) = mpsc::unbounded();
let (shutdown_sender, _) = mpsc::channel(1);
iface_map.insert(
5,
IfaceDevice {
phy_ownership: PhyOwnership { phy_id: 0, phy_assigned_id: 0 },
sme_server: SmeServer::Client(sender),
mlme_proxy,
device_info: fake_device_info(),
shutdown_sender,
},
);
{
let hashmap = iface_map.inner.lock();
assert!(!hashmap.is_empty());
}
iface_map.remove(&5);
{
let hashmap = iface_map.inner.lock();
assert!(hashmap.is_empty());
}
}
#[test]
fn test_iface_map_remove_nonexistent_iface() {
let _exec = fasync::TestExecutor::new().expect("failed to create an executor");
let iface_map = IfaceMap::new();
let (mlme_proxy, _) = create_proxy::<MlmeMarker>().expect("failed to create MlmeProxy");
let (sender, _) = mpsc::unbounded();
let (shutdown_sender, _) = mpsc::channel(1);
iface_map.insert(
5,
IfaceDevice {
phy_ownership: PhyOwnership { phy_id: 0, phy_assigned_id: 0 },
sme_server: SmeServer::Client(sender),
mlme_proxy,
device_info: fake_device_info(),
shutdown_sender,
},
);
{
let hashmap = iface_map.inner.lock();
assert!(!hashmap.is_empty());
}
iface_map.remove(&0);
{
let hashmap = iface_map.inner.lock();
assert!(!hashmap.is_empty());
}
}
#[test]
fn test_iface_map_insert_get() {
let _exec = fasync::TestExecutor::new().expect("failed to create an executor");
let iface_map = IfaceMap::new();
let (mlme_proxy, _) = create_proxy::<MlmeMarker>().expect("failed to create MlmeProxy");
let (sender, _) = mpsc::unbounded();
let (shutdown_sender, _) = mpsc::channel(1);
iface_map.insert(
5,
IfaceDevice {
phy_ownership: PhyOwnership { phy_id: 0, phy_assigned_id: 0 },
sme_server: SmeServer::Client(sender),
mlme_proxy,
device_info: fake_device_info(),
shutdown_sender,
},
);
let map_entry = iface_map.get(&5).expect("iface is missing from map");
assert_eq!(map_entry.phy_ownership, PhyOwnership { phy_id: 0, phy_assigned_id: 0 });
assert_eq!(map_entry.device_info, fake_device_info());
}
#[test]
fn test_iface_map_insert_get_nonexistent_iface() {
let _exec = fasync::TestExecutor::new().expect("failed to create an executor");
let iface_map = IfaceMap::new();
let (mlme_proxy, _) = create_proxy::<MlmeMarker>().expect("failed to create MlmeProxy");
let (sender, _) = mpsc::unbounded();
let (shutdown_sender, _) = mpsc::channel(1);
iface_map.insert(
5,
IfaceDevice {
phy_ownership: PhyOwnership { phy_id: 0, phy_assigned_id: 0 },
sme_server: SmeServer::Client(sender),
mlme_proxy,
device_info: fake_device_info(),
shutdown_sender,
},
);
assert!(iface_map.get(&0).is_none());
}
#[test]
fn test_get_snapshot() {
let _exec = fasync::TestExecutor::new().expect("failed to create an executor");
// The initial snapshot should be empty.
let iface_map = IfaceMap::new();
let hashmap = iface_map.get_snapshot();
assert!(hashmap.is_empty());
// Add an entry to the map and request another snapshot. This one should contain a single
// interface record.
let (mlme_proxy, _) = create_proxy::<MlmeMarker>().expect("failed to create MlmeProxy");
let (sender, _) = mpsc::unbounded();
let (shutdown_sender, _) = mpsc::channel(1);
iface_map.insert(
5,
IfaceDevice {
phy_ownership: PhyOwnership { phy_id: 0, phy_assigned_id: 0 },
sme_server: SmeServer::Client(sender),
mlme_proxy,
device_info: fake_device_info(),
shutdown_sender,
},
);
let hashmap = iface_map.get_snapshot();
assert!(!hashmap.is_empty());
let keys: Vec<u16> = hashmap.keys().cloned().collect();
assert_eq!(keys.len(), 1);
assert_eq!(keys[0], 5);
// Remove the interface and again expect the snapshot to be empty.
iface_map.remove(&5);
let hashmap = iface_map.get_snapshot();
assert!(hashmap.is_empty());
}
}