Skip to main content

wlan_sme/serve/
client.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::client::{
6    self as client_sme, ConnectResult, ConnectTransactionEvent, ConnectTransactionStream,
7    RoamResult, ScheduledScanReceiver,
8};
9use crate::{MlmeEventStream, MlmeSink, MlmeStream};
10use fidl::endpoints::{RequestStream, ServerEnd};
11use fidl_fuchsia_wlan_common as fidl_common;
12use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
13use fidl_fuchsia_wlan_mlme as fidl_mlme;
14use fidl_fuchsia_wlan_sme::{self as fidl_sme, ClientSmeRequest, TelemetryRequest};
15use fuchsia_sync::Mutex;
16use futures::channel::mpsc;
17use futures::prelude::*;
18use futures::select;
19use ieee80211::MacAddrBytes;
20use log::error;
21use std::pin::pin;
22use std::sync::Arc;
23use wlan_common::scan::write_vmo;
24
25pub type Endpoint = ServerEnd<fidl_sme::ClientSmeMarker>;
26type Sme = client_sme::ClientSme;
27
28#[allow(clippy::too_many_arguments, reason = "mass allow for https://fxbug.dev/381896734")]
29pub fn serve(
30    cfg: crate::Config,
31    device_info: fidl_mlme::DeviceInfo,
32    security_support: fidl_common::SecuritySupport,
33    spectrum_management_support: fidl_common::SpectrumManagementSupport,
34    event_stream: MlmeEventStream,
35    new_fidl_clients: mpsc::UnboundedReceiver<Endpoint>,
36    new_telemetry_fidl_clients: mpsc::UnboundedReceiver<
37        fidl::endpoints::ServerEnd<fidl_sme::TelemetryMarker>,
38    >,
39    inspector: fuchsia_inspect::Inspector,
40    inspect_node: fuchsia_inspect::Node,
41) -> (MlmeSink, MlmeStream, impl Future<Output = Result<(), anyhow::Error>>) {
42    let wpa3_supported =
43        security_support.mfp.as_ref().is_some_and(|mfp| mfp.supported.unwrap_or(false))
44            && security_support.sae.as_ref().is_some_and(|sae| {
45                sae.driver_handler_supported.unwrap_or(false)
46                    || sae.sme_handler_supported.unwrap_or(false)
47            });
48    let owe_supported =
49        security_support.mfp.as_ref().is_some_and(|mfp| mfp.supported.unwrap_or(false))
50            && security_support.owe.as_ref().is_some_and(|owe| owe.supported.unwrap_or(false));
51    let cfg = client_sme::ClientConfig::from_config(cfg, wpa3_supported, owe_supported);
52    let (sme, mlme_sink, mlme_stream, time_stream) = Sme::new(
53        cfg,
54        device_info,
55        inspector,
56        inspect_node,
57        security_support,
58        spectrum_management_support,
59    );
60    let fut = async move {
61        let sme = Arc::new(Mutex::new(sme));
62        let mlme_sme = super::serve_mlme_sme(event_stream, Arc::clone(&sme), time_stream);
63        let sme_fidl = super::serve_fidl(&*sme, new_fidl_clients, handle_fidl_request);
64        let telemetry_fidl =
65            super::serve_fidl(&*sme, new_telemetry_fidl_clients, handle_telemetry_fidl_request);
66        let mlme_sme = pin!(mlme_sme);
67        let sme_fidl = pin!(sme_fidl);
68        select! {
69            mlme_sme = mlme_sme.fuse() => mlme_sme?,
70            sme_fidl = sme_fidl.fuse() => match sme_fidl? {},
71            telemetry_fidl = telemetry_fidl.fuse() => match telemetry_fidl? {},
72        }
73        Ok(())
74    };
75    (mlme_sink, mlme_stream, fut)
76}
77
78async fn handle_fidl_request(
79    sme: &Mutex<Sme>,
80    request: fidl_sme::ClientSmeRequest,
81) -> Result<(), fidl::Error> {
82    #[allow(clippy::unit_arg, reason = "mass allow for https://fxbug.dev/381896734")]
83    match request {
84        ClientSmeRequest::Scan { req, responder } => Ok(scan(sme, req, |result| match result {
85            Ok(scan_results) => responder.send(Ok(write_vmo(scan_results)?)).map_err(|e| e.into()),
86            Err(e) => responder.send(Err(e)).map_err(|e| e.into()),
87        })
88        .await
89        .unwrap_or_else(|e| error!("Error handling a scan transaction: {:?}", e))),
90        ClientSmeRequest::Connect { req, txn, .. } => Ok(connect(sme, txn, req)
91            .await
92            .unwrap_or_else(|e| error!("Error handling a connect transaction: {:?}", e))),
93        ClientSmeRequest::Roam { req, .. } => Ok(roam(sme, req)),
94        ClientSmeRequest::Disconnect { responder, reason } => {
95            disconnect(sme, reason, responder);
96            Ok(())
97        }
98        ClientSmeRequest::Status { responder } => responder.send(&status(sme)),
99        ClientSmeRequest::WmmStatus { responder } => wmm_status(sme, responder).await,
100        ClientSmeRequest::StartScheduledScan { req, txn, responder } => {
101            start_scheduled_scan(sme, req, txn, responder).await
102        }
103
104        ClientSmeRequest::GetScheduledScanEnabled { responder } => {
105            let receiver = sme.lock().on_get_scheduled_scan_enabled_command();
106            let resp = match receiver.await {
107                Ok(result) => result,
108                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
109            };
110            responder
111                .send(resp.as_ref().map(|r| !r.active_txn_ids.is_empty()).map_err(|e| *e))
112                .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
113            Ok(())
114        }
115        ClientSmeRequest::ScanForController { req, responder } => {
116            Ok(scan(sme, req, |result| match result {
117                Ok(results) => responder.send(Ok(&results[..])).map_err(|e| e.into()),
118                Err(e) => responder.send(Err(e)).map_err(|e| e.into()),
119            })
120            .await
121            .unwrap_or_else(|e| error!("Error handling a test scan transaction: {:?}", e)))
122        }
123        ClientSmeRequest::SetMacAddress { mac_addr, responder } => {
124            Ok(set_mac_address(sme, mac_addr, responder).await?)
125        }
126        ClientSmeRequest::InstallApfPacketFilter { program, responder } => {
127            let receiver = sme.lock().install_apf_packet_filter(program);
128            let resp = match receiver.await {
129                Ok(result) => result,
130                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
131            };
132            responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
133            Ok(())
134        }
135        ClientSmeRequest::ReadApfPacketFilterData { responder } => {
136            let receiver = sme.lock().read_apf_packet_filter_data();
137            let resp = match receiver.await {
138                Ok(result) => result.map(|r| r.memory),
139                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
140            };
141            responder
142                .send(resp.as_ref().map(|m| &m[..]).map_err(|e| *e))
143                .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
144            Ok(())
145        }
146        ClientSmeRequest::SetApfPacketFilterEnabled { enabled, responder } => {
147            let receiver = sme.lock().set_apf_packet_filter_enabled(enabled);
148            let resp = match receiver.await {
149                Ok(result) => result,
150                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
151            };
152            responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
153            Ok(())
154        }
155        ClientSmeRequest::GetApfPacketFilterEnabled { responder } => {
156            let receiver = sme.lock().get_apf_packet_filter_enabled();
157            let resp = match receiver.await {
158                Ok(result) => result.map(|r| r.enabled),
159                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
160            };
161            responder
162                .send(resp.as_ref().map(|e| *e).map_err(|e| *e))
163                .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
164            Ok(())
165        }
166    }
167}
168
169async fn handle_telemetry_fidl_request(
170    sme: &Mutex<Sme>,
171    request: TelemetryRequest,
172) -> Result<(), fidl::Error> {
173    match request {
174        TelemetryRequest::QueryTelemetrySupport { responder, .. } => {
175            let support_fut = sme.lock().query_telemetry_support();
176            let support = support_fut
177                .await
178                .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
179                .and_then(|result| result);
180            responder.send(support.as_ref().map_err(|e| *e))
181        }
182        TelemetryRequest::GetIfaceStats { responder, .. } => {
183            let iface_stats_fut = sme.lock().iface_stats();
184            let iface_stats = iface_stats_fut
185                .await
186                .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
187                .and_then(|stats| match stats {
188                    fidl_mlme::GetIfaceStatsResponse::Stats(stats) => Ok(stats),
189                    fidl_mlme::GetIfaceStatsResponse::ErrorStatus(err) => Err(err),
190                });
191            responder.send(iface_stats.as_ref().map_err(|e| *e))
192        }
193        TelemetryRequest::GetHistogramStats { responder, .. } => {
194            let histogram_stats_fut = sme.lock().histogram_stats();
195            let histogram_stats = histogram_stats_fut
196                .await
197                .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
198                .and_then(|stats| match stats {
199                    fidl_mlme::GetIfaceHistogramStatsResponse::Stats(stats) => Ok(stats),
200                    fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(err) => Err(err),
201                });
202            responder.send(histogram_stats.as_ref().map_err(|e| *e))
203        }
204        TelemetryRequest::GetSignalReport { responder, .. } => {
205            let signal_report_fut = sme.lock().signal_report();
206            let signal_report = signal_report_fut
207                .await
208                .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
209                .and_then(|result| result);
210            responder.send(signal_report.as_ref().map_err(|e| *e))
211        }
212        TelemetryRequest::CloneInspectVmo { responder } => {
213            let inspect_vmo =
214                sme.lock().on_clone_inspect_vmo().ok_or_else(|| zx::Status::INTERNAL.into_raw());
215            responder.send(inspect_vmo)
216        }
217    }
218}
219
220async fn scan(
221    sme: &Mutex<Sme>,
222    request: fidl_sme::ScanRequest,
223    responder: impl FnOnce(
224        Result<Vec<fidl_sme::ScanResult>, fidl_sme::ScanErrorCode>,
225    ) -> Result<(), anyhow::Error>,
226) -> Result<(), anyhow::Error> {
227    let receiver = sme.lock().on_scan_command(request);
228    let receive_result = match receiver.await {
229        Ok(receive_result) => receive_result,
230        Err(e) => {
231            error!("Scan receiver error: {:?}", e);
232            responder(Err(fidl_sme::ScanErrorCode::InternalError))?;
233            return Ok(());
234        }
235    };
236
237    match receive_result {
238        Ok(scan_results) => {
239            let results = scan_results.into_iter().map(Into::into).collect::<Vec<_>>();
240            responder(Ok(results))
241        }
242        Err(mlme_scan_result_code) => {
243            let scan_error_code = match mlme_scan_result_code {
244                fidl_mlme::ScanResultCode::Success | fidl_mlme::ScanResultCode::InvalidArgs => {
245                    error!("Internal scan error: {:?}", mlme_scan_result_code);
246                    fidl_sme::ScanErrorCode::InternalError
247                }
248                fidl_mlme::ScanResultCode::NotSupported => fidl_sme::ScanErrorCode::NotSupported,
249                fidl_mlme::ScanResultCode::InternalError => {
250                    fidl_sme::ScanErrorCode::InternalMlmeError
251                }
252                fidl_mlme::ScanResultCode::ShouldWait => fidl_sme::ScanErrorCode::ShouldWait,
253                fidl_mlme::ScanResultCode::CanceledByDriverOrFirmware => {
254                    fidl_sme::ScanErrorCode::CanceledByDriverOrFirmware
255                }
256            };
257            responder(Err(scan_error_code))
258        }
259    }?;
260    Ok(())
261}
262
263async fn connect(
264    sme: &Mutex<Sme>,
265    txn: Option<ServerEnd<fidl_sme::ConnectTransactionMarker>>,
266    req: fidl_sme::ConnectRequest,
267) -> Result<(), anyhow::Error> {
268    #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
269    let handle = match txn {
270        None => None,
271        Some(txn) => Some(txn.into_stream().control_handle()),
272    };
273    let connect_txn_stream = sme.lock().on_connect_command(req);
274    serve_connect_txn_stream(handle, connect_txn_stream).await?;
275    Ok(())
276}
277
278/// Serves a scheduled scan session by processing a stream for events sent by MLME and processed in
279/// SME's scan scheduler, and a stream for client-initiated cancellations.
280async fn serve_sched_scan_session(
281    txn_handle: fidl_sme::ScheduledScanTransactionControlHandle,
282    txn_stream: fidl_sme::ScheduledScanTransactionRequestStream,
283    session: ScheduledScanReceiver,
284) -> Result<(), anyhow::Error> {
285    // Client will close the transaction channel to cancel session.
286    let mut cancellation_stream = txn_stream.fuse();
287    let mut session_stream = session.fuse();
288    loop {
289        futures::select! {
290            scan_results = session_stream.next() => {
291                if let Some(vmo) = scan_results {
292                    txn_handle.send_on_scheduled_scan_matches_available(vmo)?;
293                } else {
294                    // Stream closed naturally (SME dropped sender because firmware stopped it).
295                    txn_handle.shutdown_with_epitaph(Ok(()));
296
297                    break;
298                }
299            },
300            _ = cancellation_stream.next() => {
301                // Client dropped transaction stream. Cleanup is handled by ScheduledScanReceiver's
302                // Drop implementation.
303                break;
304            }
305        }
306    }
307    Ok(())
308}
309
310/// Creates a new scheduled scan session and starts serving it.
311async fn start_scheduled_scan(
312    sme: &Mutex<Sme>,
313    req: fidl_common::ScheduledScanRequest,
314    txn_server: ServerEnd<fidl_sme::ScheduledScanTransactionMarker>,
315    responder: fidl_sme::ClientSmeStartScheduledScanResponder,
316) -> Result<(), fidl::Error> {
317    let txn_receiver_stream = txn_server.into_stream();
318    let txn_sender_handle = txn_receiver_stream.control_handle();
319    let (receiver, session) = sme.lock().on_start_scheduled_scan_command(req);
320    match receiver.await.unwrap_or_else(|_| Err(zx::Status::CANCELED.into_raw())) {
321        Ok(()) => {
322            responder.send(Ok(()))?;
323            serve_sched_scan_session(txn_sender_handle, txn_receiver_stream, session)
324                .await
325                .unwrap_or_else(|e| {
326                    error!("Error serving sched scan txn stream: {:?}", e);
327                });
328        }
329        Err(status) => {
330            responder.send(Err(status))?;
331            txn_sender_handle.shutdown_with_epitaph(zx::Status::from_raw(status));
332        }
333    }
334    Ok(())
335}
336
337async fn serve_connect_txn_stream(
338    handle: Option<fidl_sme::ConnectTransactionControlHandle>,
339    mut connect_txn_stream: ConnectTransactionStream,
340) -> Result<(), anyhow::Error> {
341    if let Some(handle) = handle {
342        loop {
343            match connect_txn_stream.next().await {
344                Some(event) => match event {
345                    ConnectTransactionEvent::OnConnectResult { result, is_reconnect } => {
346                        let connect_result = convert_connect_result(&result, is_reconnect);
347                        handle.send_on_connect_result(&connect_result)
348                    }
349                    ConnectTransactionEvent::OnRoamResult { result } => {
350                        let roam_result = convert_roam_result(&result);
351                        handle.send_on_roam_result(&roam_result)
352                    }
353                    ConnectTransactionEvent::OnDisconnect { info } => {
354                        handle.send_on_disconnect(&info)
355                    }
356                    ConnectTransactionEvent::OnSignalReport { ind } => {
357                        handle.send_on_signal_report(&ind)
358                    }
359                    ConnectTransactionEvent::OnChannelSwitched { info } => {
360                        handle.send_on_channel_switched(&info)
361                    }
362                }?,
363                // SME has dropped the ConnectTransaction endpoint, likely due to a disconnect.
364                None => return Ok(()),
365            }
366        }
367    }
368    Ok(())
369}
370
371fn roam(sme: &Mutex<Sme>, req: fidl_sme::RoamRequest) {
372    sme.lock().on_roam_command(req);
373}
374
375fn disconnect(
376    sme: &Mutex<Sme>,
377    policy_disconnect_reason: fidl_sme::UserDisconnectReason,
378    responder: fidl_sme::ClientSmeDisconnectResponder,
379) {
380    sme.lock().on_disconnect_command(policy_disconnect_reason, responder);
381}
382
383fn status(sme: &Mutex<Sme>) -> fidl_sme::ClientStatusResponse {
384    sme.lock().status().into()
385}
386
387async fn wmm_status(
388    sme: &Mutex<Sme>,
389    responder: fidl_sme::ClientSmeWmmStatusResponder,
390) -> Result<(), fidl::Error> {
391    let receiver = sme.lock().wmm_status();
392    let wmm_status = match receiver.await {
393        Ok(result) => result,
394        Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
395    };
396    responder.send(wmm_status.as_ref().map_err(|e| *e))
397}
398
399async fn set_mac_address(
400    sme: &Mutex<Sme>,
401    mac_addr: [u8; 6],
402    responder: fidl_sme::ClientSmeSetMacAddressResponder,
403) -> Result<(), fidl::Error> {
404    let receiver = sme.lock().set_mac_address(mac_addr);
405    let resp = match receiver.await {
406        Ok(Ok(())) => {
407            sme.lock().update_mac_address(mac_addr);
408            Ok(())
409        }
410        Ok(result @ Err(_)) => result,
411        Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
412    };
413    responder.send(resp)
414}
415
416fn convert_connect_result(result: &ConnectResult, is_reconnect: bool) -> fidl_sme::ConnectResult {
417    let (code, is_credential_rejected) = match result {
418        ConnectResult::Success => (fidl_ieee80211::StatusCode::Success, false),
419        ConnectResult::Canceled => (fidl_ieee80211::StatusCode::Canceled, false),
420        ConnectResult::Failed(failure) => {
421            (failure.status_code(), failure.likely_due_to_credential_rejected())
422        }
423    };
424    fidl_sme::ConnectResult { code, is_credential_rejected, is_reconnect }
425}
426
427fn convert_roam_result(result: &RoamResult) -> fidl_sme::RoamResult {
428    match result {
429        RoamResult::Success(bss) => {
430            let bss_description =
431                Some(Box::new(fidl_ieee80211::BssDescription::from(*bss.clone())));
432            fidl_sme::RoamResult {
433                bssid: bss.bssid.to_array(),
434                status_code: fidl_ieee80211::StatusCode::Success,
435                // Must always be false on roam success.
436                original_association_maintained: false,
437                bss_description,
438                disconnect_info: None,
439                is_credential_rejected: false,
440            }
441        }
442        RoamResult::Failed(failure) => {
443            #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
444            fidl_sme::RoamResult {
445                bssid: failure.selected_bssid.to_array(),
446                status_code: failure.status_code,
447                // Current implementation assumes that all roam attempts incur disassociation from the
448                // original BSS. When this changes (e.g. due to Fast BSS Transition support), this
449                // hard-coded field should be set from the RoamResult enum.
450                original_association_maintained: false,
451                bss_description: match &failure.selected_bss {
452                    Some(bss) => Some(Box::new(bss.clone().into())),
453                    None => None,
454                },
455                disconnect_info: Some(Box::new(failure.disconnect_info)),
456                is_credential_rejected: failure.likely_due_to_credential_rejected(),
457            }
458        }
459    }
460}
461
462#[cfg(test)]
463mod tests {
464    use super::*;
465    use crate::client::{ConnectFailure, EstablishRsnaFailure, EstablishRsnaFailureReason};
466    use crate::test_utils;
467    use assert_matches::assert_matches;
468    use fidl::endpoints::create_proxy_and_stream;
469    use fidl_fuchsia_wlan_internal as fidl_internal;
470    use fidl_fuchsia_wlan_mlme::ScanResultCode;
471    use fidl_fuchsia_wlan_sme::{self as fidl_sme};
472    use fuchsia_async as fasync;
473    use futures::stream::StreamFuture;
474    use futures::task::Poll;
475    use rand::Rng;
476    use rand::prelude::ThreadRng;
477    use std::pin::pin;
478    use test_case::test_case;
479    use wlan_common::random_bss_description;
480    use wlan_common::scan::{self, Incompatible};
481    use wlan_rsn::auth;
482
483    #[test]
484    fn test_convert_connect_result() {
485        assert_eq!(
486            convert_connect_result(&ConnectResult::Success, false),
487            fidl_sme::ConnectResult {
488                code: fidl_ieee80211::StatusCode::Success,
489                is_credential_rejected: false,
490                is_reconnect: false,
491            }
492        );
493        assert_eq!(
494            convert_connect_result(&ConnectResult::Canceled, true),
495            fidl_sme::ConnectResult {
496                code: fidl_ieee80211::StatusCode::Canceled,
497                is_credential_rejected: false,
498                is_reconnect: true,
499            }
500        );
501        let connect_result =
502            ConnectResult::Failed(ConnectFailure::ScanFailure(ScanResultCode::ShouldWait));
503        assert_eq!(
504            convert_connect_result(&connect_result, false),
505            fidl_sme::ConnectResult {
506                code: fidl_ieee80211::StatusCode::Canceled,
507                is_credential_rejected: false,
508                is_reconnect: false,
509            }
510        );
511
512        let connect_result =
513            ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
514                auth_method: Some(auth::MethodName::Psk),
515                reason: EstablishRsnaFailureReason::InternalError,
516            }));
517        assert_eq!(
518            convert_connect_result(&connect_result, false),
519            fidl_sme::ConnectResult {
520                code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
521                is_credential_rejected: false,
522                is_reconnect: false,
523            }
524        );
525
526        let connect_result =
527            ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
528                auth_method: Some(auth::MethodName::Psk),
529                reason: EstablishRsnaFailureReason::RsnaResponseTimeout(
530                    wlan_rsn::Error::LikelyWrongCredential,
531                ),
532            }));
533        assert_eq!(
534            convert_connect_result(&connect_result, false),
535            fidl_sme::ConnectResult {
536                code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
537                is_credential_rejected: true,
538                is_reconnect: false,
539            }
540        );
541
542        let connect_result =
543            ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
544                auth_method: Some(auth::MethodName::Psk),
545                reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(
546                    wlan_rsn::Error::LikelyWrongCredential,
547                ),
548            }));
549        assert_eq!(
550            convert_connect_result(&connect_result, false),
551            fidl_sme::ConnectResult {
552                code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
553                is_credential_rejected: true,
554                is_reconnect: false,
555            }
556        );
557
558        let connect_result =
559            ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
560                auth_method: Some(auth::MethodName::Psk),
561                reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(
562                    wlan_rsn::Error::MissingGtkProvider,
563                ),
564            }));
565        assert_eq!(
566            convert_connect_result(&connect_result, false),
567            fidl_sme::ConnectResult {
568                code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
569                is_credential_rejected: false,
570                is_reconnect: false,
571            }
572        );
573
574        let connect_result =
575            ConnectResult::Failed(ConnectFailure::ScanFailure(ScanResultCode::InternalError));
576        assert_eq!(
577            convert_connect_result(&connect_result, false),
578            fidl_sme::ConnectResult {
579                code: fidl_ieee80211::StatusCode::RefusedReasonUnspecified,
580                is_credential_rejected: false,
581                is_reconnect: false,
582            }
583        );
584    }
585
586    // TODO(https://fxbug.dev/42164611): There is no test coverage for consistency between MLME scan results
587    // and SME scan results produced by wlanstack. In particular, the timestamp_nanos field
588    // of fidl_mlme::ScanResult is dropped in SME, and no tests reveal this problem.
589
590    #[test_case(1, true; "with 1 result")]
591    #[test_case(2, true; "with 2 results")]
592    #[test_case(30, true; "with 30 results")]
593    #[test_case(4000, true; "with 4000 results")]
594    #[test_case(50000, false; "with 50000 results")]
595    #[test_case(100000, false; "with 100000 results")]
596    fn scan_results_are_effectively_unbounded(number_of_scan_results: usize, randomize: bool) {
597        let mut exec = fasync::TestExecutor::new();
598        let (client_sme_proxy, mut client_sme_stream) =
599            create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
600
601        // Request scan
602        async fn request_and_collect_result(
603            client_sme_proxy: &fidl_sme::ClientSmeProxy,
604        ) -> fidl_sme::ClientSmeScanResult {
605            client_sme_proxy
606                .scan(&fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {
607                    channels: vec![],
608                }))
609                .await
610                .expect("FIDL request failed")
611        }
612
613        let result_fut = request_and_collect_result(&client_sme_proxy);
614        let mut result_fut = pin!(result_fut);
615
616        assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Pending);
617
618        // Generate and send scan results
619        let mut rng = rand::rng();
620        let scan_result_list = if randomize {
621            (0..number_of_scan_results).map(|_| random_scan_result(&mut rng).into()).collect()
622        } else {
623            vec![random_scan_result(&mut rng).into(); number_of_scan_results]
624        };
625        assert_matches!(exec.run_until_stalled(&mut client_sme_stream.next()),
626                        Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
627                            req: _, responder,
628                        }))) => {
629                            let vmo = write_vmo(scan_result_list.clone()).expect("failed to write VMO");
630                            responder.send(Ok(vmo)).expect("failed to send scan results");
631                        }
632        );
633
634        // Verify scan results
635        assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Ready(Ok(vmo)) => {
636            assert_eq!(scan_result_list, scan::read_vmo(vmo).expect("failed to read VMO"));
637        })
638    }
639
640    #[test]
641    fn test_serve_connect_txn_stream() {
642        let mut exec = fasync::TestExecutor::new();
643
644        let (sme_proxy, sme_connect_txn_stream) = mpsc::unbounded();
645        let (fidl_client_proxy, fidl_connect_txn_stream) =
646            create_proxy_and_stream::<fidl_sme::ConnectTransactionMarker>();
647        let fidl_client_fut = fidl_client_proxy.take_event_stream().into_future();
648        let mut fidl_client_fut = pin!(fidl_client_fut);
649        let fidl_connect_txn_handle = fidl_connect_txn_stream.control_handle();
650
651        let test_fut =
652            serve_connect_txn_stream(Some(fidl_connect_txn_handle), sme_connect_txn_stream);
653        let mut test_fut = pin!(test_fut);
654
655        // Test sending OnConnectResult
656        sme_proxy
657            .unbounded_send(ConnectTransactionEvent::OnConnectResult {
658                result: ConnectResult::Success,
659                is_reconnect: true,
660            })
661            .expect("expect sending ConnectTransactionEvent to succeed");
662        assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
663        let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
664        assert_matches!(
665            event,
666            fidl_sme::ConnectTransactionEvent::OnConnectResult {
667                result: fidl_sme::ConnectResult {
668                    code: fidl_ieee80211::StatusCode::Success,
669                    is_credential_rejected: false,
670                    is_reconnect: true,
671                }
672            }
673        );
674
675        // Test sending OnDisconnect
676        let input_info = fidl_sme::DisconnectInfo {
677            is_sme_reconnecting: true,
678            disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
679                reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
680                mlme_event_name: fidl_sme::DisconnectMlmeEventName::DeauthenticateIndication,
681            }),
682        };
683        sme_proxy
684            .unbounded_send(ConnectTransactionEvent::OnDisconnect { info: input_info })
685            .expect("expect sending ConnectTransactionEvent to succeed");
686        assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
687        let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
688        assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnDisconnect { info: output_info } => {
689            assert_eq!(input_info, output_info);
690        });
691
692        // Test sending OnSignalReport
693        let input_ind = fidl_internal::SignalReportIndication { rssi_dbm: -40, snr_db: 30 };
694        sme_proxy
695            .unbounded_send(ConnectTransactionEvent::OnSignalReport { ind: input_ind })
696            .expect("expect sending ConnectTransactionEvent to succeed");
697        assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
698        let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
699        assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnSignalReport { ind } => {
700            assert_eq!(input_ind, ind);
701        });
702
703        // Test sending OnChannelSwitched
704        let input_info = fidl_internal::ChannelSwitchInfo {
705            new_primary_channel: fidl_ieee80211::ChannelNumber {
706                band: fidl_ieee80211::WlanBand::TwoGhz,
707                number: 8,
708            },
709            bandwidth: fidl_ieee80211::ChannelBandwidth::Cbw20,
710            vht_secondary_80_channel: fidl_ieee80211::ChannelNumber {
711                band: fidl_ieee80211::WlanBand::TwoGhz,
712                number: 0,
713            },
714        };
715        sme_proxy
716            .unbounded_send(ConnectTransactionEvent::OnChannelSwitched { info: input_info.clone() })
717            .expect("expect sending ConnectTransactionEvent to succeed");
718        assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
719        let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
720        assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnChannelSwitched { info } => {
721            assert_eq!(input_info, info);
722        });
723
724        // When SME proxy is dropped, the fut should terminate
725        std::mem::drop(sme_proxy);
726        assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Ready(Ok(())));
727    }
728
729    fn poll_stream_fut<S: Stream + std::marker::Unpin>(
730        exec: &mut fasync::TestExecutor,
731        stream_fut: &mut StreamFuture<S>,
732    ) -> Poll<Option<S::Item>> {
733        exec.run_until_stalled(stream_fut).map(|(item, stream)| {
734            *stream_fut = stream.into_future();
735            item
736        })
737    }
738
739    // Create roughly over 2k bytes ScanResult
740    fn random_scan_result(rng: &mut ThreadRng) -> wlan_common::scan::ScanResult {
741        use wlan_common::security::SecurityDescriptor;
742
743        // TODO(https://fxbug.dev/42164451): Merge this with a similar function in wlancfg.
744        wlan_common::scan::ScanResult {
745            compatibility: match rng.random_range(0..4) {
746                0 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::OPEN]),
747                1 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
748                2 => wlan_common::scan::Compatible::expect_ok([
749                    SecurityDescriptor::WPA2_PERSONAL,
750                    SecurityDescriptor::WPA3_PERSONAL,
751                ]),
752                _ => Incompatible::unknown(),
753            },
754            timestamp: zx::MonotonicInstant::from_nanos(rng.random()),
755            bss_description: random_bss_description!(),
756        }
757    }
758
759    #[test]
760    fn test_handle_fidl_request_apf() {
761        let mut exec = fasync::TestExecutor::new();
762        let inspector = fuchsia_inspect::Inspector::default();
763        let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
764            client_sme::ClientConfig::default(),
765            test_utils::fake_device_info([0; 6].into()),
766            inspector.clone(),
767            inspector.root().create_child("sme"),
768            wlan_common::test_utils::fake_features::fake_security_support(),
769            wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
770        );
771        let sme = Mutex::new(sme);
772
773        // Test InstallApfPacketFilter
774        let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
775        let program = vec![1, 2, 3];
776        let mut install_fut = proxy.install_apf_packet_filter(&program);
777        let mut stream = pin!(stream);
778
779        // Run handle_fidl_request for the install request
780        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
781            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
782            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
783
784            // Forward from SME to MLME
785            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::InstallApfPacketFilter(req, responder))) => {
786                assert_eq!(req.program, program);
787                responder.respond(Ok(()));
788            });
789
790            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
791        });
792        assert_matches!(exec.run_until_stalled(&mut install_fut), Poll::Ready(Ok(Ok(()))));
793
794        // Test ReadApfPacketFilterData
795        let mut read_fut = proxy.read_apf_packet_filter_data();
796        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
797            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
798            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
799
800            // Forward from SME to MLME
801            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::ReadApfPacketFilterData(responder))) => {
802                responder.respond(Ok(fidl_mlme::MlmeReadApfPacketFilterDataResponse {
803                    memory: vec![4, 5, 6],
804                }));
805            });
806
807            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
808        });
809        assert_matches!(exec.run_until_stalled(&mut read_fut), Poll::Ready(Ok(Ok(data))) => {
810            assert_eq!(data, vec![4, 5, 6]);
811        });
812
813        // Test SetApfPacketFilterEnabled
814        let mut set_enabled_fut = proxy.set_apf_packet_filter_enabled(true);
815        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
816            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
817            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
818
819            // Forward from SME to MLME
820            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetApfPacketFilterEnabled(req, responder))) => {
821                assert!(req.enabled);
822                responder.respond(Ok(()));
823            });
824
825            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
826        });
827        assert_matches!(exec.run_until_stalled(&mut set_enabled_fut), Poll::Ready(Ok(Ok(()))));
828
829        // Test GetApfPacketFilterEnabled
830        let mut get_enabled_fut = proxy.get_apf_packet_filter_enabled();
831        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
832            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
833            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
834
835            // Forward from SME to MLME
836            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::GetApfPacketFilterEnabled(responder))) => {
837                responder.respond(Ok(fidl_mlme::MlmeGetApfPacketFilterEnabledResponse {
838                    enabled: true,
839                }));
840            });
841
842            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
843        });
844        assert_matches!(exec.run_until_stalled(&mut get_enabled_fut), Poll::Ready(Ok(Ok(true))));
845    }
846
847    #[test_case(Ok(()); "ok")]
848    #[test_case(Err(123); "err")]
849    fn test_handle_fidl_request_set_mac_address(set_mac_address_result: Result<(), i32>) {
850        let mut exec = fasync::TestExecutor::new();
851        let inspector = fuchsia_inspect::Inspector::default();
852        let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
853            client_sme::ClientConfig::default(),
854            test_utils::fake_device_info([0; 6].into()),
855            inspector.clone(),
856            inspector.root().create_child("sme"),
857            wlan_common::test_utils::fake_features::fake_security_support(),
858            wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
859        );
860        let sme = Mutex::new(sme);
861
862        // Send SetMacAddress request
863        let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
864        let new_mac = [1, 2, 3, 4, 5, 6];
865        let mut set_mac_fut = proxy.set_mac_address(&new_mac);
866        let mut stream = pin!(stream);
867
868        // Handle SetMacAddress request
869        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
870            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
871            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
872
873            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetMacAddress(mac, responder))) => {
874                assert_eq!(mac, new_mac);
875                responder.respond(set_mac_address_result);
876            });
877
878            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
879        });
880
881        if set_mac_address_result.is_ok() {
882            assert_matches!(exec.run_until_stalled(&mut set_mac_fut), Poll::Ready(Ok(Ok(()))));
883            // Verify the device_info in client_sme is updated
884            assert_eq!(sme.lock().device_info().sta_addr, new_mac);
885        } else {
886            assert_matches!(exec.run_until_stalled(&mut set_mac_fut), Poll::Ready(Ok(Err(123))));
887            assert_eq!(sme.lock().device_info().sta_addr, [0; 6]);
888        }
889    }
890
891    #[test]
892    fn test_handle_fidl_request_set_mac_address_cancelled() {
893        let mut exec = fasync::TestExecutor::new();
894        let inspector = fuchsia_inspect::Inspector::default();
895        let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
896            client_sme::ClientConfig::default(),
897            test_utils::fake_device_info([0; 6].into()),
898            inspector.clone(),
899            inspector.root().create_child("sme"),
900            wlan_common::test_utils::fake_features::fake_security_support(),
901            wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
902        );
903        let sme = Mutex::new(sme);
904
905        // Send SetMacAddress request
906        let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
907        let new_mac = [1, 2, 3, 4, 5, 6];
908        let mut set_mac_fut = proxy.set_mac_address(&new_mac);
909        let mut stream = pin!(stream);
910
911        // Handle SetMacAddress request
912        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
913            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
914            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
915
916            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetMacAddress(_mac, _responder))) => {
917                // Drop the responder to simulate an error
918            });
919
920            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
921        });
922
923        assert_matches!(
924            exec.run_until_stalled(&mut set_mac_fut),
925            Poll::Ready(Ok(Err(zx::sys::ZX_ERR_CANCELED)))
926        );
927        assert_eq!(sme.lock().device_info().sta_addr, [0; 6]);
928    }
929}