1mod event;
6mod inspect;
7mod protection;
8mod rsn;
9mod scan;
10mod state;
11
12mod wpa;
13
14#[cfg(test)]
15pub mod test_utils;
16
17use self::event::Event;
18use self::protection::{Protection, SecurityContext};
19pub use self::scan::ScheduledScanReceiver;
20use self::scan::{DiscoveryScan, ScanScheduler};
21use self::state::{ClientState, ConnectCommand};
22use crate::responder::Responder;
23use crate::{Config, MlmeRequest, MlmeSink, MlmeStream};
24use fidl_fuchsia_wlan_common as fidl_common;
25use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
26use fidl_fuchsia_wlan_internal as fidl_internal;
27use fidl_fuchsia_wlan_mlme as fidl_mlme;
28use fidl_fuchsia_wlan_sme as fidl_sme;
29use fidl_fuchsia_wlan_stats as fidl_stats;
30use futures::channel::{mpsc, oneshot};
31use ieee80211::{Bssid, MacAddrBytes, Ssid};
32use log::{error, info, warn};
33use std::sync::Arc;
34use wlan_common::bss::{BssDescription, Protection as BssProtection};
35use wlan_common::capabilities::derive_join_capabilities;
36use wlan_common::ie::rsn::rsne;
37use wlan_common::ie::{self, wsc};
38use wlan_common::mac::MacRole;
39use wlan_common::scan::{Compatibility, Compatible, Incompatible, ScanResult};
40use wlan_common::security::{SecurityAuthenticator, SecurityDescriptor};
41use wlan_common::sink::UnboundedSink;
42use wlan_common::timer;
43use wlan_rsn::auth;
44
45mod internal {
50 use crate::MlmeSink;
51 use crate::client::event::Event;
52 use crate::client::{ConnectionAttemptId, inspect};
53 use fidl_fuchsia_wlan_common as fidl_common;
54 use fidl_fuchsia_wlan_mlme as fidl_mlme;
55 use std::sync::Arc;
56 use wlan_common::timer::Timer;
57
58 pub struct Context {
59 pub device_info: Arc<fidl_mlme::DeviceInfo>,
60 pub mlme_sink: MlmeSink,
61 pub(crate) timer: Timer<Event>,
62 pub att_id: ConnectionAttemptId,
63 pub(crate) inspect: Arc<inspect::SmeTree>,
64 pub security_support: fidl_common::SecuritySupport,
65 }
66}
67
68use self::internal::*;
69
70pub type ConnectionAttemptId = u64;
74
75#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
76pub struct ClientConfig {
77 cfg: Config,
78 pub wpa3_supported: bool,
79 pub owe_supported: bool,
80}
81
82impl ClientConfig {
83 pub fn from_config(cfg: Config, wpa3_supported: bool, owe_supported: bool) -> Self {
84 Self { cfg, wpa3_supported, owe_supported }
85 }
86
87 pub fn create_scan_result(
89 &self,
90 timestamp: zx::MonotonicInstant,
91 bss_description: BssDescription,
92 device_info: &fidl_mlme::DeviceInfo,
93 security_support: &fidl_common::SecuritySupport,
94 ) -> ScanResult {
95 ScanResult {
96 compatibility: self.bss_compatibility(&bss_description, device_info, security_support),
97 timestamp,
98 bss_description,
99 }
100 }
101
102 pub fn bss_compatibility(
107 &self,
108 bss: &BssDescription,
109 device_info: &fidl_mlme::DeviceInfo,
110 security_support: &fidl_common::SecuritySupport,
111 ) -> Compatibility {
112 self.has_compatible_channel_and_data_rates(bss, device_info)
115 .then(|| {
116 Compatible::try_from_features(
117 self.security_protocol_intersection(bss, security_support),
118 )
119 })
120 .flatten()
121 .ok_or_else(|| {
122 Incompatible::try_from_features(
123 "incompatible channel, PHY data rates, or security protocols",
124 Some(self.security_protocols_by_mac_role(bss)),
125 )
126 .unwrap_or_else(|| {
127 Incompatible::from_description("incompatible channel or PHY data rates")
128 })
129 })
130 }
131
132 fn security_protocol_intersection(
137 &self,
138 bss: &BssDescription,
139 security_support: &fidl_common::SecuritySupport,
140 ) -> Vec<SecurityDescriptor> {
141 let has_privacy = wlan_common::mac::CapabilityInfo(bss.capability_info).privacy();
144 let has_owe_support = || {
145 self.owe_supported
146 && has_privacy
147 && bss.rsne().is_some_and(|rsne| {
148 rsne::from_bytes(rsne)
149 .is_ok_and(|(_, a_rsne)| a_rsne.is_owe_rsn_compatible(security_support))
150 })
151 };
152 let has_wep_support = || self.cfg.wep_supported;
153 let has_wpa1_support = || self.cfg.wpa1_supported;
154 let has_wpa2_support = || {
155 has_privacy
159 && bss.rsne().is_some_and(|rsne| {
160 rsne::from_bytes(rsne)
161 .is_ok_and(|(_, a_rsne)| a_rsne.is_wpa2_rsn_compatible(security_support))
162 })
163 };
164 let has_wpa3_support = || {
165 self.wpa3_supported
166 && has_privacy
167 && bss.rsne().is_some_and(|rsne| {
168 rsne::from_bytes(rsne)
169 .is_ok_and(|(_, a_rsne)| a_rsne.is_wpa3_rsn_compatible(security_support))
170 })
171 };
172
173 match bss.protection() {
177 BssProtection::Open => vec![SecurityDescriptor::OPEN],
178 BssProtection::OpenOweTransition if self.owe_supported => {
181 vec![SecurityDescriptor::OWE, SecurityDescriptor::OPEN]
182 }
183 BssProtection::OpenOweTransition => vec![SecurityDescriptor::OPEN],
184 BssProtection::Owe if has_owe_support() => vec![SecurityDescriptor::OWE],
185 BssProtection::Owe => vec![],
186 BssProtection::Wep if has_wep_support() => vec![SecurityDescriptor::WEP],
187 BssProtection::Wep => vec![],
188 BssProtection::Wpa1 if has_wpa1_support() => vec![SecurityDescriptor::WPA1],
189 BssProtection::Wpa1 => vec![],
190 BssProtection::Wpa1Wpa2PersonalTkipOnly | BssProtection::Wpa1Wpa2Personal => {
191 has_wpa2_support()
192 .then_some(SecurityDescriptor::WPA2_PERSONAL)
193 .into_iter()
194 .chain(has_wpa1_support().then_some(SecurityDescriptor::WPA1))
195 .collect()
196 }
197 BssProtection::Wpa2PersonalTkipOnly | BssProtection::Wpa2Personal
198 if has_wpa2_support() =>
199 {
200 vec![SecurityDescriptor::WPA2_PERSONAL]
201 }
202 BssProtection::Wpa2PersonalTkipOnly | BssProtection::Wpa2Personal => vec![],
203 BssProtection::Wpa2Wpa3Personal => match (has_wpa3_support(), has_wpa2_support()) {
204 (true, true) => {
205 vec![SecurityDescriptor::WPA3_PERSONAL, SecurityDescriptor::WPA2_PERSONAL]
206 }
207 (true, false) => vec![SecurityDescriptor::WPA3_PERSONAL],
208 (false, true) => vec![SecurityDescriptor::WPA2_PERSONAL],
209 (false, false) => vec![],
210 },
211 BssProtection::Wpa3Personal if has_wpa3_support() => {
212 vec![SecurityDescriptor::WPA3_PERSONAL]
213 }
214 BssProtection::Wpa3Personal => vec![],
215 BssProtection::Wpa2Enterprise | BssProtection::Wpa3Enterprise => vec![],
217 BssProtection::Unknown => vec![],
218 }
219 }
220
221 fn security_protocols_by_mac_role(
222 &self,
223 bss: &BssDescription,
224 ) -> impl Iterator<Item = (SecurityDescriptor, MacRole)> {
225 let has_privacy = wlan_common::mac::CapabilityInfo(bss.capability_info).privacy();
226 let has_wep_support = || self.cfg.wep_supported;
227 let has_wpa1_support = || self.cfg.wpa1_supported;
228 let has_wpa2_support = || {
229 has_privacy
233 };
234 let has_wpa3_support = || self.wpa3_supported && has_privacy;
235 let client_security_protocols = Some(SecurityDescriptor::OPEN)
236 .into_iter()
237 .chain(has_wep_support().then_some(SecurityDescriptor::WEP))
238 .chain(has_wpa1_support().then_some(SecurityDescriptor::WPA1))
239 .chain(has_wpa2_support().then_some(SecurityDescriptor::WPA2_PERSONAL))
240 .chain(has_wpa3_support().then_some(SecurityDescriptor::WPA3_PERSONAL))
241 .map(|descriptor| (descriptor, MacRole::Client));
242
243 let bss_security_protocols = match bss.protection() {
244 BssProtection::Open => &[SecurityDescriptor::OPEN][..],
245 BssProtection::OpenOweTransition => &[SecurityDescriptor::OPEN][..],
246 BssProtection::Owe => &[SecurityDescriptor::OWE][..],
247 BssProtection::Wep => &[SecurityDescriptor::WEP][..],
248 BssProtection::Wpa1 => &[SecurityDescriptor::WPA1][..],
249 BssProtection::Wpa1Wpa2PersonalTkipOnly | BssProtection::Wpa1Wpa2Personal => {
250 &[SecurityDescriptor::WPA1, SecurityDescriptor::WPA2_PERSONAL][..]
251 }
252 BssProtection::Wpa2PersonalTkipOnly | BssProtection::Wpa2Personal => {
253 &[SecurityDescriptor::WPA2_PERSONAL][..]
254 }
255 BssProtection::Wpa2Wpa3Personal => {
256 &[SecurityDescriptor::WPA3_PERSONAL, SecurityDescriptor::WPA2_PERSONAL][..]
257 }
258 BssProtection::Wpa3Personal => &[SecurityDescriptor::WPA3_PERSONAL][..],
259 BssProtection::Wpa2Enterprise | BssProtection::Wpa3Enterprise => &[],
261 BssProtection::Unknown => &[],
262 }
263 .iter()
264 .cloned()
265 .map(|descriptor| (descriptor, MacRole::Ap));
266
267 client_security_protocols.chain(bss_security_protocols)
268 }
269
270 fn has_compatible_channel_and_data_rates(
271 &self,
272 bss: &BssDescription,
273 device_info: &fidl_mlme::DeviceInfo,
274 ) -> bool {
275 derive_join_capabilities(bss.channel, bss.rates(), device_info).is_ok()
276 }
277}
278
279pub struct ClientSme {
280 cfg: ClientConfig,
281 state: Option<ClientState>,
282 scan_sched: ScanScheduler<Responder<Result<Vec<ScanResult>, fidl_mlme::ScanResultCode>>>,
283 wmm_status_responders: Vec<Responder<fidl_sme::ClientSmeWmmStatusResult>>,
284 context: Context,
285}
286
287#[derive(Debug, PartialEq)]
288pub enum ConnectResult {
289 Success,
290 Canceled,
291 Failed(ConnectFailure),
292}
293
294impl<T: Into<ConnectFailure>> From<T> for ConnectResult {
295 fn from(failure: T) -> Self {
296 ConnectResult::Failed(failure.into())
297 }
298}
299
300#[derive(Debug, PartialEq)]
301pub enum RoamResult {
302 Success(Box<BssDescription>),
303 Failed(Box<RoamFailure>),
304}
305
306impl<T: Into<RoamFailure>> From<T> for RoamResult {
307 fn from(failure: T) -> Self {
308 RoamResult::Failed(Box::new(failure.into()))
309 }
310}
311
312#[derive(Debug)]
313pub struct ConnectTransactionSink {
314 sink: UnboundedSink<ConnectTransactionEvent>,
315 is_reconnecting: bool,
316}
317
318impl ConnectTransactionSink {
319 pub fn new_unbounded() -> (Self, ConnectTransactionStream) {
320 let (sender, receiver) = mpsc::unbounded();
321 let sink =
322 ConnectTransactionSink { sink: UnboundedSink::new(sender), is_reconnecting: false };
323 (sink, receiver)
324 }
325
326 pub fn is_reconnecting(&self) -> bool {
327 self.is_reconnecting
328 }
329
330 pub fn send_connect_result(&mut self, result: ConnectResult) {
331 let event =
332 ConnectTransactionEvent::OnConnectResult { result, is_reconnect: self.is_reconnecting };
333 self.send(event);
334 }
335
336 pub fn send_roam_result(&mut self, result: RoamResult) {
337 let event = ConnectTransactionEvent::OnRoamResult { result };
338 self.send(event);
339 }
340
341 pub fn send(&mut self, event: ConnectTransactionEvent) {
342 if let ConnectTransactionEvent::OnDisconnect { info } = &event {
343 self.is_reconnecting = info.is_sme_reconnecting;
344 };
345 self.sink.send(event);
346 }
347}
348
349pub type ConnectTransactionStream = mpsc::UnboundedReceiver<ConnectTransactionEvent>;
350
351#[derive(Debug, PartialEq)]
352pub enum ConnectTransactionEvent {
353 OnConnectResult { result: ConnectResult, is_reconnect: bool },
354 OnRoamResult { result: RoamResult },
355 OnDisconnect { info: fidl_sme::DisconnectInfo },
356 OnSignalReport { ind: fidl_internal::SignalReportIndication },
357 OnChannelSwitched { info: fidl_internal::ChannelSwitchInfo },
358}
359
360#[derive(Debug, PartialEq)]
361pub enum ConnectFailure {
362 SelectNetworkFailure(SelectNetworkFailure),
363 ScanFailure(fidl_mlme::ScanResultCode),
366 JoinFailure(fidl_ieee80211::StatusCode),
369 AuthenticationFailure(fidl_ieee80211::StatusCode),
370 AssociationFailure(AssociationFailure),
371 EstablishRsnaFailure(EstablishRsnaFailure),
372}
373
374impl ConnectFailure {
375 #[allow(clippy::collapsible_match, reason = "mass allow for https://fxbug.dev/381896734")]
377 #[allow(
378 clippy::match_like_matches_macro,
379 reason = "mass allow for https://fxbug.dev/381896734"
380 )]
381 pub fn is_timeout(&self) -> bool {
382 match self {
385 ConnectFailure::AuthenticationFailure(failure) => match failure {
386 fidl_ieee80211::StatusCode::RejectedSequenceTimeout => true,
387 _ => false,
388 },
389 ConnectFailure::EstablishRsnaFailure(failure) => match failure {
390 EstablishRsnaFailure {
391 reason: EstablishRsnaFailureReason::RsnaResponseTimeout(_),
392 ..
393 }
394 | EstablishRsnaFailure {
395 reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(_),
396 ..
397 } => true,
398 _ => false,
399 },
400 _ => false,
401 }
402 }
403
404 pub fn likely_due_to_credential_rejected(&self) -> bool {
410 match self {
411 ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
429 auth_method: Some(auth::MethodName::Psk),
430 reason:
431 EstablishRsnaFailureReason::RsnaResponseTimeout(
432 wlan_rsn::Error::LikelyWrongCredential,
433 ),
434 })
435 | ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
436 auth_method: Some(auth::MethodName::Psk),
437 reason:
438 EstablishRsnaFailureReason::RsnaCompletionTimeout(
439 wlan_rsn::Error::LikelyWrongCredential,
440 ),
441 }) => true,
442
443 ConnectFailure::AssociationFailure(AssociationFailure {
452 bss_protection: BssProtection::Wep,
453 code: fidl_ieee80211::StatusCode::RefusedUnauthenticatedAccessNotSupported,
454 }) => true,
455
456 ConnectFailure::AssociationFailure(AssociationFailure {
460 bss_protection: BssProtection::Wpa3Personal,
461 code: fidl_ieee80211::StatusCode::RejectedSequenceTimeout,
462 })
463 | ConnectFailure::AssociationFailure(AssociationFailure {
464 bss_protection: BssProtection::Wpa2Wpa3Personal,
465 code: fidl_ieee80211::StatusCode::RejectedSequenceTimeout,
466 }) => true,
467 _ => false,
468 }
469 }
470
471 pub fn status_code(&self) -> fidl_ieee80211::StatusCode {
472 match self {
473 ConnectFailure::JoinFailure(code)
474 | ConnectFailure::AuthenticationFailure(code)
475 | ConnectFailure::AssociationFailure(AssociationFailure { code, .. }) => *code,
476 ConnectFailure::EstablishRsnaFailure(..) => {
477 fidl_ieee80211::StatusCode::EstablishRsnaFailure
478 }
479 ConnectFailure::ScanFailure(fidl_mlme::ScanResultCode::ShouldWait) => {
481 fidl_ieee80211::StatusCode::Canceled
482 }
483 ConnectFailure::SelectNetworkFailure(..) | ConnectFailure::ScanFailure(..) => {
484 fidl_ieee80211::StatusCode::RefusedReasonUnspecified
485 }
486 }
487 }
488}
489
490#[derive(Debug, PartialEq)]
491pub enum RoamFailureType {
492 SelectNetworkFailure,
493 RoamStartMalformedFailure,
494 RoamResultMalformedFailure,
495 RoamRequestMalformedFailure,
496 RoamConfirmationMalformedFailure,
497 ReassociationFailure,
498 EstablishRsnaFailure,
499}
500
501#[derive(Debug, PartialEq)]
502pub struct RoamFailure {
503 failure_type: RoamFailureType,
504 pub selected_bssid: Bssid,
505 pub status_code: fidl_ieee80211::StatusCode,
506 pub disconnect_info: fidl_sme::DisconnectInfo,
507 auth_method: Option<auth::MethodName>,
508 pub selected_bss: Option<BssDescription>,
509 establish_rsna_failure_reason: Option<EstablishRsnaFailureReason>,
510}
511
512impl RoamFailure {
513 #[allow(
516 clippy::match_like_matches_macro,
517 reason = "mass allow for https://fxbug.dev/381896734"
518 )]
519 pub fn likely_due_to_credential_rejected(&self) -> bool {
520 match self.failure_type {
521 RoamFailureType::EstablishRsnaFailure => match self.auth_method {
523 Some(auth::MethodName::Psk) => match self.establish_rsna_failure_reason {
524 Some(EstablishRsnaFailureReason::RsnaResponseTimeout(
525 wlan_rsn::Error::LikelyWrongCredential,
526 ))
527 | Some(EstablishRsnaFailureReason::RsnaCompletionTimeout(
528 wlan_rsn::Error::LikelyWrongCredential,
529 )) => true,
530 _ => false,
531 },
532 _ => false,
533 },
534 RoamFailureType::ReassociationFailure => {
535 match &self.selected_bss {
536 Some(selected_bss) => match selected_bss.protection() {
537 BssProtection::Wep => match self.status_code {
539 fidl_ieee80211::StatusCode::RefusedUnauthenticatedAccessNotSupported => true,
540 _ => false,
541 },
542 BssProtection::Wpa3Personal
544 | BssProtection::Wpa2Wpa3Personal => match self.status_code {
545 fidl_ieee80211::StatusCode::RejectedSequenceTimeout => true,
546 _ => false,
547 },
548 _ => false,
549 },
550 None => false,
553 }
554 }
555 _ => false,
556 }
557 }
558}
559
560#[derive(Debug, PartialEq)]
561pub enum SelectNetworkFailure {
562 NoScanResultWithSsid,
563 IncompatibleConnectRequest,
564 InternalProtectionError,
565}
566
567impl From<SelectNetworkFailure> for ConnectFailure {
568 fn from(failure: SelectNetworkFailure) -> Self {
569 ConnectFailure::SelectNetworkFailure(failure)
570 }
571}
572
573#[derive(Debug, PartialEq)]
574pub struct AssociationFailure {
575 pub bss_protection: BssProtection,
576 pub code: fidl_ieee80211::StatusCode,
577}
578
579impl From<AssociationFailure> for ConnectFailure {
580 fn from(failure: AssociationFailure) -> Self {
581 ConnectFailure::AssociationFailure(failure)
582 }
583}
584
585#[derive(Debug, PartialEq)]
586pub struct EstablishRsnaFailure {
587 pub auth_method: Option<auth::MethodName>,
588 pub reason: EstablishRsnaFailureReason,
589}
590
591#[derive(Debug, PartialEq)]
592pub enum EstablishRsnaFailureReason {
593 StartSupplicantFailed,
594 RsnaResponseTimeout(wlan_rsn::Error),
595 RsnaCompletionTimeout(wlan_rsn::Error),
596 InternalError,
597}
598
599impl From<EstablishRsnaFailure> for ConnectFailure {
600 fn from(failure: EstablishRsnaFailure) -> Self {
601 ConnectFailure::EstablishRsnaFailure(failure)
602 }
603}
604
605#[derive(Clone, Debug, PartialEq)]
608pub struct ServingApInfo {
609 pub bssid: Bssid,
610 pub ssid: Ssid,
611 pub rssi_dbm: i8,
612 pub snr_db: i8,
613 pub signal_report_time: zx::MonotonicInstant,
614 pub channel: wlan_common::channel::Channel,
615 pub protection: BssProtection,
616 pub ht_cap: Option<fidl_ieee80211::HtCapabilities>,
617 pub vht_cap: Option<fidl_ieee80211::VhtCapabilities>,
618 pub probe_resp_wsc: Option<wsc::ProbeRespWsc>,
619 pub wmm_param: Option<ie::WmmParam>,
620}
621
622impl From<ServingApInfo> for fidl_sme::ServingApInfo {
623 fn from(ap: ServingApInfo) -> fidl_sme::ServingApInfo {
624 fidl_sme::ServingApInfo {
625 bssid: ap.bssid.to_array(),
626 ssid: ap.ssid.to_vec(),
627 rssi_dbm: ap.rssi_dbm,
628 snr_db: ap.snr_db,
629 channel: ap.channel.into(),
630 protection: ap.protection.into(),
631 }
632 }
633}
634
635#[derive(Clone, Debug, PartialEq)]
637pub enum ClientSmeStatus {
638 Connected(Box<ServingApInfo>),
639 Connecting(Ssid),
640 Roaming(Bssid),
641 Idle,
642}
643
644impl ClientSmeStatus {
645 pub fn is_connecting(&self) -> bool {
646 matches!(self, ClientSmeStatus::Connecting(_))
647 }
648
649 pub fn is_connected(&self) -> bool {
650 matches!(self, ClientSmeStatus::Connected(_))
651 }
652}
653
654impl From<ClientSmeStatus> for fidl_sme::ClientStatusResponse {
655 fn from(client_sme_status: ClientSmeStatus) -> fidl_sme::ClientStatusResponse {
656 match client_sme_status {
657 ClientSmeStatus::Connected(serving_ap_info) => {
658 fidl_sme::ClientStatusResponse::Connected((*serving_ap_info).into())
659 }
660 ClientSmeStatus::Connecting(ssid) => {
661 fidl_sme::ClientStatusResponse::Connecting(ssid.to_vec())
662 }
663 ClientSmeStatus::Roaming(bssid) => {
664 fidl_sme::ClientStatusResponse::Roaming(bssid.to_array())
665 }
666 ClientSmeStatus::Idle => fidl_sme::ClientStatusResponse::Idle(fidl_sme::Empty {}),
667 }
668 }
669}
670
671impl ClientSme {
672 #[allow(clippy::too_many_arguments, reason = "mass allow for https://fxbug.dev/381896734")]
673 pub fn new(
674 cfg: ClientConfig,
675 info: fidl_mlme::DeviceInfo,
676 inspector: fuchsia_inspect::Inspector,
677 inspect_node: fuchsia_inspect::Node,
678 security_support: fidl_common::SecuritySupport,
679 spectrum_management_support: fidl_common::SpectrumManagementSupport,
680 ) -> (Self, MlmeSink, MlmeStream, timer::EventStream<Event>) {
681 let device_info = Arc::new(info);
682 let (mlme_sink, mlme_stream) = mpsc::unbounded();
683 let (mut timer, time_stream) = timer::create_timer();
684 let inspect = Arc::new(inspect::SmeTree::new(
685 inspector,
686 inspect_node,
687 &device_info,
688 &spectrum_management_support,
689 ));
690 let _ = timer.schedule(event::InspectPulseCheck);
691
692 (
693 ClientSme {
694 cfg,
695 state: Some(ClientState::new(cfg)),
696 scan_sched: <ScanScheduler<
697 Responder<Result<Vec<ScanResult>, fidl_mlme::ScanResultCode>>,
698 >>::new(
699 Arc::clone(&device_info), spectrum_management_support
700 ),
701 wmm_status_responders: vec![],
702 context: Context {
703 mlme_sink: MlmeSink::new(mlme_sink.clone()),
704 device_info,
705 timer,
706 att_id: 0,
707 inspect,
708 security_support,
709 },
710 },
711 MlmeSink::new(mlme_sink),
712 mlme_stream,
713 time_stream,
714 )
715 }
716
717 pub fn on_connect_command(
718 &mut self,
719 req: fidl_sme::ConnectRequest,
720 ) -> ConnectTransactionStream {
721 let (mut connect_txn_sink, connect_txn_stream) = ConnectTransactionSink::new_unbounded();
722
723 self.state = self.state.take().map(|state| state.cancel_ongoing_connect(&mut self.context));
725
726 let bss_description: BssDescription = match req.bss_description.try_into() {
727 Ok(bss_description) => bss_description,
728 Err(e) => {
729 error!("Failed converting FIDL BssDescription in ConnectRequest: {:?}", e);
730 connect_txn_sink
731 .send_connect_result(SelectNetworkFailure::IncompatibleConnectRequest.into());
732 return connect_txn_stream;
733 }
734 };
735
736 info!("Received ConnectRequest for {}", bss_description);
737
738 if self
739 .cfg
740 .bss_compatibility(
741 &bss_description,
742 &self.context.device_info,
743 &self.context.security_support,
744 )
745 .is_err()
746 {
747 warn!("BSS is incompatible");
748 connect_txn_sink
749 .send_connect_result(SelectNetworkFailure::IncompatibleConnectRequest.into());
750 return connect_txn_stream;
751 }
752
753 let authentication = req.authentication.clone();
754 let protection = match SecurityAuthenticator::try_from(req.authentication)
755 .map_err(From::from)
756 .and_then(|authenticator| {
757 Protection::try_from(SecurityContext {
758 security: &authenticator,
759 device: &self.context.device_info,
760 security_support: &self.context.security_support,
761 config: &self.cfg,
762 bss: &bss_description,
763 })
764 }) {
765 Ok(protection) => protection,
766 Err(error) => {
767 warn!(
768 "{:?}",
769 format!(
770 "Failed to configure protection for network {} ({}): {:?}",
771 bss_description.ssid, bss_description.bssid, error
772 )
773 );
774 connect_txn_sink
775 .send_connect_result(SelectNetworkFailure::IncompatibleConnectRequest.into());
776 return connect_txn_stream;
777 }
778 };
779 let cmd = ConnectCommand {
780 bss: Box::new(bss_description),
781 connect_txn_sink,
782 protection,
783 authentication,
784 };
785
786 self.state = self.state.take().map(|state| state.connect(cmd, &mut self.context));
787 connect_txn_stream
788 }
789
790 pub fn on_roam_command(&mut self, req: fidl_sme::RoamRequest) {
791 if !self.status().is_connected() {
792 error!("SME ignoring roam request because client is not connected");
793 } else {
794 self.state =
795 self.state.take().map(|state| state.roam(&mut self.context, req.bss_description));
796 }
797 }
798
799 pub fn on_disconnect_command(
800 &mut self,
801 policy_disconnect_reason: fidl_sme::UserDisconnectReason,
802 responder: fidl_sme::ClientSmeDisconnectResponder,
803 ) {
804 self.state = self
805 .state
806 .take()
807 .map(|state| state.disconnect(&mut self.context, policy_disconnect_reason, responder));
808 self.context.inspect.update_pulse(self.status());
809 }
810
811 pub fn on_scan_command(
812 &mut self,
813 scan_request: fidl_sme::ScanRequest,
814 ) -> oneshot::Receiver<Result<Vec<wlan_common::scan::ScanResult>, fidl_mlme::ScanResultCode>>
815 {
816 let (responder, receiver) = Responder::new();
817 if self.status().is_connecting() {
818 info!("SME ignoring scan request because a connect is in progress");
819 responder.respond(Err(fidl_mlme::ScanResultCode::ShouldWait));
820 } else {
821 info!(
822 "SME received a scan command, initiating a{} discovery scan",
823 match scan_request {
824 fidl_sme::ScanRequest::Active(_) => "n active",
825 fidl_sme::ScanRequest::Passive(_) => " passive",
826 }
827 );
828 let scan = DiscoveryScan::new(responder, scan_request);
829 let req = self.scan_sched.enqueue_scan_to_discover(scan);
830 self.send_scan_request(req);
831 }
832 receiver
833 }
834
835 pub fn on_start_scheduled_scan_command(
836 &mut self,
837 req: fidl_common::ScheduledScanRequest,
838 ) -> (oneshot::Receiver<Result<(), i32>>, ScheduledScanReceiver) {
839 let (responder, receiver) = Responder::new();
840 let session =
841 self.scan_sched.start_scheduled_scan(req, self.context.mlme_sink.clone(), responder);
842 (receiver, session)
843 }
844
845 pub fn on_get_scheduled_scan_enabled_command(
846 &mut self,
847 ) -> oneshot::Receiver<Result<fidl_mlme::MlmeGetScheduledScanEnabledResponse, i32>> {
848 let (responder, receiver) = Responder::new();
849 self.context.mlme_sink.send(MlmeRequest::GetScheduledScanEnabled(responder));
850 receiver
851 }
852
853 pub fn on_clone_inspect_vmo(&self) -> Option<fidl::Vmo> {
854 self.context.inspect.clone_vmo_data()
855 }
856
857 pub fn status(&self) -> ClientSmeStatus {
858 #[expect(clippy::expect_used)]
860 self.state.as_ref().expect("expected state to be always present").status()
861 }
862
863 pub fn wmm_status(&mut self) -> oneshot::Receiver<fidl_sme::ClientSmeWmmStatusResult> {
864 let (responder, receiver) = Responder::new();
865 self.wmm_status_responders.push(responder);
866 self.context.mlme_sink.send(MlmeRequest::WmmStatusReq);
867 receiver
868 }
869
870 fn send_scan_request(&mut self, req: Option<fidl_mlme::ScanRequest>) {
871 if let Some(req) = req {
872 self.context.mlme_sink.send(MlmeRequest::Scan(req));
873 }
874 }
875
876 pub fn query_telemetry_support(
877 &mut self,
878 ) -> oneshot::Receiver<Result<fidl_stats::TelemetrySupport, i32>> {
879 let (responder, receiver) = Responder::new();
880 self.context.mlme_sink.send(MlmeRequest::QueryTelemetrySupport(responder));
881 receiver
882 }
883
884 pub fn iface_stats(&mut self) -> oneshot::Receiver<fidl_mlme::GetIfaceStatsResponse> {
885 let (responder, receiver) = Responder::new();
886 self.context.mlme_sink.send(MlmeRequest::GetIfaceStats(responder));
887 receiver
888 }
889
890 pub fn histogram_stats(
891 &mut self,
892 ) -> oneshot::Receiver<fidl_mlme::GetIfaceHistogramStatsResponse> {
893 let (responder, receiver) = Responder::new();
894 self.context.mlme_sink.send(MlmeRequest::GetIfaceHistogramStats(responder));
895 receiver
896 }
897
898 pub fn signal_report(&mut self) -> oneshot::Receiver<Result<fidl_stats::SignalReport, i32>> {
899 let (responder, receiver) = Responder::new();
900 self.context.mlme_sink.send(MlmeRequest::GetSignalReport(responder));
901 receiver
902 }
903
904 pub fn set_mac_address(&mut self, mac_addr: [u8; 6]) -> oneshot::Receiver<Result<(), i32>> {
905 let (responder, receiver) = Responder::new();
906 self.context.mlme_sink.send(MlmeRequest::SetMacAddress(mac_addr, responder));
907 receiver
908 }
909
910 pub fn query_apf_packet_filter_support(
911 &mut self,
912 ) -> oneshot::Receiver<Result<fidl_common::ApfPacketFilterSupport, i32>> {
913 let (responder, receiver) = Responder::new();
914 self.context.mlme_sink.send(MlmeRequest::QueryApfPacketFilterSupport(responder));
915 receiver
916 }
917
918 pub fn install_apf_packet_filter(
919 &mut self,
920 program: Vec<u8>,
921 ) -> oneshot::Receiver<Result<(), i32>> {
922 let (responder, receiver) = Responder::new();
923 self.context.mlme_sink.send(MlmeRequest::InstallApfPacketFilter(
924 fidl_mlme::MlmeInstallApfPacketFilterRequest { program },
925 responder,
926 ));
927 receiver
928 }
929
930 pub fn read_apf_packet_filter_data(
931 &mut self,
932 ) -> oneshot::Receiver<Result<fidl_mlme::MlmeReadApfPacketFilterDataResponse, i32>> {
933 let (responder, receiver) = Responder::new();
934 self.context.mlme_sink.send(MlmeRequest::ReadApfPacketFilterData(responder));
935 receiver
936 }
937
938 pub fn set_apf_packet_filter_enabled(
939 &mut self,
940 enabled: bool,
941 ) -> oneshot::Receiver<Result<(), i32>> {
942 let (responder, receiver) = Responder::new();
943 self.context.mlme_sink.send(MlmeRequest::SetApfPacketFilterEnabled(
944 fidl_mlme::MlmeSetApfPacketFilterEnabledRequest { enabled },
945 responder,
946 ));
947 receiver
948 }
949
950 pub fn get_apf_packet_filter_enabled(
951 &mut self,
952 ) -> oneshot::Receiver<Result<fidl_mlme::MlmeGetApfPacketFilterEnabledResponse, i32>> {
953 let (responder, receiver) = Responder::new();
954 self.context.mlme_sink.send(MlmeRequest::GetApfPacketFilterEnabled(responder));
955 receiver
956 }
957}
958
959impl super::Station for ClientSme {
960 type Event = Event;
961
962 fn on_mlme_event(&mut self, event: fidl_mlme::MlmeEvent) {
963 match event {
964 fidl_mlme::MlmeEvent::OnScanResult { result } => {
965 self.scan_sched
966 .on_mlme_scan_result(result)
967 .unwrap_or_else(|e| error!("scan result error: {:?}", e));
968 }
969 fidl_mlme::MlmeEvent::OnScanEnd { end } => {
970 match self.scan_sched.on_mlme_scan_end(end, &self.context.inspect) {
971 Err(e) => error!("scan end error: {:?}", e),
972 Ok((scan_end, next_request)) => {
973 self.send_scan_request(next_request);
976
977 match scan_end.result_code {
978 fidl_mlme::ScanResultCode::Success => {
979 let scan_result_list: Vec<ScanResult> = scan_end
980 .bss_description_list
981 .into_iter()
982 .map(|bss_description| {
983 self.cfg.create_scan_result(
984 zx::MonotonicInstant::from_nanos(0),
986 bss_description,
987 &self.context.device_info,
988 &self.context.security_support,
989 )
990 })
991 .collect();
992 for responder in scan_end.tokens {
993 responder.respond(Ok(scan_result_list.clone()));
994 }
995 }
996 result_code => {
997 let count = scan_end.bss_description_list.len();
998 if count > 0 {
999 warn!("Incomplete scan with {} pending results.", count);
1000 }
1001 for responder in scan_end.tokens {
1002 responder.respond(Err(result_code));
1003 }
1004 }
1005 }
1006 }
1007 }
1008 }
1009 fidl_mlme::MlmeEvent::OnScheduledScanMatchesAvailable { txn_id } => {
1010 self.scan_sched.on_scheduled_scan_matches_available(
1011 txn_id,
1012 &self.context.inspect,
1013 &self.cfg,
1014 &self.context.device_info,
1015 &self.context.security_support,
1016 );
1017 }
1018 fidl_mlme::MlmeEvent::OnScheduledScanStoppedByFirmware { txn_id } => {
1019 self.scan_sched.on_scheduled_scan_stopped_by_firmware(txn_id);
1020 }
1021 fidl_mlme::MlmeEvent::OnWmmStatusResp { status, resp } => {
1022 for responder in self.wmm_status_responders.drain(..) {
1023 let result = if status == zx::sys::ZX_OK { Ok(resp) } else { Err(status) };
1024 responder.respond(result);
1025 }
1026 let event = fidl_mlme::MlmeEvent::OnWmmStatusResp { status, resp };
1027 self.state =
1028 self.state.take().map(|state| state.on_mlme_event(event, &mut self.context));
1029 }
1030 other => {
1031 self.state =
1032 self.state.take().map(|state| state.on_mlme_event(other, &mut self.context));
1033 }
1034 };
1035
1036 self.context.inspect.update_pulse(self.status());
1037 }
1038
1039 fn on_timeout(&mut self, timed_event: timer::Event<Event>) {
1040 self.state = self.state.take().map(|state| match timed_event.event {
1041 event @ Event::RsnaCompletionTimeout(..)
1042 | event @ Event::RsnaResponseTimeout(..)
1043 | event @ Event::RsnaRetransmissionTimeout(..)
1044 | event @ Event::SaeTimeout(..)
1045 | event @ Event::DeauthenticateTimeout(..) => {
1046 state.handle_timeout(event, &mut self.context)
1047 }
1048 Event::InspectPulseCheck(..) => {
1049 self.context.mlme_sink.send(MlmeRequest::WmmStatusReq);
1050 let _ = self.context.timer.schedule(event::InspectPulseCheck);
1051 state
1052 }
1053 });
1054
1055 self.context.inspect.update_pulse(self.status());
1058 }
1059}
1060
1061fn report_connect_finished(connect_txn_sink: &mut ConnectTransactionSink, result: ConnectResult) {
1062 connect_txn_sink.send_connect_result(result);
1063}
1064
1065fn report_roam_finished(connect_txn_sink: &mut ConnectTransactionSink, result: RoamResult) {
1066 connect_txn_sink.send_roam_result(result);
1067}
1068
1069#[cfg(test)]
1070mod tests {
1071 use super::*;
1072 use crate::Config as SmeConfig;
1073 use assert_matches::assert_matches;
1074 use fidl_fuchsia_wlan_common as fidl_common;
1075 use fidl_fuchsia_wlan_mlme as fidl_mlme;
1076 use fuchsia_inspect as finspect;
1077 use ieee80211::MacAddr;
1078 use std::collections::HashSet;
1079 use std::sync::LazyLock;
1080 use test_case::test_case;
1081 use wlan_common::{
1082 channel::{Cbw, Channel},
1083 fake_bss_description, fake_fidl_bss_description,
1084 ie::{IeType, fake_ht_cap_bytes, fake_vht_cap_bytes},
1085 security::{wep::WEP40_KEY_BYTES, wpa::credential::PSK_SIZE_BYTES},
1086 test_utils::{
1087 fake_features::{
1088 fake_security_support, fake_security_support_empty,
1089 fake_spectrum_management_support_empty,
1090 },
1091 fake_stas::{FakeProtectionCfg, IesOverrides},
1092 },
1093 };
1094
1095 use super::test_utils::{create_on_wmm_status_resp, fake_wmm_param, fake_wmm_status_resp};
1096
1097 use crate::{Station, test_utils};
1098
1099 static CLIENT_ADDR: LazyLock<MacAddr> =
1100 LazyLock::new(|| [0x7A, 0xE7, 0x76, 0xD9, 0xF2, 0x67].into());
1101
1102 fn authentication_open() -> fidl_internal::Authentication {
1103 fidl_internal::Authentication { protocol: fidl_internal::Protocol::Open, credentials: None }
1104 }
1105
1106 fn authentication_wep40() -> fidl_internal::Authentication {
1107 fidl_internal::Authentication {
1108 protocol: fidl_internal::Protocol::Wep,
1109 credentials: Some(Box::new(fidl_internal::Credentials::Wep(
1110 fidl_internal::WepCredentials { key: [1; WEP40_KEY_BYTES].into() },
1111 ))),
1112 }
1113 }
1114
1115 fn authentication_wpa1_passphrase() -> fidl_internal::Authentication {
1116 fidl_internal::Authentication {
1117 protocol: fidl_internal::Protocol::Wpa1,
1118 credentials: Some(Box::new(fidl_internal::Credentials::Wpa(
1119 fidl_internal::WpaCredentials::Passphrase(b"password".as_slice().into()),
1120 ))),
1121 }
1122 }
1123
1124 fn authentication_wpa2_personal_psk() -> fidl_internal::Authentication {
1125 fidl_internal::Authentication {
1126 protocol: fidl_internal::Protocol::Wpa2Personal,
1127 credentials: Some(Box::new(fidl_internal::Credentials::Wpa(
1128 fidl_internal::WpaCredentials::Psk([1; PSK_SIZE_BYTES]),
1129 ))),
1130 }
1131 }
1132
1133 fn authentication_wpa2_personal_passphrase() -> fidl_internal::Authentication {
1134 fidl_internal::Authentication {
1135 protocol: fidl_internal::Protocol::Wpa2Personal,
1136 credentials: Some(Box::new(fidl_internal::Credentials::Wpa(
1137 fidl_internal::WpaCredentials::Passphrase(b"password".as_slice().into()),
1138 ))),
1139 }
1140 }
1141
1142 fn authentication_wpa3_personal_passphrase() -> fidl_internal::Authentication {
1143 fidl_internal::Authentication {
1144 protocol: fidl_internal::Protocol::Wpa3Personal,
1145 credentials: Some(Box::new(fidl_internal::Credentials::Wpa(
1146 fidl_internal::WpaCredentials::Passphrase(b"password".as_slice().into()),
1147 ))),
1148 }
1149 }
1150
1151 fn report_fake_scan_result(
1152 sme: &mut ClientSme,
1153 timestamp_nanos: i64,
1154 bss: fidl_ieee80211::BssDescription,
1155 ) {
1156 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScanResult {
1157 result: fidl_mlme::ScanResult { txn_id: 1, timestamp_nanos, bss },
1158 });
1159 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScanEnd {
1160 end: fidl_mlme::ScanEnd { txn_id: 1, code: fidl_mlme::ScanResultCode::Success },
1161 });
1162 }
1163
1164 #[test_case(FakeProtectionCfg::Open)]
1165 #[test_case(FakeProtectionCfg::Wpa1Wpa2TkipOnly)]
1166 #[test_case(FakeProtectionCfg::Wpa2TkipOnly)]
1167 #[test_case(FakeProtectionCfg::Wpa2)]
1168 #[test_case(FakeProtectionCfg::Wpa2Wpa3)]
1169 fn default_client_protection_is_bss_compatible(protection: FakeProtectionCfg) {
1170 let cfg = ClientConfig::default();
1171 let fake_device_info = test_utils::fake_device_info([1u8; 6].into());
1172 assert!(
1173 cfg.bss_compatibility(
1174 &fake_bss_description!(protection => protection),
1175 &fake_device_info,
1176 &fake_security_support_empty()
1177 )
1178 .is_ok(),
1179 );
1180 }
1181
1182 #[test_case(FakeProtectionCfg::Wpa1)]
1183 #[test_case(FakeProtectionCfg::Wpa3)]
1184 #[test_case(FakeProtectionCfg::Wpa3Transition)]
1185 #[test_case(FakeProtectionCfg::Eap)]
1186 fn default_client_protection_is_bss_incompatible(protection: FakeProtectionCfg) {
1187 let cfg = ClientConfig::default();
1188 let fake_device_info = test_utils::fake_device_info([1u8; 6].into());
1189 assert!(
1190 cfg.bss_compatibility(
1191 &fake_bss_description!(protection => protection),
1192 &fake_device_info,
1193 &fake_security_support_empty()
1194 )
1195 .is_err(),
1196 );
1197 }
1198
1199 #[test_case(FakeProtectionCfg::Open)]
1200 #[test_case(FakeProtectionCfg::OpenOweTransition)]
1201 #[test_case(FakeProtectionCfg::Wpa1Wpa2TkipOnly)]
1202 #[test_case(FakeProtectionCfg::Wpa2TkipOnly)]
1203 #[test_case(FakeProtectionCfg::Wpa2)]
1204 #[test_case(FakeProtectionCfg::Wpa2Wpa3)]
1205 fn compatible_default_client_protection_security_protocol_intersection_is_non_empty(
1206 protection: FakeProtectionCfg,
1207 ) {
1208 let cfg = ClientConfig::default();
1209 assert!(
1210 !cfg.security_protocol_intersection(
1211 &fake_bss_description!(protection => protection),
1212 &fake_security_support_empty()
1213 )
1214 .is_empty()
1215 );
1216 }
1217
1218 #[test_case(FakeProtectionCfg::Owe)]
1219 #[test_case(FakeProtectionCfg::Wpa1)]
1220 #[test_case(FakeProtectionCfg::Wpa3)]
1221 #[test_case(FakeProtectionCfg::Wpa3Transition)]
1222 #[test_case(FakeProtectionCfg::Eap)]
1223 fn incompatible_default_client_protection_security_protocol_intersection_is_empty(
1224 protection: FakeProtectionCfg,
1225 ) {
1226 let cfg = ClientConfig::default();
1227 assert!(
1228 cfg.security_protocol_intersection(
1229 &fake_bss_description!(protection => protection),
1230 &fake_security_support_empty()
1231 )
1232 .is_empty(),
1233 );
1234 }
1235
1236 #[test_case(FakeProtectionCfg::Wpa1, [SecurityDescriptor::WPA1])]
1237 #[test_case(FakeProtectionCfg::Wpa3, [SecurityDescriptor::WPA3_PERSONAL])]
1238 #[test_case(FakeProtectionCfg::Wpa3Transition, [SecurityDescriptor::WPA3_PERSONAL])]
1239 #[test_case(FakeProtectionCfg::Eap, [])]
1241 fn default_client_protection_security_protocols_by_mac_role_eq(
1242 protection: FakeProtectionCfg,
1243 expected: impl IntoIterator<Item = SecurityDescriptor>,
1244 ) {
1245 let cfg = ClientConfig::default();
1246 let security_protocols: HashSet<_> = cfg
1247 .security_protocols_by_mac_role(&fake_bss_description!(protection => protection))
1248 .collect();
1249 assert_eq!(
1252 security_protocols,
1253 HashSet::from_iter(
1254 [
1255 (SecurityDescriptor::OPEN, MacRole::Client),
1256 (SecurityDescriptor::WPA2_PERSONAL, MacRole::Client),
1257 ]
1258 .into_iter()
1259 .chain(expected.into_iter().map(|protocol| (protocol, MacRole::Ap)))
1260 ),
1261 );
1262 }
1263
1264 #[test]
1265 fn configured_client_bss_owe_compatible() {
1266 let cfg = ClientConfig::from_config(Config::default(), false, true);
1268 let mut security_support = fake_security_support_empty();
1269 security_support.mfp.as_mut().unwrap().supported = Some(true);
1270 security_support.owe.as_mut().unwrap().supported = Some(true);
1271 assert!(
1272 !cfg.security_protocol_intersection(&fake_bss_description!(Owe), &security_support)
1273 .is_empty()
1274 );
1275 }
1276
1277 #[test]
1278 fn configured_client_bss_wep_compatible() {
1279 let cfg = ClientConfig::from_config(Config::default().with_wep(), false, false);
1281 assert!(
1282 !cfg.security_protocol_intersection(
1283 &fake_bss_description!(Wep),
1284 &fake_security_support_empty()
1285 )
1286 .is_empty()
1287 );
1288 }
1289
1290 #[test]
1291 fn configured_client_bss_wpa1_compatible() {
1292 let cfg = ClientConfig::from_config(Config::default().with_wpa1(), false, false);
1294 assert!(
1295 !cfg.security_protocol_intersection(
1296 &fake_bss_description!(Wpa1),
1297 &fake_security_support_empty()
1298 )
1299 .is_empty()
1300 );
1301 }
1302
1303 #[test]
1304 fn configured_client_bss_wpa3_compatible() {
1305 let cfg = ClientConfig::from_config(Config::default(), true, false);
1307 let mut security_support = fake_security_support_empty();
1308 security_support.mfp.as_mut().unwrap().supported = Some(true);
1309 assert!(
1310 !cfg.security_protocol_intersection(&fake_bss_description!(Wpa3), &security_support)
1311 .is_empty()
1312 );
1313 assert!(
1314 !cfg.security_protocol_intersection(
1315 &fake_bss_description!(Wpa3Transition),
1316 &security_support,
1317 )
1318 .is_empty()
1319 );
1320 }
1321
1322 #[test]
1323 fn verify_rates_compatibility() {
1324 let cfg = ClientConfig::default();
1326 let device_info = test_utils::fake_device_info([1u8; 6].into());
1327 assert!(
1328 cfg.has_compatible_channel_and_data_rates(&fake_bss_description!(Open), &device_info)
1329 );
1330
1331 let bss = fake_bss_description!(Open, rates: vec![0x8C, 0xFF]);
1333 assert!(cfg.has_compatible_channel_and_data_rates(&bss, &device_info));
1334
1335 let bss = fake_bss_description!(Open, rates: vec![0x81]);
1337 assert!(!cfg.has_compatible_channel_and_data_rates(&bss, &device_info));
1338 }
1339
1340 #[test]
1341 fn convert_scan_result() {
1342 let cfg = ClientConfig::default();
1343 let bss_description = fake_bss_description!(Wpa2,
1344 ssid: Ssid::empty(),
1345 bssid: [0u8; 6],
1346 rssi_dbm: -30,
1347 snr_db: 0,
1348 channel: Channel::new(1, Cbw::Cbw20),
1349 ies_overrides: IesOverrides::new()
1350 .set(IeType::HT_CAPABILITIES, fake_ht_cap_bytes().to_vec())
1351 .set(IeType::VHT_CAPABILITIES, fake_vht_cap_bytes().to_vec()),
1352 );
1353 let device_info = test_utils::fake_device_info([1u8; 6].into());
1354 let timestamp = zx::MonotonicInstant::get();
1355 let scan_result = cfg.create_scan_result(
1356 timestamp,
1357 bss_description.clone(),
1358 &device_info,
1359 &fake_security_support(),
1360 );
1361
1362 assert_eq!(
1363 scan_result,
1364 ScanResult {
1365 compatibility: Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
1366 timestamp,
1367 bss_description,
1368 }
1369 );
1370
1371 let wmm_param = *ie::parse_wmm_param(&fake_wmm_param().bytes[..])
1372 .expect("expect WMM param to be parseable");
1373 let bss_description = fake_bss_description!(Wpa2,
1374 ssid: Ssid::empty(),
1375 bssid: [0u8; 6],
1376 rssi_dbm: -30,
1377 snr_db: 0,
1378 channel: Channel::new(1, Cbw::Cbw20),
1379 wmm_param: Some(wmm_param),
1380 ies_overrides: IesOverrides::new()
1381 .set(IeType::HT_CAPABILITIES, fake_ht_cap_bytes().to_vec())
1382 .set(IeType::VHT_CAPABILITIES, fake_vht_cap_bytes().to_vec()),
1383 );
1384 let timestamp = zx::MonotonicInstant::get();
1385 let scan_result = cfg.create_scan_result(
1386 timestamp,
1387 bss_description.clone(),
1388 &device_info,
1389 &fake_security_support(),
1390 );
1391
1392 assert_eq!(
1393 scan_result,
1394 ScanResult {
1395 compatibility: Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
1396 timestamp,
1397 bss_description,
1398 }
1399 );
1400
1401 let bss_description = fake_bss_description!(Wep,
1402 ssid: Ssid::empty(),
1403 bssid: [0u8; 6],
1404 rssi_dbm: -30,
1405 snr_db: 0,
1406 channel: Channel::new(1, Cbw::Cbw20),
1407 ies_overrides: IesOverrides::new()
1408 .set(IeType::HT_CAPABILITIES, fake_ht_cap_bytes().to_vec())
1409 .set(IeType::VHT_CAPABILITIES, fake_vht_cap_bytes().to_vec()),
1410 );
1411 let timestamp = zx::MonotonicInstant::get();
1412 let scan_result = cfg.create_scan_result(
1413 timestamp,
1414 bss_description.clone(),
1415 &device_info,
1416 &fake_security_support(),
1417 );
1418 assert_eq!(
1419 scan_result,
1420 ScanResult {
1421 compatibility: Incompatible::expect_err(
1422 "incompatible channel, PHY data rates, or security protocols",
1423 Some([
1424 (SecurityDescriptor::WEP, MacRole::Ap),
1425 (SecurityDescriptor::OPEN, MacRole::Client),
1426 (SecurityDescriptor::WPA2_PERSONAL, MacRole::Client),
1427 ])
1428 ),
1429 timestamp,
1430 bss_description,
1431 },
1432 );
1433
1434 let cfg = ClientConfig::from_config(Config::default().with_wep(), false, false);
1435 let bss_description = fake_bss_description!(Wep,
1436 ssid: Ssid::empty(),
1437 bssid: [0u8; 6],
1438 rssi_dbm: -30,
1439 snr_db: 0,
1440 channel: Channel::new(1, Cbw::Cbw20),
1441 ies_overrides: IesOverrides::new()
1442 .set(IeType::HT_CAPABILITIES, fake_ht_cap_bytes().to_vec())
1443 .set(IeType::VHT_CAPABILITIES, fake_vht_cap_bytes().to_vec()),
1444 );
1445 let timestamp = zx::MonotonicInstant::get();
1446 let scan_result = cfg.create_scan_result(
1447 timestamp,
1448 bss_description.clone(),
1449 &device_info,
1450 &fake_security_support(),
1451 );
1452 assert_eq!(
1453 scan_result,
1454 ScanResult {
1455 compatibility: Compatible::expect_ok([SecurityDescriptor::WEP]),
1456 timestamp,
1457 bss_description,
1458 }
1459 );
1460 }
1461
1462 #[test_case(EstablishRsnaFailureReason::RsnaResponseTimeout(
1463 wlan_rsn::Error::LikelyWrongCredential
1464 ))]
1465 #[test_case(EstablishRsnaFailureReason::RsnaCompletionTimeout(
1466 wlan_rsn::Error::LikelyWrongCredential
1467 ))]
1468 fn test_connect_detection_of_rejected_wpa1_or_wpa2_credentials(
1469 reason: EstablishRsnaFailureReason,
1470 ) {
1471 let failure = ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
1472 auth_method: Some(auth::MethodName::Psk),
1473 reason,
1474 });
1475 assert!(failure.likely_due_to_credential_rejected());
1476 }
1477
1478 #[test_case(fake_bss_description!(Wpa1), EstablishRsnaFailureReason::RsnaResponseTimeout(wlan_rsn::Error::LikelyWrongCredential))]
1479 #[test_case(fake_bss_description!(Wpa1), EstablishRsnaFailureReason::RsnaCompletionTimeout(wlan_rsn::Error::LikelyWrongCredential))]
1480 #[test_case(fake_bss_description!(Wpa1Wpa2TkipOnly), EstablishRsnaFailureReason::RsnaResponseTimeout(wlan_rsn::Error::LikelyWrongCredential))]
1481 #[test_case(fake_bss_description!(Wpa1Wpa2TkipOnly), EstablishRsnaFailureReason::RsnaCompletionTimeout(wlan_rsn::Error::LikelyWrongCredential))]
1482 #[test_case(fake_bss_description!(Wpa2), EstablishRsnaFailureReason::RsnaResponseTimeout(wlan_rsn::Error::LikelyWrongCredential))]
1483 #[test_case(fake_bss_description!(Wpa2), EstablishRsnaFailureReason::RsnaCompletionTimeout(wlan_rsn::Error::LikelyWrongCredential))]
1484 fn test_roam_detection_of_rejected_wpa1_or_wpa2_credentials(
1485 selected_bss: BssDescription,
1486 failure_reason: EstablishRsnaFailureReason,
1487 ) {
1488 let disconnect_info = fidl_sme::DisconnectInfo {
1489 is_sme_reconnecting: false,
1490 disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
1491 mlme_event_name: fidl_sme::DisconnectMlmeEventName::RoamResultIndication,
1492 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
1493 }),
1494 };
1495 let failure = RoamFailure {
1496 status_code: fidl_ieee80211::StatusCode::RefusedUnauthenticatedAccessNotSupported,
1497 failure_type: RoamFailureType::EstablishRsnaFailure,
1498 selected_bssid: selected_bss.bssid,
1499 disconnect_info,
1500 auth_method: Some(auth::MethodName::Psk),
1501 establish_rsna_failure_reason: Some(failure_reason),
1502 selected_bss: Some(selected_bss),
1503 };
1504 assert!(failure.likely_due_to_credential_rejected());
1505 }
1506
1507 #[test]
1508 fn test_connect_detection_of_rejected_wpa3_credentials() {
1509 let bss = fake_bss_description!(Wpa3);
1510 let failure = ConnectFailure::AssociationFailure(AssociationFailure {
1511 bss_protection: bss.protection(),
1512 code: fidl_ieee80211::StatusCode::RejectedSequenceTimeout,
1513 });
1514
1515 assert!(failure.likely_due_to_credential_rejected());
1516 }
1517
1518 #[test]
1519 fn test_roam_detection_of_rejected_wpa3_credentials() {
1520 let selected_bss = fake_bss_description!(Wpa3);
1521 let disconnect_info = fidl_sme::DisconnectInfo {
1522 is_sme_reconnecting: false,
1523 disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
1524 mlme_event_name: fidl_sme::DisconnectMlmeEventName::RoamResultIndication,
1525 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
1526 }),
1527 };
1528 let failure = RoamFailure {
1529 status_code: fidl_ieee80211::StatusCode::RejectedSequenceTimeout,
1530 failure_type: RoamFailureType::ReassociationFailure,
1531 selected_bssid: selected_bss.bssid,
1532 disconnect_info,
1533 auth_method: Some(auth::MethodName::Sae),
1534 establish_rsna_failure_reason: None,
1535 selected_bss: Some(selected_bss),
1536 };
1537 assert!(failure.likely_due_to_credential_rejected());
1538 }
1539
1540 #[test]
1541 fn test_connect_detection_of_rejected_wep_credentials() {
1542 let failure = ConnectFailure::AssociationFailure(AssociationFailure {
1543 bss_protection: BssProtection::Wep,
1544 code: fidl_ieee80211::StatusCode::RefusedUnauthenticatedAccessNotSupported,
1545 });
1546 assert!(failure.likely_due_to_credential_rejected());
1547 }
1548
1549 #[test]
1550 fn test_roam_detection_of_rejected_wep_credentials() {
1551 let selected_bss = fake_bss_description!(Wep);
1552 let disconnect_info = fidl_sme::DisconnectInfo {
1553 is_sme_reconnecting: false,
1554 disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
1555 mlme_event_name: fidl_sme::DisconnectMlmeEventName::RoamResultIndication,
1556 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
1557 }),
1558 };
1559 let failure = RoamFailure {
1560 status_code: fidl_ieee80211::StatusCode::RefusedUnauthenticatedAccessNotSupported,
1561 failure_type: RoamFailureType::ReassociationFailure,
1562 selected_bssid: selected_bss.bssid,
1563 disconnect_info,
1564 auth_method: Some(auth::MethodName::Psk),
1565 establish_rsna_failure_reason: None,
1566 selected_bss: Some(selected_bss),
1567 };
1568 assert!(failure.likely_due_to_credential_rejected());
1569 }
1570
1571 #[test]
1572 fn test_connect_no_detection_of_rejected_wpa1_or_wpa2_credentials() {
1573 let failure = ConnectFailure::ScanFailure(fidl_mlme::ScanResultCode::InternalError);
1574 assert!(!failure.likely_due_to_credential_rejected());
1575
1576 let failure = ConnectFailure::AssociationFailure(AssociationFailure {
1577 bss_protection: BssProtection::Wpa2Personal,
1578 code: fidl_ieee80211::StatusCode::RefusedUnauthenticatedAccessNotSupported,
1579 });
1580 assert!(!failure.likely_due_to_credential_rejected());
1581 }
1582
1583 #[test_case(fake_bss_description!(Wpa1))]
1584 #[test_case(fake_bss_description!(Wpa1Wpa2TkipOnly))]
1585 #[test_case(fake_bss_description!(Wpa2))]
1586 fn test_roam_no_detection_of_rejected_wpa1_or_wpa2_credentials(selected_bss: BssDescription) {
1587 let disconnect_info = fidl_sme::DisconnectInfo {
1588 is_sme_reconnecting: false,
1589 disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
1590 mlme_event_name: fidl_sme::DisconnectMlmeEventName::RoamResultIndication,
1591 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
1592 }),
1593 };
1594 let failure = RoamFailure {
1595 status_code: fidl_ieee80211::StatusCode::RefusedUnauthenticatedAccessNotSupported,
1596 failure_type: RoamFailureType::EstablishRsnaFailure,
1597 selected_bssid: selected_bss.bssid,
1598 disconnect_info,
1599 auth_method: Some(auth::MethodName::Psk),
1600 establish_rsna_failure_reason: Some(EstablishRsnaFailureReason::StartSupplicantFailed),
1601 selected_bss: Some(selected_bss),
1602 };
1603 assert!(!failure.likely_due_to_credential_rejected());
1604 }
1605
1606 #[test]
1607 fn test_connect_no_detection_of_rejected_wpa3_credentials() {
1608 let bss = fake_bss_description!(Wpa3);
1609 let failure = ConnectFailure::AssociationFailure(AssociationFailure {
1610 bss_protection: bss.protection(),
1611 code: fidl_ieee80211::StatusCode::RefusedUnauthenticatedAccessNotSupported,
1612 });
1613
1614 assert!(!failure.likely_due_to_credential_rejected());
1615 }
1616
1617 #[test]
1618 fn test_roam_no_detection_of_rejected_wpa3_credentials() {
1619 let selected_bss = fake_bss_description!(Wpa3);
1620 let disconnect_info = fidl_sme::DisconnectInfo {
1621 is_sme_reconnecting: false,
1622 disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
1623 mlme_event_name: fidl_sme::DisconnectMlmeEventName::RoamResultIndication,
1624 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
1625 }),
1626 };
1627 let failure = RoamFailure {
1628 status_code: fidl_ieee80211::StatusCode::RefusedUnauthenticatedAccessNotSupported,
1629 failure_type: RoamFailureType::ReassociationFailure,
1630 selected_bssid: selected_bss.bssid,
1631 disconnect_info,
1632 auth_method: Some(auth::MethodName::Sae),
1633 establish_rsna_failure_reason: None,
1634 selected_bss: Some(selected_bss),
1635 };
1636 assert!(!failure.likely_due_to_credential_rejected());
1637 }
1638
1639 #[test]
1640 fn test_connect_no_detection_of_rejected_wep_credentials() {
1641 let failure = ConnectFailure::AssociationFailure(AssociationFailure {
1642 bss_protection: BssProtection::Wep,
1643 code: fidl_ieee80211::StatusCode::InvalidParameters,
1644 });
1645 assert!(!failure.likely_due_to_credential_rejected());
1646 }
1647
1648 #[test]
1649 fn test_roam_no_detection_of_rejected_wep_credentials() {
1650 let selected_bss = fake_bss_description!(Wep);
1651 let disconnect_info = fidl_sme::DisconnectInfo {
1652 is_sme_reconnecting: false,
1653 disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
1654 mlme_event_name: fidl_sme::DisconnectMlmeEventName::RoamResultIndication,
1655 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
1656 }),
1657 };
1658 let failure = RoamFailure {
1659 status_code: fidl_ieee80211::StatusCode::StatusInvalidElement,
1660 failure_type: RoamFailureType::ReassociationFailure,
1661 selected_bssid: selected_bss.bssid,
1662 disconnect_info,
1663 auth_method: Some(auth::MethodName::Psk),
1664 establish_rsna_failure_reason: None,
1665 selected_bss: Some(selected_bss),
1666 };
1667 assert!(!failure.likely_due_to_credential_rejected());
1668 }
1669
1670 #[test_case(fake_bss_description!(Open), authentication_open() => matches Ok(Protection::Open))]
1671 #[test_case(fake_bss_description!(Open), authentication_wpa2_personal_passphrase() => matches Err(_))]
1672 #[test_case(fake_bss_description!(Wpa2), authentication_wpa2_personal_passphrase() => matches Ok(Protection::Rsna(_)))]
1673 #[test_case(fake_bss_description!(Wpa2), authentication_wpa2_personal_psk() => matches Ok(Protection::Rsna(_)))]
1674 #[test_case(fake_bss_description!(Wpa2), authentication_open() => matches Err(_))]
1675 fn test_protection_from_authentication(
1676 bss: BssDescription,
1677 authentication: fidl_internal::Authentication,
1678 ) -> Result<Protection, anyhow::Error> {
1679 let device = test_utils::fake_device_info(*CLIENT_ADDR);
1680 let security_support = fake_security_support();
1681 let config = Default::default();
1682
1683 let authenticator = SecurityAuthenticator::try_from(authentication).unwrap();
1685 Protection::try_from(SecurityContext {
1686 security: &authenticator,
1687 device: &device,
1688 security_support: &security_support,
1689 config: &config,
1690 bss: &bss,
1691 })
1692 }
1693
1694 #[fuchsia::test(allow_stalls = false)]
1695 async fn status_connecting() {
1696 let (mut sme, _mlme_stream, _time_stream) = create_sme().await;
1697 assert_eq!(ClientSmeStatus::Idle, sme.status());
1698
1699 let bss_description =
1701 fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap());
1702 let _recv = sme.on_connect_command(connect_req(
1703 Ssid::try_from("foo").unwrap(),
1704 bss_description,
1705 authentication_open(),
1706 ));
1707 assert_eq!(ClientSmeStatus::Connecting(Ssid::try_from("foo").unwrap()), sme.status());
1708
1709 let ssid = assert_matches!(sme.state.as_ref().unwrap().status(), ClientSmeStatus::Connecting(ssid) => ssid);
1712 assert_eq!(Ssid::try_from("foo").unwrap(), ssid);
1713 assert_eq!(ClientSmeStatus::Connecting(Ssid::try_from("foo").unwrap()), sme.status());
1714
1715 let bss_description =
1717 fake_fidl_bss_description!(Open, ssid: Ssid::try_from("bar").unwrap());
1718 let _recv2 = sme.on_connect_command(connect_req(
1719 Ssid::try_from("bar").unwrap(),
1720 bss_description,
1721 authentication_open(),
1722 ));
1723 assert_eq!(ClientSmeStatus::Connecting(Ssid::try_from("bar").unwrap()), sme.status());
1724 }
1725
1726 #[test]
1727 fn connecting_to_wep_network_supported() {
1728 let _executor = fuchsia_async::TestExecutor::new();
1729 let inspector = finspect::Inspector::default();
1730 let sme_root_node = inspector.root().create_child("sme");
1731 let (mut sme, _mlme_sink, mut mlme_stream, _time_stream) = ClientSme::new(
1732 ClientConfig::from_config(SmeConfig::default().with_wep(), false, false),
1733 test_utils::fake_device_info(*CLIENT_ADDR),
1734 inspector,
1735 sme_root_node,
1736 fake_security_support(),
1737 fake_spectrum_management_support_empty(),
1738 );
1739 assert_eq!(ClientSmeStatus::Idle, sme.status());
1740
1741 let bss_description = fake_fidl_bss_description!(Wep, ssid: Ssid::try_from("foo").unwrap());
1743 let req =
1744 connect_req(Ssid::try_from("foo").unwrap(), bss_description, authentication_wep40());
1745 let _recv = sme.on_connect_command(req);
1746 assert_eq!(ClientSmeStatus::Connecting(Ssid::try_from("foo").unwrap()), sme.status());
1747
1748 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Connect(..))));
1749 }
1750
1751 #[fuchsia::test(allow_stalls = false)]
1752 async fn test_scheduled_scan_session_events() {
1753 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
1754
1755 let req = fidl_common::ScheduledScanRequest { ..Default::default() };
1756
1757 let (receiver, mut session_event_stream) = sme.on_start_scheduled_scan_command(req.clone());
1758
1759 assert_matches!(
1760 mlme_stream.try_next(),
1761 Ok(Some(MlmeRequest::StartScheduledScan(fidl_mlme::MlmeStartScheduledScanRequest { txn_id: id, req: _ }, responder))) => {
1762 assert_eq!(id, 1);
1763 responder.respond(Ok(()));
1764 }
1765 );
1766
1767 let result = receiver.await.expect("receiver failed");
1768 assert!(result.is_ok());
1769
1770 let bss = fake_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap());
1771 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScanResult {
1772 result: fidl_mlme::ScanResult { txn_id: 1, timestamp_nanos: 1000, bss: bss.into() },
1773 });
1774
1775 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScheduledScanMatchesAvailable { txn_id: 1 });
1776
1777 assert_matches!(
1778 session_event_stream.try_next(),
1779 Ok(Some(scan_results)) => {
1780 let results = wlan_common::scan::read_vmo(scan_results).unwrap();
1781 assert_eq!(results.len(), 1);
1782 let parsed_bss = wlan_common::bss::BssDescription::try_from(results[0].bss_description.clone()).unwrap();
1783 assert_eq!(parsed_bss.ssid, Ssid::try_from("foo").unwrap());
1784 }
1785 );
1786
1787 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScheduledScanStoppedByFirmware { txn_id: 1 });
1788
1789 assert_matches!(session_event_stream.try_next(), Ok(None));
1790 }
1791
1792 #[fuchsia::test(allow_stalls = false)]
1793 async fn test_concurrent_scheduled_scan_sessions() {
1794 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
1795 let req = fidl_common::ScheduledScanRequest { ..Default::default() };
1796
1797 let (receiver1, mut session_event_stream1) =
1799 sme.on_start_scheduled_scan_command(req.clone());
1800
1801 assert_matches!(
1802 mlme_stream.try_next(),
1803 Ok(Some(MlmeRequest::StartScheduledScan(fidl_mlme::MlmeStartScheduledScanRequest { txn_id: id, req: _ }, responder))) => {
1804 assert_eq!(id, 1);
1805 assert_eq!(session_event_stream1.txn_id, 1);
1806 responder.respond(Ok(()));
1807 }
1808 );
1809 let _ = receiver1.await.unwrap();
1810
1811 let (receiver2, mut session_event_stream2) =
1813 sme.on_start_scheduled_scan_command(req.clone());
1814
1815 assert_matches!(
1816 mlme_stream.try_next(),
1817 Ok(Some(MlmeRequest::StartScheduledScan(fidl_mlme::MlmeStartScheduledScanRequest { txn_id: id, req: _ }, responder))) => {
1818 assert_eq!(id, 2);
1819 assert_eq!(session_event_stream2.txn_id, 2);
1820 responder.respond(Ok(()));
1821 }
1822 );
1823 let _ = receiver2.await.unwrap();
1824
1825 let bss1 = fake_bss_description!(Open, ssid: Ssid::try_from("session1").unwrap());
1827 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScanResult {
1828 result: fidl_mlme::ScanResult { txn_id: 1, timestamp_nanos: 1000, bss: bss1.into() },
1829 });
1830 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScheduledScanMatchesAvailable { txn_id: 1 });
1831
1832 assert_matches!(
1834 session_event_stream1.try_next(),
1835 Ok(Some(scan_results)) => {
1836 let results = wlan_common::scan::read_vmo(scan_results).unwrap();
1837 assert_eq!(results.len(), 1);
1838 let parsed_bss = wlan_common::bss::BssDescription::try_from(results[0].bss_description.clone()).unwrap();
1839 assert_eq!(parsed_bss.ssid, Ssid::try_from("session1").unwrap());
1840 }
1841 );
1842
1843 assert_matches!(session_event_stream2.try_next(), Err(_));
1845
1846 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScheduledScanStoppedByFirmware { txn_id: 2 });
1848
1849 assert_matches!(session_event_stream2.try_next(), Ok(None));
1850
1851 assert!(sme.scan_sched.scheduled_scan_receivers.contains_key(&1));
1853 }
1854
1855 #[fuchsia::test(allow_stalls = false)]
1856 async fn connecting_to_wep_network_unsupported() {
1857 let (mut sme, mut _mlme_stream, _time_stream) = create_sme().await;
1858 assert_eq!(ClientSmeStatus::Idle, sme.status());
1859
1860 let bss_description = fake_fidl_bss_description!(Wep, ssid: Ssid::try_from("foo").unwrap());
1862 let req =
1863 connect_req(Ssid::try_from("foo").unwrap(), bss_description, authentication_wep40());
1864 let mut _connect_fut = sme.on_connect_command(req);
1865 assert_eq!(ClientSmeStatus::Idle, sme.state.as_ref().unwrap().status());
1866 }
1867
1868 #[fuchsia::test(allow_stalls = false)]
1869 async fn connecting_password_supplied_for_protected_network() {
1870 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
1871 assert_eq!(ClientSmeStatus::Idle, sme.status());
1872
1873 let bss_description =
1875 fake_fidl_bss_description!(Wpa2, ssid: Ssid::try_from("foo").unwrap());
1876 let req = connect_req(
1877 Ssid::try_from("foo").unwrap(),
1878 bss_description,
1879 authentication_wpa2_personal_passphrase(),
1880 );
1881 let _recv = sme.on_connect_command(req);
1882 assert_eq!(ClientSmeStatus::Connecting(Ssid::try_from("foo").unwrap()), sme.status());
1883
1884 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Connect(..))));
1885 }
1886
1887 #[fuchsia::test(allow_stalls = false)]
1888 async fn connecting_psk_supplied_for_protected_network() {
1889 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
1890 assert_eq!(ClientSmeStatus::Idle, sme.status());
1891
1892 let bss_description =
1894 fake_fidl_bss_description!(Wpa2, ssid: Ssid::try_from("IEEE").unwrap());
1895 let req = connect_req(
1896 Ssid::try_from("IEEE").unwrap(),
1897 bss_description,
1898 authentication_wpa2_personal_psk(),
1899 );
1900 let _recv = sme.on_connect_command(req);
1901 assert_eq!(ClientSmeStatus::Connecting(Ssid::try_from("IEEE").unwrap()), sme.status());
1902
1903 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Connect(..))));
1904 }
1905
1906 #[fuchsia::test(allow_stalls = false)]
1907 async fn connecting_password_supplied_for_unprotected_network() {
1908 let (mut sme, mut _mlme_stream, _time_stream) = create_sme().await;
1909 assert_eq!(ClientSmeStatus::Idle, sme.status());
1910
1911 let bss_description =
1912 fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap());
1913 let req = connect_req(
1914 Ssid::try_from("foo").unwrap(),
1915 bss_description,
1916 authentication_wpa2_personal_passphrase(),
1917 );
1918 let mut connect_txn_stream = sme.on_connect_command(req);
1919 assert_eq!(ClientSmeStatus::Idle, sme.status());
1920
1921 assert_matches!(
1923 connect_txn_stream.try_next(),
1924 Ok(Some(ConnectTransactionEvent::OnConnectResult { result, is_reconnect: false })) => {
1925 assert_eq!(result, SelectNetworkFailure::IncompatibleConnectRequest.into());
1926 }
1927 );
1928 }
1929
1930 #[fuchsia::test(allow_stalls = false)]
1931 async fn connecting_psk_supplied_for_unprotected_network() {
1932 let (mut sme, mut _mlme_stream, _time_stream) = create_sme().await;
1933 assert_eq!(ClientSmeStatus::Idle, sme.status());
1934
1935 let bss_description =
1936 fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap());
1937 let req = connect_req(
1938 Ssid::try_from("foo").unwrap(),
1939 bss_description,
1940 authentication_wpa2_personal_psk(),
1941 );
1942 let mut connect_txn_stream = sme.on_connect_command(req);
1943 assert_eq!(ClientSmeStatus::Idle, sme.state.as_ref().unwrap().status());
1944
1945 assert_matches!(
1947 connect_txn_stream.try_next(),
1948 Ok(Some(ConnectTransactionEvent::OnConnectResult { result, is_reconnect: false })) => {
1949 assert_eq!(result, SelectNetworkFailure::IncompatibleConnectRequest.into());
1950 }
1951 );
1952 }
1953
1954 #[fuchsia::test(allow_stalls = false)]
1955 async fn connecting_no_password_supplied_for_protected_network() {
1956 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
1957 assert_eq!(ClientSmeStatus::Idle, sme.status());
1958
1959 let bss_description =
1960 fake_fidl_bss_description!(Wpa2, ssid: Ssid::try_from("foo").unwrap());
1961 let req =
1962 connect_req(Ssid::try_from("foo").unwrap(), bss_description, authentication_open());
1963 let mut connect_txn_stream = sme.on_connect_command(req);
1964 assert_eq!(ClientSmeStatus::Idle, sme.state.as_ref().unwrap().status());
1965
1966 assert_no_connect(&mut mlme_stream);
1968
1969 assert_matches!(
1971 connect_txn_stream.try_next(),
1972 Ok(Some(ConnectTransactionEvent::OnConnectResult { result, is_reconnect: false })) => {
1973 assert_eq!(result, SelectNetworkFailure::IncompatibleConnectRequest.into());
1974 }
1975 );
1976 }
1977
1978 #[fuchsia::test(allow_stalls = false)]
1979 async fn connecting_bypass_join_scan_open() {
1980 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
1981 assert_eq!(ClientSmeStatus::Idle, sme.status());
1982
1983 let bss_description =
1984 fake_fidl_bss_description!(Open, ssid: Ssid::try_from("bssname").unwrap());
1985 let req =
1986 connect_req(Ssid::try_from("bssname").unwrap(), bss_description, authentication_open());
1987 let mut connect_txn_stream = sme.on_connect_command(req);
1988
1989 assert_eq!(ClientSmeStatus::Connecting(Ssid::try_from("bssname").unwrap()), sme.status());
1990 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Connect(..))));
1991 assert_matches!(connect_txn_stream.try_next(), Err(_));
1993 }
1994
1995 #[fuchsia::test(allow_stalls = false)]
1996 async fn connecting_bypass_join_scan_protected() {
1997 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
1998 assert_eq!(ClientSmeStatus::Idle, sme.status());
1999
2000 let bss_description =
2001 fake_fidl_bss_description!(Wpa2, ssid: Ssid::try_from("bssname").unwrap());
2002 let req = connect_req(
2003 Ssid::try_from("bssname").unwrap(),
2004 bss_description,
2005 authentication_wpa2_personal_passphrase(),
2006 );
2007 let mut connect_txn_stream = sme.on_connect_command(req);
2008
2009 assert_eq!(ClientSmeStatus::Connecting(Ssid::try_from("bssname").unwrap()), sme.status());
2010 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Connect(..))));
2011 assert_matches!(connect_txn_stream.try_next(), Err(_));
2013 }
2014
2015 #[fuchsia::test(allow_stalls = false)]
2016 async fn connecting_bypass_join_scan_mismatched_credential() {
2017 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
2018 assert_eq!(ClientSmeStatus::Idle, sme.status());
2019
2020 let bss_description =
2021 fake_fidl_bss_description!(Wpa2, ssid: Ssid::try_from("bssname").unwrap());
2022 let req =
2023 connect_req(Ssid::try_from("bssname").unwrap(), bss_description, authentication_open());
2024 let mut connect_txn_stream = sme.on_connect_command(req);
2025
2026 assert_eq!(ClientSmeStatus::Idle, sme.status());
2027 assert_no_connect(&mut mlme_stream);
2028
2029 assert_matches!(
2031 connect_txn_stream.try_next(),
2032 Ok(Some(ConnectTransactionEvent::OnConnectResult { result, is_reconnect: false })) => {
2033 assert_eq!(result, SelectNetworkFailure::IncompatibleConnectRequest.into());
2034 }
2035 );
2036 }
2037
2038 #[fuchsia::test(allow_stalls = false)]
2039 async fn connecting_bypass_join_scan_unsupported_bss() {
2040 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
2041 assert_eq!(ClientSmeStatus::Idle, sme.status());
2042
2043 let bss_description =
2044 fake_fidl_bss_description!(Wpa3Enterprise, ssid: Ssid::try_from("bssname").unwrap());
2045 let req = connect_req(
2046 Ssid::try_from("bssname").unwrap(),
2047 bss_description,
2048 authentication_wpa3_personal_passphrase(),
2049 );
2050 let mut connect_txn_stream = sme.on_connect_command(req);
2051
2052 assert_eq!(ClientSmeStatus::Idle, sme.status());
2053 assert_no_connect(&mut mlme_stream);
2054
2055 assert_matches!(
2057 connect_txn_stream.try_next(),
2058 Ok(Some(ConnectTransactionEvent::OnConnectResult { result, is_reconnect: false })) => {
2059 assert_eq!(result, SelectNetworkFailure::IncompatibleConnectRequest.into());
2060 }
2061 );
2062 }
2063
2064 #[fuchsia::test(allow_stalls = false)]
2065 async fn connecting_right_credential_type_no_privacy() {
2066 let (mut sme, _mlme_stream, _time_stream) = create_sme().await;
2067
2068 let bss_description = fake_fidl_bss_description!(
2069 Wpa2,
2070 ssid: Ssid::try_from("foo").unwrap(),
2071 );
2072 let bss_description = fidl_ieee80211::BssDescription {
2075 capability_info: wlan_common::mac::CapabilityInfo(bss_description.capability_info)
2076 .with_privacy(false)
2077 .0,
2078 ..bss_description
2079 };
2080 let mut connect_txn_stream = sme.on_connect_command(connect_req(
2081 Ssid::try_from("foo").unwrap(),
2082 bss_description,
2083 authentication_wpa2_personal_passphrase(),
2084 ));
2085
2086 assert_matches!(
2087 connect_txn_stream.try_next(),
2088 Ok(Some(ConnectTransactionEvent::OnConnectResult { result, is_reconnect: false })) => {
2089 assert_eq!(result, SelectNetworkFailure::IncompatibleConnectRequest.into());
2090 }
2091 );
2092 }
2093
2094 #[fuchsia::test(allow_stalls = false)]
2095 async fn connecting_mismatched_security_protocol() {
2096 let (mut sme, _mlme_stream, _time_stream) = create_sme().await;
2097
2098 let bss_description =
2099 fake_fidl_bss_description!(Wpa2, ssid: Ssid::try_from("wpa2").unwrap());
2100 let mut connect_txn_stream = sme.on_connect_command(connect_req(
2101 Ssid::try_from("wpa2").unwrap(),
2102 bss_description,
2103 authentication_wep40(),
2104 ));
2105 assert_matches!(
2106 connect_txn_stream.try_next(),
2107 Ok(Some(ConnectTransactionEvent::OnConnectResult { result, is_reconnect: false })) => {
2108 assert_eq!(result, SelectNetworkFailure::IncompatibleConnectRequest.into());
2109 }
2110 );
2111
2112 let bss_description =
2113 fake_fidl_bss_description!(Wpa2, ssid: Ssid::try_from("wpa2").unwrap());
2114 let mut connect_txn_stream = sme.on_connect_command(connect_req(
2115 Ssid::try_from("wpa2").unwrap(),
2116 bss_description,
2117 authentication_wpa1_passphrase(),
2118 ));
2119 assert_matches!(
2120 connect_txn_stream.try_next(),
2121 Ok(Some(ConnectTransactionEvent::OnConnectResult { result, is_reconnect: false })) => {
2122 assert_eq!(result, SelectNetworkFailure::IncompatibleConnectRequest.into());
2123 }
2124 );
2125
2126 let bss_description =
2127 fake_fidl_bss_description!(Wpa3, ssid: Ssid::try_from("wpa3").unwrap());
2128 let mut connect_txn_stream = sme.on_connect_command(connect_req(
2129 Ssid::try_from("wpa3").unwrap(),
2130 bss_description,
2131 authentication_wpa2_personal_passphrase(),
2132 ));
2133 assert_matches!(
2134 connect_txn_stream.try_next(),
2135 Ok(Some(ConnectTransactionEvent::OnConnectResult { result, is_reconnect: false })) => {
2136 assert_eq!(result, SelectNetworkFailure::IncompatibleConnectRequest.into());
2137 }
2138 );
2139 }
2140
2141 #[fuchsia::test(allow_stalls = false, logging = false)]
2143 async fn connecting_right_credential_type_but_short_password() {
2144 let (mut sme, _mlme_stream, _time_stream) = create_sme().await;
2145
2146 let bss_description =
2147 fake_fidl_bss_description!(Wpa2, ssid: Ssid::try_from("foo").unwrap());
2148 let mut connect_txn_stream = sme.on_connect_command(connect_req(
2149 Ssid::try_from("foo").unwrap(),
2150 bss_description.clone(),
2151 fidl_internal::Authentication {
2152 protocol: fidl_internal::Protocol::Wpa2Personal,
2153 credentials: Some(Box::new(fidl_internal::Credentials::Wpa(
2154 fidl_internal::WpaCredentials::Passphrase(b"nope".as_slice().into()),
2155 ))),
2156 },
2157 ));
2158 report_fake_scan_result(
2159 &mut sme,
2160 zx::MonotonicInstant::get().into_nanos(),
2161 bss_description,
2162 );
2163
2164 assert_matches!(
2165 connect_txn_stream.try_next(),
2166 Ok(Some(ConnectTransactionEvent::OnConnectResult { result, is_reconnect: false })) => {
2167 assert_eq!(result, SelectNetworkFailure::IncompatibleConnectRequest.into());
2168 }
2169 );
2170 }
2171
2172 #[fuchsia::test(allow_stalls = false, logging = false)]
2174 async fn new_connect_attempt_cancels_pending_connect() {
2175 let (mut sme, _mlme_stream, _time_stream) = create_sme().await;
2176
2177 let bss_description =
2178 fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap());
2179 let req = connect_req(
2180 Ssid::try_from("foo").unwrap(),
2181 bss_description.clone(),
2182 authentication_open(),
2183 );
2184 let mut connect_txn_stream1 = sme.on_connect_command(req);
2185
2186 let req2 = connect_req(
2187 Ssid::try_from("foo").unwrap(),
2188 bss_description.clone(),
2189 authentication_open(),
2190 );
2191 let mut connect_txn_stream2 = sme.on_connect_command(req2);
2192
2193 assert_matches!(
2195 connect_txn_stream1.try_next(),
2196 Ok(Some(ConnectTransactionEvent::OnConnectResult {
2197 result: ConnectResult::Canceled,
2198 is_reconnect: false
2199 }))
2200 );
2201
2202 report_fake_scan_result(
2205 &mut sme,
2206 zx::MonotonicInstant::get().into_nanos(),
2207 fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap()),
2208 );
2209
2210 let req3 = connect_req(
2211 Ssid::try_from("foo").unwrap(),
2212 bss_description.clone(),
2213 authentication_open(),
2214 );
2215 let mut _connect_fut3 = sme.on_connect_command(req3);
2216
2217 assert_matches!(
2219 connect_txn_stream2.try_next(),
2220 Ok(Some(ConnectTransactionEvent::OnConnectResult {
2221 result: ConnectResult::Canceled,
2222 is_reconnect: false
2223 }))
2224 );
2225 }
2226
2227 #[fuchsia::test(allow_stalls = false)]
2228 async fn test_simple_scan_error() {
2229 let (mut sme, _mlme_strem, _time_stream) = create_sme().await;
2230 let mut recv =
2231 sme.on_scan_command(fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {
2232 channels: vec![],
2233 }));
2234
2235 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScanEnd {
2236 end: fidl_mlme::ScanEnd {
2237 txn_id: 1,
2238 code: fidl_mlme::ScanResultCode::CanceledByDriverOrFirmware,
2239 },
2240 });
2241
2242 assert_eq!(
2243 recv.try_recv(),
2244 Ok(Some(Err(fidl_mlme::ScanResultCode::CanceledByDriverOrFirmware)))
2245 );
2246 }
2247
2248 #[fuchsia::test(allow_stalls = false)]
2249 async fn test_scan_error_after_some_results_returned() {
2250 let (mut sme, _mlme_strem, _time_stream) = create_sme().await;
2251 let mut recv =
2252 sme.on_scan_command(fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {
2253 channels: vec![],
2254 }));
2255
2256 let mut bss = fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap());
2257 bss.bssid = [3; 6];
2258 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScanResult {
2259 result: fidl_mlme::ScanResult {
2260 txn_id: 1,
2261 timestamp_nanos: zx::MonotonicInstant::get().into_nanos(),
2262 bss,
2263 },
2264 });
2265 let mut bss = fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap());
2266 bss.bssid = [4; 6];
2267 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScanResult {
2268 result: fidl_mlme::ScanResult {
2269 txn_id: 1,
2270 timestamp_nanos: zx::MonotonicInstant::get().into_nanos(),
2271 bss,
2272 },
2273 });
2274
2275 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnScanEnd {
2276 end: fidl_mlme::ScanEnd {
2277 txn_id: 1,
2278 code: fidl_mlme::ScanResultCode::CanceledByDriverOrFirmware,
2279 },
2280 });
2281
2282 assert_eq!(
2284 recv.try_recv(),
2285 Ok(Some(Err(fidl_mlme::ScanResultCode::CanceledByDriverOrFirmware)))
2286 );
2287 }
2288
2289 #[fuchsia::test(allow_stalls = false)]
2290 async fn test_scan_is_rejected_while_connecting() {
2291 let (mut sme, _mlme_strem, _time_stream) = create_sme().await;
2292
2293 let bss_description =
2295 fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap());
2296 let _recv = sme.on_connect_command(connect_req(
2297 Ssid::try_from("foo").unwrap(),
2298 bss_description,
2299 authentication_open(),
2300 ));
2301 assert_matches!(sme.status(), ClientSmeStatus::Connecting(_));
2302
2303 let mut recv =
2305 sme.on_scan_command(fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {
2306 channels: vec![],
2307 }));
2308 assert_eq!(recv.try_recv(), Ok(Some(Err(fidl_mlme::ScanResultCode::ShouldWait))));
2309 }
2310
2311 #[fuchsia::test(allow_stalls = false)]
2312 async fn test_wmm_status_success() {
2313 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
2314 let mut receiver = sme.wmm_status();
2315
2316 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::WmmStatusReq)));
2317
2318 let resp = fake_wmm_status_resp();
2319 #[allow(
2320 clippy::redundant_field_names,
2321 reason = "mass allow for https://fxbug.dev/381896734"
2322 )]
2323 sme.on_mlme_event(fidl_mlme::MlmeEvent::OnWmmStatusResp {
2324 status: zx::sys::ZX_OK,
2325 resp: resp,
2326 });
2327
2328 assert_eq!(receiver.try_recv(), Ok(Some(Ok(resp))));
2329 }
2330
2331 #[fuchsia::test(allow_stalls = false)]
2332 async fn test_wmm_status_failed() {
2333 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
2334 let mut receiver = sme.wmm_status();
2335
2336 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::WmmStatusReq)));
2337 sme.on_mlme_event(create_on_wmm_status_resp(zx::sys::ZX_ERR_IO));
2338 assert_eq!(receiver.try_recv(), Ok(Some(Err(zx::sys::ZX_ERR_IO))));
2339 }
2340
2341 #[fuchsia::test(allow_stalls = false)]
2342 async fn test_query_apf_packet_filter_support() {
2343 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
2344 let mut _receiver = sme.query_apf_packet_filter_support();
2345 assert_matches!(
2346 mlme_stream.try_next(),
2347 Ok(Some(MlmeRequest::QueryApfPacketFilterSupport(..)))
2348 );
2349 }
2350
2351 #[fuchsia::test(allow_stalls = false)]
2352 async fn test_install_apf_packet_filter() {
2353 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
2354 let program = vec![1, 2, 3];
2355 let mut _receiver = sme.install_apf_packet_filter(program.clone());
2356 let req = assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::InstallApfPacketFilter(req, ..))) => req);
2357 assert_eq!(req.program, program);
2358 }
2359
2360 #[fuchsia::test(allow_stalls = false)]
2361 async fn test_read_apf_packet_filter_data() {
2362 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
2363 let mut _receiver = sme.read_apf_packet_filter_data();
2364 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::ReadApfPacketFilterData(..))));
2365 }
2366
2367 #[fuchsia::test(allow_stalls = false)]
2368 async fn test_set_apf_packet_filter_enabled() {
2369 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
2370 let mut _receiver = sme.set_apf_packet_filter_enabled(true);
2371 let req = assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::SetApfPacketFilterEnabled(req, ..))) => req);
2372 assert!(req.enabled);
2373 }
2374
2375 #[fuchsia::test(allow_stalls = false)]
2376 async fn test_get_apf_packet_filter_enabled() {
2377 let (mut sme, mut mlme_stream, _time_stream) = create_sme().await;
2378 let mut _receiver = sme.get_apf_packet_filter_enabled();
2379 assert_matches!(
2380 mlme_stream.try_next(),
2381 Ok(Some(MlmeRequest::GetApfPacketFilterEnabled(..)))
2382 );
2383 }
2384
2385 fn assert_no_connect(mlme_stream: &mut mpsc::UnboundedReceiver<MlmeRequest>) {
2386 loop {
2387 match mlme_stream.try_next() {
2388 Ok(event) => match event {
2389 Some(MlmeRequest::Connect(..)) => {
2390 panic!("unexpected connect request sent to MLME")
2391 }
2392 None => break,
2393 _ => (),
2394 },
2395 Err(e) => {
2396 assert_eq!(e.to_string(), "receiver channel is empty");
2397 break;
2398 }
2399 }
2400 }
2401 }
2402
2403 fn connect_req(
2404 ssid: Ssid,
2405 bss_description: fidl_ieee80211::BssDescription,
2406 authentication: fidl_internal::Authentication,
2407 ) -> fidl_sme::ConnectRequest {
2408 fidl_sme::ConnectRequest {
2409 ssid: ssid.to_vec(),
2410 bss_description,
2411 multiple_bss_candidates: true,
2412 authentication,
2413 deprecated_scan_type: fidl_common::ScanType::Passive,
2414 }
2415 }
2416
2417 async fn create_sme() -> (ClientSme, MlmeStream, timer::EventStream<Event>) {
2424 let inspector = finspect::Inspector::default();
2425 let sme_root_node = inspector.root().create_child("sme");
2426 let (client_sme, _mlme_sink, mlme_stream, time_stream) = ClientSme::new(
2427 ClientConfig::default(),
2428 test_utils::fake_device_info(*CLIENT_ADDR),
2429 inspector,
2430 sme_root_node,
2431 fake_security_support(),
2432 fake_spectrum_management_support_empty(),
2433 );
2434 (client_sme, mlme_stream, time_stream)
2435 }
2436}