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