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