1use crate::ap::{BufferedFrame, Context, TimedEvent, frame_writer};
6use crate::device::DeviceOps;
7use crate::disconnect::LocallyInitiated;
8use crate::error::Error;
9use fdf::ArenaStaticBox;
10use ieee80211::{MacAddr, MacAddrBytes, Ssid};
11use log::warn;
12use std::collections::VecDeque;
13use wlan_common::append::Append;
14use wlan_common::buffer_writer::BufferWriter;
15use wlan_common::mac::{self, Aid, AuthAlgorithmNumber, FrameClass, ReasonCode};
16use wlan_common::timer::EventHandle;
17use wlan_common::{TimeUnit, ie};
18use wlan_statemachine::StateMachine;
19use zerocopy::SplitByteSlice;
20use {
21 fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme,
22 fidl_fuchsia_wlan_softmac as fidl_softmac, fuchsia_trace as trace, wlan_trace as wtrace,
23};
24
25const BSS_MAX_IDLE_PERIOD: u16 = 90;
31
32#[derive(Debug)]
33enum PowerSaveState {
34 Awake,
36
37 Dozing {
39 buffered: VecDeque<BufferedFrame>,
41 },
42}
43
44#[derive(Debug)]
47enum State {
48 Authenticating,
52
53 Authenticated,
55
56 Associated {
58 aid: Aid,
60
61 eapol_controlled_port: Option<fidl_mlme::ControlledPortState>,
69
70 active_timeout: Option<EventHandle>,
72
73 ps_state: PowerSaveState,
75 },
76
77 Deauthenticated,
80}
81
82impl State {
83 fn max_frame_class(&self) -> FrameClass {
84 match self {
85 State::Deauthenticated | State::Authenticating => FrameClass::Class1,
86 State::Authenticated => FrameClass::Class2,
87 State::Associated { .. } => FrameClass::Class3,
88 }
89 }
90}
91
92pub struct RemoteClient {
93 pub addr: MacAddr,
94 state: StateMachine<State>,
95}
96
97#[derive(Debug)]
98pub enum ClientRejection {
99 NotPermitted,
101
102 Unsupported,
104
105 NotAuthenticated,
107
108 NotAssociated,
110
111 ControlledPortClosed,
113
114 ParseFailed,
116
117 SmeSendError(Error),
119
120 WlanSendError(Error),
122
123 EthSendError(Error),
125
126 DeviceError(Error),
128}
129
130impl ClientRejection {
131 pub fn log_level(&self) -> log::Level {
132 match self {
133 Self::ParseFailed
134 | Self::SmeSendError(..)
135 | Self::WlanSendError(..)
136 | Self::EthSendError(..) => log::Level::Error,
137 Self::ControlledPortClosed | Self::Unsupported => log::Level::Warn,
138 _ => log::Level::Trace,
139 }
140 }
141}
142
143#[derive(Debug)]
144pub enum ClientEvent {
145 BssIdleTimeout,
149}
150
151impl RemoteClient {
154 pub fn new(addr: MacAddr) -> Self {
155 Self { addr, state: StateMachine::new(State::Authenticating) }
156 }
157
158 pub fn deauthenticated(&self) -> bool {
161 match self.state.as_ref() {
162 State::Deauthenticated => true,
163 _ => false,
164 }
165 }
166
167 pub fn aid(&self) -> Option<Aid> {
169 match self.state.as_ref() {
170 State::Associated { aid, .. } => Some(*aid),
171 _ => None,
172 }
173 }
174
175 pub fn has_buffered_frames(&self) -> bool {
177 match self.state.as_ref() {
178 State::Associated { ps_state: PowerSaveState::Dozing { buffered }, .. } => {
179 !buffered.is_empty()
180 }
181 _ => false,
182 }
183 }
184
185 pub fn dozing(&self) -> bool {
186 match self.state.as_ref() {
187 State::Associated { ps_state: PowerSaveState::Dozing { .. }, .. } => true,
188 _ => false,
189 }
190 }
191
192 async fn change_state<D: DeviceOps>(
193 &mut self,
194 ctx: &mut Context<D>,
195 next_state: State,
196 ) -> Result<(), Error> {
197 match self.state.as_mut() {
198 State::Associated { .. } => {
199 ctx.device
200 .clear_association(&fidl_softmac::WlanSoftmacBaseClearAssociationRequest {
201 peer_addr: Some(self.addr.to_array()),
202 ..Default::default()
203 })
204 .await
205 .map_err(|s| Error::Status(format!("failed to clear association"), s))?;
206 }
207 _ => (),
208 }
209 self.state.replace_state_with(next_state);
210 Ok(())
211 }
212
213 fn schedule_after<D>(
214 &self,
215 ctx: &mut Context<D>,
216 duration: zx::MonotonicDuration,
217 event: ClientEvent,
218 ) -> EventHandle {
219 ctx.schedule_after(duration, TimedEvent::ClientEvent(self.addr, event))
220 }
221
222 fn schedule_bss_idle_timeout<D>(&self, ctx: &mut Context<D>) -> EventHandle {
223 self.schedule_after(
224 ctx,
225 zx::MonotonicDuration::from(TimeUnit(1000)) * (BSS_MAX_IDLE_PERIOD as i64),
230 ClientEvent::BssIdleTimeout,
231 )
232 }
233
234 async fn handle_bss_idle_timeout<D: DeviceOps>(
235 &mut self,
236 ctx: &mut Context<D>,
237 ) -> Result<(), ClientRejection> {
238 match self.state.as_ref() {
239 State::Associated { .. } => {}
240 _ => {
241 return Ok(());
243 }
244 }
245
246 self.change_state(ctx, State::Authenticated).await.map_err(ClientRejection::DeviceError)?;
247
248 let buffer = ctx
251 .make_disassoc_frame(
252 self.addr.clone(),
253 fidl_ieee80211::ReasonCode::ReasonInactivity.into(),
254 )
255 .map_err(ClientRejection::WlanSendError)?;
256 self.send_wlan_frame(ctx, buffer, fidl_softmac::WlanTxInfoFlags::empty(), None).map_err(
257 |s| {
258 ClientRejection::WlanSendError(Error::Status(
259 format!("error sending disassoc frame on BSS idle timeout"),
260 s,
261 ))
262 },
263 )?;
264 ctx.send_mlme_disassoc_ind(
265 self.addr.clone(),
266 fidl_ieee80211::ReasonCode::ReasonInactivity,
267 LocallyInitiated(true),
268 )
269 .map_err(ClientRejection::SmeSendError)?;
270 Ok(())
271 }
272
273 fn reset_bss_max_idle_timeout<D>(&mut self, ctx: &mut Context<D>) {
278 let new_active_timeout = match self.state.as_ref() {
285 State::Associated { .. } => Some(self.schedule_bss_idle_timeout(ctx)),
286 _ => None,
287 };
288
289 match self.state.as_mut() {
290 State::Associated { active_timeout, .. } => {
291 *active_timeout = new_active_timeout;
292 }
293 _ => (),
294 }
295 }
296
297 fn is_frame_class_permitted(&self, frame_class: FrameClass) -> bool {
298 frame_class <= self.state.as_ref().max_frame_class()
299 }
300
301 pub async fn handle_event<D: DeviceOps>(
302 &mut self,
303 ctx: &mut Context<D>,
304 event: ClientEvent,
305 ) -> Result<(), ClientRejection> {
306 match event {
307 ClientEvent::BssIdleTimeout => self.handle_bss_idle_timeout(ctx).await,
308 }
309 }
310
311 pub async fn handle_mlme_auth_resp<D: DeviceOps>(
319 &mut self,
320 ctx: &mut Context<D>,
321 result_code: fidl_mlme::AuthenticateResultCode,
322 ) -> Result<(), Error> {
323 log::info!("enter handle_mlme_auth_resp");
325 self.change_state(
326 ctx,
327 if result_code == fidl_mlme::AuthenticateResultCode::Success {
328 State::Authenticated
329 } else {
330 State::Deauthenticated
331 },
332 )
333 .await?;
334
335 log::info!("creating auth frame");
337
338 let buffer = ctx.make_auth_frame(
342 self.addr.clone(),
343 AuthAlgorithmNumber::OPEN,
344 2,
345 match result_code {
346 fidl_mlme::AuthenticateResultCode::Success => {
347 fidl_ieee80211::StatusCode::Success.into()
348 }
349 fidl_mlme::AuthenticateResultCode::Refused => {
350 fidl_ieee80211::StatusCode::RefusedReasonUnspecified.into()
351 }
352 fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired => {
353 fidl_ieee80211::StatusCode::AntiCloggingTokenRequired.into()
354 }
355 fidl_mlme::AuthenticateResultCode::FiniteCyclicGroupNotSupported => {
356 fidl_ieee80211::StatusCode::UnsupportedFiniteCyclicGroup.into()
357 }
358 fidl_mlme::AuthenticateResultCode::AuthenticationRejected => {
359 fidl_ieee80211::StatusCode::ChallengeFailure.into()
360 }
361 fidl_mlme::AuthenticateResultCode::AuthFailureTimeout => {
362 fidl_ieee80211::StatusCode::RejectedSequenceTimeout.into()
363 }
364 },
365 )?;
366 log::info!("Sending auth frame to driver: {} bytes", buffer.len());
368 self.send_wlan_frame(ctx, buffer, fidl_softmac::WlanTxInfoFlags::empty(), None)
369 .map_err(|s| Error::Status(format!("error sending auth frame"), s))
370 }
371
372 pub async fn handle_mlme_deauth_req<D: DeviceOps>(
378 &mut self,
379 ctx: &mut Context<D>,
380 reason_code: fidl_ieee80211::ReasonCode,
381 ) -> Result<(), Error> {
382 self.change_state(ctx, State::Deauthenticated).await?;
383
384 let buffer = ctx.make_deauth_frame(self.addr.clone(), reason_code.into())?;
390 self.send_wlan_frame(ctx, buffer, fidl_softmac::WlanTxInfoFlags::empty(), None)
391 .map_err(|s| Error::Status(format!("error sending deauth frame"), s))
392 }
393
394 pub async fn handle_mlme_assoc_resp<D: DeviceOps>(
401 &mut self,
402 ctx: &mut Context<D>,
403 is_rsn: bool,
404 channel: u8,
405 capabilities: mac::CapabilityInfo,
406 result_code: fidl_mlme::AssociateResultCode,
407 aid: Aid,
408 rates: &[u8],
409 ) -> Result<(), Error> {
410 self.change_state(
411 ctx,
412 if result_code == fidl_mlme::AssociateResultCode::Success {
413 State::Associated {
414 aid,
415 eapol_controlled_port: if is_rsn {
416 Some(fidl_mlme::ControlledPortState::Closed)
417 } else {
418 None
419 },
420 active_timeout: None,
421 ps_state: PowerSaveState::Awake,
422 }
423 } else {
424 State::Authenticated
425 },
426 )
427 .await?;
428
429 if let State::Associated { .. } = self.state.as_ref() {
430 self.reset_bss_max_idle_timeout(ctx);
433 ctx.device
434 .notify_association_complete(fidl_softmac::WlanAssociationConfig {
435 bssid: Some(self.addr.to_array()),
436 aid: Some(aid),
437 listen_interval: None, channel: Some(fidl_ieee80211::WlanChannel {
439 primary: channel,
440 cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
442 secondary80: 0,
443 }),
444
445 qos: Some(false),
446 wmm_params: None,
447
448 rates: Some(rates.to_vec()),
449 capability_info: Some(capabilities.raw()),
450
451 ht_cap: None,
453 ht_op: None,
454 vht_cap: None,
455 vht_op: None,
456 ..Default::default()
457 })
458 .await
459 .map_err(|s| Error::Status(format!("failed to configure association"), s))?;
460 }
461
462 let buffer = match result_code {
463 fidl_mlme::AssociateResultCode::Success => ctx.make_assoc_resp_frame(
464 self.addr,
465 capabilities,
466 aid,
467 rates,
468 Some(BSS_MAX_IDLE_PERIOD),
469 ),
470 _ => ctx.make_assoc_resp_frame_error(
471 self.addr,
472 capabilities,
473 match result_code {
474 fidl_mlme::AssociateResultCode::Success => {
475 panic!("Success should have already been handled");
476 }
477 fidl_mlme::AssociateResultCode::RefusedReasonUnspecified => {
478 fidl_ieee80211::StatusCode::RefusedReasonUnspecified.into()
479 }
480 fidl_mlme::AssociateResultCode::RefusedNotAuthenticated => {
481 fidl_ieee80211::StatusCode::RefusedUnauthenticatedAccessNotSupported.into()
482 }
483 fidl_mlme::AssociateResultCode::RefusedCapabilitiesMismatch => {
484 fidl_ieee80211::StatusCode::RefusedCapabilitiesMismatch.into()
485 }
486 fidl_mlme::AssociateResultCode::RefusedExternalReason => {
487 fidl_ieee80211::StatusCode::RefusedExternalReason.into()
488 }
489 fidl_mlme::AssociateResultCode::RefusedApOutOfMemory => {
490 fidl_ieee80211::StatusCode::RefusedApOutOfMemory.into()
491 }
492 fidl_mlme::AssociateResultCode::RefusedBasicRatesMismatch => {
493 fidl_ieee80211::StatusCode::RefusedBasicRatesMismatch.into()
494 }
495 fidl_mlme::AssociateResultCode::RejectedEmergencyServicesNotSupported => {
496 fidl_ieee80211::StatusCode::RejectedEmergencyServicesNotSupported.into()
497 }
498 fidl_mlme::AssociateResultCode::RefusedTemporarily => {
499 fidl_ieee80211::StatusCode::RefusedTemporarily.into()
500 }
501 },
502 ),
503 }?;
504 self.send_wlan_frame(ctx, buffer, fidl_softmac::WlanTxInfoFlags::empty(), None)
505 .map_err(|s| Error::Status(format!("error sending assoc frame"), s))
506 }
507
508 pub async fn handle_mlme_disassoc_req<D: DeviceOps>(
515 &mut self,
516 ctx: &mut Context<D>,
517 reason_code: u16,
518 ) -> Result<(), Error> {
519 self.change_state(ctx, State::Authenticated).await?;
520
521 let buffer = ctx.make_disassoc_frame(self.addr.clone(), ReasonCode(reason_code))?;
526 self.send_wlan_frame(ctx, buffer, fidl_softmac::WlanTxInfoFlags::empty(), None)
527 .map_err(|s| Error::Status(format!("error sending disassoc frame"), s))
528 }
529
530 pub fn handle_mlme_set_controlled_port_req(
533 &mut self,
534 state: fidl_mlme::ControlledPortState,
535 ) -> Result<(), Error> {
536 match self.state.as_mut() {
537 State::Associated {
538 eapol_controlled_port: eapol_controlled_port @ Some(_), ..
539 } => {
540 eapol_controlled_port.replace(state);
541 Ok(())
542 }
543 State::Associated { eapol_controlled_port: None, .. } => {
544 Err(Error::Status(format!("client is not in an RSN"), zx::Status::BAD_STATE))
545 }
546 _ => Err(Error::Status(format!("client is not associated"), zx::Status::BAD_STATE)),
547 }
548 }
549
550 pub fn handle_mlme_eapol_req<D: DeviceOps>(
554 &mut self,
555 ctx: &mut Context<D>,
556 src_addr: MacAddr,
557 data: &[u8],
558 ) -> Result<(), Error> {
559 let buffer = ctx.make_eapol_frame(self.addr, src_addr, false, data)?;
563 self.send_wlan_frame(ctx, buffer, fidl_softmac::WlanTxInfoFlags::FAVOR_RELIABILITY, None)
564 .map_err(|s| Error::Status(format!("error sending eapol frame"), s))
565 }
566
567 async fn handle_disassoc_frame<D: DeviceOps>(
573 &mut self,
574 ctx: &mut Context<D>,
575 reason_code: ReasonCode,
576 ) -> Result<(), ClientRejection> {
577 self.change_state(ctx, State::Authenticated).await.map_err(ClientRejection::DeviceError)?;
578 ctx.send_mlme_disassoc_ind(
579 self.addr.clone(),
580 Option::<fidl_ieee80211::ReasonCode>::from(reason_code)
581 .unwrap_or(fidl_ieee80211::ReasonCode::UnspecifiedReason),
582 LocallyInitiated(false),
583 )
584 .map_err(ClientRejection::SmeSendError)
585 }
586
587 fn handle_assoc_req_frame<D: DeviceOps>(
589 &self,
590 ctx: &mut Context<D>,
591 capabilities: mac::CapabilityInfo,
592 listen_interval: u16,
593 ssid: Option<Ssid>,
594 rates: Vec<ie::SupportedRate>,
595 rsne: Option<Vec<u8>>,
596 ) -> Result<(), ClientRejection> {
597 ctx.send_mlme_assoc_ind(self.addr.clone(), listen_interval, ssid, capabilities, rates, rsne)
598 .map_err(ClientRejection::SmeSendError)
599 }
600
601 async fn handle_auth_frame<D: DeviceOps>(
606 &mut self,
607 ctx: &mut Context<D>,
608 auth_alg_num: AuthAlgorithmNumber,
609 ) -> Result<(), ClientRejection> {
610 ctx.send_mlme_auth_ind(
611 self.addr.clone(),
612 match auth_alg_num {
613 AuthAlgorithmNumber::OPEN => fidl_mlme::AuthenticationTypes::OpenSystem,
614 AuthAlgorithmNumber::SHARED_KEY => fidl_mlme::AuthenticationTypes::SharedKey,
615 AuthAlgorithmNumber::FAST_BSS_TRANSITION => {
616 fidl_mlme::AuthenticationTypes::FastBssTransition
617 }
618 AuthAlgorithmNumber::SAE => fidl_mlme::AuthenticationTypes::Sae,
619 _ => {
620 self.change_state(ctx, State::Deauthenticated)
621 .await
622 .map_err(ClientRejection::DeviceError)?;
623
624 let buffer = ctx
627 .make_auth_frame(
628 self.addr.clone(),
629 auth_alg_num,
630 2,
631 fidl_ieee80211::StatusCode::UnsupportedAuthAlgorithm.into(),
632 )
633 .map_err(ClientRejection::WlanSendError)?;
634 return self
635 .send_wlan_frame(ctx, buffer, fidl_softmac::WlanTxInfoFlags::empty(), None)
636 .map_err(|s| {
637 ClientRejection::WlanSendError(Error::Status(
638 format!("failed to send auth frame"),
639 s,
640 ))
641 });
642 }
643 },
644 )
645 .map_err(ClientRejection::SmeSendError)
646 }
647
648 async fn handle_deauth_frame<D: DeviceOps>(
652 &mut self,
653 ctx: &mut Context<D>,
654 reason_code: ReasonCode,
655 ) -> Result<(), ClientRejection> {
656 self.change_state(ctx, State::Deauthenticated)
657 .await
658 .map_err(ClientRejection::DeviceError)?;
659 ctx.send_mlme_deauth_ind(
660 self.addr.clone(),
661 Option::<fidl_ieee80211::ReasonCode>::from(reason_code)
662 .unwrap_or(fidl_ieee80211::ReasonCode::UnspecifiedReason),
663 LocallyInitiated(false),
664 )
665 .map_err(ClientRejection::SmeSendError)
666 }
667
668 fn handle_action_frame<D>(&self, _ctx: &mut Context<D>) -> Result<(), ClientRejection> {
670 Ok(())
672 }
673
674 pub fn handle_ps_poll<D: DeviceOps>(
676 &mut self,
677 ctx: &mut Context<D>,
678 aid: Aid,
679 ) -> Result<(), ClientRejection> {
680 self.reject_frame_class_if_not_permitted(ctx, mac::FrameClass::Class3)?;
682
683 match self.state.as_mut() {
684 State::Associated { aid: current_aid, ps_state, .. } => {
685 if aid != *current_aid {
686 return Err(ClientRejection::NotPermitted);
687 }
688
689 match ps_state {
690 PowerSaveState::Dozing { buffered } => {
691 let BufferedFrame { mut buffer, tx_flags, async_id } =
692 match buffered.pop_front() {
693 Some(buffered) => buffered,
694 None => {
695 return Ok(());
698 }
699 };
700 if !buffered.is_empty() {
701 frame_writer::set_more_data(&mut buffer[..]).map_err(|e| {
702 wtrace::async_end_wlansoftmac_tx(async_id, zx::Status::INTERNAL);
703 ClientRejection::WlanSendError(e)
704 })?;
705 }
706 ctx.device.send_wlan_frame(buffer, tx_flags, None).map_err(|s| {
707 wtrace::async_end_wlansoftmac_tx(async_id, s);
708 ClientRejection::WlanSendError(Error::Status(
709 format!("error sending buffered frame on PS-Poll"),
710 s,
711 ))
712 })?;
713 }
714 _ => {
715 return Err(ClientRejection::NotPermitted);
716 }
717 }
718 }
719 _ => {
720 return Err(ClientRejection::NotAssociated);
721 }
722 };
723 Ok(())
724 }
725
726 fn doze(&mut self) -> Result<(), ClientRejection> {
728 match self.state.as_mut() {
729 State::Associated { ps_state, .. } => match ps_state {
730 PowerSaveState::Awake => {
731 *ps_state = PowerSaveState::Dozing {
732 buffered: VecDeque::new(),
734 }
735 }
736 PowerSaveState::Dozing { .. } => {}
737 },
738 _ => {
739 return Err(ClientRejection::NotAssociated);
741 }
742 };
743 Ok(())
744 }
745
746 fn wake<D: DeviceOps>(&mut self, ctx: &mut Context<D>) -> Result<(), ClientRejection> {
750 match self.state.as_mut() {
751 State::Associated { ps_state, .. } => {
752 let mut old_ps_state = PowerSaveState::Awake;
753 std::mem::swap(ps_state, &mut old_ps_state);
754
755 let mut buffered = match old_ps_state {
756 PowerSaveState::Awake => {
757 return Ok(());
759 }
760 PowerSaveState::Dozing { buffered } => buffered.into_iter().peekable(),
761 };
762
763 while let Some(BufferedFrame { mut buffer, tx_flags, async_id }) = buffered.next() {
764 if buffered.peek().is_some() {
765 frame_writer::set_more_data(&mut buffer[..])
776 .map_err(ClientRejection::WlanSendError)?;
777 }
778 ctx.device.send_wlan_frame(buffer, tx_flags, Some(async_id)).map_err(|s| {
779 ClientRejection::WlanSendError(Error::Status(
780 format!("error sending buffered frame on wake"),
781 s,
782 ))
783 })?;
784 }
785 }
786 _ => {
787 return Ok(());
789 }
790 };
791 Ok(())
792 }
793
794 pub fn set_power_state<D: DeviceOps>(
795 &mut self,
796 ctx: &mut Context<D>,
797 power_state: mac::PowerState,
798 ) -> Result<(), ClientRejection> {
799 match power_state {
800 mac::PowerState::AWAKE => self.wake(ctx),
801 mac::PowerState::DOZE => self.doze(),
802 }
803 }
804
805 fn handle_eapol_llc_frame<D: DeviceOps>(
807 &self,
808 ctx: &mut Context<D>,
809 dst_addr: MacAddr,
810 src_addr: MacAddr,
811 body: &[u8],
812 ) -> Result<(), ClientRejection> {
813 ctx.send_mlme_eapol_ind(dst_addr, src_addr, &body).map_err(ClientRejection::SmeSendError)
814 }
815
816 fn handle_llc_frame<D: DeviceOps>(
818 &self,
819 ctx: &mut Context<D>,
820 dst_addr: MacAddr,
821 src_addr: MacAddr,
822 ether_type: u16,
823 body: &[u8],
824 ) -> Result<(), ClientRejection> {
825 ctx.deliver_eth_frame(dst_addr, src_addr, ether_type, body)
826 .map_err(ClientRejection::EthSendError)
827 }
828
829 fn reject_frame_class_if_not_permitted<D: DeviceOps>(
835 &mut self,
836 ctx: &mut Context<D>,
837 frame_class: FrameClass,
838 ) -> Result<(), ClientRejection> {
839 if self.is_frame_class_permitted(frame_class) {
840 return Ok(());
841 }
842
843 let reason_code = match frame_class {
844 FrameClass::Class1 => panic!("class 1 frames should always be permitted"),
845 FrameClass::Class2 => fidl_ieee80211::ReasonCode::InvalidClass2Frame,
846 FrameClass::Class3 => fidl_ieee80211::ReasonCode::InvalidClass3Frame,
847 };
848
849 match self.state.as_ref() {
851 State::Deauthenticated | State::Authenticating => {
852 let buffer = ctx
853 .make_deauth_frame(self.addr, reason_code.into())
854 .map_err(ClientRejection::WlanSendError)?;
855 self.send_wlan_frame(ctx, buffer, fidl_softmac::WlanTxInfoFlags::empty(), None)
856 .map_err(|s| {
857 ClientRejection::WlanSendError(Error::Status(
858 format!("failed to send deauth frame"),
859 s,
860 ))
861 })?;
862
863 ctx.send_mlme_deauth_ind(self.addr, reason_code, LocallyInitiated(true))
864 .map_err(ClientRejection::SmeSendError)?;
865 }
866 State::Authenticated => {
867 let buffer = ctx
868 .make_disassoc_frame(self.addr, reason_code.into())
869 .map_err(ClientRejection::WlanSendError)?;
870 self.send_wlan_frame(ctx, buffer, fidl_softmac::WlanTxInfoFlags::empty(), None)
871 .map_err(|s| {
872 ClientRejection::WlanSendError(Error::Status(
873 format!("failed to send disassoc frame"),
874 s,
875 ))
876 })?;
877
878 ctx.send_mlme_disassoc_ind(self.addr, reason_code, LocallyInitiated(true))
879 .map_err(ClientRejection::SmeSendError)?;
880 }
881 State::Associated { .. } => {
882 panic!("all frames should be permitted for an associated client")
883 }
884 };
885
886 return Err(ClientRejection::NotPermitted);
887 }
888
889 pub async fn handle_mgmt_frame<B: SplitByteSlice, D: DeviceOps>(
893 &mut self,
894 ctx: &mut Context<D>,
895 capabilities: mac::CapabilityInfo,
896 ssid: Option<Ssid>,
897 mgmt_frame: mac::MgmtFrame<B>,
898 ) -> Result<(), ClientRejection> {
899 self.reject_frame_class_if_not_permitted(ctx, mac::frame_class(&mgmt_frame.frame_ctrl()))?;
900
901 self.reset_bss_max_idle_timeout(ctx);
902
903 match mgmt_frame.try_into_mgmt_body().1.ok_or(ClientRejection::ParseFailed)? {
904 mac::MgmtBody::Authentication(mac::AuthFrame { auth_hdr, .. }) => {
905 self.handle_auth_frame(ctx, auth_hdr.auth_alg_num).await
906 }
907 mac::MgmtBody::AssociationReq(assoc_req_frame) => {
908 let mut rates = vec![];
909 let mut rsne = None;
910
911 for (id, ie_body) in assoc_req_frame.ies() {
913 match id {
914 ie::Id::SUPPORTED_RATES => {
919 match ie::parse_supported_rates(ie_body) {
920 Ok(supported_rates) => rates.extend(&*supported_rates),
921 Err(e) => warn!("{:?}", e),
922 };
923 }
924 ie::Id::EXTENDED_SUPPORTED_RATES => {
925 match ie::parse_extended_supported_rates(ie_body) {
926 Ok(extended_supported_rates) => {
927 rates.extend(&*extended_supported_rates)
928 }
929 Err(e) => warn!("{:?}", e),
930 };
931 }
932 ie::Id::RSNE => {
933 rsne = Some({
934 let mut buffer =
936 vec![0; std::mem::size_of::<ie::Header>() + ie_body.len()];
937 let mut w = BufferWriter::new(&mut buffer[..]);
938 w.append_value(&ie::Header {
939 id: ie::Id::RSNE,
940 body_len: ie_body.len() as u8,
941 })
942 .expect("expected enough room in buffer for IE header");
943 w.append_bytes(ie_body)
944 .expect("expected enough room in buffer for IE body");
945 buffer
946 });
947 }
948 _ => {}
949 }
950 }
951
952 self.handle_assoc_req_frame(
953 ctx,
954 capabilities,
955 assoc_req_frame.assoc_req_hdr.listen_interval,
956 ssid,
957 rates,
958 rsne,
959 )
960 }
961 mac::MgmtBody::Deauthentication { deauth_hdr, .. } => {
962 self.handle_deauth_frame(ctx, deauth_hdr.reason_code).await
963 }
964 mac::MgmtBody::Disassociation { disassoc_hdr, .. } => {
965 self.handle_disassoc_frame(ctx, disassoc_hdr.reason_code).await
966 }
967 mac::MgmtBody::Action(_) => self.handle_action_frame(ctx),
968 _ => Err(ClientRejection::Unsupported),
969 }
970 }
971
972 pub fn handle_data_frame<B: SplitByteSlice, D: DeviceOps>(
978 &mut self,
979 ctx: &mut Context<D>,
980 data_frame: mac::DataFrame<B>,
981 ) -> Result<(), ClientRejection> {
982 self.reject_frame_class_if_not_permitted(ctx, mac::frame_class(&data_frame.frame_ctrl()))?;
983
984 self.reset_bss_max_idle_timeout(ctx);
985
986 for msdu in data_frame {
987 let mac::Msdu { dst_addr, src_addr, llc_frame } = msdu;
988 match llc_frame.hdr.protocol_id.get() {
989 mac::ETHER_TYPE_EAPOL => {
991 self.handle_eapol_llc_frame(ctx, dst_addr, src_addr, &llc_frame.body)?
992 }
993 _ => match self.state.as_ref() {
995 State::Associated {
997 eapol_controlled_port: Some(fidl_mlme::ControlledPortState::Closed),
998 ..
999 } => (),
1000 _ => self.handle_llc_frame(
1003 ctx,
1004 dst_addr,
1005 src_addr,
1006 llc_frame.hdr.protocol_id.get(),
1007 &llc_frame.body,
1008 )?,
1009 },
1010 }
1011 }
1012 Ok(())
1013 }
1014
1015 pub fn handle_eth_frame<D: DeviceOps>(
1017 &mut self,
1018 ctx: &mut Context<D>,
1019 dst_addr: MacAddr,
1020 src_addr: MacAddr,
1021 ether_type: u16,
1022 body: &[u8],
1023 async_id: trace::Id,
1024 ) -> Result<(), ClientRejection> {
1025 let eapol_controlled_port = match self.state.as_ref() {
1026 State::Associated { eapol_controlled_port, .. } => eapol_controlled_port,
1027 _ => {
1028 return Err(ClientRejection::NotAssociated);
1029 }
1030 };
1031
1032 let protection = match eapol_controlled_port {
1033 None => false,
1034 Some(fidl_mlme::ControlledPortState::Open) => true,
1035 Some(fidl_mlme::ControlledPortState::Closed) => {
1036 return Err(ClientRejection::ControlledPortClosed);
1037 }
1038 };
1039
1040 let buffer = ctx
1041 .make_data_frame(
1042 dst_addr, src_addr, protection,
1043 false, ether_type, body,
1045 )
1046 .map_err(ClientRejection::WlanSendError)?;
1047
1048 self.send_wlan_frame(ctx, buffer, fidl_softmac::WlanTxInfoFlags::empty(), Some(async_id))
1049 .map_err(move |s| {
1050 ClientRejection::WlanSendError(Error::Status(
1051 format!("error sending eapol frame"),
1052 s,
1053 ))
1054 })
1055 }
1056
1057 pub fn send_wlan_frame<D: DeviceOps>(
1058 &mut self,
1059 ctx: &mut Context<D>,
1060 buffer: ArenaStaticBox<[u8]>,
1061 tx_flags: fidl_softmac::WlanTxInfoFlags,
1062 async_id: Option<trace::Id>,
1063 ) -> Result<(), zx::Status> {
1064 let async_id = async_id.unwrap_or_else(|| {
1065 let async_id = trace::Id::new();
1066 wtrace::async_begin_wlansoftmac_tx(async_id, "mlme");
1067 async_id
1068 });
1069
1070 match self.state.as_mut() {
1071 State::Associated { ps_state, .. } => match ps_state {
1072 PowerSaveState::Awake => {
1073 ctx.device.send_wlan_frame(buffer, tx_flags, Some(async_id))
1074 }
1075 PowerSaveState::Dozing { buffered } => {
1076 buffered.push_back(BufferedFrame { buffer, tx_flags, async_id });
1077 Ok(())
1078 }
1079 },
1080 _ => ctx.device.send_wlan_frame(buffer, tx_flags, Some(async_id)),
1081 }
1082 }
1083}
1084
1085#[cfg(test)]
1086mod tests {
1087 use super::*;
1088 use crate::device::FakeDevice;
1089 use assert_matches::assert_matches;
1090 use ieee80211::Bssid;
1091 use std::sync::LazyLock;
1092 use test_case::test_case;
1093 use wlan_common::mac::{CapabilityInfo, IntoBytesExt as _};
1094 use wlan_common::test_utils::fake_frames::*;
1095 use wlan_common::timer::{self, create_timer};
1096 use zerocopy::Unalign;
1097
1098 static CLIENT_ADDR: LazyLock<MacAddr> = LazyLock::new(|| [1; 6].into());
1099 static AP_ADDR: LazyLock<Bssid> = LazyLock::new(|| [2; 6].into());
1100 static CLIENT_ADDR2: LazyLock<MacAddr> = LazyLock::new(|| [3; 6].into());
1101 fn make_remote_client() -> RemoteClient {
1102 RemoteClient::new(*CLIENT_ADDR)
1103 }
1104
1105 fn make_context(
1106 fake_device: FakeDevice,
1107 ) -> (Context<FakeDevice>, timer::EventStream<TimedEvent>) {
1108 let (timer, time_stream) = create_timer();
1109 (Context::new(fake_device, timer, *AP_ADDR), time_stream)
1110 }
1111
1112 #[fuchsia::test(allow_stalls = false)]
1113 async fn handle_mlme_auth_resp() {
1114 let (fake_device, fake_device_state) = FakeDevice::new().await;
1115 let mut r_sta = make_remote_client();
1116 let (mut ctx, _) = make_context(fake_device);
1117 r_sta
1118 .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1119 .await
1120 .expect("expected OK");
1121 assert_matches!(r_sta.state.as_ref(), State::Authenticated);
1122 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1123 #[rustfmt::skip]
1124 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1125 0b10110000, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 0, 0, 2, 0, 0, 0, ][..]);
1137 }
1138
1139 #[fuchsia::test(allow_stalls = false)]
1140 async fn handle_mlme_auth_resp_failure() {
1141 let (fake_device, fake_device_state) = FakeDevice::new().await;
1142 let mut r_sta = make_remote_client();
1143 let (mut ctx, _) = make_context(fake_device);
1144 r_sta
1145 .handle_mlme_auth_resp(
1146 &mut ctx,
1147 fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
1148 )
1149 .await
1150 .expect("expected OK");
1151 assert_matches!(r_sta.state.as_ref(), State::Deauthenticated);
1152 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1153 #[rustfmt::skip]
1154 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1155 0b10110000, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 0, 0, 2, 0, 76, 0, ][..]);
1167 }
1168
1169 #[test_case(State::Authenticating; "in authenticating state")]
1170 #[test_case(State::Authenticated; "in authenticated state")]
1171 #[test_case(State::Associated {
1172 aid: 1,
1173 eapol_controlled_port: None,
1174 active_timeout: None,
1175 ps_state: PowerSaveState::Awake,
1176 }; "in associated state")]
1177 #[fuchsia::test(allow_stalls = false)]
1178 async fn handle_mlme_deauth_req(init_state: State) {
1179 let (fake_device, fake_device_state) = FakeDevice::new().await;
1180 let mut r_sta = make_remote_client();
1181 r_sta.state = StateMachine::new(init_state);
1182 let (mut ctx, _) = make_context(fake_device);
1183 r_sta
1184 .handle_mlme_deauth_req(&mut ctx, fidl_ieee80211::ReasonCode::LeavingNetworkDeauth)
1185 .await
1186 .expect("expected OK");
1187 assert_matches!(r_sta.state.as_ref(), State::Deauthenticated);
1188 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1189 #[rustfmt::skip]
1190 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1191 0b11000000, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 3, 0, ][..]);
1201 }
1202
1203 #[fuchsia::test(allow_stalls = false)]
1204 async fn handle_mlme_assoc_resp() {
1205 let (fake_device, fake_device_state) = FakeDevice::new().await;
1206 let mut r_sta = make_remote_client();
1207 let (mut ctx, mut time_stream) = make_context(fake_device);
1208 r_sta
1209 .handle_mlme_assoc_resp(
1210 &mut ctx,
1211 true,
1212 1,
1213 CapabilityInfo(0),
1214 fidl_mlme::AssociateResultCode::Success,
1215 1,
1216 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1217 )
1218 .await
1219 .expect("expected OK");
1220
1221 assert_matches!(
1222 r_sta.state.as_ref(),
1223 State::Associated {
1224 eapol_controlled_port: Some(fidl_mlme::ControlledPortState::Closed),
1225 ..
1226 }
1227 );
1228
1229 assert_matches!(r_sta.aid(), Some(aid) => {
1230 assert_eq!(aid, 1);
1231 });
1232
1233 let active_timeout = match r_sta.state.as_ref() {
1234 State::Associated { active_timeout: Some(active_timeout), .. } => active_timeout,
1235 _ => panic!("no active timeout?"),
1236 };
1237
1238 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1239 #[rustfmt::skip]
1240 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1241 0b00010000, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 0, 0, 0, 0, 1, 0, 1, 8, 1, 2, 3, 4, 5, 6, 7, 8, 50, 2, 9, 10, 90, 3, 90, 0, 0, ][..]);
1257 let (_, timed_event, _) =
1258 time_stream.try_next().unwrap().expect("Should have scheduled a timeout");
1259 assert_eq!(timed_event.id, active_timeout.id());
1260
1261 assert!(fake_device_state.lock().assocs.contains_key(&CLIENT_ADDR));
1262 }
1263
1264 #[fuchsia::test(allow_stalls = false)]
1265 async fn handle_mlme_assoc_resp_then_handle_mlme_disassoc_req() {
1266 let (fake_device, fake_device_state) = FakeDevice::new().await;
1267 let mut r_sta = make_remote_client();
1268 let (mut ctx, _) = make_context(fake_device);
1269
1270 r_sta
1271 .handle_mlme_assoc_resp(
1272 &mut ctx,
1273 true,
1274 1,
1275 CapabilityInfo(0),
1276 fidl_mlme::AssociateResultCode::Success,
1277 1,
1278 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1279 )
1280 .await
1281 .expect("expected OK");
1282 assert!(fake_device_state.lock().assocs.contains_key(&CLIENT_ADDR));
1283
1284 r_sta
1285 .handle_mlme_disassoc_req(
1286 &mut ctx,
1287 fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc.into_primitive(),
1288 )
1289 .await
1290 .expect("expected OK");
1291 assert!(!fake_device_state.lock().assocs.contains_key(&CLIENT_ADDR));
1292 }
1293
1294 #[fuchsia::test(allow_stalls = false)]
1295 async fn handle_mlme_assoc_resp_then_handle_mlme_deauth_req() {
1296 let (fake_device, fake_device_state) = FakeDevice::new().await;
1297 let mut r_sta = make_remote_client();
1298 let (mut ctx, _) = make_context(fake_device);
1299
1300 r_sta
1301 .handle_mlme_assoc_resp(
1302 &mut ctx,
1303 true,
1304 1,
1305 CapabilityInfo(0),
1306 fidl_mlme::AssociateResultCode::Success,
1307 1,
1308 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1309 )
1310 .await
1311 .expect("expected OK");
1312 assert!(fake_device_state.lock().assocs.contains_key(&CLIENT_ADDR));
1313
1314 r_sta
1315 .handle_mlme_deauth_req(&mut ctx, fidl_ieee80211::ReasonCode::LeavingNetworkDeauth)
1316 .await
1317 .expect("expected OK");
1318 assert!(!fake_device_state.lock().assocs.contains_key(&CLIENT_ADDR));
1319 }
1320
1321 #[fuchsia::test(allow_stalls = false)]
1322 async fn handle_mlme_assoc_resp_no_rsn() {
1323 let (fake_device, _) = FakeDevice::new().await;
1324 let mut r_sta = make_remote_client();
1325 let (mut ctx, _) = make_context(fake_device);
1326 r_sta
1327 .handle_mlme_assoc_resp(
1328 &mut ctx,
1329 false,
1330 1,
1331 CapabilityInfo(0),
1332 fidl_mlme::AssociateResultCode::Success,
1333 1,
1334 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1335 )
1336 .await
1337 .expect("expected OK");
1338 assert_matches!(
1339 r_sta.state.as_ref(),
1340 State::Associated { eapol_controlled_port: None, active_timeout: Some(_), .. }
1341 );
1342 }
1343
1344 #[fuchsia::test(allow_stalls = false)]
1345 async fn handle_mlme_assoc_resp_failure_reason_unspecified() {
1346 let (fake_device, fake_device_state) = FakeDevice::new().await;
1347 let mut r_sta = make_remote_client();
1348 let (mut ctx, _) = make_context(fake_device);
1349 r_sta
1350 .handle_mlme_assoc_resp(
1351 &mut ctx,
1352 false,
1353 1,
1354 CapabilityInfo(0),
1355 fidl_mlme::AssociateResultCode::RefusedReasonUnspecified,
1356 1, &[][..],
1358 )
1359 .await
1360 .expect("expected OK");
1361 assert_matches!(r_sta.state.as_ref(), State::Authenticated);
1362 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1363 #[rustfmt::skip]
1364 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1365 0b00010000, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 0, 0, 1, 0, 0, 0, ][..]);
1377 }
1378
1379 #[fuchsia::test(allow_stalls = false)]
1380 async fn handle_mlme_assoc_resp_failure_emergency_services_not_supported() {
1381 let (fake_device, fake_device_state) = FakeDevice::new().await;
1382 let mut r_sta = make_remote_client();
1383 let (mut ctx, _) = make_context(fake_device);
1384 r_sta
1385 .handle_mlme_assoc_resp(
1386 &mut ctx,
1387 false,
1388 1,
1389 CapabilityInfo(0),
1390 fidl_mlme::AssociateResultCode::RejectedEmergencyServicesNotSupported,
1391 1, &[][..],
1393 )
1394 .await
1395 .expect("expected OK");
1396 assert_matches!(r_sta.state.as_ref(), State::Authenticated);
1397 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1398 #[rustfmt::skip]
1399 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1400 0b00010000, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 0, 0, 94, 0, 0, 0, ][..]);
1412 }
1413
1414 #[fuchsia::test(allow_stalls = false)]
1415 async fn handle_mlme_disassoc_req() {
1416 let (fake_device, fake_device_state) = FakeDevice::new().await;
1417 let mut r_sta = make_remote_client();
1418 let (mut ctx, _) = make_context(fake_device);
1419 r_sta
1420 .handle_mlme_disassoc_req(
1421 &mut ctx,
1422 fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc.into_primitive(),
1423 )
1424 .await
1425 .expect("expected OK");
1426 assert_matches!(r_sta.state.as_ref(), State::Authenticated);
1427 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1428 #[rustfmt::skip]
1429 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1430 0b10100000, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 8, 0, ][..]);
1440 }
1441
1442 #[test]
1443 fn handle_mlme_set_controlled_port_req() {
1444 let mut r_sta = make_remote_client();
1445 r_sta.state = StateMachine::new(State::Associated {
1446 aid: 1,
1447 eapol_controlled_port: Some(fidl_mlme::ControlledPortState::Closed),
1448 active_timeout: None,
1449 ps_state: PowerSaveState::Awake,
1450 });
1451 r_sta
1452 .handle_mlme_set_controlled_port_req(fidl_mlme::ControlledPortState::Open)
1453 .expect("expected OK");
1454 assert_matches!(
1455 r_sta.state.as_ref(),
1456 State::Associated {
1457 eapol_controlled_port: Some(fidl_mlme::ControlledPortState::Open),
1458 ..
1459 }
1460 );
1461 }
1462
1463 #[test]
1464 fn handle_mlme_set_controlled_port_req_closed() {
1465 let mut r_sta = make_remote_client();
1466 r_sta.state = StateMachine::new(State::Associated {
1467 aid: 1,
1468 eapol_controlled_port: Some(fidl_mlme::ControlledPortState::Open),
1469 active_timeout: None,
1470 ps_state: PowerSaveState::Awake,
1471 });
1472 r_sta
1473 .handle_mlme_set_controlled_port_req(fidl_mlme::ControlledPortState::Closed)
1474 .expect("expected OK");
1475 assert_matches!(
1476 r_sta.state.as_ref(),
1477 State::Associated {
1478 eapol_controlled_port: Some(fidl_mlme::ControlledPortState::Closed),
1479 ..
1480 }
1481 );
1482 }
1483
1484 #[test]
1485 fn handle_mlme_set_controlled_port_req_no_rsn() {
1486 let mut r_sta = make_remote_client();
1487 r_sta.state = StateMachine::new(State::Associated {
1488 aid: 1,
1489 eapol_controlled_port: None,
1490 active_timeout: None,
1491 ps_state: PowerSaveState::Awake,
1492 });
1493 assert_eq!(
1494 zx::Status::from(
1495 r_sta
1496 .handle_mlme_set_controlled_port_req(fidl_mlme::ControlledPortState::Open)
1497 .expect_err("expected err")
1498 ),
1499 zx::Status::BAD_STATE
1500 );
1501 assert_matches!(
1502 r_sta.state.as_ref(),
1503 State::Associated { eapol_controlled_port: None, .. }
1504 );
1505 }
1506
1507 #[test]
1508 fn handle_mlme_set_controlled_port_req_wrong_state() {
1509 let mut r_sta = make_remote_client();
1510 r_sta.state = StateMachine::new(State::Authenticating);
1511 assert_eq!(
1512 zx::Status::from(
1513 r_sta
1514 .handle_mlme_set_controlled_port_req(fidl_mlme::ControlledPortState::Open)
1515 .expect_err("expected err")
1516 ),
1517 zx::Status::BAD_STATE
1518 );
1519 }
1520
1521 #[fuchsia::test(allow_stalls = false)]
1522 async fn handle_mlme_eapol_req() {
1523 let (fake_device, fake_device_state) = FakeDevice::new().await;
1524 let mut r_sta = make_remote_client();
1525 let (mut ctx, _) = make_context(fake_device);
1526 r_sta.handle_mlme_eapol_req(&mut ctx, *CLIENT_ADDR2, &[1, 2, 3][..]).expect("expected OK");
1527 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1528 #[rustfmt::skip]
1529 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1530 0b00001000, 0b00000010, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 0x10, 0, 0xAA, 0xAA, 0x03, 0, 0, 0, 0x88, 0x8E, 1, 2, 3,
1542 ][..]);
1543 }
1544
1545 #[fuchsia::test(allow_stalls = false)]
1546 async fn handle_disassoc_frame() {
1547 let (fake_device, fake_device_state) = FakeDevice::new().await;
1548 let mut r_sta = make_remote_client();
1549 let (mut ctx, _) = make_context(fake_device);
1550 r_sta
1551 .handle_disassoc_frame(
1552 &mut ctx,
1553 ReasonCode(fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc.into_primitive()),
1554 )
1555 .await
1556 .expect("expected OK");
1557
1558 let msg = fake_device_state
1559 .lock()
1560 .next_mlme_msg::<fidl_mlme::DisassociateIndication>()
1561 .expect("expected MLME message");
1562 assert_eq!(
1563 msg,
1564 fidl_mlme::DisassociateIndication {
1565 peer_sta_address: CLIENT_ADDR.to_array(),
1566 reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc,
1567 locally_initiated: false,
1568 },
1569 );
1570 assert_matches!(r_sta.state.as_ref(), State::Authenticated);
1571 }
1572
1573 #[test_case(State::Authenticating; "in authenticating state")]
1574 #[test_case(State::Authenticated; "in authenticated state")]
1575 #[test_case(State::Associated {
1576 aid: 1,
1577 eapol_controlled_port: None,
1578 active_timeout: None,
1579 ps_state: PowerSaveState::Awake,
1580 }; "in associated state")]
1581 #[fuchsia::test(allow_stalls = false)]
1582 async fn handle_assoc_req_frame(init_state: State) {
1583 let (fake_device, fake_device_state) = FakeDevice::new().await;
1584 let mut r_sta = make_remote_client();
1585 r_sta.state = StateMachine::new(init_state);
1586 let (mut ctx, _) = make_context(fake_device);
1587 r_sta
1588 .handle_assoc_req_frame(
1589 &mut ctx,
1590 CapabilityInfo(0).with_short_preamble(true),
1591 1,
1592 Some(Ssid::try_from("coolnet").unwrap()),
1593 vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].iter().map(|r| ie::SupportedRate(*r)).collect(),
1594 None,
1595 )
1596 .expect("expected OK");
1597
1598 let msg = fake_device_state
1599 .lock()
1600 .next_mlme_msg::<fidl_mlme::AssociateIndication>()
1601 .expect("expected MLME message");
1602 assert_eq!(
1603 msg,
1604 fidl_mlme::AssociateIndication {
1605 peer_sta_address: CLIENT_ADDR.to_array(),
1606 listen_interval: 1,
1607 ssid: Some(Ssid::try_from("coolnet").unwrap().into()),
1608 capability_info: CapabilityInfo(0).with_short_preamble(true).raw(),
1609 rates: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
1610 rsne: None,
1611 },
1612 );
1613 }
1614
1615 #[test_case(State::Authenticating; "in authenticating state")]
1616 #[test_case(State::Authenticated; "in authenticated state")]
1617 #[test_case(State::Associated {
1618 aid: 1,
1619 eapol_controlled_port: None,
1620 active_timeout: None,
1621 ps_state: PowerSaveState::Awake,
1622 }; "in associated state")]
1623 #[fuchsia::test(allow_stalls = false)]
1624 async fn handle_auth_frame(init_state: State) {
1625 let (fake_device, fake_device_state) = FakeDevice::new().await;
1626 let mut r_sta = make_remote_client();
1627 r_sta.state = StateMachine::new(init_state);
1628 let (mut ctx, _) = make_context(fake_device);
1629
1630 r_sta
1631 .handle_auth_frame(&mut ctx, AuthAlgorithmNumber::SHARED_KEY)
1632 .await
1633 .expect("expected OK");
1634 let msg = fake_device_state
1635 .lock()
1636 .next_mlme_msg::<fidl_mlme::AuthenticateIndication>()
1637 .expect("expected MLME message");
1638 assert_eq!(
1639 msg,
1640 fidl_mlme::AuthenticateIndication {
1641 peer_sta_address: CLIENT_ADDR.to_array(),
1642 auth_type: fidl_mlme::AuthenticationTypes::SharedKey,
1643 },
1644 );
1645 }
1646
1647 #[fuchsia::test(allow_stalls = false)]
1648 async fn handle_auth_frame_unknown_algorithm() {
1649 let (fake_device, fake_device_state) = FakeDevice::new().await;
1650 let mut r_sta = make_remote_client();
1651 let (mut ctx, _) = make_context(fake_device);
1652
1653 r_sta.handle_auth_frame(&mut ctx, AuthAlgorithmNumber(0xffff)).await.expect("expected OK");
1654 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1655 #[rustfmt::skip]
1656 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1657 0b10110000, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 0xff, 0xff, 2, 0, 13, 0, ][..]);
1669 assert_matches!(r_sta.state.as_ref(), State::Deauthenticated);
1670 }
1671
1672 #[test_case(false; "from idle state")]
1673 #[test_case(true; "while already authenticated")]
1674 #[fuchsia::test(allow_stalls = false)]
1675 async fn handle_deauth_frame(already_authenticated: bool) {
1676 let (fake_device, fake_device_state) = FakeDevice::new().await;
1677 let mut r_sta = make_remote_client();
1678 if already_authenticated {
1679 r_sta.state = StateMachine::new(State::Authenticated);
1680 }
1681 let (mut ctx, _) = make_context(fake_device);
1682
1683 r_sta
1684 .handle_deauth_frame(
1685 &mut ctx,
1686 ReasonCode(fidl_ieee80211::ReasonCode::LeavingNetworkDeauth.into_primitive()),
1687 )
1688 .await
1689 .expect("expected OK");
1690 let msg = fake_device_state
1691 .lock()
1692 .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
1693 .expect("expected MLME message");
1694 assert_eq!(
1695 msg,
1696 fidl_mlme::DeauthenticateIndication {
1697 peer_sta_address: CLIENT_ADDR.to_array(),
1698 reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
1699 locally_initiated: false,
1700 }
1701 );
1702 assert_matches!(r_sta.state.as_ref(), State::Deauthenticated);
1703 }
1704
1705 #[test]
1706 fn handle_action_frame() {
1707 }
1709
1710 #[fuchsia::test(allow_stalls = false)]
1711 async fn handle_ps_poll() {
1712 let (fake_device, fake_device_state) = FakeDevice::new().await;
1713 let (mut ctx, _) = make_context(fake_device);
1714
1715 let mut r_sta = make_remote_client();
1716 r_sta.state = StateMachine::new(State::Associated {
1717 aid: 1,
1718 eapol_controlled_port: None,
1719 active_timeout: None,
1720 ps_state: PowerSaveState::Awake,
1721 });
1722
1723 r_sta.set_power_state(&mut ctx, mac::PowerState::DOZE).expect("expected doze OK");
1724
1725 r_sta
1727 .handle_eth_frame(
1728 &mut ctx,
1729 *CLIENT_ADDR2,
1730 *CLIENT_ADDR,
1731 0x1234,
1732 &[1, 2, 3, 4, 5][..],
1733 0.into(),
1734 )
1735 .expect("expected OK");
1736 r_sta
1737 .handle_eth_frame(
1738 &mut ctx,
1739 *CLIENT_ADDR2,
1740 *CLIENT_ADDR,
1741 0x1234,
1742 &[6, 7, 8, 9, 0][..],
1743 0.into(),
1744 )
1745 .expect("expected OK");
1746
1747 assert_eq!(fake_device_state.lock().wlan_queue.len(), 0);
1749
1750 r_sta.handle_ps_poll(&mut ctx, 1).expect("expected handle_ps_poll OK");
1751 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1752 assert_eq!(
1753 &fake_device_state.lock().wlan_queue[0].0[..],
1754 &[
1755 0b00001000, 0b00100010, 0, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0x10, 0, 0xAA, 0xAA, 0x03, 0, 0, 0, 0x12, 0x34, 1, 2, 3, 4, 5,
1767 ][..]
1768 );
1769
1770 r_sta.handle_ps_poll(&mut ctx, 1).expect("expected handle_ps_poll OK");
1771 assert_eq!(fake_device_state.lock().wlan_queue.len(), 2);
1772 assert_eq!(
1773 &fake_device_state.lock().wlan_queue[1].0[..],
1774 &[
1775 0b00001000, 0b00000010, 0, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0x20, 0, 0xAA, 0xAA, 0x03, 0, 0, 0, 0x12, 0x34, 6, 7, 8, 9, 0,
1787 ][..]
1788 );
1789
1790 r_sta.handle_ps_poll(&mut ctx, 1).expect("expected handle_ps_poll OK");
1791 assert_eq!(fake_device_state.lock().wlan_queue.len(), 2);
1792 }
1793
1794 #[fuchsia::test(allow_stalls = false)]
1795 async fn handle_ps_poll_not_buffered() {
1796 let (fake_device, _) = FakeDevice::new().await;
1797 let (mut ctx, _) = make_context(fake_device);
1798
1799 let mut r_sta = make_remote_client();
1800 r_sta.state = StateMachine::new(State::Associated {
1801 aid: 1,
1802 eapol_controlled_port: None,
1803 active_timeout: None,
1804 ps_state: PowerSaveState::Awake,
1805 });
1806
1807 r_sta.set_power_state(&mut ctx, mac::PowerState::DOZE).expect("expected doze OK");
1808
1809 r_sta.handle_ps_poll(&mut ctx, 1).expect("expected handle_ps_poll OK");
1810 }
1811
1812 #[fuchsia::test(allow_stalls = false)]
1813 async fn handle_ps_poll_wrong_aid() {
1814 let (fake_device, _) = FakeDevice::new().await;
1815 let (mut ctx, _) = make_context(fake_device);
1816
1817 let mut r_sta = make_remote_client();
1818 r_sta.state = StateMachine::new(State::Associated {
1819 aid: 1,
1820 eapol_controlled_port: None,
1821 active_timeout: None,
1822 ps_state: PowerSaveState::Awake,
1823 });
1824
1825 r_sta.set_power_state(&mut ctx, mac::PowerState::DOZE).expect("expected doze OK");
1826
1827 assert_matches!(
1828 r_sta.handle_ps_poll(&mut ctx, 2).expect_err("expected handle_ps_poll error"),
1829 ClientRejection::NotPermitted
1830 );
1831 }
1832
1833 #[fuchsia::test(allow_stalls = false)]
1834 async fn handle_ps_poll_not_dozing() {
1835 let (fake_device, _) = FakeDevice::new().await;
1836 let (mut ctx, _) = make_context(fake_device);
1837
1838 let mut r_sta = make_remote_client();
1839 r_sta.state = StateMachine::new(State::Associated {
1840 aid: 1,
1841 eapol_controlled_port: None,
1842 active_timeout: None,
1843 ps_state: PowerSaveState::Awake,
1844 });
1845
1846 assert_matches!(
1847 r_sta.handle_ps_poll(&mut ctx, 1).expect_err("expected handle_ps_poll error"),
1848 ClientRejection::NotPermitted
1849 );
1850 }
1851
1852 #[fuchsia::test(allow_stalls = false)]
1853 async fn handle_eapol_llc_frame() {
1854 let (fake_device, fake_device_state) = FakeDevice::new().await;
1855 let mut r_sta = make_remote_client();
1856 let (mut ctx, _) = make_context(fake_device);
1857
1858 r_sta.state = StateMachine::new(State::Associated {
1859 aid: 1,
1860 eapol_controlled_port: None,
1861 active_timeout: None,
1862 ps_state: PowerSaveState::Awake,
1863 });
1864 r_sta
1865 .handle_eapol_llc_frame(&mut ctx, *CLIENT_ADDR2, *CLIENT_ADDR, &[1, 2, 3, 4, 5][..])
1866 .expect("expected OK");
1867 let msg = fake_device_state
1868 .lock()
1869 .next_mlme_msg::<fidl_mlme::EapolIndication>()
1870 .expect("expected MLME message");
1871 assert_eq!(
1872 msg,
1873 fidl_mlme::EapolIndication {
1874 dst_addr: CLIENT_ADDR2.to_array(),
1875 src_addr: CLIENT_ADDR.to_array(),
1876 data: vec![1, 2, 3, 4, 5],
1877 },
1878 );
1879 }
1880
1881 #[fuchsia::test(allow_stalls = false)]
1882 async fn handle_llc_frame() {
1883 let (fake_device, fake_device_state) = FakeDevice::new().await;
1884 let mut r_sta = make_remote_client();
1885 let (mut ctx, _) = make_context(fake_device);
1886
1887 r_sta.state = StateMachine::new(State::Associated {
1888 aid: 1,
1889 eapol_controlled_port: None,
1890 active_timeout: None,
1891 ps_state: PowerSaveState::Awake,
1892 });
1893 r_sta
1894 .handle_llc_frame(&mut ctx, *CLIENT_ADDR2, *CLIENT_ADDR, 0x1234, &[1, 2, 3, 4, 5][..])
1895 .expect("expected OK");
1896 assert_eq!(fake_device_state.lock().eth_queue.len(), 1);
1897 #[rustfmt::skip]
1898 assert_eq!(&fake_device_state.lock().eth_queue[0][..], &[
1899 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 0x12, 0x34, 1, 2, 3, 4, 5,
1904 ][..]);
1905 }
1906
1907 #[fuchsia::test(allow_stalls = false)]
1908 async fn handle_eth_frame_no_eapol_controlled_port() {
1909 let (fake_device, fake_device_state) = FakeDevice::new().await;
1910 let mut r_sta = make_remote_client();
1911 let (mut ctx, _) = make_context(fake_device);
1912
1913 r_sta.state = StateMachine::new(State::Associated {
1914 aid: 1,
1915 eapol_controlled_port: None,
1916 active_timeout: None,
1917 ps_state: PowerSaveState::Awake,
1918 });
1919 r_sta
1920 .handle_eth_frame(
1921 &mut ctx,
1922 *CLIENT_ADDR2,
1923 *CLIENT_ADDR,
1924 0x1234,
1925 &[1, 2, 3, 4, 5][..],
1926 0.into(),
1927 )
1928 .expect("expected OK");
1929 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1930 #[rustfmt::skip]
1931 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1932 0b00001000, 0b00000010, 0, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0x10, 0, 0xAA, 0xAA, 0x03, 0, 0, 0, 0x12, 0x34, 1, 2, 3, 4, 5,
1944 ][..]);
1945 }
1946
1947 #[fuchsia::test(allow_stalls = false)]
1948 async fn handle_eth_frame_not_associated() {
1949 let (fake_device, _) = FakeDevice::new().await;
1950 let mut r_sta = make_remote_client();
1951 let (mut ctx, _) = make_context(fake_device);
1952
1953 r_sta.state = StateMachine::new(State::Authenticated);
1954 assert_matches!(
1955 r_sta
1956 .handle_eth_frame(
1957 &mut ctx,
1958 *CLIENT_ADDR2,
1959 *CLIENT_ADDR,
1960 0x1234,
1961 &[1, 2, 3, 4, 5][..],
1962 0.into()
1963 )
1964 .expect_err("expected error"),
1965 ClientRejection::NotAssociated
1966 );
1967 }
1968
1969 #[fuchsia::test(allow_stalls = false)]
1970 async fn handle_eth_frame_eapol_controlled_port_closed() {
1971 let (fake_device, _) = FakeDevice::new().await;
1972 let mut r_sta = make_remote_client();
1973 let (mut ctx, _) = make_context(fake_device);
1974
1975 r_sta.state = StateMachine::new(State::Associated {
1976 aid: 1,
1977 eapol_controlled_port: Some(fidl_mlme::ControlledPortState::Closed),
1978 active_timeout: None,
1979 ps_state: PowerSaveState::Awake,
1980 });
1981 assert_matches!(
1982 r_sta
1983 .handle_eth_frame(
1984 &mut ctx,
1985 *CLIENT_ADDR2,
1986 *CLIENT_ADDR,
1987 0x1234,
1988 &[1, 2, 3, 4, 5][..],
1989 0.into()
1990 )
1991 .expect_err("expected error"),
1992 ClientRejection::ControlledPortClosed
1993 );
1994 }
1995
1996 #[fuchsia::test(allow_stalls = false)]
1997 async fn handle_eth_frame_eapol_controlled_port_open() {
1998 let (fake_device, fake_device_state) = FakeDevice::new().await;
1999 let mut r_sta = make_remote_client();
2000 let (mut ctx, _) = make_context(fake_device);
2001
2002 r_sta.state = StateMachine::new(State::Associated {
2003 aid: 1,
2004 eapol_controlled_port: Some(fidl_mlme::ControlledPortState::Open),
2005 active_timeout: None,
2006 ps_state: PowerSaveState::Awake,
2007 });
2008 r_sta
2009 .handle_eth_frame(
2010 &mut ctx,
2011 *CLIENT_ADDR2,
2012 *CLIENT_ADDR,
2013 0x1234,
2014 &[1, 2, 3, 4, 5][..],
2015 0.into(),
2016 )
2017 .expect("expected OK");
2018 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2019 #[rustfmt::skip]
2020 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
2021 0b00001000, 0b01000010, 0, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0x10, 0, 0xAA, 0xAA, 0x03, 0, 0, 0, 0x12, 0x34, 1, 2, 3, 4, 5,
2033 ][..]);
2034 }
2035
2036 #[fuchsia::test(allow_stalls = false)]
2037 async fn handle_data_frame_not_permitted() {
2038 let (fake_device, fake_device_state) = FakeDevice::new().await;
2039 let mut r_sta = make_remote_client();
2040 r_sta.state = StateMachine::new(State::Authenticating);
2041 let (mut ctx, _) = make_context(fake_device);
2042
2043 assert_matches!(
2044 r_sta
2045 .handle_data_frame(
2046 &mut ctx,
2047 mac::DataFrame {
2048 fixed_fields: mac::FixedDataHdrFields {
2049 frame_ctrl: mac::FrameControl(0b000000010_00001000),
2050 duration: 0,
2051 addr1: *CLIENT_ADDR,
2052 addr2: (*AP_ADDR).into(),
2053 addr3: *CLIENT_ADDR2,
2054 seq_ctrl: mac::SequenceControl(10),
2055 }
2056 .as_bytes_ref(),
2057 addr4: None,
2058 qos_ctrl: None,
2059 ht_ctrl: None,
2060 body: &[
2061 7, 7, 7, 8, 8, 8, 9, 10, 11, 11, 11,
2066 ][..],
2067 },
2068 )
2069 .expect_err("expected err"),
2070 ClientRejection::NotPermitted
2071 );
2072
2073 let msg = fake_device_state
2074 .lock()
2075 .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
2076 .expect("expected MLME message");
2077 assert_eq!(
2078 msg,
2079 fidl_mlme::DeauthenticateIndication {
2080 peer_sta_address: CLIENT_ADDR.to_array(),
2081 reason_code: fidl_ieee80211::ReasonCode::InvalidClass3Frame,
2082 locally_initiated: true,
2083 },
2084 );
2085
2086 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2087 assert_eq!(
2088 fake_device_state.lock().wlan_queue[0].0,
2089 &[
2090 0b11000000, 0b00000000, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 7, 0, ][..]
2100 );
2101 }
2102
2103 #[fuchsia::test(allow_stalls = false)]
2104 async fn handle_data_frame_not_permitted_disassoc() {
2105 let (fake_device, fake_device_state) = FakeDevice::new().await;
2106 let mut r_sta = make_remote_client();
2107 r_sta.state = StateMachine::new(State::Authenticated);
2108 let (mut ctx, _) = make_context(fake_device);
2109
2110 assert_matches!(
2111 r_sta
2112 .handle_data_frame(
2113 &mut ctx,
2114 mac::DataFrame {
2115 fixed_fields: mac::FixedDataHdrFields {
2116 frame_ctrl: mac::FrameControl(0b000000010_00001000),
2117 duration: 0,
2118 addr1: *CLIENT_ADDR,
2119 addr2: (*AP_ADDR).into(),
2120 addr3: *CLIENT_ADDR2,
2121 seq_ctrl: mac::SequenceControl(10),
2122 }
2123 .as_bytes_ref(),
2124 addr4: None,
2125 qos_ctrl: None,
2126 ht_ctrl: None,
2127 body: &[
2128 7, 7, 7, 8, 8, 8, 9, 10, 11, 11, 11,
2133 ][..],
2134 },
2135 )
2136 .expect_err("expected err"),
2137 ClientRejection::NotPermitted
2138 );
2139
2140 let msg = fake_device_state
2141 .lock()
2142 .next_mlme_msg::<fidl_mlme::DisassociateIndication>()
2143 .expect("expected MLME message");
2144 assert_eq!(
2145 msg,
2146 fidl_mlme::DisassociateIndication {
2147 peer_sta_address: CLIENT_ADDR.to_array(),
2148 reason_code: fidl_ieee80211::ReasonCode::InvalidClass3Frame,
2149 locally_initiated: true,
2150 },
2151 );
2152
2153 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2154 assert_eq!(
2155 fake_device_state.lock().wlan_queue[0].0,
2156 &[
2157 0b10100000, 0b00000000, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 7, 0, ][..]
2167 );
2168 }
2169
2170 #[fuchsia::test(allow_stalls = false)]
2171 async fn handle_data_frame_single_llc() {
2172 let (fake_device, fake_device_state) = FakeDevice::new().await;
2173 let mut r_sta = make_remote_client();
2174 r_sta.state = StateMachine::new(State::Associated {
2175 aid: 1,
2176 eapol_controlled_port: None,
2177 active_timeout: None,
2178 ps_state: PowerSaveState::Awake,
2179 });
2180 let (mut ctx, _) = make_context(fake_device);
2181
2182 r_sta
2183 .handle_data_frame(
2184 &mut ctx,
2185 mac::DataFrame {
2186 fixed_fields: mac::FixedDataHdrFields {
2187 frame_ctrl: mac::FrameControl(0b000000010_00001000),
2188 duration: 0,
2189 addr1: *CLIENT_ADDR,
2190 addr2: (*AP_ADDR).into(),
2191 addr3: *CLIENT_ADDR2,
2192 seq_ctrl: mac::SequenceControl(10),
2193 }
2194 .as_bytes_ref(),
2195 addr4: None,
2196 qos_ctrl: None,
2197 ht_ctrl: None,
2198 body: &[
2199 7, 7, 7, 8, 8, 8, 9, 10, 11, 11, 11,
2204 ][..],
2205 },
2206 )
2207 .expect("expected OK");
2208
2209 assert_eq!(fake_device_state.lock().eth_queue.len(), 1);
2210 match r_sta.state.as_ref() {
2211 State::Associated { active_timeout, .. } => assert!(active_timeout.is_some()),
2212 _ => panic!("expected Associated"),
2213 }
2214 }
2215
2216 #[fuchsia::test(allow_stalls = false)]
2217 async fn handle_data_frame_amsdu() {
2218 let (fake_device, fake_device_state) = FakeDevice::new().await;
2219 let mut r_sta = make_remote_client();
2220 r_sta.state = StateMachine::new(State::Associated {
2221 aid: 1,
2222 eapol_controlled_port: None,
2223 active_timeout: None,
2224 ps_state: PowerSaveState::Awake,
2225 });
2226 let (mut ctx, _) = make_context(fake_device);
2227
2228 let mut amsdu_data_frame_body = vec![];
2229 amsdu_data_frame_body.extend(&[
2230 0x78, 0x8a, 0x20, 0x0d, 0x67, 0x03, 0xb4, 0xf7, 0xa1, 0xbe, 0xb9, 0xab, 0x00, 0x74, ]);
2235 amsdu_data_frame_body.extend(MSDU_1_LLC_HDR);
2236 amsdu_data_frame_body.extend(MSDU_1_PAYLOAD);
2237 amsdu_data_frame_body.extend(&[
2238 0x00, 0x00, 0x78, 0x8a, 0x20, 0x0d, 0x67, 0x04, 0xb4, 0xf7, 0xa1, 0xbe, 0xb9, 0xac, 0x00, 0x66, ]);
2244 amsdu_data_frame_body.extend(MSDU_2_LLC_HDR);
2245 amsdu_data_frame_body.extend(MSDU_2_PAYLOAD);
2246
2247 r_sta
2248 .handle_data_frame(
2249 &mut ctx,
2250 mac::DataFrame {
2251 fixed_fields: mac::FixedDataHdrFields {
2252 frame_ctrl: mac::FrameControl(0b000000010_00001000),
2253 duration: 0,
2254 addr1: *CLIENT_ADDR,
2255 addr2: (*AP_ADDR).into(),
2256 addr3: *CLIENT_ADDR2,
2257 seq_ctrl: mac::SequenceControl(10),
2258 }
2259 .as_bytes_ref(),
2260 addr4: None,
2261 qos_ctrl: Some(
2262 Unalign::new(mac::QosControl(0).with_amsdu_present(true)).as_bytes_ref(),
2263 ),
2264 ht_ctrl: None,
2265 body: &amsdu_data_frame_body[..],
2266 },
2267 )
2268 .expect("expected OK");
2269
2270 assert_eq!(fake_device_state.lock().eth_queue.len(), 2);
2271 match r_sta.state.as_ref() {
2272 State::Associated { active_timeout, .. } => assert!(active_timeout.is_some()),
2273 _ => panic!("expected Associated"),
2274 }
2275 }
2276
2277 #[fuchsia::test(allow_stalls = false)]
2278 async fn handle_mgmt_frame() {
2279 let (fake_device, _) = FakeDevice::new().await;
2280 let mut r_sta = make_remote_client();
2281 r_sta.state = StateMachine::new(State::Authenticating);
2282 let (mut ctx, _) = make_context(fake_device);
2283
2284 r_sta
2285 .handle_mgmt_frame(
2286 &mut ctx,
2287 mac::CapabilityInfo(0),
2288 None,
2289 mac::MgmtFrame {
2290 mgmt_hdr: mac::MgmtHdr {
2291 frame_ctrl: mac::FrameControl(0b00000000_10110000), duration: 0,
2293 addr1: [1; 6].into(),
2294 addr2: [2; 6].into(),
2295 addr3: [3; 6].into(),
2296 seq_ctrl: mac::SequenceControl(10),
2297 }
2298 .as_bytes_ref(),
2299 ht_ctrl: None,
2300 body: &[
2301 0, 0, 1, 0, 0, 0, ][..],
2305 },
2306 )
2307 .await
2308 .expect("expected OK");
2309 }
2310
2311 #[test_case(Ssid::try_from("coolnet").unwrap(), true; "with ssid and rsne")]
2312 #[test_case(Ssid::try_from("").unwrap(), true; "with empty ssid")]
2313 #[test_case(Ssid::try_from("coolnet").unwrap(), false; "without rsne")]
2314 #[fuchsia::test(allow_stalls = false)]
2315 async fn handle_mgmt_frame_assoc_req(ssid: Ssid, has_rsne: bool) {
2316 let (fake_device, fake_device_state) = FakeDevice::new().await;
2317 let mut r_sta = make_remote_client();
2318 r_sta.state = StateMachine::new(State::Authenticated);
2319 let (mut ctx, _) = make_context(fake_device);
2320
2321 let mut assoc_frame_body = vec![
2322 0, 0, 10, 0, 1, 8, 1, 2, 3, 4, 5, 6, 7, 8, 50, 2, 9, 10, ];
2328 if has_rsne {
2329 assoc_frame_body.extend(&[48, 2, 77, 88][..]); }
2331
2332 r_sta
2333 .handle_mgmt_frame(
2334 &mut ctx,
2335 mac::CapabilityInfo(0),
2336 Some(ssid.clone()),
2337 mac::MgmtFrame {
2338 mgmt_hdr: mac::MgmtHdr {
2339 frame_ctrl: mac::FrameControl(0b00000000_00000000), duration: 0,
2341 addr1: [1; 6].into(),
2342 addr2: [2; 6].into(),
2343 addr3: [3; 6].into(),
2344 seq_ctrl: mac::SequenceControl(10),
2345 }
2346 .as_bytes_ref(),
2347 ht_ctrl: None,
2348 body: &assoc_frame_body[..],
2349 },
2350 )
2351 .await
2352 .expect("expected OK");
2353
2354 let msg = fake_device_state
2355 .lock()
2356 .next_mlme_msg::<fidl_mlme::AssociateIndication>()
2357 .expect("expected MLME message");
2358 let expected_rsne = if has_rsne { Some(vec![48, 2, 77, 88]) } else { None };
2359 assert_eq!(
2360 msg,
2361 fidl_mlme::AssociateIndication {
2362 peer_sta_address: CLIENT_ADDR.to_array(),
2363 listen_interval: 10,
2364 ssid: Some(ssid.into()),
2365 capability_info: CapabilityInfo(0).raw(),
2366 rates: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
2367 rsne: expected_rsne,
2368 },
2369 );
2370 }
2371
2372 #[test_case(vec![1, 0],
2373 vec![50, 2, 9, 10],
2374 vec![9, 10] ; "when no supported rates")]
2375 #[test_case(vec![1, 8, 1, 2, 3, 4, 5, 6, 7, 8],
2376 vec![50, 0],
2377 vec![1, 2, 3, 4, 5, 6, 7, 8] ; "when no extended supported rates")]
2378 #[test_case(vec![1, 0],
2379 vec![50, 0],
2380 vec![] ; "when no rates")]
2381 #[test_case(vec![1, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9],
2385 vec![50, 9, 10],
2386 vec![1, 2, 3, 4, 5, 6, 7, 8, 9] ; "when too many supported rates")]
2387 #[fuchsia::test(allow_stalls = false)]
2388 async fn assoc_req_with_bad_rates_still_passed_to_sme(
2389 supported_rates_ie: Vec<u8>,
2390 extended_supported_rates_ie: Vec<u8>,
2391 expected_rates: Vec<u8>,
2392 ) {
2393 let (fake_device, fake_device_state) = FakeDevice::new().await;
2394 let mut r_sta = make_remote_client();
2395 r_sta.state = StateMachine::new(State::Authenticated);
2396 let (mut ctx, _) = make_context(fake_device);
2397 let mut ies = vec![
2398 0, 0, 10, 0, ];
2401 ies.extend(supported_rates_ie);
2402 ies.extend(extended_supported_rates_ie);
2403
2404 r_sta
2405 .handle_mgmt_frame(
2406 &mut ctx,
2407 mac::CapabilityInfo(0),
2408 Some(Ssid::try_from("coolnet").unwrap()),
2409 mac::MgmtFrame {
2410 mgmt_hdr: mac::MgmtHdr {
2411 frame_ctrl: mac::FrameControl(0b00000000_00000000), duration: 0,
2413 addr1: [1; 6].into(),
2414 addr2: [2; 6].into(),
2415 addr3: [3; 6].into(),
2416 seq_ctrl: mac::SequenceControl(10),
2417 }
2418 .as_bytes_ref(),
2419 ht_ctrl: None,
2420 body: &ies[..],
2421 },
2422 )
2423 .await
2424 .expect("parsing should not fail");
2425
2426 let msg = fake_device_state
2427 .lock()
2428 .next_mlme_msg::<fidl_mlme::AssociateIndication>()
2429 .expect("expected MLME message");
2430 assert_eq!(
2431 msg,
2432 fidl_mlme::AssociateIndication {
2433 peer_sta_address: CLIENT_ADDR.to_array(),
2434 listen_interval: 10,
2435 ssid: Some(Ssid::try_from("coolnet").unwrap().into()),
2436 capability_info: CapabilityInfo(0).raw(),
2437 rates: expected_rates,
2438 rsne: None,
2439 },
2440 );
2441 }
2442
2443 #[fuchsia::test(allow_stalls = false)]
2444 async fn handle_mgmt_frame_not_permitted() {
2445 let (fake_device, fake_device_state) = FakeDevice::new().await;
2446 let mut r_sta = make_remote_client();
2447 r_sta.state = StateMachine::new(State::Authenticating);
2448 let (mut ctx, _) = make_context(fake_device);
2449
2450 assert_matches!(
2451 r_sta
2452 .handle_mgmt_frame(
2453 &mut ctx,
2454 mac::CapabilityInfo(0),
2455 None,
2456 mac::MgmtFrame {
2457 mgmt_hdr: mac::MgmtHdr {
2458 frame_ctrl: mac::FrameControl(0b00000000_00000000), duration: 0,
2460 addr1: [1; 6].into(),
2461 addr2: [2; 6].into(),
2462 addr3: [3; 6].into(),
2463 seq_ctrl: mac::SequenceControl(10),
2464 }
2465 .as_bytes_ref(),
2466 ht_ctrl: None,
2467 body: &[
2468 0, 0, 10, 0, ][..],
2471 },
2472 )
2473 .await
2474 .expect_err("expected error"),
2475 ClientRejection::NotPermitted
2476 );
2477
2478 let msg = fake_device_state
2479 .lock()
2480 .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
2481 .expect("expected MLME message");
2482 assert_eq!(
2483 msg,
2484 fidl_mlme::DeauthenticateIndication {
2485 peer_sta_address: CLIENT_ADDR.to_array(),
2486 reason_code: fidl_ieee80211::ReasonCode::InvalidClass2Frame,
2487 locally_initiated: true,
2488 },
2489 );
2490
2491 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2492 assert_eq!(
2493 fake_device_state.lock().wlan_queue[0].0,
2494 &[
2495 0b11000000, 0b00000000, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 6, 0, ][..]
2505 );
2506 }
2507
2508 #[fuchsia::test(allow_stalls = false)]
2509 async fn handle_mgmt_frame_not_handled() {
2510 let (fake_device, _) = FakeDevice::new().await;
2511 let mut r_sta = make_remote_client();
2512 r_sta.state = StateMachine::new(State::Associated {
2513 aid: 1,
2514 eapol_controlled_port: None,
2515 active_timeout: None,
2516 ps_state: PowerSaveState::Awake,
2517 });
2518 let (mut ctx, _) = make_context(fake_device);
2519
2520 assert_matches!(
2521 r_sta
2522 .handle_mgmt_frame(
2523 &mut ctx,
2524 mac::CapabilityInfo(0),
2525 None,
2526 mac::MgmtFrame {
2527 mgmt_hdr: mac::MgmtHdr {
2528 frame_ctrl: mac::FrameControl(0b00000000_00010000), duration: 0,
2530 addr1: [1; 6].into(),
2531 addr2: [2; 6].into(),
2532 addr3: [3; 6].into(),
2533 seq_ctrl: mac::SequenceControl(10),
2534 }
2535 .as_bytes_ref(),
2536 ht_ctrl: None,
2537 body: &[
2538 0, 0, 0, 0, 1, 0, ][..],
2542 },
2543 )
2544 .await
2545 .expect_err("expected error"),
2546 ClientRejection::Unsupported
2547 );
2548 }
2549
2550 #[fuchsia::test(allow_stalls = false)]
2551 async fn handle_mgmt_frame_resets_active_timer() {
2552 let (fake_device, _) = FakeDevice::new().await;
2553 let mut r_sta = make_remote_client();
2554 r_sta.state = StateMachine::new(State::Associated {
2555 aid: 1,
2556 eapol_controlled_port: None,
2557 active_timeout: None,
2558 ps_state: PowerSaveState::Awake,
2559 });
2560 let (mut ctx, _) = make_context(fake_device);
2561
2562 r_sta
2563 .handle_mgmt_frame(
2564 &mut ctx,
2565 mac::CapabilityInfo(0),
2566 None,
2567 mac::MgmtFrame {
2568 mgmt_hdr: mac::MgmtHdr {
2569 frame_ctrl: mac::FrameControl(0b00000000_00000000), duration: 0,
2571 addr1: [1; 6].into(),
2572 addr2: [2; 6].into(),
2573 addr3: [3; 6].into(),
2574 seq_ctrl: mac::SequenceControl(10),
2575 }
2576 .as_bytes_ref(),
2577 ht_ctrl: None,
2578 body: &[
2579 0, 0, 10, 0, ][..],
2582 },
2583 )
2584 .await
2585 .expect("expected OK");
2586 match r_sta.state.as_ref() {
2587 State::Associated { active_timeout, .. } => assert!(active_timeout.is_some()),
2588 _ => panic!("expected Associated"),
2589 }
2590 }
2591
2592 #[fuchsia::test(allow_stalls = false)]
2593 async fn handle_bss_idle_timeout() {
2594 let (fake_device, fake_device_state) = FakeDevice::new().await;
2595 let (mut ctx, _) = make_context(fake_device);
2596
2597 let mut r_sta = make_remote_client();
2598 let event_handle = r_sta.schedule_bss_idle_timeout(&mut ctx);
2599 r_sta.state = StateMachine::new(State::Associated {
2600 aid: 1,
2601 eapol_controlled_port: None,
2602 active_timeout: Some(event_handle),
2603 ps_state: PowerSaveState::Awake,
2604 });
2605
2606 r_sta.handle_bss_idle_timeout(&mut ctx).await.expect("expected OK");
2607 assert_matches!(r_sta.state.as_ref(), State::Authenticated);
2608 assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2609 #[rustfmt::skip]
2610 assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
2611 0b10100000, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 4, 0, ][..]);
2621 let msg = fake_device_state
2622 .lock()
2623 .next_mlme_msg::<fidl_mlme::DisassociateIndication>()
2624 .expect("expected MLME message");
2625 assert_eq!(
2626 msg,
2627 fidl_mlme::DisassociateIndication {
2628 peer_sta_address: CLIENT_ADDR.to_array(),
2629 reason_code: fidl_ieee80211::ReasonCode::ReasonInactivity,
2630 locally_initiated: true,
2631 },
2632 );
2633 }
2634
2635 #[fuchsia::test(allow_stalls = false)]
2636 async fn doze_then_wake() {
2637 let (fake_device, fake_device_state) = FakeDevice::new().await;
2638 let (mut ctx, _) = make_context(fake_device);
2639
2640 let mut r_sta = make_remote_client();
2641 r_sta.state = StateMachine::new(State::Associated {
2642 aid: 1,
2643 eapol_controlled_port: None,
2644 active_timeout: None,
2645 ps_state: PowerSaveState::Awake,
2646 });
2647
2648 r_sta.set_power_state(&mut ctx, mac::PowerState::DOZE).expect("expected doze OK");
2649
2650 r_sta
2652 .handle_eth_frame(
2653 &mut ctx,
2654 *CLIENT_ADDR2,
2655 *CLIENT_ADDR,
2656 0x1234,
2657 &[1, 2, 3, 4, 5][..],
2658 0.into(),
2659 )
2660 .expect("expected OK");
2661 r_sta
2662 .handle_eth_frame(
2663 &mut ctx,
2664 *CLIENT_ADDR2,
2665 *CLIENT_ADDR,
2666 0x1234,
2667 &[6, 7, 8, 9, 0][..],
2668 0.into(),
2669 )
2670 .expect("expected OK");
2671
2672 assert!(r_sta.has_buffered_frames());
2673
2674 assert_eq!(fake_device_state.lock().wlan_queue.len(), 0);
2676
2677 r_sta.set_power_state(&mut ctx, mac::PowerState::AWAKE).expect("expected wake OK");
2678 assert!(!r_sta.has_buffered_frames());
2679 assert_eq!(fake_device_state.lock().wlan_queue.len(), 2);
2680
2681 assert_eq!(
2682 &fake_device_state.lock().wlan_queue[0].0[..],
2683 &[
2684 0b00001000, 0b00100010, 0, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0x10, 0, 0xAA, 0xAA, 0x03, 0, 0, 0, 0x12, 0x34, 1, 2, 3, 4, 5,
2696 ][..]
2697 );
2698 assert_eq!(
2699 &fake_device_state.lock().wlan_queue[1].0[..],
2700 &[
2701 0b00001000, 0b00000010, 0, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0x20, 0, 0xAA, 0xAA, 0x03, 0, 0, 0, 0x12, 0x34, 6, 7, 8, 9, 0,
2713 ][..]
2714 );
2715 }
2716
2717 #[fuchsia::test(allow_stalls = false)]
2718 async fn doze_then_doze() {
2719 let (fake_device, _) = FakeDevice::new().await;
2720 let (mut ctx, _) = make_context(fake_device);
2721
2722 let mut r_sta = make_remote_client();
2723 r_sta.state = StateMachine::new(State::Associated {
2724 aid: 1,
2725 eapol_controlled_port: None,
2726 active_timeout: None,
2727 ps_state: PowerSaveState::Awake,
2728 });
2729
2730 r_sta.set_power_state(&mut ctx, mac::PowerState::DOZE).expect("expected doze OK");
2731 r_sta.set_power_state(&mut ctx, mac::PowerState::DOZE).expect("expected doze OK");
2732 }
2733
2734 #[fuchsia::test(allow_stalls = false)]
2735 async fn wake_then_wake() {
2736 let (fake_device, _) = FakeDevice::new().await;
2737 let (mut ctx, _) = make_context(fake_device);
2738
2739 let mut r_sta = make_remote_client();
2740 r_sta.state = StateMachine::new(State::Associated {
2741 aid: 1,
2742 eapol_controlled_port: None,
2743 active_timeout: None,
2744 ps_state: PowerSaveState::Awake,
2745 });
2746
2747 r_sta.set_power_state(&mut ctx, mac::PowerState::AWAKE).expect("expected wake OK");
2748 r_sta.set_power_state(&mut ctx, mac::PowerState::AWAKE).expect("expected wake OK");
2749 }
2750
2751 #[fuchsia::test(allow_stalls = false)]
2752 async fn doze_not_associated() {
2753 let (fake_device, _) = FakeDevice::new().await;
2754 let (mut ctx, _) = make_context(fake_device);
2755
2756 let mut r_sta = make_remote_client();
2757 r_sta.state = StateMachine::new(State::Authenticating);
2758
2759 assert_matches!(
2760 r_sta
2761 .set_power_state(&mut ctx, mac::PowerState::DOZE)
2762 .expect_err("expected doze error"),
2763 ClientRejection::NotAssociated
2764 );
2765 }
2766
2767 #[fuchsia::test(allow_stalls = false)]
2768 async fn wake_not_associated() {
2769 let (fake_device, _) = FakeDevice::new().await;
2770 let (mut ctx, _) = make_context(fake_device);
2771
2772 let mut r_sta = make_remote_client();
2773 r_sta.state = StateMachine::new(State::Authenticating);
2774
2775 r_sta.set_power_state(&mut ctx, mac::PowerState::AWAKE).expect("expected wake OK");
2776 }
2777}