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