1use crate::ap::TimedEvent;
6use crate::device::DeviceOps;
7use crate::disconnect::LocallyInitiated;
8use crate::error::Error;
9use anyhow::format_err;
10use fdf::ArenaStaticBox;
11use ieee80211::{Bssid, MacAddr, MacAddrBytes, Ssid};
12use wlan_common::big_endian::BigEndianU16;
13use wlan_common::ie::rsn::rsne;
14use wlan_common::ie::{self};
15use wlan_common::mac::{self, Aid, AuthAlgorithmNumber, StatusCode};
16use wlan_common::sequence::SequenceManager;
17use wlan_common::timer::{EventHandle, Timer};
18use wlan_common::{data_writer, mgmt_writer, wmm, TimeUnit};
19use wlan_frame_writer::{write_frame, write_frame_with_fixed_slice};
20use {fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme};
21
22pub struct BeaconOffloadParams {
24 pub tim_ele_offset: usize,
26}
27
28pub struct Context<D> {
29 pub device: D,
30 pub timer: Timer<TimedEvent>,
31 pub seq_mgr: SequenceManager,
32 pub bssid: Bssid,
33}
34
35impl<D> Context<D> {
36 pub fn new(device: D, timer: Timer<TimedEvent>, bssid: Bssid) -> Self {
37 Self { device, timer, seq_mgr: SequenceManager::new(), bssid }
38 }
39
40 pub fn schedule_after(
41 &mut self,
42 duration: zx::MonotonicDuration,
43 event: TimedEvent,
44 ) -> EventHandle {
45 self.timer.schedule_after(duration, event)
46 }
47
48 pub fn make_auth_frame(
50 &mut self,
51 addr: MacAddr,
52 auth_alg_num: AuthAlgorithmNumber,
53 auth_txn_seq_num: u16,
54 status_code: StatusCode,
55 ) -> Result<ArenaStaticBox<[u8]>, Error> {
56 Ok(write_frame!({
57 headers: {
58 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
59 mac::FrameControl(0)
60 .with_frame_type(mac::FrameType::MGMT)
61 .with_mgmt_subtype(mac::MgmtSubtype::AUTH),
62 addr,
63 self.bssid,
64 mac::SequenceControl(0).with_seq_num(self.seq_mgr.next_sns1(&addr) as u16)
65 ),
66 mac::AuthHdr: &mac::AuthHdr { auth_alg_num, auth_txn_seq_num, status_code },
67 }
68 })?)
69 }
70
71 pub fn make_assoc_resp_frame(
73 &mut self,
74 addr: MacAddr,
75 capabilities: mac::CapabilityInfo,
76 aid: Aid,
77 rates: &[u8],
78 max_idle_period: Option<u16>,
79 ) -> Result<ArenaStaticBox<[u8]>, Error> {
80 Ok(write_frame!({
81 headers: {
82 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
83 mac::FrameControl(0)
84 .with_frame_type(mac::FrameType::MGMT)
85 .with_mgmt_subtype(mac::MgmtSubtype::ASSOC_RESP),
86 addr,
87 self.bssid,
88 mac::SequenceControl(0).with_seq_num(self.seq_mgr.next_sns1(&addr) as u16)
89 ),
90 mac::AssocRespHdr: &mac::AssocRespHdr {
91 capabilities,
92 status_code: fidl_ieee80211::StatusCode::Success.into(),
93 aid
94 },
95 },
96 ies: {
99 supported_rates: &rates,
101 extended_supported_rates: {},
103 bss_max_idle_period?: if let Some(max_idle_period) = max_idle_period {
105 ie::BssMaxIdlePeriod {
106 max_idle_period,
107 idle_options: ie::IdleOptions(0)
108 .with_protected_keep_alive_required(false),
110 }
111 },
112 }
113 })?)
114 }
115
116 pub fn make_assoc_resp_frame_error(
119 &mut self,
120 addr: MacAddr,
121 capabilities: mac::CapabilityInfo,
122 status_code: StatusCode,
123 ) -> Result<ArenaStaticBox<[u8]>, Error> {
124 Ok(write_frame!({
125 headers: {
126 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
127 mac::FrameControl(0)
128 .with_frame_type(mac::FrameType::MGMT)
129 .with_mgmt_subtype(mac::MgmtSubtype::ASSOC_RESP),
130 addr,
131 self.bssid,
132 mac::SequenceControl(0).with_seq_num(self.seq_mgr.next_sns1(&addr) as u16)
133 ),
134 mac::AssocRespHdr: &mac::AssocRespHdr {
135 capabilities,
136 status_code,
137 aid: 0,
138 },
139 },
140 })?)
141 }
142
143 pub fn make_deauth_frame(
145 &mut self,
146 addr: MacAddr,
147 reason_code: mac::ReasonCode,
148 ) -> Result<ArenaStaticBox<[u8]>, Error> {
149 Ok(write_frame!({
150 headers: {
151 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
152 mac::FrameControl(0)
153 .with_frame_type(mac::FrameType::MGMT)
154 .with_mgmt_subtype(mac::MgmtSubtype::DEAUTH),
155 addr,
156 self.bssid,
157 mac::SequenceControl(0).with_seq_num(self.seq_mgr.next_sns1(&addr) as u16)
158 ),
159 mac::DeauthHdr: &mac::DeauthHdr { reason_code },
160 },
161 })?)
162 }
163
164 pub fn make_disassoc_frame(
166 &mut self,
167 addr: MacAddr,
168 reason_code: mac::ReasonCode,
169 ) -> Result<ArenaStaticBox<[u8]>, Error> {
170 Ok(write_frame!({
171 headers: {
172 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
173 mac::FrameControl(0)
174 .with_frame_type(mac::FrameType::MGMT)
175 .with_mgmt_subtype(mac::MgmtSubtype::DISASSOC),
176 addr,
177 self.bssid,
178 mac::SequenceControl(0).with_seq_num(self.seq_mgr.next_sns1(&addr) as u16)
179 ),
180 mac::DisassocHdr: &mac::DisassocHdr { reason_code },
181 },
182 })?)
183 }
184
185 pub fn make_probe_resp_frame(
188 &mut self,
189 addr: MacAddr,
190 beacon_interval: TimeUnit,
191 capabilities: mac::CapabilityInfo,
192 ssid: &Ssid,
193 rates: &[u8],
194 channel: u8,
195 rsne: &[u8],
196 ) -> Result<ArenaStaticBox<[u8]>, Error> {
197 let rsne = (!rsne.is_empty())
198 .then(|| match rsne::from_bytes(rsne) {
199 Ok((_, x)) => Ok(x),
200 Err(e) => Err(format_err!("error parsing rsne {:?} : {:?}", rsne, e)),
201 })
202 .transpose()?;
203 Ok(write_frame!({
204 headers: {
205 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
206 mac::FrameControl(0)
207 .with_frame_type(mac::FrameType::MGMT)
208 .with_mgmt_subtype(mac::MgmtSubtype::PROBE_RESP),
209 addr,
210 self.bssid,
211 mac::SequenceControl(0).with_seq_num(self.seq_mgr.next_sns1(&addr) as u16)
212 ),
213 mac::ProbeRespHdr: &mac::ProbeRespHdr::new(beacon_interval, capabilities),
214 },
215 ies: {
218 ssid: ssid,
220 supported_rates: rates,
222 dsss_param_set: &ie::DsssParamSet { current_channel: channel },
224 extended_supported_rates: {},
226 rsne?: rsne,
228
229 }
230 })?)
231 }
232
233 pub fn make_beacon_frame(
234 &self,
235 beacon_interval: TimeUnit,
236 capabilities: mac::CapabilityInfo,
237 ssid: &Ssid,
238 rates: &[u8],
239 channel: u8,
240 tim_header: ie::TimHeader,
241 tim_bitmap: &[u8],
242 rsne: &[u8],
243 ) -> Result<(ArenaStaticBox<[u8]>, BeaconOffloadParams), Error> {
244 let mut tim_ele_offset = 0;
245 let rsne = (!rsne.is_empty())
246 .then(|| match rsne::from_bytes(rsne) {
247 Ok((_, x)) => Ok(x),
248 Err(e) => Err(format_err!("error parsing rsne {:?} : {:?}", rsne, e)),
249 })
250 .transpose()?;
251 let buffer = write_frame!({
252 headers: {
253 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
254 mac::FrameControl(0)
255 .with_frame_type(mac::FrameType::MGMT)
256 .with_mgmt_subtype(mac::MgmtSubtype::BEACON),
257 ieee80211::BROADCAST_ADDR,
258 self.bssid,
259 mac::SequenceControl(0)
261 ),
262 mac::BeaconHdr: &mac::BeaconHdr::new(beacon_interval, capabilities),
263 },
264 ies: {
267 ssid: ssid,
269 supported_rates: rates,
271 dsss_param_set: &ie::DsssParamSet { current_channel: channel },
273 tim_ele_offset @ tim: ie::TimView {
277 header: tim_header,
278 bitmap: tim_bitmap,
279 },
280 extended_supported_rates: {},
282 rsne?: rsne,
284
285 }
286 })?;
287 Ok((buffer, BeaconOffloadParams { tim_ele_offset }))
288 }
289 pub fn make_data_frame(
291 &mut self,
292 dst: MacAddr,
293 src: MacAddr,
294 protected: bool,
295 qos_ctrl: bool,
296 ether_type: u16,
297 payload: &[u8],
298 ) -> Result<ArenaStaticBox<[u8]>, Error> {
299 let qos_ctrl = if qos_ctrl {
300 Some(
301 wmm::derive_tid(ether_type, payload)
302 .map_or(mac::QosControl(0), |tid| mac::QosControl(0).with_tid(tid as u16)),
303 )
304 } else {
305 None
306 };
307
308 Ok(write_frame!({
309 headers: {
310 mac::FixedDataHdrFields: &mac::FixedDataHdrFields {
311 frame_ctrl: mac::FrameControl(0)
312 .with_frame_type(mac::FrameType::DATA)
313 .with_data_subtype(mac::DataSubtype(0).with_qos(qos_ctrl.is_some()))
314 .with_protected(protected)
315 .with_from_ds(true),
316 duration: 0,
317 addr1: dst,
318 addr2: self.bssid.into(),
319 addr3: src,
320 seq_ctrl: mac::SequenceControl(0).with_seq_num(
321 match qos_ctrl.as_ref() {
322 None => self.seq_mgr.next_sns1(&dst),
323 Some(qos_ctrl) => self.seq_mgr.next_sns2(&dst, qos_ctrl.tid()),
324 } as u16
325 ),
326 },
327 mac::QosControl?: qos_ctrl,
328 mac::LlcHdr: &data_writer::make_snap_llc_hdr(ether_type),
329 },
330 payload: payload,
331 })?)
332 }
333
334 pub fn make_eapol_frame(
336 &mut self,
337 dst_addr: MacAddr,
338 src_addr: MacAddr,
339 is_protected: bool,
340 eapol_frame: &[u8],
341 ) -> Result<ArenaStaticBox<[u8]>, Error> {
342 self.make_data_frame(
343 dst_addr,
344 src_addr,
345 is_protected,
346 false, mac::ETHER_TYPE_EAPOL,
348 eapol_frame,
349 )
350 }
351}
352
353impl<D: DeviceOps> Context<D> {
354 pub fn send_mlme_start_conf(
358 &mut self,
359 result_code: fidl_mlme::StartResultCode,
360 ) -> Result<(), Error> {
361 self.device
362 .send_mlme_event(fidl_mlme::MlmeEvent::StartConf {
363 resp: fidl_mlme::StartConfirm { result_code },
364 })
365 .map_err(|e| e.into())
366 }
367
368 pub fn send_mlme_stop_conf(
370 &mut self,
371 result_code: fidl_mlme::StopResultCode,
372 ) -> Result<(), Error> {
373 self.device
374 .send_mlme_event(fidl_mlme::MlmeEvent::StopConf {
375 resp: fidl_mlme::StopConfirm { result_code },
376 })
377 .map_err(|e| e.into())
378 }
379
380 pub fn send_mlme_eapol_conf(
382 &mut self,
383 result_code: fidl_mlme::EapolResultCode,
384 dst_addr: MacAddr,
385 ) -> Result<(), Error> {
386 self.device
387 .send_mlme_event(fidl_mlme::MlmeEvent::EapolConf {
388 resp: fidl_mlme::EapolConfirm { result_code, dst_addr: dst_addr.to_array() },
389 })
390 .map_err(|e| e.into())
391 }
392
393 pub fn send_mlme_auth_ind(
395 &mut self,
396 peer_sta_address: MacAddr,
397 auth_type: fidl_mlme::AuthenticationTypes,
398 ) -> Result<(), Error> {
399 self.device
400 .send_mlme_event(fidl_mlme::MlmeEvent::AuthenticateInd {
401 ind: fidl_mlme::AuthenticateIndication {
402 peer_sta_address: peer_sta_address.to_array(),
403 auth_type,
404 },
405 })
406 .map_err(|e| e.into())
407 }
408
409 pub fn send_mlme_deauth_ind(
411 &mut self,
412 peer_sta_address: MacAddr,
413 reason_code: fidl_ieee80211::ReasonCode,
414 locally_initiated: LocallyInitiated,
415 ) -> Result<(), Error> {
416 self.device
417 .send_mlme_event(fidl_mlme::MlmeEvent::DeauthenticateInd {
418 ind: fidl_mlme::DeauthenticateIndication {
419 peer_sta_address: peer_sta_address.to_array(),
420 reason_code,
421 locally_initiated: locally_initiated.0,
422 },
423 })
424 .map_err(|e| e.into())
425 }
426
427 pub fn send_mlme_assoc_ind(
429 &mut self,
430 peer_sta_address: MacAddr,
431 listen_interval: u16,
432 ssid: Option<Ssid>,
433 capabilities: mac::CapabilityInfo,
434 rates: Vec<ie::SupportedRate>,
435 rsne: Option<Vec<u8>>,
436 ) -> Result<(), Error> {
437 self.device
438 .send_mlme_event(fidl_mlme::MlmeEvent::AssociateInd {
439 ind: fidl_mlme::AssociateIndication {
440 peer_sta_address: peer_sta_address.to_array(),
441 listen_interval,
442 ssid: ssid.map(|s| s.into()),
443 capability_info: capabilities.raw(),
444 rates: rates.iter().map(|r| r.0).collect(),
445 rsne,
446 },
448 })
449 .map_err(|e| e.into())
450 }
451
452 pub fn send_mlme_disassoc_ind(
454 &mut self,
455 peer_sta_address: MacAddr,
456 reason_code: fidl_ieee80211::ReasonCode,
457 locally_initiated: LocallyInitiated,
458 ) -> Result<(), Error> {
459 self.device
460 .send_mlme_event(fidl_mlme::MlmeEvent::DisassociateInd {
461 ind: fidl_mlme::DisassociateIndication {
462 peer_sta_address: peer_sta_address.to_array(),
463 reason_code,
464 locally_initiated: locally_initiated.0,
465 },
466 })
467 .map_err(|e| e.into())
468 }
469
470 pub fn send_mlme_eapol_ind(
472 &mut self,
473 dst_addr: MacAddr,
474 src_addr: MacAddr,
475 data: &[u8],
476 ) -> Result<(), Error> {
477 self.device
478 .send_mlme_event(fidl_mlme::MlmeEvent::EapolInd {
479 ind: fidl_mlme::EapolIndication {
480 dst_addr: dst_addr.to_array(),
481 src_addr: src_addr.to_array(),
482 data: data.to_vec(),
483 },
484 })
485 .map_err(|e| e.into())
486 }
487
488 pub fn deliver_eth_frame(
492 &mut self,
493 dst_addr: MacAddr,
494 src_addr: MacAddr,
495 protocol_id: u16,
496 body: &[u8],
497 ) -> Result<(), Error> {
498 let mut packet = [0u8; mac::MAX_ETH_FRAME_LEN];
499 let (frame_start, frame_end) = write_frame_with_fixed_slice!(&mut packet[..], {
500 headers: {
501 mac::EthernetIIHdr: &mac::EthernetIIHdr {
502 da: dst_addr,
503 sa: src_addr,
504 ether_type: BigEndianU16::from_native(protocol_id),
505 },
506 },
507 payload: body,
508 })?;
509 self.device
510 .deliver_eth_frame(&packet[frame_start..frame_end])
511 .map_err(|s| Error::Status(format!("could not deliver Ethernet II frame"), s))
512 }
513}
514
515#[cfg(test)]
516mod test {
517 use super::*;
518 use crate::ap::ClientEvent;
519 use crate::device::FakeDevice;
520 use lazy_static::lazy_static;
521 use wlan_common::assert_variant;
522 use wlan_common::timer::{self, create_timer};
523
524 lazy_static! {
525 static ref CLIENT_ADDR: MacAddr = [1u8; 6].into();
526 static ref BSSID: Bssid = [2u8; 6].into();
527 static ref CLIENT_ADDR2: MacAddr = [3u8; 6].into();
528 }
529
530 fn make_context(
531 fake_device: FakeDevice,
532 ) -> (Context<FakeDevice>, timer::EventStream<TimedEvent>) {
533 let (timer, time_stream) = create_timer();
534 (Context::new(fake_device, timer, *BSSID), time_stream)
535 }
536
537 #[fuchsia::test(allow_stalls = false)]
538 async fn send_mlme_auth_ind() {
539 let (fake_device, fake_device_state) = FakeDevice::new().await;
540 let (mut ctx, _) = make_context(fake_device);
541 ctx.send_mlme_auth_ind(*CLIENT_ADDR, fidl_mlme::AuthenticationTypes::OpenSystem)
542 .expect("expected OK");
543 let msg = fake_device_state
544 .lock()
545 .next_mlme_msg::<fidl_mlme::AuthenticateIndication>()
546 .expect("expected MLME message");
547 assert_eq!(
548 msg,
549 fidl_mlme::AuthenticateIndication {
550 peer_sta_address: CLIENT_ADDR.to_array(),
551 auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
552 },
553 );
554 }
555
556 #[fuchsia::test(allow_stalls = false)]
557 async fn send_mlme_deauth_ind() {
558 let (fake_device, fake_device_state) = FakeDevice::new().await;
559 let (mut ctx, _) = make_context(fake_device);
560 ctx.send_mlme_deauth_ind(
561 *CLIENT_ADDR,
562 fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
563 LocallyInitiated(true),
564 )
565 .expect("expected OK");
566 let msg = fake_device_state
567 .lock()
568 .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
569 .expect("expected MLME message");
570 assert_eq!(
571 msg,
572 fidl_mlme::DeauthenticateIndication {
573 peer_sta_address: CLIENT_ADDR.to_array(),
574 reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
575 locally_initiated: true,
576 },
577 );
578 }
579
580 #[fuchsia::test(allow_stalls = false)]
581 async fn send_mlme_assoc_ind() {
582 let (fake_device, fake_device_state) = FakeDevice::new().await;
583 let (mut ctx, _) = make_context(fake_device);
584 ctx.send_mlme_assoc_ind(
585 *CLIENT_ADDR,
586 1,
587 Some(Ssid::try_from("coolnet").unwrap()),
588 mac::CapabilityInfo(0),
589 vec![ie::SupportedRate(1), ie::SupportedRate(2), ie::SupportedRate(3)],
590 None,
591 )
592 .expect("expected OK");
593 let msg = fake_device_state
594 .lock()
595 .next_mlme_msg::<fidl_mlme::AssociateIndication>()
596 .expect("expected MLME message");
597 assert_eq!(
598 msg,
599 fidl_mlme::AssociateIndication {
600 peer_sta_address: CLIENT_ADDR.to_array(),
601 listen_interval: 1,
602 ssid: Some(Ssid::try_from("coolnet").unwrap().into()),
603 capability_info: mac::CapabilityInfo(0).raw(),
604 rates: vec![1, 2, 3],
605 rsne: None,
606 },
607 );
608 }
609
610 #[fuchsia::test(allow_stalls = false)]
611 async fn send_mlme_disassoc_ind() {
612 let (fake_device, fake_device_state) = FakeDevice::new().await;
613 let (mut ctx, _) = make_context(fake_device);
614 ctx.send_mlme_disassoc_ind(
615 *CLIENT_ADDR,
616 fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc,
617 LocallyInitiated(true),
618 )
619 .expect("expected OK");
620 let msg = fake_device_state
621 .lock()
622 .next_mlme_msg::<fidl_mlme::DisassociateIndication>()
623 .expect("expected MLME message");
624 assert_eq!(
625 msg,
626 fidl_mlme::DisassociateIndication {
627 peer_sta_address: CLIENT_ADDR.to_array(),
628 reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc,
629 locally_initiated: true,
630 },
631 );
632 }
633
634 #[fuchsia::test(allow_stalls = false)]
635 async fn send_mlme_eapol_ind() {
636 let (fake_device, fake_device_state) = FakeDevice::new().await;
637 let (mut ctx, _) = make_context(fake_device);
638 ctx.send_mlme_eapol_ind(*CLIENT_ADDR2, *CLIENT_ADDR, &[1, 2, 3, 4, 5][..])
639 .expect("expected OK");
640 let msg = fake_device_state
641 .lock()
642 .next_mlme_msg::<fidl_mlme::EapolIndication>()
643 .expect("expected MLME message");
644 assert_eq!(
645 msg,
646 fidl_mlme::EapolIndication {
647 dst_addr: CLIENT_ADDR2.to_array(),
648 src_addr: CLIENT_ADDR.to_array(),
649 data: vec![1, 2, 3, 4, 5],
650 },
651 );
652 }
653
654 #[fuchsia::test(allow_stalls = false)]
655 async fn schedule_after() {
656 let (fake_device, _) = FakeDevice::new().await;
657 let (mut ctx, mut time_stream) = make_context(fake_device);
658 let event_handle = ctx.schedule_after(
659 zx::MonotonicDuration::from_seconds(5),
660 TimedEvent::ClientEvent(MacAddr::from([1; 6]), ClientEvent::BssIdleTimeout),
661 );
662 let (_, timed_event, _) =
663 time_stream.try_next().unwrap().expect("Should have scheduled an event");
664 assert_eq!(timed_event.id, event_handle.id());
665
666 assert_variant!(
667 timed_event.event,
668 TimedEvent::ClientEvent(mac_addr, ClientEvent::BssIdleTimeout) => {
669 assert_eq!(MacAddr::from([1; 6]), mac_addr);
670 }
671 );
672 assert!(time_stream.try_next().is_err());
673 }
674
675 #[fuchsia::test(allow_stalls = false)]
676 async fn make_auth_frame() {
677 let (fake_device, _) = FakeDevice::new().await;
678 let (mut ctx, _) = make_context(fake_device);
679 let buffer = ctx
680 .make_auth_frame(
681 *CLIENT_ADDR,
682 AuthAlgorithmNumber::FAST_BSS_TRANSITION,
683 3,
684 fidl_ieee80211::StatusCode::TransactionSequenceError.into(),
685 )
686 .expect("error making auth frame");
687 assert_eq!(
688 &buffer[..],
689 &[
690 0b10110000, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0, 2, 0, 3, 0, 14, 0, ][..]
702 );
703 }
704
705 #[fuchsia::test(allow_stalls = false)]
706 async fn make_assoc_resp_frame() {
707 let (fake_device, _) = FakeDevice::new().await;
708 let (mut ctx, _) = make_context(fake_device);
709 let buffer = ctx
710 .make_assoc_resp_frame(
711 *CLIENT_ADDR,
712 mac::CapabilityInfo(0),
713 1,
714 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
715 Some(99),
716 )
717 .expect("error making assoc resp frame");
718 assert_eq!(
719 &buffer[..],
720 &[
721 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, 99, 0, 0, ][..]
737 );
738 }
739
740 #[fuchsia::test(allow_stalls = false)]
741 async fn make_assoc_resp_frame_error() {
742 let (fake_device, _) = FakeDevice::new().await;
743 let (mut ctx, _) = make_context(fake_device);
744 let buffer = ctx
745 .make_assoc_resp_frame_error(
746 *CLIENT_ADDR,
747 mac::CapabilityInfo(0),
748 fidl_ieee80211::StatusCode::RejectedEmergencyServicesNotSupported.into(),
749 )
750 .expect("error making assoc resp frame error");
751 assert_eq!(
752 &buffer[..],
753 &[
754 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, ][..]
766 );
767 }
768
769 #[fuchsia::test(allow_stalls = false)]
770 async fn make_assoc_resp_frame_no_bss_max_idle_period() {
771 let (fake_device, _) = FakeDevice::new().await;
772 let (mut ctx, _) = make_context(fake_device);
773 let buffer = ctx
774 .make_assoc_resp_frame(
775 *CLIENT_ADDR,
776 mac::CapabilityInfo(0),
777 1,
778 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
779 None,
780 )
781 .expect("error making assoc resp frame");
782 assert_eq!(
783 &buffer[..],
784 &[
785 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, ][..]
800 );
801 }
802
803 #[fuchsia::test(allow_stalls = false)]
804 async fn make_disassoc_frame() {
805 let (fake_device, _) = FakeDevice::new().await;
806 let (mut ctx, _) = make_context(fake_device);
807 let buffer = ctx
808 .make_disassoc_frame(
809 *CLIENT_ADDR,
810 fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc.into(),
811 )
812 .expect("error making disassoc frame");
813 assert_eq!(
814 &buffer[..],
815 &[
816 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, ][..]
826 );
827 }
828
829 #[fuchsia::test(allow_stalls = false)]
830 async fn make_probe_resp_frame() {
831 let (fake_device, _) = FakeDevice::new().await;
832 let (mut ctx, _) = make_context(fake_device);
833 let buffer = ctx
834 .make_probe_resp_frame(
835 *CLIENT_ADDR,
836 TimeUnit(10),
837 mac::CapabilityInfo(33),
838 &Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
839 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
840 2,
841 &[48, 2, 77, 88][..],
842 )
843 .expect("error making probe resp frame");
844 assert_eq!(
845 &buffer[..],
846 &[
847 0b01010000, 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, 0, 0, 0, 0, 10, 0, 33, 0, 0, 5, 1, 2, 3, 4, 5, 1, 8, 1, 2, 3, 4, 5, 6, 7, 8, 3, 1, 2, 50, 2, 9, 10, 48, 2, 77, 88, ][..]
865 );
866 }
867
868 #[fuchsia::test(allow_stalls = false)]
869 async fn make_beacon_frame() {
870 let (fake_device, _) = FakeDevice::new().await;
871 let (ctx, _) = make_context(fake_device);
872
873 let (buffer, params) = ctx
874 .make_beacon_frame(
875 TimeUnit(10),
876 mac::CapabilityInfo(33),
877 &Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
878 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
879 2,
880 ie::TimHeader { dtim_count: 1, dtim_period: 2, bmp_ctrl: ie::BitmapControl(0) },
881 &[1, 2, 3][..],
882 &[48, 2, 77, 88][..],
883 )
884 .expect("error making probe resp frame");
885 assert_eq!(
886 &buffer[..],
887 &[
888 0b10000000, 0, 0, 0, 255, 255, 255, 255, 255, 255, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 33, 0, 0, 5, 1, 2, 3, 4, 5, 1, 8, 1, 2, 3, 4, 5, 6, 7, 8, 3, 1, 2, 5, 6, 1, 2, 0, 1, 2, 3, 50, 2, 9, 10, 48, 2, 77, 88, ][..]
907 );
908 assert_eq!(params.tim_ele_offset, 56);
909 }
910
911 #[fuchsia::test(allow_stalls = false)]
912 async fn make_data_frame() {
913 let (fake_device, _) = FakeDevice::new().await;
914 let (mut ctx, _) = make_context(fake_device);
915 let buffer = ctx
916 .make_data_frame(*CLIENT_ADDR2, *CLIENT_ADDR, false, false, 0x1234, &[1, 2, 3, 4, 5])
917 .expect("error making data frame");
918 assert_eq!(
919 &buffer[..],
920 &[
921 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,
933 ][..]
934 );
935 }
936
937 #[fuchsia::test(allow_stalls = false)]
938 async fn make_data_frame_ipv4_qos() {
939 let (fake_device, _) = FakeDevice::new().await;
940 let (mut ctx, _) = make_context(fake_device);
941 let buffer = ctx
942 .make_data_frame(
943 *CLIENT_ADDR2,
944 *CLIENT_ADDR,
945 false,
946 true,
947 0x0800, &[1, 0xB0, 3, 4, 5], )
952 .expect("error making data frame");
953 assert_eq!(
954 &buffer[..],
955 &[
956 0b10001000, 0b00000010, 0, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0x10, 0, 0x06, 0, 0xAA, 0xAA, 0x03, 0, 0, 0, 0x08, 0x00, 1, 0xB0, 3, 4, 5,
969 ][..]
970 );
971 }
972
973 #[fuchsia::test(allow_stalls = false)]
974 async fn make_data_frame_ipv6_qos() {
975 let (fake_device, _) = FakeDevice::new().await;
976 let (mut ctx, _) = make_context(fake_device);
977 let buffer = ctx
978 .make_data_frame(
979 *CLIENT_ADDR2,
980 *CLIENT_ADDR,
981 false,
982 true,
983 0x86DD, &[0b0101, 0b10000000, 3, 4, 5], )
988 .expect("error making data frame");
989 assert_eq!(
990 &buffer[..],
991 &[
992 0b10001000, 0b00000010, 0, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0x10, 0, 0x03, 0, 0xAA, 0xAA, 0x03, 0, 0, 0, 0x86, 0xDD, 0b0101, 0b10000000, 3, 4, 5,
1005 ][..]
1006 );
1007 }
1008
1009 #[fuchsia::test(allow_stalls = false)]
1010 async fn make_eapol_frame() {
1011 let (fake_device, _) = FakeDevice::new().await;
1012 let (mut ctx, _) = make_context(fake_device);
1013 let buffer = ctx
1014 .make_eapol_frame(*CLIENT_ADDR2, *CLIENT_ADDR, false, &[1, 2, 3, 4, 5])
1015 .expect("error making eapol frame");
1016 assert_eq!(
1017 &buffer[..],
1018 &[
1019 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, 0x88, 0x8E, 1, 2, 3, 4, 5,
1031 ][..]
1032 );
1033 }
1034
1035 #[fuchsia::test(allow_stalls = false)]
1036 async fn deliver_eth_frame() {
1037 let (fake_device, fake_device_state) = FakeDevice::new().await;
1038 let (mut ctx, _) = make_context(fake_device);
1039 ctx.deliver_eth_frame(*CLIENT_ADDR2, *CLIENT_ADDR, 0x1234, &[1, 2, 3, 4, 5][..])
1040 .expect("expected OK");
1041 assert_eq!(fake_device_state.lock().eth_queue.len(), 1);
1042 #[rustfmt::skip]
1043 assert_eq!(&fake_device_state.lock().eth_queue[0][..], &[
1044 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 0x12, 0x34, 1, 2, 3, 4, 5,
1049 ][..]);
1050 }
1051}