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