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::ie::rsn::rsne;
13use wlan_common::ie::{self};
14use wlan_common::mac::{self, Aid, AuthAlgorithmNumber, StatusCode};
15use wlan_common::sequence::SequenceManager;
16use wlan_common::timer::{EventHandle, Timer};
17use wlan_common::{TimeUnit, data_writer, mgmt_writer, wmm};
18use wlan_frame_writer::{write_frame, write_frame_with_fixed_slice};
19use zerocopy::byteorder::big_endian::U16 as BigEndianU16;
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::new(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 assert_matches::assert_matches;
521 use std::sync::LazyLock;
522 use wlan_common::timer::{self, create_timer};
523
524 static CLIENT_ADDR: LazyLock<MacAddr> = LazyLock::new(|| [1u8; 6].into());
525 static BSSID: LazyLock<Bssid> = LazyLock::new(|| [2u8; 6].into());
526 static CLIENT_ADDR2: LazyLock<MacAddr> = LazyLock::new(|| [3u8; 6].into());
527
528 fn make_context(
529 fake_device: FakeDevice,
530 ) -> (Context<FakeDevice>, timer::EventStream<TimedEvent>) {
531 let (timer, time_stream) = create_timer();
532 (Context::new(fake_device, timer, *BSSID), time_stream)
533 }
534
535 #[fuchsia::test(allow_stalls = false)]
536 async fn send_mlme_auth_ind() {
537 let (fake_device, fake_device_state) = FakeDevice::new().await;
538 let (mut ctx, _) = make_context(fake_device);
539 ctx.send_mlme_auth_ind(*CLIENT_ADDR, fidl_mlme::AuthenticationTypes::OpenSystem)
540 .expect("expected OK");
541 let msg = fake_device_state
542 .lock()
543 .next_mlme_msg::<fidl_mlme::AuthenticateIndication>()
544 .expect("expected MLME message");
545 assert_eq!(
546 msg,
547 fidl_mlme::AuthenticateIndication {
548 peer_sta_address: CLIENT_ADDR.to_array(),
549 auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
550 },
551 );
552 }
553
554 #[fuchsia::test(allow_stalls = false)]
555 async fn send_mlme_deauth_ind() {
556 let (fake_device, fake_device_state) = FakeDevice::new().await;
557 let (mut ctx, _) = make_context(fake_device);
558 ctx.send_mlme_deauth_ind(
559 *CLIENT_ADDR,
560 fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
561 LocallyInitiated(true),
562 )
563 .expect("expected OK");
564 let msg = fake_device_state
565 .lock()
566 .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
567 .expect("expected MLME message");
568 assert_eq!(
569 msg,
570 fidl_mlme::DeauthenticateIndication {
571 peer_sta_address: CLIENT_ADDR.to_array(),
572 reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
573 locally_initiated: true,
574 },
575 );
576 }
577
578 #[fuchsia::test(allow_stalls = false)]
579 async fn send_mlme_assoc_ind() {
580 let (fake_device, fake_device_state) = FakeDevice::new().await;
581 let (mut ctx, _) = make_context(fake_device);
582 ctx.send_mlme_assoc_ind(
583 *CLIENT_ADDR,
584 1,
585 Some(Ssid::try_from("coolnet").unwrap()),
586 mac::CapabilityInfo(0),
587 vec![ie::SupportedRate(1), ie::SupportedRate(2), ie::SupportedRate(3)],
588 None,
589 )
590 .expect("expected OK");
591 let msg = fake_device_state
592 .lock()
593 .next_mlme_msg::<fidl_mlme::AssociateIndication>()
594 .expect("expected MLME message");
595 assert_eq!(
596 msg,
597 fidl_mlme::AssociateIndication {
598 peer_sta_address: CLIENT_ADDR.to_array(),
599 listen_interval: 1,
600 ssid: Some(Ssid::try_from("coolnet").unwrap().into()),
601 capability_info: mac::CapabilityInfo(0).raw(),
602 rates: vec![1, 2, 3],
603 rsne: None,
604 },
605 );
606 }
607
608 #[fuchsia::test(allow_stalls = false)]
609 async fn send_mlme_disassoc_ind() {
610 let (fake_device, fake_device_state) = FakeDevice::new().await;
611 let (mut ctx, _) = make_context(fake_device);
612 ctx.send_mlme_disassoc_ind(
613 *CLIENT_ADDR,
614 fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc,
615 LocallyInitiated(true),
616 )
617 .expect("expected OK");
618 let msg = fake_device_state
619 .lock()
620 .next_mlme_msg::<fidl_mlme::DisassociateIndication>()
621 .expect("expected MLME message");
622 assert_eq!(
623 msg,
624 fidl_mlme::DisassociateIndication {
625 peer_sta_address: CLIENT_ADDR.to_array(),
626 reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc,
627 locally_initiated: true,
628 },
629 );
630 }
631
632 #[fuchsia::test(allow_stalls = false)]
633 async fn send_mlme_eapol_ind() {
634 let (fake_device, fake_device_state) = FakeDevice::new().await;
635 let (mut ctx, _) = make_context(fake_device);
636 ctx.send_mlme_eapol_ind(*CLIENT_ADDR2, *CLIENT_ADDR, &[1, 2, 3, 4, 5][..])
637 .expect("expected OK");
638 let msg = fake_device_state
639 .lock()
640 .next_mlme_msg::<fidl_mlme::EapolIndication>()
641 .expect("expected MLME message");
642 assert_eq!(
643 msg,
644 fidl_mlme::EapolIndication {
645 dst_addr: CLIENT_ADDR2.to_array(),
646 src_addr: CLIENT_ADDR.to_array(),
647 data: vec![1, 2, 3, 4, 5],
648 },
649 );
650 }
651
652 #[fuchsia::test(allow_stalls = false)]
653 async fn schedule_after() {
654 let (fake_device, _) = FakeDevice::new().await;
655 let (mut ctx, mut time_stream) = make_context(fake_device);
656 let event_handle = ctx.schedule_after(
657 zx::MonotonicDuration::from_seconds(5),
658 TimedEvent::ClientEvent(MacAddr::from([1; 6]), ClientEvent::BssIdleTimeout),
659 );
660 let (_, timed_event, _) =
661 time_stream.try_next().unwrap().expect("Should have scheduled an event");
662 assert_eq!(timed_event.id, event_handle.id());
663
664 assert_matches!(
665 timed_event.event,
666 TimedEvent::ClientEvent(mac_addr, ClientEvent::BssIdleTimeout) => {
667 assert_eq!(MacAddr::from([1; 6]), mac_addr);
668 }
669 );
670 assert!(time_stream.try_next().is_err());
671 }
672
673 #[fuchsia::test(allow_stalls = false)]
674 async fn make_auth_frame() {
675 let (fake_device, _) = FakeDevice::new().await;
676 let (mut ctx, _) = make_context(fake_device);
677 let buffer = ctx
678 .make_auth_frame(
679 *CLIENT_ADDR,
680 AuthAlgorithmNumber::FAST_BSS_TRANSITION,
681 3,
682 fidl_ieee80211::StatusCode::TransactionSequenceError.into(),
683 )
684 .expect("error making auth frame");
685 assert_eq!(
686 &buffer[..],
687 &[
688 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, ][..]
700 );
701 }
702
703 #[fuchsia::test(allow_stalls = false)]
704 async fn make_assoc_resp_frame() {
705 let (fake_device, _) = FakeDevice::new().await;
706 let (mut ctx, _) = make_context(fake_device);
707 let buffer = ctx
708 .make_assoc_resp_frame(
709 *CLIENT_ADDR,
710 mac::CapabilityInfo(0),
711 1,
712 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
713 Some(99),
714 )
715 .expect("error making assoc resp frame");
716 assert_eq!(
717 &buffer[..],
718 &[
719 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, ][..]
735 );
736 }
737
738 #[fuchsia::test(allow_stalls = false)]
739 async fn make_assoc_resp_frame_error() {
740 let (fake_device, _) = FakeDevice::new().await;
741 let (mut ctx, _) = make_context(fake_device);
742 let buffer = ctx
743 .make_assoc_resp_frame_error(
744 *CLIENT_ADDR,
745 mac::CapabilityInfo(0),
746 fidl_ieee80211::StatusCode::RejectedEmergencyServicesNotSupported.into(),
747 )
748 .expect("error making assoc resp frame error");
749 assert_eq!(
750 &buffer[..],
751 &[
752 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, ][..]
764 );
765 }
766
767 #[fuchsia::test(allow_stalls = false)]
768 async fn make_assoc_resp_frame_no_bss_max_idle_period() {
769 let (fake_device, _) = FakeDevice::new().await;
770 let (mut ctx, _) = make_context(fake_device);
771 let buffer = ctx
772 .make_assoc_resp_frame(
773 *CLIENT_ADDR,
774 mac::CapabilityInfo(0),
775 1,
776 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
777 None,
778 )
779 .expect("error making assoc resp frame");
780 assert_eq!(
781 &buffer[..],
782 &[
783 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, ][..]
798 );
799 }
800
801 #[fuchsia::test(allow_stalls = false)]
802 async fn make_disassoc_frame() {
803 let (fake_device, _) = FakeDevice::new().await;
804 let (mut ctx, _) = make_context(fake_device);
805 let buffer = ctx
806 .make_disassoc_frame(
807 *CLIENT_ADDR,
808 fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc.into(),
809 )
810 .expect("error making disassoc frame");
811 assert_eq!(
812 &buffer[..],
813 &[
814 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, ][..]
824 );
825 }
826
827 #[fuchsia::test(allow_stalls = false)]
828 async fn make_probe_resp_frame() {
829 let (fake_device, _) = FakeDevice::new().await;
830 let (mut ctx, _) = make_context(fake_device);
831 let buffer = ctx
832 .make_probe_resp_frame(
833 *CLIENT_ADDR,
834 TimeUnit(10),
835 mac::CapabilityInfo(33),
836 &Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
837 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
838 2,
839 &[48, 2, 77, 88][..],
840 )
841 .expect("error making probe resp frame");
842 assert_eq!(
843 &buffer[..],
844 &[
845 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, ][..]
863 );
864 }
865
866 #[fuchsia::test(allow_stalls = false)]
867 async fn make_beacon_frame() {
868 let (fake_device, _) = FakeDevice::new().await;
869 let (ctx, _) = make_context(fake_device);
870
871 let (buffer, params) = ctx
872 .make_beacon_frame(
873 TimeUnit(10),
874 mac::CapabilityInfo(33),
875 &Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
876 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
877 2,
878 ie::TimHeader { dtim_count: 1, dtim_period: 2, bmp_ctrl: ie::BitmapControl(0) },
879 &[1, 2, 3][..],
880 &[48, 2, 77, 88][..],
881 )
882 .expect("error making probe resp frame");
883 assert_eq!(
884 &buffer[..],
885 &[
886 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, ][..]
905 );
906 assert_eq!(params.tim_ele_offset, 56);
907 }
908
909 #[fuchsia::test(allow_stalls = false)]
910 async fn make_data_frame() {
911 let (fake_device, _) = FakeDevice::new().await;
912 let (mut ctx, _) = make_context(fake_device);
913 let buffer = ctx
914 .make_data_frame(*CLIENT_ADDR2, *CLIENT_ADDR, false, false, 0x1234, &[1, 2, 3, 4, 5])
915 .expect("error making data frame");
916 assert_eq!(
917 &buffer[..],
918 &[
919 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,
931 ][..]
932 );
933 }
934
935 #[fuchsia::test(allow_stalls = false)]
936 async fn make_data_frame_ipv4_qos() {
937 let (fake_device, _) = FakeDevice::new().await;
938 let (mut ctx, _) = make_context(fake_device);
939 let buffer = ctx
940 .make_data_frame(
941 *CLIENT_ADDR2,
942 *CLIENT_ADDR,
943 false,
944 true,
945 0x0800, &[1, 0xB0, 3, 4, 5], )
950 .expect("error making data frame");
951 assert_eq!(
952 &buffer[..],
953 &[
954 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,
967 ][..]
968 );
969 }
970
971 #[fuchsia::test(allow_stalls = false)]
972 async fn make_data_frame_ipv6_qos() {
973 let (fake_device, _) = FakeDevice::new().await;
974 let (mut ctx, _) = make_context(fake_device);
975 let buffer = ctx
976 .make_data_frame(
977 *CLIENT_ADDR2,
978 *CLIENT_ADDR,
979 false,
980 true,
981 0x86DD, &[0b0101, 0b10000000, 3, 4, 5], )
986 .expect("error making data frame");
987 assert_eq!(
988 &buffer[..],
989 &[
990 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,
1003 ][..]
1004 );
1005 }
1006
1007 #[fuchsia::test(allow_stalls = false)]
1008 async fn make_eapol_frame() {
1009 let (fake_device, _) = FakeDevice::new().await;
1010 let (mut ctx, _) = make_context(fake_device);
1011 let buffer = ctx
1012 .make_eapol_frame(*CLIENT_ADDR2, *CLIENT_ADDR, false, &[1, 2, 3, 4, 5])
1013 .expect("error making eapol frame");
1014 assert_eq!(
1015 &buffer[..],
1016 &[
1017 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,
1029 ][..]
1030 );
1031 }
1032
1033 #[fuchsia::test(allow_stalls = false)]
1034 async fn deliver_eth_frame() {
1035 let (fake_device, fake_device_state) = FakeDevice::new().await;
1036 let (mut ctx, _) = make_context(fake_device);
1037 ctx.deliver_eth_frame(*CLIENT_ADDR2, *CLIENT_ADDR, 0x1234, &[1, 2, 3, 4, 5][..])
1038 .expect("expected OK");
1039 assert_eq!(fake_device_state.lock().eth_queue.len(), 1);
1040 #[rustfmt::skip]
1041 assert_eq!(&fake_device_state.lock().eth_queue[0][..], &[
1042 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 0x12, 0x34, 1, 2, 3, 4, 5,
1047 ][..]);
1048 }
1049}