1mod aid;
6mod authenticator;
7mod event;
8mod remote_client;
9#[cfg(test)]
10pub mod test_utils;
11
12use event::*;
13use remote_client::*;
14
15use crate::responder::Responder;
16use crate::{MlmeRequest, MlmeSink, mlme_event_name};
17use fidl_fuchsia_wlan_common as fidl_common;
18use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
19use fidl_fuchsia_wlan_internal as fidl_internal;
20use fidl_fuchsia_wlan_mlme::{self as fidl_mlme, DeviceInfo, MlmeEvent};
21use fidl_fuchsia_wlan_sme as fidl_sme;
22use futures::channel::{mpsc, oneshot};
23use ieee80211::{MacAddr, MacAddrBytes, Ssid};
24use log::{debug, error, info, warn};
25use std::collections::HashMap;
26use wlan_common::capabilities::get_band_cap_for_channel;
27use wlan_common::channel::{Bandwidth, Channel};
28use wlan_common::ie::rsn::rsne::{RsnCapabilities, Rsne};
29use wlan_common::ie::{ChanWidthSet, SupportedRate, parse_ht_capabilities};
30use wlan_common::timer::{self, EventHandle, Timer};
31use wlan_common::{RadioConfig, mac};
32use wlan_rsn::psk;
33
34const DEFAULT_BEACON_PERIOD: u16 = 100;
35const DEFAULT_DTIM_PERIOD: u8 = 2;
36
37#[derive(Clone, Debug, PartialEq)]
38pub struct Config {
39 pub ssid: Ssid,
40 pub password: Vec<u8>,
41 pub radio_cfg: RadioConfig,
42}
43
44#[derive(Clone, Debug, PartialEq)]
46pub struct OpRadioConfig {
47 phy: fidl_ieee80211::WlanPhyType,
48 channel: Channel,
49 basic_rates: Vec<u8>,
50}
51
52struct StartingState {
53 ctx: Context,
54 ssid: Ssid,
55 rsn_cfg: Option<RsnCfg>,
56 _capabilities: mac::CapabilityInfo,
57 _rates: Vec<SupportedRate>,
58 start_responder: Responder<StartResult>,
59 stop_responders: Vec<Responder<fidl_sme::StopApResultCode>>,
60 _start_timeout: EventHandle,
61 op_radio_cfg: OpRadioConfig,
62}
63
64enum State {
65 Idle(Box<IdleState>),
66 Started(Box<StartedState>),
67 Starting(Box<StartingState>),
68 Stopping(Box<StoppingState>),
69}
70
71struct StoppingState {
72 ctx: Context,
73 stop_req: fidl_mlme::StopRequest,
74 responders: Vec<Responder<fidl_sme::StopApResultCode>>,
75 stop_timeout: Option<EventHandle>,
76}
77
78#[derive(Clone)]
79pub struct RsnCfg {
80 psk: psk::Psk,
81 rsne: Rsne,
82}
83
84struct StartedState {
85 ssid: Ssid,
86 rsn_cfg: Option<RsnCfg>,
87 clients: HashMap<MacAddr, RemoteClient>,
88 aid_map: aid::Map,
89 op_radio_cfg: OpRadioConfig,
90 ctx: Context,
91}
92
93pub struct Context {
94 device_info: DeviceInfo,
95 spectrum_management_support: fidl_common::SpectrumManagementSupport,
96 mlme_sink: MlmeSink,
97 timer: Timer<Event>,
98}
99
100pub struct ApSme {
101 state: Option<State>,
102}
103
104struct IdleState {
105 ctx: Context,
106}
107
108#[derive(Debug, PartialEq)]
109pub enum StartResult {
110 Success,
111 Canceled,
112 TimedOut,
113 InvalidArguments(String),
114 PreviousStartInProgress,
115 AlreadyStarted,
116 InternalError,
117}
118
119impl ApSme {
120 pub fn new(
121 device_info: DeviceInfo,
122 spectrum_management_support: fidl_common::SpectrumManagementSupport,
123 ) -> (Self, crate::MlmeSink, crate::MlmeStream, timer::EventStream<Event>) {
124 let (mlme_sink, mlme_stream) = mpsc::unbounded();
125 let (timer, time_stream) = timer::create_timer();
126 let sme = ApSme {
127 state: Some(State::Idle(Box::new(IdleState {
128 ctx: Context {
129 device_info,
130 spectrum_management_support,
131 mlme_sink: MlmeSink::new(mlme_sink.clone()),
132 timer,
133 },
134 }))),
135 };
136 (sme, MlmeSink::new(mlme_sink), mlme_stream, time_stream)
137 }
138
139 pub fn on_start_command(&mut self, config: Config) -> oneshot::Receiver<StartResult> {
140 let (responder, receiver) = Responder::new();
141 self.state = self.state.take().map(|state| match state {
142 State::Idle(idle_state) => {
143 let mut ctx = idle_state.ctx;
144 let op_radio_cfg = match validate_radio_cfg(
145 &ctx.device_info.bands[..],
146 &config.radio_cfg,
147 ctx.spectrum_management_support.clone(),
148 ) {
149 Err(result) => {
150 responder.respond(result);
151 return State::Idle(Box::new(IdleState { ctx }));
152 }
153 Ok(op_radio_cfg) => op_radio_cfg,
154 };
155
156 let rsn_cfg_result = create_rsn_cfg(&config.ssid, &config.password[..]);
157 let rsn_cfg = match rsn_cfg_result {
158 Err(e) => {
159 responder.respond(e);
160 return State::Idle(Box::new(IdleState { ctx }));
161 }
162 Ok(rsn_cfg) => rsn_cfg,
163 };
164
165 let capabilities =
166 mac::CapabilityInfo(ctx.device_info.softmac_hardware_capability as u16)
167 .with_ess(true)
170 .with_ibss(false)
171 .with_privacy(rsn_cfg.is_some());
175
176 let req = match create_start_request(
177 &op_radio_cfg,
178 &config.ssid,
179 rsn_cfg.as_ref(),
180 capabilities,
181 ) {
182 Ok(req) => req,
183 Err(result) => {
184 responder.respond(result);
185 return State::Idle(Box::new(IdleState { ctx }));
186 }
187 };
188
189 let rates = op_radio_cfg.basic_rates.iter().map(|r| SupportedRate(*r)).collect();
191
192 ctx.mlme_sink.send(MlmeRequest::Start(req));
193 let event = Event::Sme { event: SmeEvent::StartTimeout };
194 let start_timeout = ctx.timer.schedule(event);
195
196 State::Starting(Box::new(StartingState {
197 ctx,
198 ssid: config.ssid,
199 rsn_cfg,
200 _capabilities: capabilities,
201 _rates: rates,
202 start_responder: responder,
203 stop_responders: vec![],
204 _start_timeout: start_timeout,
205 op_radio_cfg,
206 }))
207 }
208 s @ State::Starting(_) => {
209 responder.respond(StartResult::PreviousStartInProgress);
210 s
211 }
212 s @ State::Stopping(_) => {
213 responder.respond(StartResult::Canceled);
214 s
215 }
216 s @ State::Started(_) => {
217 responder.respond(StartResult::AlreadyStarted);
218 s
219 }
220 });
221 receiver
222 }
223
224 pub fn on_stop_command(&mut self) -> oneshot::Receiver<fidl_sme::StopApResultCode> {
225 let (responder, receiver) = Responder::new();
226 self.state = self.state.take().map(|mut state| match state {
227 State::Idle(idle_state) => {
228 let mut ctx = idle_state.ctx;
229 let stop_req = fidl_mlme::StopRequest { ssid: Ssid::empty().into() };
232 let timeout = send_stop_req(&mut ctx, stop_req.clone());
233 State::Stopping(Box::new(StoppingState {
234 ctx,
235 stop_req,
236 responders: vec![responder],
237 stop_timeout: Some(timeout),
238 }))
239 }
240 State::Starting(ref mut starting_state) => {
241 starting_state.stop_responders.push(responder);
242 state
243 }
244 State::Stopping(mut state) => {
245 state.responders.push(responder);
246 state.stop_timeout = state
250 .stop_timeout
251 .or_else(|| Some(send_stop_req(&mut state.ctx, state.stop_req.clone())));
252 State::Stopping(state)
253 }
254 State::Started(mut bss) => {
255 for client_addr in bss.clients.keys() {
259 bss.ctx.mlme_sink.send(MlmeRequest::Deauthenticate(
260 fidl_mlme::DeauthenticateRequest {
261 peer_sta_address: client_addr.to_array(),
262 reason_code: fidl_ieee80211::ReasonCode::StaLeaving,
267 },
268 ));
269 }
270
271 let stop_req = fidl_mlme::StopRequest { ssid: bss.ssid.to_vec() };
272 let timeout = send_stop_req(&mut bss.ctx, stop_req.clone());
273 State::Stopping(Box::new(StoppingState {
274 ctx: bss.ctx,
275 stop_req,
276 responders: vec![responder],
277 stop_timeout: Some(timeout),
278 }))
279 }
280 });
281 receiver
282 }
283
284 pub fn get_running_ap(&self) -> Option<fidl_sme::Ap> {
285 match self.state.as_ref() {
286 Some(State::Started(bss)) => Some(fidl_sme::Ap {
287 ssid: bss.ssid.to_vec(),
288 channel: bss.op_radio_cfg.channel.primary,
289 num_clients: bss.clients.len() as u16,
290 }),
291 _ => None,
292 }
293 }
294}
295
296fn send_stop_req(ctx: &mut Context, stop_req: fidl_mlme::StopRequest) -> EventHandle {
297 let event = Event::Sme { event: SmeEvent::StopTimeout };
298 let stop_timeout = ctx.timer.schedule(event);
299 ctx.mlme_sink.send(MlmeRequest::Stop(stop_req));
300 stop_timeout
301}
302
303impl super::Station for ApSme {
304 type Event = Event;
305
306 fn on_mlme_event(&mut self, event: MlmeEvent) {
307 debug!("received MLME event: {:?}", event);
308 self.state = self.state.take().map(|state| match state {
309 State::Idle(_) => {
310 warn!("received MlmeEvent while ApSme is idle {:?}", mlme_event_name(&event));
311 state
312 }
313 State::Starting(state) => match event {
314 MlmeEvent::StartConf { resp } => handle_start_conf(resp, *state),
315 _ => {
316 warn!(
317 "received MlmeEvent while ApSme is starting {:?}",
318 mlme_event_name(&event)
319 );
320 State::Starting(state)
321 }
322 },
323 State::Stopping(mut state) => match event {
324 MlmeEvent::StopConf { resp } => match resp.result_code {
325 fidl_mlme::StopResultCode::Success
326 | fidl_mlme::StopResultCode::BssAlreadyStopped => {
327 for responder in state.responders.drain(..) {
328 responder.respond(fidl_sme::StopApResultCode::Success);
329 }
330 State::Idle(Box::new(IdleState { ctx: state.ctx }))
331 }
332 fidl_mlme::StopResultCode::InternalError => {
333 for responder in state.responders.drain(..) {
334 responder.respond(fidl_sme::StopApResultCode::InternalError);
335 }
336 state.stop_timeout = None;
337 State::Stopping(state)
338 }
339 },
340 _ => {
341 warn!(
342 "received MlmeEvent while ApSme is stopping {:?}",
343 mlme_event_name(&event)
344 );
345 State::Stopping(state)
346 }
347 },
348 State::Started(mut bss) => {
349 match event {
350 MlmeEvent::OnChannelSwitched { info } => bss.handle_channel_switch(info),
351 MlmeEvent::AuthenticateInd { ind } => bss.handle_auth_ind(ind),
352 MlmeEvent::DeauthenticateInd { ind } => {
353 bss.handle_deauth(&ind.peer_sta_address.into())
354 }
355 MlmeEvent::DeauthenticateConf { resp } => {
358 bss.handle_deauth(&resp.peer_sta_address.into())
359 }
360 MlmeEvent::AssociateInd { ind } => bss.handle_assoc_ind(ind),
361 MlmeEvent::DisassociateInd { ind } => bss.handle_disassoc_ind(ind),
362 MlmeEvent::EapolInd { ind } => bss.handle_eapol_ind(ind),
363 MlmeEvent::EapolConf { resp } => bss.handle_eapol_conf(resp),
364 _ => {
365 warn!("unsupported MlmeEvent type {:?}; ignoring", mlme_event_name(&event))
366 }
367 }
368 State::Started(bss)
369 }
370 });
371 }
372
373 fn on_timeout(&mut self, timed_event: timer::Event<Event>) {
374 self.state = self.state.take().map(|state| match state {
375 State::Idle(_) => state,
376 State::Starting(state) => match timed_event.event {
377 Event::Sme { event: SmeEvent::StartTimeout } => {
378 let StartingState { mut ctx, start_responder, stop_responders, ssid, .. } =
379 *state;
380 warn!("Timed out waiting for MLME to start");
381 start_responder.respond(StartResult::TimedOut);
382 if stop_responders.is_empty() {
383 State::Idle(Box::new(IdleState { ctx }))
384 } else {
385 let stop_req = fidl_mlme::StopRequest { ssid: ssid.to_vec() };
386 let timeout = send_stop_req(&mut ctx, stop_req.clone());
387 State::Stopping(Box::new(StoppingState {
388 ctx,
389 stop_req,
390 responders: stop_responders,
391 stop_timeout: Some(timeout),
392 }))
393 }
394 }
395 _ => State::Starting(state),
396 },
397 State::Stopping(mut state) => {
398 if let Event::Sme { event: SmeEvent::StopTimeout } = timed_event.event {
399 for responder in state.responders.drain(..) {
400 responder.respond(fidl_sme::StopApResultCode::TimedOut);
401 }
402 state.stop_timeout = None;
403 }
404 State::Stopping(state)
407 }
408 State::Started(mut bss) => {
409 bss.handle_timeout(timed_event);
410 State::Started(bss)
411 }
412 });
413 }
414}
415
416fn validate_radio_cfg(
418 bands: &[fidl_mlme::BandCapability],
419 radio_cfg: &RadioConfig,
420 spectrum_management_support: fidl_common::SpectrumManagementSupport,
421) -> Result<OpRadioConfig, StartResult> {
422 let band_cap = get_band_cap_for_channel(bands, radio_cfg.channel).map_err(|e| {
423 let e = e.context(format!(
424 "No band capabilities for channel {}: {bands:?}",
425 radio_cfg.channel.primary
426 ));
427 StartResult::InvalidArguments(format!("{e:?}"))
428 })?;
429 let channel = radio_cfg.channel;
430
431 if channel.band == fidl_ieee80211::WlanBand::FiveGhz
434 && !spectrum_management_support
435 .dfs
436 .as_ref()
437 .is_some_and(|dfs| dfs.supported.unwrap_or(false))
438 {
439 return Err(StartResult::InvalidArguments(format!(
440 "5 GHz channels not supported: {channel}"
441 )));
442 }
443
444 let phy = radio_cfg.phy;
445 match phy {
446 fidl_ieee80211::WlanPhyType::Dsss
447 | fidl_ieee80211::WlanPhyType::Hr
448 | fidl_ieee80211::WlanPhyType::Ofdm
449 | fidl_ieee80211::WlanPhyType::Erp => match channel.bandwidth {
450 Bandwidth::Cbw20 => (),
451 _ => {
452 return Err(StartResult::InvalidArguments(format!(
453 "PHY type {phy:?} not supported on channel {channel}"
454 )));
455 }
456 },
457 fidl_ieee80211::WlanPhyType::Ht => {
458 match channel.bandwidth {
459 Bandwidth::Cbw20 | Bandwidth::Cbw40 | Bandwidth::Cbw40Below => (),
460 _ => {
461 return Err(StartResult::InvalidArguments(format!(
462 "HT-mode not supported for channel {channel}"
463 )));
464 }
465 }
466
467 match band_cap.ht_cap.as_ref() {
468 None => {
469 return Err(StartResult::InvalidArguments(format!(
470 "No HT capabilities: {channel}"
471 )));
472 }
473 Some(ht_cap) => {
474 let ht_cap = parse_ht_capabilities(&ht_cap.bytes[..]).map_err(|e| {
475 error!("failed to parse HT capability bytes: {:?}", e);
476 StartResult::InternalError
477 })?;
478 let ht_cap_info = ht_cap.ht_cap_info;
479 if ht_cap_info.chan_width_set() == ChanWidthSet::TWENTY_ONLY
480 && channel.bandwidth != Bandwidth::Cbw20
481 {
482 return Err(StartResult::InvalidArguments(format!(
483 "20 MHz band capabilities does not support channel {channel}"
484 )));
485 }
486 }
487 }
488 }
489 fidl_ieee80211::WlanPhyType::Vht => {
490 match channel.bandwidth {
491 Bandwidth::Cbw160 | Bandwidth::Cbw80P80 { .. } => {
492 return Err(StartResult::InvalidArguments(format!(
493 "Supported for channel {channel} in VHT mode not available"
494 )));
495 }
496 _ => (),
497 }
498
499 if channel.band != fidl_ieee80211::WlanBand::FiveGhz {
500 return Err(StartResult::InvalidArguments(format!(
501 "VHT only supported on 5 GHz channels: {channel}"
502 )));
503 }
504
505 if band_cap.vht_cap.is_none() {
506 return Err(StartResult::InvalidArguments(format!(
507 "No VHT capabilities: {channel}"
508 )));
509 }
510 }
511 fidl_ieee80211::WlanPhyType::Dmg
512 | fidl_ieee80211::WlanPhyType::Tvht
513 | fidl_ieee80211::WlanPhyType::S1G
514 | fidl_ieee80211::WlanPhyType::Cdmg
515 | fidl_ieee80211::WlanPhyType::Cmmg
516 | fidl_ieee80211::WlanPhyType::He => {
517 return Err(StartResult::InvalidArguments(format!("Unsupported PHY type: {phy:?}")));
518 }
519 fidl_common::WlanPhyTypeUnknown!() => {
520 return Err(StartResult::InvalidArguments(format!("Unknown PHY type: {phy:?}")));
521 }
522 }
523
524 Ok(OpRadioConfig { phy, channel, basic_rates: band_cap.basic_rates.clone() })
525}
526
527#[allow(clippy::too_many_arguments, reason = "mass allow for https://fxbug.dev/381896734")]
528fn handle_start_conf(conf: fidl_mlme::StartConfirm, mut state: StartingState) -> State {
529 if state.stop_responders.is_empty() {
530 match conf.result_code {
531 fidl_mlme::StartResultCode::Success => {
532 state.start_responder.respond(StartResult::Success);
533 State::Started(Box::new(StartedState {
534 ssid: state.ssid,
535 rsn_cfg: state.rsn_cfg,
536 clients: HashMap::new(),
537 aid_map: aid::Map::default(),
538 op_radio_cfg: state.op_radio_cfg,
539 ctx: state.ctx,
540 }))
541 }
542 result_code => {
543 error!("failed to start BSS: {:?}", result_code);
544 state.start_responder.respond(StartResult::InternalError);
545 State::Idle(Box::new(IdleState { ctx: state.ctx }))
546 }
547 }
548 } else {
549 state.start_responder.respond(StartResult::Canceled);
550 let stop_req = fidl_mlme::StopRequest { ssid: state.ssid.to_vec() };
551 let timeout = send_stop_req(&mut state.ctx, stop_req.clone());
552 State::Stopping(Box::new(StoppingState {
553 ctx: state.ctx,
554 stop_req,
555 responders: state.stop_responders,
556 stop_timeout: Some(timeout),
557 }))
558 }
559}
560
561impl StartedState {
562 fn remove_client(&mut self, addr: &MacAddr) -> bool {
576 if let Some(client) = self.clients.remove(addr) {
577 if let Some(aid) = client.aid() {
578 self.aid_map.release_aid(aid);
579 }
580 true
581 } else {
582 false
583 }
584 }
585
586 fn handle_channel_switch(&mut self, info: fidl_internal::ChannelSwitchInfo) {
587 info!("Channel switch for AP {:?}", info);
588 self.op_radio_cfg.channel.primary = info.new_primary_channel.number;
589 self.op_radio_cfg.channel.band = info.new_primary_channel.band;
590
591 match Bandwidth::from_fidl(info.bandwidth, info.vht_secondary_80_channel.number) {
592 Ok(cbw) => self.op_radio_cfg.channel.bandwidth = cbw,
593 Err(e) => warn!("Invalid CBW: {}", e),
594 }
595 }
596
597 fn handle_auth_ind(&mut self, ind: fidl_mlme::AuthenticateIndication) {
598 let peer_addr: MacAddr = ind.peer_sta_address.into();
599 if self.remove_client(&peer_addr) {
600 warn!(
607 "client {} is trying to reauthenticate; removing client and starting again",
608 peer_addr
609 );
610 }
611 let mut client = RemoteClient::new(peer_addr);
612 client.handle_auth_ind(&mut self.ctx, ind.auth_type);
613 if !client.authenticated() {
614 info!("client {} was not authenticated", peer_addr);
615 return;
616 }
617
618 info!("client {} authenticated", peer_addr);
619 let _ = self.clients.insert(peer_addr, client);
620 }
621
622 fn handle_deauth(&mut self, peer_addr: &MacAddr) {
623 if !self.remove_client(peer_addr) {
624 warn!("client {} never authenticated, ignoring deauthentication request", peer_addr);
625 return;
626 }
627
628 info!("client {} deauthenticated", peer_addr);
629 }
630
631 fn handle_assoc_ind(&mut self, ind: fidl_mlme::AssociateIndication) {
632 let peer_addr: MacAddr = ind.peer_sta_address.into();
633
634 let client = match self.clients.get_mut(&peer_addr) {
635 None => {
636 warn!("client {} never authenticated, ignoring association indication", peer_addr);
637 return;
638 }
639 Some(client) => client,
640 };
641
642 client.handle_assoc_ind(
643 &mut self.ctx,
644 &mut self.aid_map,
645 ind.capability_info,
646 ind.rates.into_iter().map(SupportedRate).collect::<Vec<_>>(),
647 &self.rsn_cfg,
648 ind.rsne,
649 );
650 if !client.authenticated() {
651 warn!("client {} failed to associate and was deauthenticated", peer_addr);
652 let _ = self.remove_client(&peer_addr);
653 } else if !client.associated() {
654 warn!("client {} failed to associate but did not deauthenticate", peer_addr);
655 } else {
656 info!("client {} associated", peer_addr);
657 }
658 }
659
660 fn handle_disassoc_ind(&mut self, ind: fidl_mlme::DisassociateIndication) {
661 let peer_addr: MacAddr = ind.peer_sta_address.into();
662
663 let client = match self.clients.get_mut(&peer_addr) {
664 None => {
665 warn!(
666 "client {} never authenticated, ignoring disassociation indication",
667 peer_addr
668 );
669 return;
670 }
671 Some(client) => client,
672 };
673
674 client.handle_disassoc_ind(&mut self.ctx, &mut self.aid_map);
675 if client.associated() {
676 panic!("client {peer_addr} didn't disassociate? this should never happen!")
677 } else {
678 info!("client {} disassociated", peer_addr);
679 }
680 }
681
682 fn handle_timeout(&mut self, timed_event: timer::Event<Event>) {
683 match timed_event.event {
684 Event::Sme { .. } => (),
685 Event::Client { addr, event } => {
686 let client = match self.clients.get_mut(&addr) {
687 None => {
688 return;
689 }
690 Some(client) => client,
691 };
692
693 client.handle_timeout(&mut self.ctx, event);
694 if !client.authenticated() {
695 if !self.remove_client(&addr) {
696 error!("failed to remove client {} from AID map", addr);
697 }
698 info!("client {} lost authentication", addr);
699 }
700 }
701 }
702 }
703
704 fn handle_eapol_ind(&mut self, ind: fidl_mlme::EapolIndication) {
705 let peer_addr: MacAddr = ind.src_addr.into();
706 let client = match self.clients.get_mut(&peer_addr) {
707 None => {
708 warn!("client {} never authenticated, ignoring EAPoL indication", peer_addr);
709 return;
710 }
711 Some(client) => client,
712 };
713
714 client.handle_eapol_ind(&mut self.ctx, &ind.data[..]);
715 }
716
717 fn handle_eapol_conf(&mut self, resp: fidl_mlme::EapolConfirm) {
718 let dst_addr: MacAddr = resp.dst_addr.into();
719 let client = match self.clients.get_mut(&dst_addr) {
720 None => {
721 warn!("never sent EAPOL frame to client {}, ignoring confirm", dst_addr);
722 return;
723 }
724 Some(client) => client,
725 };
726
727 client.handle_eapol_conf(&mut self.ctx, resp.result_code);
728 }
729}
730
731fn create_rsn_cfg(ssid: &Ssid, password: &[u8]) -> Result<Option<RsnCfg>, StartResult> {
732 if password.is_empty() {
733 Ok(None)
734 } else {
735 let psk_result = psk::compute(password, ssid);
736 let psk = match psk_result {
737 Err(e) => {
738 return Err(StartResult::InvalidArguments(e.to_string()));
739 }
740 Ok(o) => o,
741 };
742
743 Ok(Some(RsnCfg { psk, rsne: Rsne::wpa2_rsne_with_caps(RsnCapabilities(0)) }))
746 }
747}
748
749fn create_start_request(
750 op_radio_cfg: &OpRadioConfig,
751 ssid: &Ssid,
752 ap_rsn: Option<&RsnCfg>,
753 capabilities: mac::CapabilityInfo,
754) -> Result<fidl_mlme::StartRequest, StartResult> {
755 let rsne_bytes = ap_rsn.as_ref().map(|RsnCfg { rsne, .. }| {
756 let mut buf = Vec::with_capacity(rsne.len());
757 if let Err(e) = rsne.write_into(&mut buf) {
758 error!("error writing RSNE into MLME-START.request: {}", e);
759 }
760 buf
761 });
762
763 let (channel_bandwidth, _vht_secondary_80_channel) = op_radio_cfg.channel.bandwidth.to_fidl();
764
765 if op_radio_cfg.basic_rates.len() > fidl_internal::MAX_ASSOC_BASIC_RATES as usize {
766 error!(
767 "Too many basic rates ({}). Max is {}.",
768 op_radio_cfg.basic_rates.len(),
769 fidl_internal::MAX_ASSOC_BASIC_RATES
770 );
771 return Err(StartResult::InternalError);
772 }
773
774 Ok(fidl_mlme::StartRequest {
775 ssid: ssid.to_vec(),
776 bss_type: fidl_ieee80211::BssType::Infrastructure,
777 beacon_period: DEFAULT_BEACON_PERIOD,
778 dtim_period: DEFAULT_DTIM_PERIOD,
779 primary: op_radio_cfg.channel.into(),
780 capability_info: capabilities.raw(),
781 rates: op_radio_cfg.basic_rates.clone(),
782 country: fidl_mlme::Country {
783 alpha2: *b"US",
785 suffix: fidl_mlme::COUNTRY_ENVIRON_ALL,
786 },
787 rsne: rsne_bytes,
788 mesh_id: vec![],
789 phy: op_radio_cfg.phy,
790 bandwidth: channel_bandwidth,
791 })
792}
793
794#[cfg(test)]
795mod tests {
796 use super::*;
797 use crate::test_utils::*;
798 use crate::{MlmeStream, Station};
799 use assert_matches::assert_matches;
800 use fidl_fuchsia_wlan_mlme as fidl_mlme;
801 use fidl_ieee80211::WlanBand::{FiveGhz, TwoGhz};
802 use std::sync::LazyLock;
803 use test_case::test_case;
804 use wlan_common::channel::Bandwidth;
805 use wlan_common::mac::Aid;
806 use wlan_common::test_utils::fake_capabilities::{
807 fake_2ghz_band_capability_ht, fake_5ghz_band_capability, fake_5ghz_band_capability_ht,
808 fake_5ghz_band_capability_vht,
809 };
810 use wlan_common::test_utils::fake_features::{
811 fake_dfs_supported, fake_spectrum_management_support_empty,
812 };
813
814 static AP_ADDR: LazyLock<MacAddr> =
815 LazyLock::new(|| [0x11, 0x22, 0x33, 0x44, 0x55, 0x66].into());
816 static CLIENT_ADDR: LazyLock<MacAddr> =
817 LazyLock::new(|| [0x7A, 0xE7, 0x76, 0xD9, 0xF2, 0x67].into());
818 static CLIENT_ADDR2: LazyLock<MacAddr> =
819 LazyLock::new(|| [0x22, 0x22, 0x22, 0x22, 0x22, 0x22].into());
820 static SSID: LazyLock<Ssid> =
821 LazyLock::new(|| Ssid::try_from([0x46, 0x55, 0x43, 0x48, 0x53, 0x49, 0x41]).unwrap());
822
823 const RSNE: &[u8] = &[
824 0x30, 0x2A, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x02, 0xa8, 0x04, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
836 0x11, 0x00, 0x0f, 0xac, 0x04, ];
838
839 fn unprotected_config() -> Config {
840 Config {
841 ssid: SSID.clone(),
842 password: vec![],
843 radio_cfg: RadioConfig::new(
844 fidl_ieee80211::WlanPhyType::Ht,
845 Bandwidth::Cbw20,
846 11,
847 TwoGhz,
848 ),
849 }
850 }
851
852 fn protected_config() -> Config {
853 Config {
854 ssid: SSID.clone(),
855 password: vec![0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68],
856 radio_cfg: RadioConfig::new(
857 fidl_ieee80211::WlanPhyType::Ht,
858 Bandwidth::Cbw20,
859 11,
860 TwoGhz,
861 ),
862 }
863 }
864
865 fn create_channel_switch_ind(channel: u8, band: fidl_ieee80211::WlanBand) -> MlmeEvent {
866 MlmeEvent::OnChannelSwitched {
867 info: fidl_internal::ChannelSwitchInfo {
868 new_primary_channel: fidl_ieee80211::ChannelNumber { band, number: channel },
869 bandwidth: fidl_ieee80211::ChannelBandwidth::Cbw20,
870 vht_secondary_80_channel: fidl_ieee80211::ChannelNumber { band, number: 0 },
871 },
872 }
873 }
874
875 #[derive(Clone, Debug)]
876 struct ValidateRadioConfigArgs {
877 bands: Vec<fidl_mlme::BandCapability>,
878 radio_cfg: RadioConfig,
879 spectrum_management_support: fidl_common::SpectrumManagementSupport,
880 }
881
882 #[test_case(false, ValidateRadioConfigArgs {
883 bands: vec![fake_2ghz_band_capability_ht()],
884 radio_cfg: RadioConfig {
885 phy: fidl_ieee80211::WlanPhyType::Ht,
886 channel: Channel::new(15, Bandwidth::Cbw20, FiveGhz),
887 },
888 spectrum_management_support: fake_spectrum_management_support_empty(),
889 }; "invalid US channel")]
890 #[test_case(false, ValidateRadioConfigArgs {
891 bands: vec![fake_5ghz_band_capability()],
892 radio_cfg: RadioConfig {
893 phy: fidl_ieee80211::WlanPhyType::Ht,
894 channel: Channel::new(36, Bandwidth::Cbw20, FiveGhz),
895 },
896 spectrum_management_support: fake_spectrum_management_support_empty(),
897 }; "5 GHz channel and no DFS support")]
898 #[test_case(false, ValidateRadioConfigArgs {
899 bands: vec![fake_2ghz_band_capability_ht()],
900 radio_cfg: RadioConfig {
901 phy: fidl_ieee80211::WlanPhyType::Dmg,
902 channel: Channel::new(1, Bandwidth::Cbw20, TwoGhz),
903 },
904 spectrum_management_support: fake_spectrum_management_support_empty(),
905 }; "DMG not supported")]
906 #[test_case(false, ValidateRadioConfigArgs {
907 bands: vec![fake_2ghz_band_capability_ht()],
908 radio_cfg: RadioConfig {
909 phy: fidl_ieee80211::WlanPhyType::Tvht,
910 channel: Channel::new(1, Bandwidth::Cbw20, TwoGhz),
911 },
912 spectrum_management_support: fake_spectrum_management_support_empty(),
913 }; "TVHT not supported")]
914 #[test_case(false, ValidateRadioConfigArgs {
915 bands: vec![fake_2ghz_band_capability_ht()],
916 radio_cfg: RadioConfig {
917 phy: fidl_ieee80211::WlanPhyType::S1G,
918 channel: Channel::new(1, Bandwidth::Cbw20, TwoGhz),
919 },
920 spectrum_management_support: fake_spectrum_management_support_empty(),
921 }; "S1G not supported")]
922 #[test_case(false, ValidateRadioConfigArgs {
923 bands: vec![fake_2ghz_band_capability_ht()],
924 radio_cfg: RadioConfig {
925 phy: fidl_ieee80211::WlanPhyType::Cdmg,
926 channel: Channel::new(1, Bandwidth::Cbw20, TwoGhz),
927 },
928 spectrum_management_support: fake_spectrum_management_support_empty(),
929 }; "CDMG not supported")]
930 #[test_case(false, ValidateRadioConfigArgs {
931 bands: vec![fake_2ghz_band_capability_ht()],
932 radio_cfg: RadioConfig {
933 phy: fidl_ieee80211::WlanPhyType::Cmmg,
934 channel: Channel::new(1, Bandwidth::Cbw20, TwoGhz),
935 },
936 spectrum_management_support: fake_spectrum_management_support_empty(),
937 }; "CMMG not supported")]
938 #[test_case(false, ValidateRadioConfigArgs {
939 bands: vec![fake_2ghz_band_capability_ht()],
940 radio_cfg: RadioConfig {
941 phy: fidl_ieee80211::WlanPhyType::He,
942 channel: Channel::new(1, Bandwidth::Cbw20, TwoGhz),
943 },
944 spectrum_management_support: fake_spectrum_management_support_empty(),
945 }; "HE not supported")]
946 #[test_case(false, ValidateRadioConfigArgs {
947 bands: vec![fake_2ghz_band_capability_ht()],
948 radio_cfg: RadioConfig {
949 phy: fidl_ieee80211::WlanPhyType::Ht,
950 channel: Channel::new(36, Bandwidth::Cbw80, FiveGhz),
951 },
952 spectrum_management_support: fake_dfs_supported(),
953 }; "invalid HT width")]
954 #[test_case(false, ValidateRadioConfigArgs {
955 bands: vec![fake_2ghz_band_capability_ht()],
956 radio_cfg: RadioConfig {
957 phy: fidl_ieee80211::WlanPhyType::Erp,
958 channel: Channel::new(1, Bandwidth::Cbw40, TwoGhz),
959 },
960 spectrum_management_support: fake_spectrum_management_support_empty(),
961 }; "non-HT greater than 20 MHz")]
962 #[test_case(false, ValidateRadioConfigArgs {
963 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_FORTY)],
964 radio_cfg: RadioConfig {
965 phy: fidl_ieee80211::WlanPhyType::Ht,
966 channel: Channel::new(36, Bandwidth::Cbw80, FiveGhz),
967 },
968 spectrum_management_support: fake_dfs_supported(),
969 }; "HT greater than 40 MHz")]
970 #[test_case(false, ValidateRadioConfigArgs {
971 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_FORTY)],
972 radio_cfg: RadioConfig {
973 phy: fidl_ieee80211::WlanPhyType::unknown(),
974 channel: Channel::new(36, Bandwidth::Cbw40, FiveGhz),
975 },
976 spectrum_management_support: fake_dfs_supported(),
977 }; "Unknown PHY type")]
978 #[test_case(false, ValidateRadioConfigArgs {
979 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_ONLY)],
980 radio_cfg: RadioConfig {
981 phy: fidl_ieee80211::WlanPhyType::Ht,
982 channel: Channel::new(44, Bandwidth::Cbw40, FiveGhz),
983 },
984 spectrum_management_support: fake_dfs_supported(),
985 }; "HT 20 MHz only")]
986 #[test_case(false, ValidateRadioConfigArgs {
987 bands: vec![fake_5ghz_band_capability()],
988 radio_cfg: RadioConfig {
989 phy: fidl_ieee80211::WlanPhyType::Ht,
990 channel: Channel::new(48, Bandwidth::Cbw40, FiveGhz),
991 },
992 spectrum_management_support: fake_dfs_supported(),
993 }; "No HT capabilities")]
994 #[test_case(false, ValidateRadioConfigArgs {
995 bands: vec![fake_5ghz_band_capability_vht()],
996 radio_cfg: RadioConfig {
997 phy: fidl_ieee80211::WlanPhyType::Vht,
998 channel: Channel::new(36, Bandwidth::Cbw160, FiveGhz),
999 },
1000 spectrum_management_support: fake_dfs_supported(),
1001 }; "160 MHz not supported")]
1002 #[test_case(false, ValidateRadioConfigArgs {
1003 bands: vec![fake_5ghz_band_capability_vht()],
1004 radio_cfg: RadioConfig {
1005 phy: fidl_ieee80211::WlanPhyType::Vht,
1006 channel: Channel::new(36, Bandwidth::Cbw80P80 { vht_secondary_80_channel: 106 }, FiveGhz),
1007 },
1008 spectrum_management_support: fake_dfs_supported(),
1009 }; "80+80 MHz not supported")]
1010 #[test_case(false, ValidateRadioConfigArgs {
1011 bands: vec![fake_2ghz_band_capability_ht()],
1012 radio_cfg: RadioConfig {
1013 phy: fidl_ieee80211::WlanPhyType::Vht,
1014 channel: Channel::new(1, Bandwidth::Cbw20, TwoGhz),
1015 },
1016 spectrum_management_support: fake_spectrum_management_support_empty(),
1017 }; "VHT 2.4 GHz not supported")]
1018 #[test_case(false, ValidateRadioConfigArgs {
1019 bands: vec![fake_5ghz_band_capability()],
1020 radio_cfg: RadioConfig {
1021 phy: fidl_ieee80211::WlanPhyType::Vht,
1022 channel: Channel::new(149, Bandwidth::Cbw80, FiveGhz),
1023 },
1024 spectrum_management_support: fake_dfs_supported(),
1025 }; "no VHT capabilities")]
1026 #[test_case(false, ValidateRadioConfigArgs {
1027 bands: vec![fake_2ghz_band_capability_ht(), fake_5ghz_band_capability_vht()],
1028 radio_cfg: RadioConfig {
1029 phy: fidl_ieee80211::WlanPhyType::Vht,
1030 channel: Channel::new(1, Bandwidth::Cbw40, TwoGhz),
1031 },
1032 spectrum_management_support: fake_spectrum_management_support_empty(),
1033 }; "no VHT capabilities on 2.4 GHz event when 5 GHz band capabilities provided")]
1034 #[test_case(false, ValidateRadioConfigArgs {
1035 bands: vec![fidl_mlme::BandCapability {
1036 primary_channels: vec![fidl_ieee80211::ChannelNumber {
1037 number: 2,
1038 band: fidl_ieee80211::WlanBand::TwoGhz,
1039 }],
1040 ..fake_2ghz_band_capability_ht()
1041 }],
1042 radio_cfg: RadioConfig {
1043 phy: fidl_ieee80211::WlanPhyType::Hr,
1044 channel: Channel::new(1, Bandwidth::Cbw40, TwoGhz),
1045 },
1046 spectrum_management_support: fake_spectrum_management_support_empty(),
1047 }; "disallow non-operating 2.4 GHz channel")]
1048 #[test_case(false, ValidateRadioConfigArgs {
1049 bands: vec![fidl_mlme::BandCapability {
1050 primary_channels: vec![fidl_ieee80211::ChannelNumber {
1051 number: 40,
1052 band: fidl_ieee80211::WlanBand::FiveGhz,
1053 }],
1054 ..fake_5ghz_band_capability_vht()
1055 }],
1056 radio_cfg: RadioConfig {
1057 phy: fidl_ieee80211::WlanPhyType::Vht,
1058 channel: Channel::new(36, Bandwidth::Cbw80, FiveGhz),
1059 },
1060 spectrum_management_support: fake_spectrum_management_support_empty(),
1061 }; "disallow non-operating 5 GHz channel")]
1062 #[test_case(true, ValidateRadioConfigArgs {
1063 bands: vec![fake_2ghz_band_capability_ht()],
1064 radio_cfg: RadioConfig {
1065 phy: fidl_ieee80211::WlanPhyType::Hr,
1066 channel: Channel::new(1, Bandwidth::Cbw20, TwoGhz),
1067 },
1068 spectrum_management_support: fake_spectrum_management_support_empty(),
1069 })]
1070 #[test_case(true, ValidateRadioConfigArgs {
1071 bands: vec![fake_2ghz_band_capability_ht()],
1072 radio_cfg: RadioConfig {
1073 phy: fidl_ieee80211::WlanPhyType::Erp,
1074 channel: Channel::new(1, Bandwidth::Cbw20, TwoGhz),
1075 },
1076 spectrum_management_support: fake_spectrum_management_support_empty(),
1077 })]
1078 #[test_case(true, ValidateRadioConfigArgs {
1079 bands: vec![fake_2ghz_band_capability_ht()],
1080 radio_cfg: RadioConfig {
1081 phy: fidl_ieee80211::WlanPhyType::Ht,
1082 channel: Channel::new(1, Bandwidth::Cbw20, TwoGhz),
1083 },
1084 spectrum_management_support: fake_spectrum_management_support_empty(),
1085 })]
1086 #[test_case(true, ValidateRadioConfigArgs {
1087 bands: vec![fake_2ghz_band_capability_ht()],
1088 radio_cfg: RadioConfig {
1089 phy: fidl_ieee80211::WlanPhyType::Ht,
1090 channel: Channel::new(1, Bandwidth::Cbw40, TwoGhz),
1091 },
1092 spectrum_management_support: fake_spectrum_management_support_empty(),
1093 })]
1094 #[test_case(true, ValidateRadioConfigArgs {
1095 bands: vec![fake_2ghz_band_capability_ht()],
1096 radio_cfg: RadioConfig {
1097 phy: fidl_ieee80211::WlanPhyType::Ht,
1098 channel: Channel::new(11, Bandwidth::Cbw40Below, TwoGhz),
1099 },
1100 spectrum_management_support: fake_spectrum_management_support_empty(),
1101 })]
1102 #[test_case(true, ValidateRadioConfigArgs {
1103 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_ONLY)],
1104 radio_cfg: RadioConfig {
1105 phy: fidl_ieee80211::WlanPhyType::Ht,
1106 channel: Channel::new(36, Bandwidth::Cbw20, FiveGhz),
1107 },
1108 spectrum_management_support: fake_dfs_supported(),
1109 })]
1110 #[test_case(true, ValidateRadioConfigArgs {
1111 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_FORTY)],
1112 radio_cfg: RadioConfig {
1113 phy: fidl_ieee80211::WlanPhyType::Ht,
1114 channel: Channel::new(36, Bandwidth::Cbw40, FiveGhz),
1115 },
1116 spectrum_management_support: fake_dfs_supported(),
1117 })]
1118 #[test_case(true, ValidateRadioConfigArgs {
1119 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_FORTY)],
1120 radio_cfg: RadioConfig {
1121 phy: fidl_ieee80211::WlanPhyType::Ht,
1122 channel: Channel::new(40, Bandwidth::Cbw40Below, FiveGhz),
1123 },
1124 spectrum_management_support: fake_dfs_supported(),
1125 })]
1126 #[test_case(true, ValidateRadioConfigArgs {
1127 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_FORTY)],
1128 radio_cfg: RadioConfig {
1129 phy: fidl_ieee80211::WlanPhyType::Ht,
1130 channel: Channel::new(36, Bandwidth::Cbw20, FiveGhz),
1131 },
1132 spectrum_management_support: fake_dfs_supported(),
1133 })]
1134 #[test_case(true, ValidateRadioConfigArgs {
1135 bands: vec![fake_5ghz_band_capability_vht()],
1136 radio_cfg: RadioConfig {
1137 phy: fidl_ieee80211::WlanPhyType::Ht,
1138 channel: Channel::new(36, Bandwidth::Cbw40, FiveGhz),
1139 },
1140 spectrum_management_support: fake_dfs_supported(),
1141 })]
1142 #[test_case(true, ValidateRadioConfigArgs {
1143 bands: vec![fake_5ghz_band_capability_vht()],
1144 radio_cfg: RadioConfig {
1145 phy: fidl_ieee80211::WlanPhyType::Ht,
1146 channel: Channel::new(40, Bandwidth::Cbw40Below, FiveGhz),
1147 },
1148 spectrum_management_support: fake_dfs_supported(),
1149 })]
1150 #[test_case(true, ValidateRadioConfigArgs {
1151 bands: vec![fake_5ghz_band_capability_vht()],
1152 radio_cfg: RadioConfig {
1153 phy: fidl_ieee80211::WlanPhyType::Vht,
1154 channel: Channel::new(36, Bandwidth::Cbw80, FiveGhz),
1155 },
1156 spectrum_management_support: fake_dfs_supported(),
1157 })]
1158 #[test_case(true, ValidateRadioConfigArgs {
1159 bands: vec![fake_2ghz_band_capability_ht(), fake_5ghz_band_capability_vht()],
1160 radio_cfg: RadioConfig {
1161 phy: fidl_ieee80211::WlanPhyType::Ht,
1162 channel: Channel::new(1, Bandwidth::Cbw40, TwoGhz),
1163 },
1164 spectrum_management_support: fake_spectrum_management_support_empty(),
1165 })]
1166 #[test_case(true, ValidateRadioConfigArgs {
1167 bands: vec![fake_2ghz_band_capability_ht(), fake_5ghz_band_capability_vht()],
1168 radio_cfg: RadioConfig {
1169 phy: fidl_ieee80211::WlanPhyType::Vht,
1170 channel: Channel::new(36, Bandwidth::Cbw80, FiveGhz),
1171 },
1172 spectrum_management_support: fake_dfs_supported(),
1173 })]
1174 fn test_validate_radio_cfg(expect_ok: bool, fn_args: ValidateRadioConfigArgs) {
1175 match validate_radio_cfg(
1176 &fn_args.bands[..],
1177 &fn_args.radio_cfg,
1178 fn_args.spectrum_management_support.clone(),
1179 ) {
1180 Ok(op_radio_cfg) => {
1181 if !expect_ok {
1182 panic!("Unexpected successful validation: {0:?}, {op_radio_cfg:?}", fn_args);
1183 }
1184 assert_matches!(
1185 op_radio_cfg,
1186 OpRadioConfig {
1187 phy,
1188 channel,
1189 basic_rates: _,
1190 } => {
1191 assert_eq!(phy, fn_args.radio_cfg.phy);
1192 assert_eq!(channel, fn_args.radio_cfg.channel);
1193 }
1194 )
1195 }
1196 Err(e @ StartResult::InvalidArguments { .. }) => {
1197 if expect_ok {
1198 panic!("Unexpected failure to validate: {0:?}, {e:?}", fn_args)
1199 }
1200 }
1201 Err(e) => panic!("Unexpected StartResult value: {0:?}, {e:?}", fn_args),
1202 }
1203 }
1204
1205 #[fuchsia::test(allow_stalls = false)]
1206 async fn authenticate_while_sme_is_idle() {
1207 let (mut sme, mut mlme_stream, _) = create_sme().await;
1208 let client = Client::default();
1209 sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1210
1211 assert_matches!(mlme_stream.try_next(), Err(e) => {
1212 assert_eq!(e.to_string(), "receiver channel is empty");
1213 });
1214 }
1215
1216 #[fuchsia::test(allow_stalls = false)]
1218 async fn status_when_sme_is_idle() {
1219 let (sme, _, _) = create_sme().await;
1220 assert_eq!(None, sme.get_running_ap());
1221 }
1222
1223 #[fuchsia::test(allow_stalls = false)]
1224 async fn ap_starts_success() {
1225 let (mut sme, mut mlme_stream, _) = create_sme().await;
1226 let mut receiver = sme.on_start_command(unprotected_config());
1227
1228 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(start_req))) => {
1229 assert_eq!(start_req.ssid, SSID.to_vec());
1230 assert_eq!(
1231 start_req.capability_info,
1232 mac::CapabilityInfo(0).with_short_preamble(true).with_ess(true).raw(),
1233 );
1234 assert_eq!(start_req.bss_type, fidl_ieee80211::BssType::Infrastructure);
1235 assert_ne!(start_req.beacon_period, 0);
1236 assert_eq!(start_req.dtim_period, DEFAULT_DTIM_PERIOD);
1237 assert_eq!(
1238 start_req.primary,
1239 unprotected_config().radio_cfg.channel.into(),
1240 );
1241 assert!(start_req.rsne.is_none());
1242 });
1243
1244 assert_eq!(Ok(None), receiver.try_recv());
1245 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1246 assert_eq!(Ok(Some(StartResult::Success)), receiver.try_recv());
1247 }
1248
1249 #[fuchsia::test(allow_stalls = false)]
1251 async fn ap_starts_success_get_running_ap() {
1252 let (mut sme, mut mlme_stream, _) = create_sme().await;
1253 let mut receiver = sme.on_start_command(unprotected_config());
1254 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(_start_req))) => {});
1255 assert_eq!(None, sme.get_running_ap());
1257 assert_eq!(Ok(None), receiver.try_recv());
1258 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1259 assert_eq!(Ok(Some(StartResult::Success)), receiver.try_recv());
1260 assert_eq!(
1261 Some(fidl_sme::Ap {
1262 ssid: SSID.to_vec(),
1263 channel: unprotected_config().radio_cfg.channel.primary,
1264 num_clients: 0,
1265 }),
1266 sme.get_running_ap()
1267 );
1268 }
1269
1270 #[fuchsia::test(allow_stalls = false)]
1272 async fn ap_check_status_after_channel_change() {
1273 let (mut sme, _, _) = start_unprotected_ap().await;
1274 assert_eq!(
1276 Some(fidl_sme::Ap {
1277 ssid: SSID.to_vec(),
1278 channel: unprotected_config().radio_cfg.channel.primary,
1279 num_clients: 0,
1280 }),
1281 sme.get_running_ap()
1282 );
1283 sme.on_mlme_event(create_channel_switch_ind(6, TwoGhz));
1284 assert_eq!(
1286 Some(fidl_sme::Ap { ssid: SSID.to_vec(), channel: 6, num_clients: 0 }),
1287 sme.get_running_ap()
1288 );
1289 }
1290
1291 #[fuchsia::test(allow_stalls = false)]
1292 async fn ap_starts_timeout() {
1293 let (mut sme, _, mut time_stream) = create_sme().await;
1294 let mut receiver = sme.on_start_command(unprotected_config());
1295
1296 let (_, event, _) = time_stream.try_next().unwrap().expect("expect timer message");
1297 sme.on_timeout(event);
1298
1299 assert_eq!(Ok(Some(StartResult::TimedOut)), receiver.try_recv());
1300 assert_eq!(None, sme.get_running_ap());
1302 }
1303
1304 #[fuchsia::test(allow_stalls = false, logging = false)]
1306 async fn ap_starts_fails() {
1307 let (mut sme, _, _) = create_sme().await;
1308 let mut receiver = sme.on_start_command(unprotected_config());
1309
1310 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::NotSupported));
1311 assert_eq!(Ok(Some(StartResult::InternalError)), receiver.try_recv());
1312 assert_eq!(None, sme.get_running_ap());
1314 }
1315
1316 #[fuchsia::test(allow_stalls = false)]
1317 async fn start_req_while_ap_is_starting() {
1318 let (mut sme, _, _) = create_sme().await;
1319 let mut receiver_one = sme.on_start_command(unprotected_config());
1320
1321 let mut receiver_two = sme.on_start_command(unprotected_config());
1323 assert_eq!(Ok(Some(StartResult::PreviousStartInProgress)), receiver_two.try_recv());
1324
1325 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1327 assert_eq!(Ok(Some(StartResult::Success)), receiver_one.try_recv());
1328 }
1329
1330 #[fuchsia::test(allow_stalls = false)]
1331 async fn start_req_while_ap_is_stopping() {
1332 let (mut sme, _, _) = start_unprotected_ap().await;
1333 let mut stop_receiver = sme.on_stop_command();
1334 let mut start_receiver = sme.on_start_command(unprotected_config());
1335 assert_eq!(Ok(None), stop_receiver.try_recv());
1336 assert_eq!(Ok(Some(StartResult::Canceled)), start_receiver.try_recv());
1337 }
1338
1339 #[fuchsia::test(allow_stalls = false)]
1340 async fn ap_stops_while_idle() {
1341 let (mut sme, mut mlme_stream, _) = create_sme().await;
1342 let mut receiver = sme.on_stop_command();
1343 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1344 assert!(stop_req.ssid.is_empty());
1345 });
1346
1347 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1349 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver.try_recv());
1350 }
1351
1352 #[fuchsia::test(allow_stalls = false)]
1353 async fn stop_req_while_ap_is_starting_then_succeeds() {
1354 let (mut sme, mut mlme_stream, _) = create_sme().await;
1355 let mut start_receiver = sme.on_start_command(unprotected_config());
1356 let mut stop_receiver = sme.on_stop_command();
1357 assert_eq!(Ok(None), start_receiver.try_recv());
1358 assert_eq!(Ok(None), stop_receiver.try_recv());
1359
1360 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(_))));
1362 assert_matches!(mlme_stream.try_next(), Err(e) => {
1363 assert_eq!(e.to_string(), "receiver channel is empty");
1364 });
1365
1366 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1368 assert_eq!(Ok(Some(StartResult::Canceled)), start_receiver.try_recv());
1369 assert_eq!(Ok(None), stop_receiver.try_recv());
1370 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1371 assert_eq!(stop_req.ssid, SSID.to_vec());
1372 });
1373
1374 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1376 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), stop_receiver.try_recv());
1377 }
1378
1379 #[fuchsia::test(allow_stalls = false)]
1380 async fn stop_req_while_ap_is_starting_then_times_out() {
1381 let (mut sme, mut mlme_stream, mut time_stream) = create_sme().await;
1382 let mut start_receiver = sme.on_start_command(unprotected_config());
1383 let mut stop_receiver = sme.on_stop_command();
1384 assert_eq!(Ok(None), start_receiver.try_recv());
1385 assert_eq!(Ok(None), stop_receiver.try_recv());
1386
1387 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(_))));
1389 assert_matches!(mlme_stream.try_next(), Err(e) => {
1390 assert_eq!(e.to_string(), "receiver channel is empty");
1391 });
1392
1393 let (_, event, _) = time_stream.try_next().unwrap().expect("expect timer message");
1395 sme.on_timeout(event);
1396 assert_eq!(Ok(Some(StartResult::TimedOut)), start_receiver.try_recv());
1397 assert_eq!(Ok(None), stop_receiver.try_recv());
1398 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1399 assert_eq!(stop_req.ssid, SSID.to_vec());
1400 });
1401
1402 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1404 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), stop_receiver.try_recv());
1405 }
1406
1407 #[fuchsia::test(allow_stalls = false)]
1408 async fn ap_stops_after_started() {
1409 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1410 let mut receiver = sme.on_stop_command();
1411
1412 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1413 assert_eq!(stop_req.ssid, SSID.to_vec());
1414 });
1415 assert_eq!(Ok(None), receiver.try_recv());
1416 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::BssAlreadyStopped));
1417 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver.try_recv());
1418 }
1419
1420 #[fuchsia::test(allow_stalls = false)]
1421 async fn ap_stops_after_started_and_deauths_all_clients() {
1422 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1423 let client = Client::default();
1424 sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1425 client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1426
1427 assert_eq!(
1429 Some(fidl_sme::Ap {
1430 ssid: SSID.to_vec(),
1431 channel: unprotected_config().radio_cfg.channel.primary,
1432 num_clients: 1,
1433 }),
1434 sme.get_running_ap()
1435 );
1436 let mut receiver = sme.on_stop_command();
1437 assert_matches!(
1438 mlme_stream.try_next(),
1439 Ok(Some(MlmeRequest::Deauthenticate(deauth_req))) => {
1440 assert_eq!(&deauth_req.peer_sta_address, client.addr.as_array());
1441 assert_eq!(deauth_req.reason_code, fidl_ieee80211::ReasonCode::StaLeaving);
1442 });
1443
1444 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1445 assert_eq!(stop_req.ssid, SSID.to_vec());
1446 });
1447 assert_eq!(Ok(None), receiver.try_recv());
1448 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1449 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver.try_recv());
1450
1451 assert_eq!(None, sme.get_running_ap());
1453 }
1454
1455 #[fuchsia::test(allow_stalls = false)]
1456 async fn ap_queues_concurrent_stop_requests() {
1457 let (mut sme, _, _) = start_unprotected_ap().await;
1458 let mut receiver1 = sme.on_stop_command();
1459 let mut receiver2 = sme.on_stop_command();
1460
1461 assert_eq!(Ok(None), receiver1.try_recv());
1462 assert_eq!(Ok(None), receiver2.try_recv());
1463
1464 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1465 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver1.try_recv());
1466 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver2.try_recv());
1467 }
1468
1469 #[fuchsia::test(allow_stalls = false)]
1470 async fn uncleaned_stopping_state() {
1471 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1472 let mut stop_receiver1 = sme.on_stop_command();
1473 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1475 assert_eq!(stop_req.ssid, SSID.to_vec());
1476 });
1477
1478 assert_eq!(Ok(None), stop_receiver1.try_recv());
1479 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::InternalError));
1480 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::InternalError)), stop_receiver1.try_recv());
1481
1482 let mut start_receiver = sme.on_start_command(unprotected_config());
1484 assert_eq!(Ok(Some(StartResult::Canceled)), start_receiver.try_recv());
1485 assert_matches!(mlme_stream.try_next(), Err(e) => {
1486 assert_eq!(e.to_string(), "receiver channel is empty");
1487 });
1488
1489 let mut stop_receiver2 = sme.on_stop_command();
1491 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1492 assert_eq!(stop_req.ssid, SSID.to_vec());
1493 });
1494
1495 assert_eq!(Ok(None), stop_receiver2.try_recv());
1497 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1498 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), stop_receiver2.try_recv());
1499 }
1500
1501 #[fuchsia::test(allow_stalls = false)]
1502 async fn client_authenticates_supported_authentication_type() {
1503 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1504 let client = Client::default();
1505 sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1506 client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1507 }
1508
1509 #[fuchsia::test(allow_stalls = false, logging = false)]
1511 async fn client_authenticates_unsupported_authentication_type() {
1512 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1513 let client = Client::default();
1514 let auth_ind = client.create_auth_ind(fidl_mlme::AuthenticationTypes::FastBssTransition);
1515 sme.on_mlme_event(auth_ind);
1516 client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Refused);
1517 }
1518
1519 #[fuchsia::test(allow_stalls = false)]
1520 async fn client_associates_unprotected_network() {
1521 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1522 let client = Client::default();
1523 sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1524 client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1525
1526 sme.on_mlme_event(client.create_assoc_ind(None));
1527 client.verify_assoc_resp(
1528 &mut mlme_stream,
1529 1,
1530 fidl_mlme::AssociateResultCode::Success,
1531 false,
1532 );
1533 }
1534
1535 #[fuchsia::test(allow_stalls = false)]
1536 async fn client_associates_valid_rsne() {
1537 let (mut sme, mut mlme_stream, _) = start_protected_ap().await;
1538 let client = Client::default();
1539 client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1540
1541 sme.on_mlme_event(client.create_assoc_ind(Some(RSNE.to_vec())));
1542 client.verify_assoc_resp(
1543 &mut mlme_stream,
1544 1,
1545 fidl_mlme::AssociateResultCode::Success,
1546 true,
1547 );
1548 client.verify_eapol_req(&mut mlme_stream);
1549 }
1550
1551 #[fuchsia::test(allow_stalls = false, logging = false)]
1553 async fn client_associates_invalid_rsne() {
1554 let (mut sme, mut mlme_stream, _) = start_protected_ap().await;
1555 let client = Client::default();
1556 client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1557
1558 sme.on_mlme_event(client.create_assoc_ind(None));
1559 client.verify_refused_assoc_resp(
1560 &mut mlme_stream,
1561 fidl_mlme::AssociateResultCode::RefusedCapabilitiesMismatch,
1562 );
1563 }
1564
1565 #[fuchsia::test(allow_stalls = false)]
1566 async fn rsn_handshake_timeout() {
1567 let (mut sme, mut mlme_stream, mut time_stream) = start_protected_ap().await;
1568 let client = Client::default();
1569 client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1570
1571 assert_matches!(time_stream.try_next(), Ok(Some(_)));
1573
1574 sme.on_mlme_event(client.create_assoc_ind(Some(RSNE.to_vec())));
1575 client.verify_assoc_resp(
1576 &mut mlme_stream,
1577 1,
1578 fidl_mlme::AssociateResultCode::Success,
1579 true,
1580 );
1581
1582 assert_matches!(time_stream.try_next(), Ok(Some(_)));
1584
1585 for _i in 0..4 {
1586 client.verify_eapol_req(&mut mlme_stream);
1587 let (_, event, _) = time_stream.try_next().unwrap().expect("expect timer message");
1588 sme.on_timeout(event);
1589 }
1590
1591 client.verify_deauth_req(
1592 &mut mlme_stream,
1593 fidl_ieee80211::ReasonCode::FourwayHandshakeTimeout,
1594 );
1595 }
1596
1597 #[fuchsia::test(allow_stalls = false)]
1598 async fn client_restarts_authentication_flow() {
1599 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1600 let client = Client::default();
1601 client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1602 client.associate_and_drain_mlme(&mut sme, &mut mlme_stream, None);
1603
1604 sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1605 client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1606
1607 sme.on_mlme_event(client.create_assoc_ind(None));
1608 client.verify_assoc_resp(
1609 &mut mlme_stream,
1610 1,
1611 fidl_mlme::AssociateResultCode::Success,
1612 false,
1613 );
1614 }
1615
1616 #[fuchsia::test(allow_stalls = false)]
1617 async fn multiple_clients_associate() {
1618 let (mut sme, mut mlme_stream, _) = start_protected_ap().await;
1619 let client1 = Client::default();
1620 let client2 = Client { addr: *CLIENT_ADDR2 };
1621
1622 sme.on_mlme_event(client1.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1623 client1.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1624
1625 sme.on_mlme_event(client2.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1626 client2.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1627
1628 sme.on_mlme_event(client1.create_assoc_ind(Some(RSNE.to_vec())));
1629 client1.verify_assoc_resp(
1630 &mut mlme_stream,
1631 1,
1632 fidl_mlme::AssociateResultCode::Success,
1633 true,
1634 );
1635 client1.verify_eapol_req(&mut mlme_stream);
1636
1637 sme.on_mlme_event(client2.create_assoc_ind(Some(RSNE.to_vec())));
1638 client2.verify_assoc_resp(
1639 &mut mlme_stream,
1640 2,
1641 fidl_mlme::AssociateResultCode::Success,
1642 true,
1643 );
1644 client2.verify_eapol_req(&mut mlme_stream);
1645 }
1646
1647 fn create_start_conf(result_code: fidl_mlme::StartResultCode) -> MlmeEvent {
1648 MlmeEvent::StartConf { resp: fidl_mlme::StartConfirm { result_code } }
1649 }
1650
1651 fn create_stop_conf(result_code: fidl_mlme::StopResultCode) -> MlmeEvent {
1652 MlmeEvent::StopConf { resp: fidl_mlme::StopConfirm { result_code } }
1653 }
1654
1655 struct Client {
1656 addr: MacAddr,
1657 }
1658
1659 impl Client {
1660 fn default() -> Self {
1661 Client { addr: *CLIENT_ADDR }
1662 }
1663
1664 fn authenticate_and_drain_mlme(
1665 &self,
1666 sme: &mut ApSme,
1667 mlme_stream: &mut crate::MlmeStream,
1668 ) {
1669 sme.on_mlme_event(self.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1670 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::AuthResponse(..))));
1671 }
1672
1673 fn associate_and_drain_mlme(
1674 &self,
1675 sme: &mut ApSme,
1676 mlme_stream: &mut crate::MlmeStream,
1677 rsne: Option<Vec<u8>>,
1678 ) {
1679 sme.on_mlme_event(self.create_assoc_ind(rsne));
1680 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::AssocResponse(..))));
1681 }
1682
1683 fn create_auth_ind(&self, auth_type: fidl_mlme::AuthenticationTypes) -> MlmeEvent {
1684 MlmeEvent::AuthenticateInd {
1685 ind: fidl_mlme::AuthenticateIndication {
1686 peer_sta_address: self.addr.to_array(),
1687 auth_type,
1688 },
1689 }
1690 }
1691
1692 fn create_assoc_ind(&self, rsne: Option<Vec<u8>>) -> MlmeEvent {
1693 MlmeEvent::AssociateInd {
1694 ind: fidl_mlme::AssociateIndication {
1695 peer_sta_address: self.addr.to_array(),
1696 listen_interval: 100,
1697 ssid: Some(SSID.to_vec()),
1698 rsne,
1699 capability_info: mac::CapabilityInfo(0).with_short_preamble(true).raw(),
1700 rates: vec![
1701 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
1702 ],
1703 },
1704 }
1705 }
1706
1707 fn verify_auth_resp(
1708 &self,
1709 mlme_stream: &mut MlmeStream,
1710 result_code: fidl_mlme::AuthenticateResultCode,
1711 ) {
1712 let msg = mlme_stream.try_next();
1713 assert_matches!(msg, Ok(Some(MlmeRequest::AuthResponse(auth_resp))) => {
1714 assert_eq!(&auth_resp.peer_sta_address, self.addr.as_array());
1715 assert_eq!(auth_resp.result_code, result_code);
1716 });
1717 }
1718
1719 fn verify_assoc_resp(
1720 &self,
1721 mlme_stream: &mut MlmeStream,
1722 aid: Aid,
1723 result_code: fidl_mlme::AssociateResultCode,
1724 privacy: bool,
1725 ) {
1726 let msg = mlme_stream.try_next();
1727 assert_matches!(msg, Ok(Some(MlmeRequest::AssocResponse(assoc_resp))) => {
1728 assert_eq!(&assoc_resp.peer_sta_address, self.addr.as_array());
1729 assert_eq!(assoc_resp.association_id, aid);
1730 assert_eq!(assoc_resp.result_code, result_code);
1731 assert_eq!(
1732 assoc_resp.capability_info,
1733 mac::CapabilityInfo(0).with_short_preamble(true).with_privacy(privacy).raw(),
1734 );
1735 });
1736 }
1737
1738 fn verify_refused_assoc_resp(
1739 &self,
1740 mlme_stream: &mut MlmeStream,
1741 result_code: fidl_mlme::AssociateResultCode,
1742 ) {
1743 let msg = mlme_stream.try_next();
1744 assert_matches!(msg, Ok(Some(MlmeRequest::AssocResponse(assoc_resp))) => {
1745 assert_eq!(&assoc_resp.peer_sta_address, self.addr.as_array());
1746 assert_eq!(assoc_resp.association_id, 0);
1747 assert_eq!(assoc_resp.result_code, result_code);
1748 assert_eq!(assoc_resp.capability_info, 0);
1749 });
1750 }
1751
1752 fn verify_eapol_req(&self, mlme_stream: &mut MlmeStream) {
1753 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Eapol(eapol_req))) => {
1754 assert_eq!(&eapol_req.src_addr, AP_ADDR.as_array());
1755 assert_eq!(&eapol_req.dst_addr, self.addr.as_array());
1756 assert!(!eapol_req.data.is_empty());
1757 });
1758 }
1759
1760 fn verify_deauth_req(
1761 &self,
1762 mlme_stream: &mut MlmeStream,
1763 reason_code: fidl_ieee80211::ReasonCode,
1764 ) {
1765 let msg = mlme_stream.try_next();
1766 assert_matches!(msg, Ok(Some(MlmeRequest::Deauthenticate(deauth_req))) => {
1767 assert_eq!(&deauth_req.peer_sta_address, self.addr.as_array());
1768 assert_eq!(deauth_req.reason_code, reason_code);
1769 });
1770 }
1771 }
1772
1773 async fn start_protected_ap() -> (ApSme, crate::MlmeStream, timer::EventStream<Event>) {
1777 start_ap(true).await
1778 }
1779
1780 async fn start_unprotected_ap() -> (ApSme, crate::MlmeStream, timer::EventStream<Event>) {
1784 start_ap(false).await
1785 }
1786
1787 async fn start_ap(protected: bool) -> (ApSme, crate::MlmeStream, timer::EventStream<Event>) {
1791 let (mut sme, mut mlme_stream, mut time_stream) = create_sme().await;
1792 let config = if protected { protected_config() } else { unprotected_config() };
1793 let mut receiver = sme.on_start_command(config);
1794 assert_eq!(Ok(None), receiver.try_recv());
1795 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(..))));
1796 while time_stream.try_next().is_ok() {}
1798 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1799
1800 assert_eq!(Ok(Some(StartResult::Success)), receiver.try_recv());
1801 (sme, mlme_stream, time_stream)
1802 }
1803
1804 async fn create_sme() -> (ApSme, MlmeStream, timer::EventStream<Event>) {
1808 let (ap_sme, _mlme_sink, mlme_stream, time_stream) =
1809 ApSme::new(fake_device_info(*AP_ADDR), fake_spectrum_management_support_empty());
1810 (ap_sme, mlme_stream, time_stream)
1811 }
1812}