blob: 90cec97c8e786ce201f49f023ad9e2a3066afcf4 [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.
pub mod ap;
pub mod client;
pub mod mesh;
use {
crate::{MlmeRequest, MlmeStream, Station},
anyhow::format_err,
fidl::endpoints::create_endpoints,
fidl_fuchsia_wlan_common as fidl_common,
fidl_fuchsia_wlan_mlme::{self as fidl_mlme, MlmeEventStream, MlmeProxy},
fidl_fuchsia_wlan_sme as fidl_sme,
fuchsia_inspect_contrib::auto_persist,
fuchsia_zircon as zx,
futures::{channel::mpsc, future::FutureObj, prelude::*, select},
log::{error, warn},
std::marker::Unpin,
std::sync::{Arc, Mutex},
wlan_common::{
hasher::WlanHasher,
timer::{self, TimeEntry},
},
};
pub type ClientSmeServer = mpsc::UnboundedSender<client::Endpoint>;
pub type ApSmeServer = mpsc::UnboundedSender<ap::Endpoint>;
pub type MeshSmeServer = mpsc::UnboundedSender<mesh::Endpoint>;
#[derive(Clone)]
pub enum SmeServer {
Client(ClientSmeServer),
Ap(ApSmeServer),
Mesh(MeshSmeServer),
}
async fn serve_generic_sme(
generic_sme: fidl::endpoints::ServerEnd<fidl_sme::GenericSmeMarker>,
mut sme_server: SmeServer,
) -> Result<(), anyhow::Error> {
let mut generic_sme_stream = match generic_sme.into_stream() {
Ok(stream) => stream,
Err(e) => return Err(format_err!("Failed to handle Generic SME stream: {}", e)),
};
loop {
match generic_sme_stream.next().await {
// Right now we only support one API per-sme, but in the future we plan to support
// multiple and this fn will be more useful.
Some(Ok(req)) => {
let result = match req {
fidl_sme::GenericSmeRequest::GetClientSme { responder } => {
let (client_end, server_end) =
create_endpoints::<fidl_sme::ClientSmeMarker>()
.expect("failed to create ClientSme");
let mut response = if let SmeServer::Client(server) = &mut sme_server {
server
.send(server_end)
.await
.map(|_| client_end)
.map_err(|_| zx::Status::PEER_CLOSED.into_raw())
} else {
Err(zx::Status::NOT_SUPPORTED.into_raw())
};
responder.send(&mut response)
}
fidl_sme::GenericSmeRequest::GetApSme { responder } => {
let (client_end, server_end) = create_endpoints::<fidl_sme::ApSmeMarker>()
.expect("failed to create ApSme");
let mut response = if let SmeServer::Ap(server) = &mut sme_server {
server
.send(server_end)
.await
.map(|_| client_end)
.map_err(|_| zx::Status::PEER_CLOSED.into_raw())
} else {
Err(zx::Status::NOT_SUPPORTED.into_raw())
};
responder.send(&mut response)
}
fidl_sme::GenericSmeRequest::GetSmeTelemetry { responder } => {
// TODO(fxbug.dev/66772): Support SME Telemetry API
warn!("Requested unsupported SME telemetry API");
responder.send(&mut Err(zx::Status::NOT_SUPPORTED.into_raw()))
}
};
if let Err(e) = result {
error!("Failed to respond to SME handle request: {}", e);
}
}
Some(Err(e)) => {
return Err(format_err!("Generic SME stream failed: {}", e));
}
None => {
return Err(format_err!("Generic SME stream terminated"));
}
}
}
}
pub fn create_sme(
cfg: crate::Config,
mlme_proxy: fidl_mlme::MlmeProxy,
device_info: &fidl_mlme::DeviceInfo,
mac_sublayer_support: fidl_common::MacSublayerSupport,
security_support: fidl_common::SecuritySupport,
spectrum_management_support: fidl_common::SpectrumManagementSupport,
iface_tree_holder: Arc<wlan_inspect::iface_mgr::IfaceTreeHolder>,
hasher: WlanHasher,
persistence_req_sender: auto_persist::PersistenceReqSender,
mut shutdown_receiver: mpsc::Receiver<()>,
generic_sme: fidl::endpoints::ServerEnd<fidl_sme::GenericSmeMarker>,
) -> (SmeServer, impl Future<Output = Result<(), anyhow::Error>>) {
let device_info = device_info.clone();
let event_stream = mlme_proxy.take_event_stream();
let (server, sme_fut) = match device_info.role {
fidl_common::WlanMacRole::Client => {
let (sender, receiver) = mpsc::unbounded();
let fut = client::serve(
cfg,
mlme_proxy,
device_info,
mac_sublayer_support,
security_support,
spectrum_management_support,
event_stream,
receiver,
iface_tree_holder,
hasher,
persistence_req_sender,
);
(SmeServer::Client(sender), FutureObj::new(Box::new(fut)))
}
fidl_common::WlanMacRole::Ap => {
let (sender, receiver) = mpsc::unbounded();
let fut =
ap::serve(mlme_proxy, device_info, mac_sublayer_support, event_stream, receiver);
(SmeServer::Ap(sender), FutureObj::new(Box::new(fut)))
}
fidl_common::WlanMacRole::Mesh => {
let (sender, receiver) = mpsc::unbounded();
let fut = mesh::serve(mlme_proxy, device_info, event_stream, receiver);
(SmeServer::Mesh(sender), FutureObj::new(Box::new(fut)))
}
};
let generic_sme_fut = serve_generic_sme(generic_sme, server.clone());
let sme_fut_with_shutdown = async move {
select! {
sme_fut = sme_fut.fuse() => sme_fut,
generic_sme_fut = generic_sme_fut.fuse() => generic_sme_fut,
_ = shutdown_receiver.select_next_some() => Ok(()),
}
};
(server, sme_fut_with_shutdown)
}
// The returned future successfully terminates when MLME closes the channel
async fn serve_mlme_sme<STA, TS>(
proxy: MlmeProxy,
mut event_stream: MlmeEventStream,
station: Arc<Mutex<STA>>,
mut mlme_stream: MlmeStream,
time_stream: TS,
) -> Result<(), anyhow::Error>
where
STA: Station,
TS: Stream<Item = TimeEntry<<STA as crate::Station>::Event>> + Unpin,
{
let mut timeout_stream = timer::make_async_timed_event_stream(time_stream).fuse();
loop {
select! {
// Fuse rationale: any `none`s in the MLME stream should result in
// bailing immediately, so we don't need to track if we've seen a
// `None` or not and can `fuse` directly in the `select` call.
mlme_event = event_stream.next().fuse() => match mlme_event {
Some(Ok(mlme_event)) => station.lock().unwrap().on_mlme_event(mlme_event),
Some(Err(ref e)) if e.is_closed() => return Ok(()),
None => return Ok(()),
Some(Err(e)) => return Err(format_err!("Error reading an event from MLME channel: {}", e)),
},
mlme_req = mlme_stream.next().fuse() => match mlme_req {
Some(req) => match forward_mlme_request(req, &proxy) {
Ok(()) => {},
Err(ref e) if e.is_closed() => return Ok(()),
Err(e) => return Err(format_err!("Error forwarding a request from SME to MLME: {}", e)),
},
None => return Err(format_err!("Stream of requests from SME to MLME has ended unexpectedly")),
},
timeout = timeout_stream.next() => match timeout {
Some(timed_event) => station.lock().unwrap().on_timeout(timed_event),
None => return Err(format_err!("SME timer stream has ended unexpectedly")),
},
}
}
}
fn forward_mlme_request(req: MlmeRequest, proxy: &MlmeProxy) -> Result<(), fidl::Error> {
match req {
MlmeRequest::Scan(mut req) => proxy.start_scan(&mut req),
MlmeRequest::Join(mut req) => proxy.join_req(&mut req),
MlmeRequest::Authenticate(mut req) => proxy.authenticate_req(&mut req),
MlmeRequest::AuthResponse(mut resp) => proxy.authenticate_resp(&mut resp),
MlmeRequest::Associate(mut req) => proxy.associate_req(&mut req),
MlmeRequest::AssocResponse(mut resp) => proxy.associate_resp(&mut resp),
MlmeRequest::Connect(mut req) => proxy.connect_req(&mut req),
MlmeRequest::Reconnect(mut req) => proxy.reconnect_req(&mut req),
MlmeRequest::Deauthenticate(mut req) => proxy.deauthenticate_req(&mut req),
MlmeRequest::Eapol(mut req) => proxy.eapol_req(&mut req),
MlmeRequest::SetKeys(mut req) => proxy.set_keys_req(&mut req),
MlmeRequest::SetCtrlPort(mut req) => proxy.set_controlled_port(&mut req),
MlmeRequest::Start(mut req) => proxy.start_req(&mut req),
MlmeRequest::Stop(mut req) => proxy.stop_req(&mut req),
MlmeRequest::SendMpOpenAction(mut req) => proxy.send_mp_open_action(&mut req),
MlmeRequest::SendMpConfirmAction(mut req) => proxy.send_mp_confirm_action(&mut req),
MlmeRequest::MeshPeeringEstablished(mut req) => proxy.mesh_peering_established(&mut req),
MlmeRequest::SaeHandshakeResp(mut resp) => proxy.sae_handshake_resp(&mut resp),
MlmeRequest::SaeFrameTx(mut frame) => proxy.sae_frame_tx(&mut frame),
MlmeRequest::WmmStatusReq => proxy.wmm_status_req(),
MlmeRequest::FinalizeAssociation(mut cap) => proxy.finalize_association_req(&mut cap),
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::test_utils,
fidl::endpoints::create_proxy,
fidl_mlme::MlmeMarker,
fuchsia_async as fasync,
fuchsia_inspect::Inspector,
futures::task::Poll,
pin_utils::pin_mut,
std::{pin::Pin, sync::Arc},
wlan_common::{
assert_variant,
test_utils::fake_features::{
fake_mac_sublayer_support, fake_security_support,
fake_spectrum_management_support_empty,
},
},
wlan_inspect::IfaceTreeHolder,
};
const PLACEHOLDER_HASH_KEY: [u8; 8] = [88, 77, 66, 55, 44, 33, 22, 11];
#[test]
fn sme_shutdown() {
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 inspector = Inspector::new();
let iface_tree_holder = IfaceTreeHolder::new(inspector.root().create_child("sme"));
let (persistence_req_sender, _persistence_stream) =
test_utils::create_inspect_persistence_channel();
let (mut shutdown_sender, shutdown_receiver) = mpsc::channel(1);
let (_generic_sme_proxy, generic_sme_server) =
create_proxy::<fidl_sme::GenericSmeMarker>().expect("failed to create MlmeProxy");
let (_sme_server, serve_fut) = create_sme(
crate::Config::default(),
mlme_proxy,
&test_utils::fake_device_info([0; 6]),
fake_mac_sublayer_support(),
fake_security_support(),
fake_spectrum_management_support_empty(),
Arc::new(iface_tree_holder),
WlanHasher::new(PLACEHOLDER_HASH_KEY),
persistence_req_sender,
shutdown_receiver,
generic_sme_server,
);
pin_mut!(serve_fut);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Retrieve SME instance and close SME
shutdown_sender.try_send(()).expect("expect sending shutdown command to succeed");
// Verify SME future is finished
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Ready(Ok(())));
}
#[test]
fn sme_close_endpoints() {
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 inspector = Inspector::new();
let iface_tree_holder = IfaceTreeHolder::new(inspector.root().create_child("sme"));
let (persistence_req_sender, _persistence_stream) =
test_utils::create_inspect_persistence_channel();
let (_shutdown_sender, shutdown_receiver) = mpsc::channel(1);
let (generic_sme_proxy, generic_sme_server) =
create_proxy::<fidl_sme::GenericSmeMarker>().expect("failed to create MlmeProxy");
let (mut sme_server, serve_fut) = create_sme(
crate::Config::default(),
mlme_proxy,
&test_utils::fake_device_info([0; 6]),
fake_mac_sublayer_support(),
fake_security_support(),
fake_spectrum_management_support_empty(),
Arc::new(iface_tree_holder),
WlanHasher::new(PLACEHOLDER_HASH_KEY),
persistence_req_sender,
shutdown_receiver,
generic_sme_server,
);
pin_mut!(serve_fut);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Retrieve SME instance and close SME
let sme = assert_variant!(
sme_server,
SmeServer::Client(ref mut sme) => sme,
"expected Client SME to be spawned"
);
let close_fut = sme.close();
pin_mut!(close_fut);
assert_variant!(exec.run_until_stalled(&mut close_fut), Poll::Ready(_));
// Also close secondary SME endpoint in the Generic SME.
drop(generic_sme_proxy);
// Verify SME future is finished
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Ready(Err(_)));
}
struct GenericSmeTestHelper {
proxy: fidl_sme::GenericSmeProxy,
// These values must stay in scope or the SME will terminate, but they
// are not relevant to Generic SME tests.
_inspector: Inspector,
_shutdown_sender: mpsc::Sender<()>,
_persistence_stream: mpsc::Receiver<String>,
_mlme_server: fidl::endpoints::ServerEnd<fidl_mlme::MlmeMarker>,
// Executor goes last to avoid test shutdown failures.
exec: fasync::TestExecutor,
}
fn start_generic_sme_test(
role: fidl_common::WlanMacRole,
) -> (GenericSmeTestHelper, Pin<Box<impl Future<Output = Result<(), anyhow::Error>>>>) {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let inspector = Inspector::new();
let (mlme_proxy, mlme_server) =
create_proxy::<MlmeMarker>().expect("failed to create MlmeProxy");
let iface_tree_holder = IfaceTreeHolder::new(inspector.root().create_child("sme"));
let (persistence_req_sender, persistence_stream) =
test_utils::create_inspect_persistence_channel();
let (shutdown_sender, shutdown_receiver) = mpsc::channel(1);
let (generic_sme_proxy, generic_sme_server) =
create_proxy::<fidl_sme::GenericSmeMarker>().expect("failed to create MlmeProxy");
let device_info = fidl_mlme::DeviceInfo { role, ..test_utils::fake_device_info([0; 6]) };
let (_sme_server, serve_fut) = create_sme(
crate::Config::default(),
mlme_proxy,
&device_info,
fake_mac_sublayer_support(),
fake_security_support(),
fake_spectrum_management_support_empty(),
Arc::new(iface_tree_holder),
WlanHasher::new(PLACEHOLDER_HASH_KEY),
persistence_req_sender,
shutdown_receiver,
generic_sme_server,
);
let mut serve_fut = Box::pin(serve_fut);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
(
GenericSmeTestHelper {
proxy: generic_sme_proxy,
_inspector: inspector,
_shutdown_sender: shutdown_sender,
_persistence_stream: persistence_stream,
_mlme_server: mlme_server,
exec,
},
serve_fut,
)
}
#[test]
fn generic_sme_get_client() {
let (mut helper, mut serve_fut) = start_generic_sme_test(fidl_common::WlanMacRole::Client);
let mut client_sme_fut = helper.proxy.get_client_sme();
assert_variant!(helper.exec.run_until_stalled(&mut serve_fut), Poll::Pending);
let client_sme = assert_variant!(helper.exec.run_until_stalled(&mut client_sme_fut), Poll::Ready(Ok(Ok(sme))) => sme);
let client_proxy = client_sme.into_proxy().unwrap();
let mut status_fut = client_proxy.status();
assert_variant!(helper.exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
helper.exec.run_until_stalled(&mut status_fut),
Poll::Ready(Ok(fidl_sme::ClientStatusResponse::Idle(_)))
);
}
#[test]
fn generic_sme_get_ap_from_client_fails() {
let (mut helper, mut serve_fut) = start_generic_sme_test(fidl_common::WlanMacRole::Client);
let mut client_sme_fut = helper.proxy.get_ap_sme();
assert_variant!(helper.exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
helper.exec.run_until_stalled(&mut client_sme_fut),
Poll::Ready(Ok(Err(_)))
);
}
#[test]
fn generic_sme_get_ap() {
let (mut helper, mut serve_fut) = start_generic_sme_test(fidl_common::WlanMacRole::Ap);
let mut client_sme_fut = helper.proxy.get_ap_sme();
assert_variant!(helper.exec.run_until_stalled(&mut serve_fut), Poll::Pending);
let ap_sme = assert_variant!(helper.exec.run_until_stalled(&mut client_sme_fut), Poll::Ready(Ok(Ok(sme))) => sme);
let ap_proxy = ap_sme.into_proxy().unwrap();
let mut status_fut = ap_proxy.status();
assert_variant!(helper.exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
helper.exec.run_until_stalled(&mut status_fut),
Poll::Ready(Ok(fidl_sme::ApStatusResponse { .. }))
);
}
#[test]
fn generic_sme_get_client_from_ap_fails() {
let (mut helper, mut serve_fut) = start_generic_sme_test(fidl_common::WlanMacRole::Ap);
let mut client_sme_fut = helper.proxy.get_client_sme();
assert_variant!(helper.exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
helper.exec.run_until_stalled(&mut client_sme_fut),
Poll::Ready(Ok(Err(_)))
);
}
}