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_mlme::{self as fidl_mlme, DeviceInfo, MlmeEvent};
18use futures::channel::{mpsc, oneshot};
19use ieee80211::{MacAddr, MacAddrBytes, Ssid};
20use log::{debug, error, info, warn};
21use std::collections::HashMap;
22use wlan_common::capabilities::get_band_cap_for_channel;
23use wlan_common::channel::{Cbw, Channel};
24use wlan_common::ie::rsn::rsne::{RsnCapabilities, Rsne};
25use wlan_common::ie::{ChanWidthSet, SupportedRate, parse_ht_capabilities};
26use wlan_common::timer::{self, EventHandle, Timer};
27use wlan_common::{RadioConfig, mac};
28use wlan_rsn::psk;
29use {
30 fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
31 fidl_fuchsia_wlan_internal as fidl_internal, fidl_fuchsia_wlan_sme as fidl_sme,
32};
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_common::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.is_5ghz()
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_common::WlanPhyType::Dsss
447 | fidl_common::WlanPhyType::Hr
448 | fidl_common::WlanPhyType::Ofdm
449 | fidl_common::WlanPhyType::Erp => match channel.cbw {
450 Cbw::Cbw20 => (),
451 _ => {
452 return Err(StartResult::InvalidArguments(format!(
453 "PHY type {phy:?} not supported on channel {channel}"
454 )));
455 }
456 },
457 fidl_common::WlanPhyType::Ht => {
458 match channel.cbw {
459 Cbw::Cbw20 | Cbw::Cbw40 | Cbw::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.cbw != Cbw::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_common::WlanPhyType::Vht => {
490 match channel.cbw {
491 Cbw::Cbw160 | Cbw::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.is_5ghz() {
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_common::WlanPhyType::Dmg
512 | fidl_common::WlanPhyType::Tvht
513 | fidl_common::WlanPhyType::S1G
514 | fidl_common::WlanPhyType::Cdmg
515 | fidl_common::WlanPhyType::Cmmg
516 | fidl_common::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_channel;
589 }
590
591 fn handle_auth_ind(&mut self, ind: fidl_mlme::AuthenticateIndication) {
592 let peer_addr: MacAddr = ind.peer_sta_address.into();
593 if self.remove_client(&peer_addr) {
594 warn!(
601 "client {} is trying to reauthenticate; removing client and starting again",
602 peer_addr
603 );
604 }
605 let mut client = RemoteClient::new(peer_addr);
606 client.handle_auth_ind(&mut self.ctx, ind.auth_type);
607 if !client.authenticated() {
608 info!("client {} was not authenticated", peer_addr);
609 return;
610 }
611
612 info!("client {} authenticated", peer_addr);
613 let _ = self.clients.insert(peer_addr, client);
614 }
615
616 fn handle_deauth(&mut self, peer_addr: &MacAddr) {
617 if !self.remove_client(peer_addr) {
618 warn!("client {} never authenticated, ignoring deauthentication request", peer_addr);
619 return;
620 }
621
622 info!("client {} deauthenticated", peer_addr);
623 }
624
625 fn handle_assoc_ind(&mut self, ind: fidl_mlme::AssociateIndication) {
626 let peer_addr: MacAddr = ind.peer_sta_address.into();
627
628 let client = match self.clients.get_mut(&peer_addr) {
629 None => {
630 warn!("client {} never authenticated, ignoring association indication", peer_addr);
631 return;
632 }
633 Some(client) => client,
634 };
635
636 client.handle_assoc_ind(
637 &mut self.ctx,
638 &mut self.aid_map,
639 ind.capability_info,
640 ind.rates.into_iter().map(SupportedRate).collect::<Vec<_>>(),
641 &self.rsn_cfg,
642 ind.rsne,
643 );
644 if !client.authenticated() {
645 warn!("client {} failed to associate and was deauthenticated", peer_addr);
646 let _ = self.remove_client(&peer_addr);
647 } else if !client.associated() {
648 warn!("client {} failed to associate but did not deauthenticate", peer_addr);
649 } else {
650 info!("client {} associated", peer_addr);
651 }
652 }
653
654 fn handle_disassoc_ind(&mut self, ind: fidl_mlme::DisassociateIndication) {
655 let peer_addr: MacAddr = ind.peer_sta_address.into();
656
657 let client = match self.clients.get_mut(&peer_addr) {
658 None => {
659 warn!(
660 "client {} never authenticated, ignoring disassociation indication",
661 peer_addr
662 );
663 return;
664 }
665 Some(client) => client,
666 };
667
668 client.handle_disassoc_ind(&mut self.ctx, &mut self.aid_map);
669 if client.associated() {
670 panic!("client {peer_addr} didn't disassociate? this should never happen!")
671 } else {
672 info!("client {} disassociated", peer_addr);
673 }
674 }
675
676 fn handle_timeout(&mut self, timed_event: timer::Event<Event>) {
677 match timed_event.event {
678 Event::Sme { .. } => (),
679 Event::Client { addr, event } => {
680 let client = match self.clients.get_mut(&addr) {
681 None => {
682 return;
683 }
684 Some(client) => client,
685 };
686
687 client.handle_timeout(&mut self.ctx, event);
688 if !client.authenticated() {
689 if !self.remove_client(&addr) {
690 error!("failed to remove client {} from AID map", addr);
691 }
692 info!("client {} lost authentication", addr);
693 }
694 }
695 }
696 }
697
698 fn handle_eapol_ind(&mut self, ind: fidl_mlme::EapolIndication) {
699 let peer_addr: MacAddr = ind.src_addr.into();
700 let client = match self.clients.get_mut(&peer_addr) {
701 None => {
702 warn!("client {} never authenticated, ignoring EAPoL indication", peer_addr);
703 return;
704 }
705 Some(client) => client,
706 };
707
708 client.handle_eapol_ind(&mut self.ctx, &ind.data[..]);
709 }
710
711 fn handle_eapol_conf(&mut self, resp: fidl_mlme::EapolConfirm) {
712 let dst_addr: MacAddr = resp.dst_addr.into();
713 let client = match self.clients.get_mut(&dst_addr) {
714 None => {
715 warn!("never sent EAPOL frame to client {}, ignoring confirm", dst_addr);
716 return;
717 }
718 Some(client) => client,
719 };
720
721 client.handle_eapol_conf(&mut self.ctx, resp.result_code);
722 }
723}
724
725fn create_rsn_cfg(ssid: &Ssid, password: &[u8]) -> Result<Option<RsnCfg>, StartResult> {
726 if password.is_empty() {
727 Ok(None)
728 } else {
729 let psk_result = psk::compute(password, ssid);
730 let psk = match psk_result {
731 Err(e) => {
732 return Err(StartResult::InvalidArguments(e.to_string()));
733 }
734 Ok(o) => o,
735 };
736
737 Ok(Some(RsnCfg { psk, rsne: Rsne::wpa2_rsne_with_caps(RsnCapabilities(0)) }))
740 }
741}
742
743fn create_start_request(
744 op_radio_cfg: &OpRadioConfig,
745 ssid: &Ssid,
746 ap_rsn: Option<&RsnCfg>,
747 capabilities: mac::CapabilityInfo,
748) -> Result<fidl_mlme::StartRequest, StartResult> {
749 let rsne_bytes = ap_rsn.as_ref().map(|RsnCfg { rsne, .. }| {
750 let mut buf = Vec::with_capacity(rsne.len());
751 if let Err(e) = rsne.write_into(&mut buf) {
752 error!("error writing RSNE into MLME-START.request: {}", e);
753 }
754 buf
755 });
756
757 let (channel_bandwidth, _secondary80) = op_radio_cfg.channel.cbw.to_fidl();
758
759 if op_radio_cfg.basic_rates.len() > fidl_internal::MAX_ASSOC_BASIC_RATES as usize {
760 error!(
761 "Too many basic rates ({}). Max is {}.",
762 op_radio_cfg.basic_rates.len(),
763 fidl_internal::MAX_ASSOC_BASIC_RATES
764 );
765 return Err(StartResult::InternalError);
766 }
767
768 Ok(fidl_mlme::StartRequest {
769 ssid: ssid.to_vec(),
770 bss_type: fidl_common::BssType::Infrastructure,
771 beacon_period: DEFAULT_BEACON_PERIOD,
772 dtim_period: DEFAULT_DTIM_PERIOD,
773 channel: op_radio_cfg.channel.primary,
774 capability_info: capabilities.raw(),
775 rates: op_radio_cfg.basic_rates.clone(),
776 country: fidl_mlme::Country {
777 alpha2: [b'U', b'S'],
779 suffix: fidl_mlme::COUNTRY_ENVIRON_ALL,
780 },
781 rsne: rsne_bytes,
782 mesh_id: vec![],
783 phy: op_radio_cfg.phy,
784 channel_bandwidth,
785 })
786}
787
788#[cfg(test)]
789mod tests {
790 use super::*;
791 use crate::test_utils::*;
792 use crate::{MlmeStream, Station};
793 use assert_matches::assert_matches;
794 use fidl_fuchsia_wlan_mlme as fidl_mlme;
795 use std::sync::LazyLock;
796 use test_case::test_case;
797 use wlan_common::channel::Cbw;
798 use wlan_common::mac::Aid;
799 use wlan_common::test_utils::fake_capabilities::{
800 fake_2ghz_band_capability_ht, fake_5ghz_band_capability, fake_5ghz_band_capability_ht,
801 fake_5ghz_band_capability_vht,
802 };
803 use wlan_common::test_utils::fake_features::{
804 fake_dfs_supported, fake_spectrum_management_support_empty,
805 };
806
807 static AP_ADDR: LazyLock<MacAddr> =
808 LazyLock::new(|| [0x11, 0x22, 0x33, 0x44, 0x55, 0x66].into());
809 static CLIENT_ADDR: LazyLock<MacAddr> =
810 LazyLock::new(|| [0x7A, 0xE7, 0x76, 0xD9, 0xF2, 0x67].into());
811 static CLIENT_ADDR2: LazyLock<MacAddr> =
812 LazyLock::new(|| [0x22, 0x22, 0x22, 0x22, 0x22, 0x22].into());
813 static SSID: LazyLock<Ssid> =
814 LazyLock::new(|| Ssid::try_from([0x46, 0x55, 0x43, 0x48, 0x53, 0x49, 0x41]).unwrap());
815
816 const RSNE: &[u8] = &[
817 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,
829 0x11, 0x00, 0x0f, 0xac, 0x04, ];
831
832 fn radio_cfg(primary_channel: u8) -> RadioConfig {
833 RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, primary_channel)
834 }
835
836 fn unprotected_config() -> Config {
837 Config { ssid: SSID.clone(), password: vec![], radio_cfg: radio_cfg(11) }
838 }
839
840 fn protected_config() -> Config {
841 Config {
842 ssid: SSID.clone(),
843 password: vec![0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68],
844 radio_cfg: radio_cfg(11),
845 }
846 }
847
848 fn create_channel_switch_ind(channel: u8) -> MlmeEvent {
849 MlmeEvent::OnChannelSwitched {
850 info: fidl_internal::ChannelSwitchInfo { new_channel: channel },
851 }
852 }
853
854 #[derive(Clone, Debug)]
855 struct ValidateRadioConfigArgs {
856 bands: Vec<fidl_mlme::BandCapability>,
857 radio_cfg: RadioConfig,
858 spectrum_management_support: fidl_common::SpectrumManagementSupport,
859 }
860
861 #[test_case(false, ValidateRadioConfigArgs {
862 bands: vec![fake_2ghz_band_capability_ht()],
863 radio_cfg: RadioConfig {
864 phy: fidl_common::WlanPhyType::Ht,
865 channel: Channel::new(15, Cbw::Cbw20),
866 },
867 spectrum_management_support: fake_spectrum_management_support_empty(),
868 }; "invalid US channel")]
869 #[test_case(false, ValidateRadioConfigArgs {
870 bands: vec![fake_5ghz_band_capability()],
871 radio_cfg: RadioConfig {
872 phy: fidl_common::WlanPhyType::Ht,
873 channel: Channel::new(36, Cbw::Cbw20),
874 },
875 spectrum_management_support: fake_spectrum_management_support_empty(),
876 }; "5 GHz channel and no DFS support")]
877 #[test_case(false, ValidateRadioConfigArgs {
878 bands: vec![fake_2ghz_band_capability_ht()],
879 radio_cfg: RadioConfig {
880 phy: fidl_common::WlanPhyType::Dmg,
881 channel: Channel::new(1, Cbw::Cbw20),
882 },
883 spectrum_management_support: fake_spectrum_management_support_empty(),
884 }; "DMG not supported")]
885 #[test_case(false, ValidateRadioConfigArgs {
886 bands: vec![fake_2ghz_band_capability_ht()],
887 radio_cfg: RadioConfig {
888 phy: fidl_common::WlanPhyType::Tvht,
889 channel: Channel::new(1, Cbw::Cbw20),
890 },
891 spectrum_management_support: fake_spectrum_management_support_empty(),
892 }; "TVHT not supported")]
893 #[test_case(false, ValidateRadioConfigArgs {
894 bands: vec![fake_2ghz_band_capability_ht()],
895 radio_cfg: RadioConfig {
896 phy: fidl_common::WlanPhyType::S1G,
897 channel: Channel::new(1, Cbw::Cbw20),
898 },
899 spectrum_management_support: fake_spectrum_management_support_empty(),
900 }; "S1G not supported")]
901 #[test_case(false, ValidateRadioConfigArgs {
902 bands: vec![fake_2ghz_band_capability_ht()],
903 radio_cfg: RadioConfig {
904 phy: fidl_common::WlanPhyType::Cdmg,
905 channel: Channel::new(1, Cbw::Cbw20),
906 },
907 spectrum_management_support: fake_spectrum_management_support_empty(),
908 }; "CDMG not supported")]
909 #[test_case(false, ValidateRadioConfigArgs {
910 bands: vec![fake_2ghz_band_capability_ht()],
911 radio_cfg: RadioConfig {
912 phy: fidl_common::WlanPhyType::Cmmg,
913 channel: Channel::new(1, Cbw::Cbw20),
914 },
915 spectrum_management_support: fake_spectrum_management_support_empty(),
916 }; "CMMG not supported")]
917 #[test_case(false, ValidateRadioConfigArgs {
918 bands: vec![fake_2ghz_band_capability_ht()],
919 radio_cfg: RadioConfig {
920 phy: fidl_common::WlanPhyType::He,
921 channel: Channel::new(1, Cbw::Cbw20),
922 },
923 spectrum_management_support: fake_spectrum_management_support_empty(),
924 }; "HE not supported")]
925 #[test_case(false, ValidateRadioConfigArgs {
926 bands: vec![fake_2ghz_band_capability_ht()],
927 radio_cfg: RadioConfig {
928 phy: fidl_common::WlanPhyType::Ht,
929 channel: Channel::new(36, Cbw::Cbw80),
930 },
931 spectrum_management_support: fake_dfs_supported(),
932 }; "invalid HT width")]
933 #[test_case(false, ValidateRadioConfigArgs {
934 bands: vec![fake_2ghz_band_capability_ht()],
935 radio_cfg: RadioConfig {
936 phy: fidl_common::WlanPhyType::Erp,
937 channel: Channel::new(1, Cbw::Cbw40),
938 },
939 spectrum_management_support: fake_spectrum_management_support_empty(),
940 }; "non-HT greater than 20 MHz")]
941 #[test_case(false, ValidateRadioConfigArgs {
942 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_FORTY)],
943 radio_cfg: RadioConfig {
944 phy: fidl_common::WlanPhyType::Ht,
945 channel: Channel::new(36, Cbw::Cbw80),
946 },
947 spectrum_management_support: fake_dfs_supported(),
948 }; "HT greater than 40 MHz")]
949 #[test_case(false, ValidateRadioConfigArgs {
950 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_FORTY)],
951 radio_cfg: RadioConfig {
952 phy: fidl_common::WlanPhyType::unknown(),
953 channel: Channel::new(36, Cbw::Cbw40),
954 },
955 spectrum_management_support: fake_dfs_supported(),
956 }; "Unknown PHY type")]
957 #[test_case(false, ValidateRadioConfigArgs {
958 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_ONLY)],
959 radio_cfg: RadioConfig {
960 phy: fidl_common::WlanPhyType::Ht,
961 channel: Channel::new(44, Cbw::Cbw40),
962 },
963 spectrum_management_support: fake_dfs_supported(),
964 }; "HT 20 MHz only")]
965 #[test_case(false, ValidateRadioConfigArgs {
966 bands: vec![fake_5ghz_band_capability()],
967 radio_cfg: RadioConfig {
968 phy: fidl_common::WlanPhyType::Ht,
969 channel: Channel::new(48, Cbw::Cbw40),
970 },
971 spectrum_management_support: fake_dfs_supported(),
972 }; "No HT capabilities")]
973 #[test_case(false, ValidateRadioConfigArgs {
974 bands: vec![fake_5ghz_band_capability_vht()],
975 radio_cfg: RadioConfig {
976 phy: fidl_common::WlanPhyType::Vht,
977 channel: Channel::new(36, Cbw::Cbw160),
978 },
979 spectrum_management_support: fake_dfs_supported(),
980 }; "160 MHz not supported")]
981 #[test_case(false, ValidateRadioConfigArgs {
982 bands: vec![fake_5ghz_band_capability_vht()],
983 radio_cfg: RadioConfig {
984 phy: fidl_common::WlanPhyType::Vht,
985 channel: Channel::new(36, Cbw::Cbw80P80 { secondary80: 106 }),
986 },
987 spectrum_management_support: fake_dfs_supported(),
988 }; "80+80 MHz not supported")]
989 #[test_case(false, ValidateRadioConfigArgs {
990 bands: vec![fake_2ghz_band_capability_ht()],
991 radio_cfg: RadioConfig {
992 phy: fidl_common::WlanPhyType::Vht,
993 channel: Channel::new(1, Cbw::Cbw20),
994 },
995 spectrum_management_support: fake_spectrum_management_support_empty(),
996 }; "VHT 2.4 GHz not supported")]
997 #[test_case(false, ValidateRadioConfigArgs {
998 bands: vec![fake_5ghz_band_capability()],
999 radio_cfg: RadioConfig {
1000 phy: fidl_common::WlanPhyType::Vht,
1001 channel: Channel::new(149, Cbw::Cbw80),
1002 },
1003 spectrum_management_support: fake_dfs_supported(),
1004 }; "no VHT capabilities")]
1005 #[test_case(false, ValidateRadioConfigArgs {
1006 bands: vec![fake_2ghz_band_capability_ht(), fake_5ghz_band_capability_vht()],
1007 radio_cfg: RadioConfig {
1008 phy: fidl_common::WlanPhyType::Vht,
1009 channel: Channel::new(1, Cbw::Cbw40),
1010 },
1011 spectrum_management_support: fake_spectrum_management_support_empty(),
1012 }; "no VHT capabilities on 2.4 GHz event when 5 GHz band capabilities provided")]
1013 #[test_case(false, ValidateRadioConfigArgs {
1014 bands: vec![fidl_mlme::BandCapability {
1015 operating_channels: vec![2],
1016 ..fake_2ghz_band_capability_ht()
1017 }],
1018 radio_cfg: RadioConfig {
1019 phy: fidl_common::WlanPhyType::Hr,
1020 channel: Channel::new(1, Cbw::Cbw40),
1021 },
1022 spectrum_management_support: fake_spectrum_management_support_empty(),
1023 }; "disallow non-operating 2.4 GHz channel")]
1024 #[test_case(false, ValidateRadioConfigArgs {
1025 bands: vec![fidl_mlme::BandCapability {
1026 operating_channels: vec![40],
1027 ..fake_5ghz_band_capability_vht()
1028 }],
1029 radio_cfg: RadioConfig {
1030 phy: fidl_common::WlanPhyType::Vht,
1031 channel: Channel::new(36, Cbw::Cbw80),
1032 },
1033 spectrum_management_support: fake_spectrum_management_support_empty(),
1034 }; "disallow non-operating 5 GHz channel")]
1035 #[test_case(true, ValidateRadioConfigArgs {
1036 bands: vec![fake_2ghz_band_capability_ht()],
1037 radio_cfg: RadioConfig {
1038 phy: fidl_common::WlanPhyType::Hr,
1039 channel: Channel::new(1, Cbw::Cbw20),
1040 },
1041 spectrum_management_support: fake_spectrum_management_support_empty(),
1042 })]
1043 #[test_case(true, ValidateRadioConfigArgs {
1044 bands: vec![fake_2ghz_band_capability_ht()],
1045 radio_cfg: RadioConfig {
1046 phy: fidl_common::WlanPhyType::Erp,
1047 channel: Channel::new(1, Cbw::Cbw20),
1048 },
1049 spectrum_management_support: fake_spectrum_management_support_empty(),
1050 })]
1051 #[test_case(true, ValidateRadioConfigArgs {
1052 bands: vec![fake_2ghz_band_capability_ht()],
1053 radio_cfg: RadioConfig {
1054 phy: fidl_common::WlanPhyType::Ht,
1055 channel: Channel::new(1, Cbw::Cbw20),
1056 },
1057 spectrum_management_support: fake_spectrum_management_support_empty(),
1058 })]
1059 #[test_case(true, ValidateRadioConfigArgs {
1060 bands: vec![fake_2ghz_band_capability_ht()],
1061 radio_cfg: RadioConfig {
1062 phy: fidl_common::WlanPhyType::Ht,
1063 channel: Channel::new(1, Cbw::Cbw40),
1064 },
1065 spectrum_management_support: fake_spectrum_management_support_empty(),
1066 })]
1067 #[test_case(true, ValidateRadioConfigArgs {
1068 bands: vec![fake_2ghz_band_capability_ht()],
1069 radio_cfg: RadioConfig {
1070 phy: fidl_common::WlanPhyType::Ht,
1071 channel: Channel::new(11, Cbw::Cbw40Below),
1072 },
1073 spectrum_management_support: fake_spectrum_management_support_empty(),
1074 })]
1075 #[test_case(true, ValidateRadioConfigArgs {
1076 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_ONLY)],
1077 radio_cfg: RadioConfig {
1078 phy: fidl_common::WlanPhyType::Ht,
1079 channel: Channel::new(36, Cbw::Cbw20),
1080 },
1081 spectrum_management_support: fake_dfs_supported(),
1082 })]
1083 #[test_case(true, ValidateRadioConfigArgs {
1084 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_FORTY)],
1085 radio_cfg: RadioConfig {
1086 phy: fidl_common::WlanPhyType::Ht,
1087 channel: Channel::new(36, Cbw::Cbw40),
1088 },
1089 spectrum_management_support: fake_dfs_supported(),
1090 })]
1091 #[test_case(true, ValidateRadioConfigArgs {
1092 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_FORTY)],
1093 radio_cfg: RadioConfig {
1094 phy: fidl_common::WlanPhyType::Ht,
1095 channel: Channel::new(40, Cbw::Cbw40Below),
1096 },
1097 spectrum_management_support: fake_dfs_supported(),
1098 })]
1099 #[test_case(true, ValidateRadioConfigArgs {
1100 bands: vec![fake_5ghz_band_capability_ht(ChanWidthSet::TWENTY_FORTY)],
1101 radio_cfg: RadioConfig {
1102 phy: fidl_common::WlanPhyType::Ht,
1103 channel: Channel::new(36, Cbw::Cbw20),
1104 },
1105 spectrum_management_support: fake_dfs_supported(),
1106 })]
1107 #[test_case(true, ValidateRadioConfigArgs {
1108 bands: vec![fake_5ghz_band_capability_vht()],
1109 radio_cfg: RadioConfig {
1110 phy: fidl_common::WlanPhyType::Ht,
1111 channel: Channel::new(36, Cbw::Cbw40),
1112 },
1113 spectrum_management_support: fake_dfs_supported(),
1114 })]
1115 #[test_case(true, ValidateRadioConfigArgs {
1116 bands: vec![fake_5ghz_band_capability_vht()],
1117 radio_cfg: RadioConfig {
1118 phy: fidl_common::WlanPhyType::Ht,
1119 channel: Channel::new(40, Cbw::Cbw40Below),
1120 },
1121 spectrum_management_support: fake_dfs_supported(),
1122 })]
1123 #[test_case(true, ValidateRadioConfigArgs {
1124 bands: vec![fake_5ghz_band_capability_vht()],
1125 radio_cfg: RadioConfig {
1126 phy: fidl_common::WlanPhyType::Vht,
1127 channel: Channel::new(36, Cbw::Cbw80),
1128 },
1129 spectrum_management_support: fake_dfs_supported(),
1130 })]
1131 #[test_case(true, ValidateRadioConfigArgs {
1132 bands: vec![fake_2ghz_band_capability_ht(), fake_5ghz_band_capability_vht()],
1133 radio_cfg: RadioConfig {
1134 phy: fidl_common::WlanPhyType::Ht,
1135 channel: Channel::new(1, Cbw::Cbw40),
1136 },
1137 spectrum_management_support: fake_spectrum_management_support_empty(),
1138 })]
1139 #[test_case(true, ValidateRadioConfigArgs {
1140 bands: vec![fake_2ghz_band_capability_ht(), fake_5ghz_band_capability_vht()],
1141 radio_cfg: RadioConfig {
1142 phy: fidl_common::WlanPhyType::Vht,
1143 channel: Channel::new(36, Cbw::Cbw80),
1144 },
1145 spectrum_management_support: fake_dfs_supported(),
1146 })]
1147 fn test_validate_radio_cfg(expect_ok: bool, fn_args: ValidateRadioConfigArgs) {
1148 match validate_radio_cfg(
1149 &fn_args.bands[..],
1150 &fn_args.radio_cfg,
1151 fn_args.spectrum_management_support.clone(),
1152 ) {
1153 Ok(op_radio_cfg) => {
1154 if !expect_ok {
1155 panic!("Unexpected successful validation: {0:?}, {op_radio_cfg:?}", fn_args);
1156 }
1157 assert_matches!(
1158 op_radio_cfg,
1159 OpRadioConfig {
1160 phy,
1161 channel,
1162 basic_rates: _,
1163 } => {
1164 assert_eq!(phy, fn_args.radio_cfg.phy);
1165 assert_eq!(channel, fn_args.radio_cfg.channel);
1166 }
1167 )
1168 }
1169 Err(e @ StartResult::InvalidArguments { .. }) => {
1170 if expect_ok {
1171 panic!("Unexpected failure to validate: {0:?}, {e:?}", fn_args)
1172 }
1173 }
1174 Err(e) => panic!("Unexpected StartResult value: {0:?}, {e:?}", fn_args),
1175 }
1176 }
1177
1178 #[fuchsia::test(allow_stalls = false)]
1179 async fn authenticate_while_sme_is_idle() {
1180 let (mut sme, mut mlme_stream, _) = create_sme().await;
1181 let client = Client::default();
1182 sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1183
1184 assert_matches!(mlme_stream.try_next(), Err(e) => {
1185 assert_eq!(e.to_string(), "receiver channel is empty");
1186 });
1187 }
1188
1189 #[fuchsia::test(allow_stalls = false)]
1191 async fn status_when_sme_is_idle() {
1192 let (sme, _, _) = create_sme().await;
1193 assert_eq!(None, sme.get_running_ap());
1194 }
1195
1196 #[fuchsia::test(allow_stalls = false)]
1197 async fn ap_starts_success() {
1198 let (mut sme, mut mlme_stream, _) = create_sme().await;
1199 let mut receiver = sme.on_start_command(unprotected_config());
1200
1201 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(start_req))) => {
1202 assert_eq!(start_req.ssid, SSID.to_vec());
1203 assert_eq!(
1204 start_req.capability_info,
1205 mac::CapabilityInfo(0).with_short_preamble(true).with_ess(true).raw(),
1206 );
1207 assert_eq!(start_req.bss_type, fidl_common::BssType::Infrastructure);
1208 assert_ne!(start_req.beacon_period, 0);
1209 assert_eq!(start_req.dtim_period, DEFAULT_DTIM_PERIOD);
1210 assert_eq!(
1211 start_req.channel,
1212 unprotected_config().radio_cfg.channel.primary,
1213 );
1214 assert!(start_req.rsne.is_none());
1215 });
1216
1217 assert_eq!(Ok(None), receiver.try_recv());
1218 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1219 assert_eq!(Ok(Some(StartResult::Success)), receiver.try_recv());
1220 }
1221
1222 #[fuchsia::test(allow_stalls = false)]
1224 async fn ap_starts_success_get_running_ap() {
1225 let (mut sme, mut mlme_stream, _) = create_sme().await;
1226 let mut receiver = sme.on_start_command(unprotected_config());
1227 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(_start_req))) => {});
1228 assert_eq!(None, sme.get_running_ap());
1230 assert_eq!(Ok(None), receiver.try_recv());
1231 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1232 assert_eq!(Ok(Some(StartResult::Success)), receiver.try_recv());
1233 assert_eq!(
1234 Some(fidl_sme::Ap {
1235 ssid: SSID.to_vec(),
1236 channel: unprotected_config().radio_cfg.channel.primary,
1237 num_clients: 0,
1238 }),
1239 sme.get_running_ap()
1240 );
1241 }
1242
1243 #[fuchsia::test(allow_stalls = false)]
1245 async fn ap_check_status_after_channel_change() {
1246 let (mut sme, _, _) = start_unprotected_ap().await;
1247 assert_eq!(
1249 Some(fidl_sme::Ap {
1250 ssid: SSID.to_vec(),
1251 channel: unprotected_config().radio_cfg.channel.primary,
1252 num_clients: 0,
1253 }),
1254 sme.get_running_ap()
1255 );
1256 sme.on_mlme_event(create_channel_switch_ind(6));
1257 assert_eq!(
1259 Some(fidl_sme::Ap { ssid: SSID.to_vec(), channel: 6, num_clients: 0 }),
1260 sme.get_running_ap()
1261 );
1262 }
1263
1264 #[fuchsia::test(allow_stalls = false)]
1265 async fn ap_starts_timeout() {
1266 let (mut sme, _, mut time_stream) = create_sme().await;
1267 let mut receiver = sme.on_start_command(unprotected_config());
1268
1269 let (_, event, _) = time_stream.try_next().unwrap().expect("expect timer message");
1270 sme.on_timeout(event);
1271
1272 assert_eq!(Ok(Some(StartResult::TimedOut)), receiver.try_recv());
1273 assert_eq!(None, sme.get_running_ap());
1275 }
1276
1277 #[fuchsia::test(allow_stalls = false, logging = false)]
1279 async fn ap_starts_fails() {
1280 let (mut sme, _, _) = create_sme().await;
1281 let mut receiver = sme.on_start_command(unprotected_config());
1282
1283 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::NotSupported));
1284 assert_eq!(Ok(Some(StartResult::InternalError)), receiver.try_recv());
1285 assert_eq!(None, sme.get_running_ap());
1287 }
1288
1289 #[fuchsia::test(allow_stalls = false)]
1290 async fn start_req_while_ap_is_starting() {
1291 let (mut sme, _, _) = create_sme().await;
1292 let mut receiver_one = sme.on_start_command(unprotected_config());
1293
1294 let mut receiver_two = sme.on_start_command(unprotected_config());
1296 assert_eq!(Ok(Some(StartResult::PreviousStartInProgress)), receiver_two.try_recv());
1297
1298 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1300 assert_eq!(Ok(Some(StartResult::Success)), receiver_one.try_recv());
1301 }
1302
1303 #[fuchsia::test(allow_stalls = false)]
1304 async fn start_req_while_ap_is_stopping() {
1305 let (mut sme, _, _) = start_unprotected_ap().await;
1306 let mut stop_receiver = sme.on_stop_command();
1307 let mut start_receiver = sme.on_start_command(unprotected_config());
1308 assert_eq!(Ok(None), stop_receiver.try_recv());
1309 assert_eq!(Ok(Some(StartResult::Canceled)), start_receiver.try_recv());
1310 }
1311
1312 #[fuchsia::test(allow_stalls = false)]
1313 async fn ap_stops_while_idle() {
1314 let (mut sme, mut mlme_stream, _) = create_sme().await;
1315 let mut receiver = sme.on_stop_command();
1316 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1317 assert!(stop_req.ssid.is_empty());
1318 });
1319
1320 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1322 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver.try_recv());
1323 }
1324
1325 #[fuchsia::test(allow_stalls = false)]
1326 async fn stop_req_while_ap_is_starting_then_succeeds() {
1327 let (mut sme, mut mlme_stream, _) = create_sme().await;
1328 let mut start_receiver = sme.on_start_command(unprotected_config());
1329 let mut stop_receiver = sme.on_stop_command();
1330 assert_eq!(Ok(None), start_receiver.try_recv());
1331 assert_eq!(Ok(None), stop_receiver.try_recv());
1332
1333 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(_))));
1335 assert_matches!(mlme_stream.try_next(), Err(e) => {
1336 assert_eq!(e.to_string(), "receiver channel is empty");
1337 });
1338
1339 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1341 assert_eq!(Ok(Some(StartResult::Canceled)), start_receiver.try_recv());
1342 assert_eq!(Ok(None), stop_receiver.try_recv());
1343 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1344 assert_eq!(stop_req.ssid, SSID.to_vec());
1345 });
1346
1347 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1349 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), stop_receiver.try_recv());
1350 }
1351
1352 #[fuchsia::test(allow_stalls = false)]
1353 async fn stop_req_while_ap_is_starting_then_times_out() {
1354 let (mut sme, mut mlme_stream, mut time_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 let (_, event, _) = time_stream.try_next().unwrap().expect("expect timer message");
1368 sme.on_timeout(event);
1369 assert_eq!(Ok(Some(StartResult::TimedOut)), start_receiver.try_recv());
1370 assert_eq!(Ok(None), stop_receiver.try_recv());
1371 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1372 assert_eq!(stop_req.ssid, SSID.to_vec());
1373 });
1374
1375 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1377 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), stop_receiver.try_recv());
1378 }
1379
1380 #[fuchsia::test(allow_stalls = false)]
1381 async fn ap_stops_after_started() {
1382 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1383 let mut receiver = sme.on_stop_command();
1384
1385 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1386 assert_eq!(stop_req.ssid, SSID.to_vec());
1387 });
1388 assert_eq!(Ok(None), receiver.try_recv());
1389 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::BssAlreadyStopped));
1390 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver.try_recv());
1391 }
1392
1393 #[fuchsia::test(allow_stalls = false)]
1394 async fn ap_stops_after_started_and_deauths_all_clients() {
1395 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1396 let client = Client::default();
1397 sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1398 client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1399
1400 assert_eq!(
1402 Some(fidl_sme::Ap {
1403 ssid: SSID.to_vec(),
1404 channel: unprotected_config().radio_cfg.channel.primary,
1405 num_clients: 1,
1406 }),
1407 sme.get_running_ap()
1408 );
1409 let mut receiver = sme.on_stop_command();
1410 assert_matches!(
1411 mlme_stream.try_next(),
1412 Ok(Some(MlmeRequest::Deauthenticate(deauth_req))) => {
1413 assert_eq!(&deauth_req.peer_sta_address, client.addr.as_array());
1414 assert_eq!(deauth_req.reason_code, fidl_ieee80211::ReasonCode::StaLeaving);
1415 });
1416
1417 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1418 assert_eq!(stop_req.ssid, SSID.to_vec());
1419 });
1420 assert_eq!(Ok(None), receiver.try_recv());
1421 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1422 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver.try_recv());
1423
1424 assert_eq!(None, sme.get_running_ap());
1426 }
1427
1428 #[fuchsia::test(allow_stalls = false)]
1429 async fn ap_queues_concurrent_stop_requests() {
1430 let (mut sme, _, _) = start_unprotected_ap().await;
1431 let mut receiver1 = sme.on_stop_command();
1432 let mut receiver2 = sme.on_stop_command();
1433
1434 assert_eq!(Ok(None), receiver1.try_recv());
1435 assert_eq!(Ok(None), receiver2.try_recv());
1436
1437 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1438 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver1.try_recv());
1439 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver2.try_recv());
1440 }
1441
1442 #[fuchsia::test(allow_stalls = false)]
1443 async fn uncleaned_stopping_state() {
1444 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1445 let mut stop_receiver1 = sme.on_stop_command();
1446 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1448 assert_eq!(stop_req.ssid, SSID.to_vec());
1449 });
1450
1451 assert_eq!(Ok(None), stop_receiver1.try_recv());
1452 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::InternalError));
1453 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::InternalError)), stop_receiver1.try_recv());
1454
1455 let mut start_receiver = sme.on_start_command(unprotected_config());
1457 assert_eq!(Ok(Some(StartResult::Canceled)), start_receiver.try_recv());
1458 assert_matches!(mlme_stream.try_next(), Err(e) => {
1459 assert_eq!(e.to_string(), "receiver channel is empty");
1460 });
1461
1462 let mut stop_receiver2 = sme.on_stop_command();
1464 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1465 assert_eq!(stop_req.ssid, SSID.to_vec());
1466 });
1467
1468 assert_eq!(Ok(None), stop_receiver2.try_recv());
1470 sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1471 assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), stop_receiver2.try_recv());
1472 }
1473
1474 #[fuchsia::test(allow_stalls = false)]
1475 async fn client_authenticates_supported_authentication_type() {
1476 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1477 let client = Client::default();
1478 sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1479 client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1480 }
1481
1482 #[fuchsia::test(allow_stalls = false, logging = false)]
1484 async fn client_authenticates_unsupported_authentication_type() {
1485 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1486 let client = Client::default();
1487 let auth_ind = client.create_auth_ind(fidl_mlme::AuthenticationTypes::FastBssTransition);
1488 sme.on_mlme_event(auth_ind);
1489 client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Refused);
1490 }
1491
1492 #[fuchsia::test(allow_stalls = false)]
1493 async fn client_associates_unprotected_network() {
1494 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1495 let client = Client::default();
1496 sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1497 client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1498
1499 sme.on_mlme_event(client.create_assoc_ind(None));
1500 client.verify_assoc_resp(
1501 &mut mlme_stream,
1502 1,
1503 fidl_mlme::AssociateResultCode::Success,
1504 false,
1505 );
1506 }
1507
1508 #[fuchsia::test(allow_stalls = false)]
1509 async fn client_associates_valid_rsne() {
1510 let (mut sme, mut mlme_stream, _) = start_protected_ap().await;
1511 let client = Client::default();
1512 client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1513
1514 sme.on_mlme_event(client.create_assoc_ind(Some(RSNE.to_vec())));
1515 client.verify_assoc_resp(
1516 &mut mlme_stream,
1517 1,
1518 fidl_mlme::AssociateResultCode::Success,
1519 true,
1520 );
1521 client.verify_eapol_req(&mut mlme_stream);
1522 }
1523
1524 #[fuchsia::test(allow_stalls = false, logging = false)]
1526 async fn client_associates_invalid_rsne() {
1527 let (mut sme, mut mlme_stream, _) = start_protected_ap().await;
1528 let client = Client::default();
1529 client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1530
1531 sme.on_mlme_event(client.create_assoc_ind(None));
1532 client.verify_refused_assoc_resp(
1533 &mut mlme_stream,
1534 fidl_mlme::AssociateResultCode::RefusedCapabilitiesMismatch,
1535 );
1536 }
1537
1538 #[fuchsia::test(allow_stalls = false)]
1539 async fn rsn_handshake_timeout() {
1540 let (mut sme, mut mlme_stream, mut time_stream) = start_protected_ap().await;
1541 let client = Client::default();
1542 client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1543
1544 assert_matches!(time_stream.try_next(), Ok(Some(_)));
1546
1547 sme.on_mlme_event(client.create_assoc_ind(Some(RSNE.to_vec())));
1548 client.verify_assoc_resp(
1549 &mut mlme_stream,
1550 1,
1551 fidl_mlme::AssociateResultCode::Success,
1552 true,
1553 );
1554
1555 assert_matches!(time_stream.try_next(), Ok(Some(_)));
1557
1558 for _i in 0..4 {
1559 client.verify_eapol_req(&mut mlme_stream);
1560 let (_, event, _) = time_stream.try_next().unwrap().expect("expect timer message");
1561 sme.on_timeout(event);
1562 }
1563
1564 client.verify_deauth_req(
1565 &mut mlme_stream,
1566 fidl_ieee80211::ReasonCode::FourwayHandshakeTimeout,
1567 );
1568 }
1569
1570 #[fuchsia::test(allow_stalls = false)]
1571 async fn client_restarts_authentication_flow() {
1572 let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1573 let client = Client::default();
1574 client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1575 client.associate_and_drain_mlme(&mut sme, &mut mlme_stream, None);
1576
1577 sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1578 client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1579
1580 sme.on_mlme_event(client.create_assoc_ind(None));
1581 client.verify_assoc_resp(
1582 &mut mlme_stream,
1583 1,
1584 fidl_mlme::AssociateResultCode::Success,
1585 false,
1586 );
1587 }
1588
1589 #[fuchsia::test(allow_stalls = false)]
1590 async fn multiple_clients_associate() {
1591 let (mut sme, mut mlme_stream, _) = start_protected_ap().await;
1592 let client1 = Client::default();
1593 let client2 = Client { addr: *CLIENT_ADDR2 };
1594
1595 sme.on_mlme_event(client1.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1596 client1.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1597
1598 sme.on_mlme_event(client2.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1599 client2.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1600
1601 sme.on_mlme_event(client1.create_assoc_ind(Some(RSNE.to_vec())));
1602 client1.verify_assoc_resp(
1603 &mut mlme_stream,
1604 1,
1605 fidl_mlme::AssociateResultCode::Success,
1606 true,
1607 );
1608 client1.verify_eapol_req(&mut mlme_stream);
1609
1610 sme.on_mlme_event(client2.create_assoc_ind(Some(RSNE.to_vec())));
1611 client2.verify_assoc_resp(
1612 &mut mlme_stream,
1613 2,
1614 fidl_mlme::AssociateResultCode::Success,
1615 true,
1616 );
1617 client2.verify_eapol_req(&mut mlme_stream);
1618 }
1619
1620 fn create_start_conf(result_code: fidl_mlme::StartResultCode) -> MlmeEvent {
1621 MlmeEvent::StartConf { resp: fidl_mlme::StartConfirm { result_code } }
1622 }
1623
1624 fn create_stop_conf(result_code: fidl_mlme::StopResultCode) -> MlmeEvent {
1625 MlmeEvent::StopConf { resp: fidl_mlme::StopConfirm { result_code } }
1626 }
1627
1628 struct Client {
1629 addr: MacAddr,
1630 }
1631
1632 impl Client {
1633 fn default() -> Self {
1634 Client { addr: *CLIENT_ADDR }
1635 }
1636
1637 fn authenticate_and_drain_mlme(
1638 &self,
1639 sme: &mut ApSme,
1640 mlme_stream: &mut crate::MlmeStream,
1641 ) {
1642 sme.on_mlme_event(self.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1643 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::AuthResponse(..))));
1644 }
1645
1646 fn associate_and_drain_mlme(
1647 &self,
1648 sme: &mut ApSme,
1649 mlme_stream: &mut crate::MlmeStream,
1650 rsne: Option<Vec<u8>>,
1651 ) {
1652 sme.on_mlme_event(self.create_assoc_ind(rsne));
1653 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::AssocResponse(..))));
1654 }
1655
1656 fn create_auth_ind(&self, auth_type: fidl_mlme::AuthenticationTypes) -> MlmeEvent {
1657 MlmeEvent::AuthenticateInd {
1658 ind: fidl_mlme::AuthenticateIndication {
1659 peer_sta_address: self.addr.to_array(),
1660 auth_type,
1661 },
1662 }
1663 }
1664
1665 fn create_assoc_ind(&self, rsne: Option<Vec<u8>>) -> MlmeEvent {
1666 MlmeEvent::AssociateInd {
1667 ind: fidl_mlme::AssociateIndication {
1668 peer_sta_address: self.addr.to_array(),
1669 listen_interval: 100,
1670 ssid: Some(SSID.to_vec()),
1671 rsne,
1672 capability_info: mac::CapabilityInfo(0).with_short_preamble(true).raw(),
1673 rates: vec![
1674 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
1675 ],
1676 },
1677 }
1678 }
1679
1680 fn verify_auth_resp(
1681 &self,
1682 mlme_stream: &mut MlmeStream,
1683 result_code: fidl_mlme::AuthenticateResultCode,
1684 ) {
1685 let msg = mlme_stream.try_next();
1686 assert_matches!(msg, Ok(Some(MlmeRequest::AuthResponse(auth_resp))) => {
1687 assert_eq!(&auth_resp.peer_sta_address, self.addr.as_array());
1688 assert_eq!(auth_resp.result_code, result_code);
1689 });
1690 }
1691
1692 fn verify_assoc_resp(
1693 &self,
1694 mlme_stream: &mut MlmeStream,
1695 aid: Aid,
1696 result_code: fidl_mlme::AssociateResultCode,
1697 privacy: bool,
1698 ) {
1699 let msg = mlme_stream.try_next();
1700 assert_matches!(msg, Ok(Some(MlmeRequest::AssocResponse(assoc_resp))) => {
1701 assert_eq!(&assoc_resp.peer_sta_address, self.addr.as_array());
1702 assert_eq!(assoc_resp.association_id, aid);
1703 assert_eq!(assoc_resp.result_code, result_code);
1704 assert_eq!(
1705 assoc_resp.capability_info,
1706 mac::CapabilityInfo(0).with_short_preamble(true).with_privacy(privacy).raw(),
1707 );
1708 });
1709 }
1710
1711 fn verify_refused_assoc_resp(
1712 &self,
1713 mlme_stream: &mut MlmeStream,
1714 result_code: fidl_mlme::AssociateResultCode,
1715 ) {
1716 let msg = mlme_stream.try_next();
1717 assert_matches!(msg, Ok(Some(MlmeRequest::AssocResponse(assoc_resp))) => {
1718 assert_eq!(&assoc_resp.peer_sta_address, self.addr.as_array());
1719 assert_eq!(assoc_resp.association_id, 0);
1720 assert_eq!(assoc_resp.result_code, result_code);
1721 assert_eq!(assoc_resp.capability_info, 0);
1722 });
1723 }
1724
1725 fn verify_eapol_req(&self, mlme_stream: &mut MlmeStream) {
1726 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Eapol(eapol_req))) => {
1727 assert_eq!(&eapol_req.src_addr, AP_ADDR.as_array());
1728 assert_eq!(&eapol_req.dst_addr, self.addr.as_array());
1729 assert!(!eapol_req.data.is_empty());
1730 });
1731 }
1732
1733 fn verify_deauth_req(
1734 &self,
1735 mlme_stream: &mut MlmeStream,
1736 reason_code: fidl_ieee80211::ReasonCode,
1737 ) {
1738 let msg = mlme_stream.try_next();
1739 assert_matches!(msg, Ok(Some(MlmeRequest::Deauthenticate(deauth_req))) => {
1740 assert_eq!(&deauth_req.peer_sta_address, self.addr.as_array());
1741 assert_eq!(deauth_req.reason_code, reason_code);
1742 });
1743 }
1744 }
1745
1746 async fn start_protected_ap() -> (ApSme, crate::MlmeStream, timer::EventStream<Event>) {
1750 start_ap(true).await
1751 }
1752
1753 async fn start_unprotected_ap() -> (ApSme, crate::MlmeStream, timer::EventStream<Event>) {
1757 start_ap(false).await
1758 }
1759
1760 async fn start_ap(protected: bool) -> (ApSme, crate::MlmeStream, timer::EventStream<Event>) {
1764 let (mut sme, mut mlme_stream, mut time_stream) = create_sme().await;
1765 let config = if protected { protected_config() } else { unprotected_config() };
1766 let mut receiver = sme.on_start_command(config);
1767 assert_eq!(Ok(None), receiver.try_recv());
1768 assert_matches!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(..))));
1769 while time_stream.try_next().is_ok() {}
1771 sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1772
1773 assert_eq!(Ok(Some(StartResult::Success)), receiver.try_recv());
1774 (sme, mlme_stream, time_stream)
1775 }
1776
1777 async fn create_sme() -> (ApSme, MlmeStream, timer::EventStream<Event>) {
1781 let (ap_sme, _mlme_sink, mlme_stream, time_stream) =
1782 ApSme::new(fake_device_info(*AP_ADDR), fake_spectrum_management_support_empty());
1783 (ap_sme, mlme_stream, time_stream)
1784 }
1785}