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