⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/drivers/wifi/
mac80211.rs

1//! IEEE 802.11 MAC Layer Implementation
2//!
3//! Provides frame parsing/construction, BSS scanning, station state machine,
4//! and information element handling for WiFi connectivity.
5
6use alloc::vec::Vec;
7
8use crate::net::MacAddress;
9
10// ============================================================================
11// Frame Types and Subtypes
12// ============================================================================
13
14/// 802.11 frame type (2-bit field in Frame Control)
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[repr(u8)]
17pub enum FrameType {
18    /// Management frames (beacons, probes, auth, assoc)
19    Management = 0,
20    /// Control frames (ACK, RTS, CTS)
21    Control = 1,
22    /// Data frames (payload transport)
23    Data = 2,
24}
25
26impl FrameType {
27    /// Parse frame type from 2-bit value
28    pub fn from_bits(bits: u8) -> Option<Self> {
29        match bits & 0x03 {
30            0 => Some(Self::Management),
31            1 => Some(Self::Control),
32            2 => Some(Self::Data),
33            _ => None,
34        }
35    }
36}
37
38/// Management frame subtypes
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40#[repr(u8)]
41pub enum ManagementSubtype {
42    AssocReq = 0,
43    AssocResp = 1,
44    ProbeReq = 4,
45    ProbeResp = 5,
46    Beacon = 8,
47    Disassoc = 10,
48    Auth = 11,
49    Deauth = 12,
50    Action = 13,
51}
52
53impl ManagementSubtype {
54    /// Parse management subtype from 4-bit value
55    pub fn from_bits(bits: u8) -> Option<Self> {
56        match bits & 0x0F {
57            0 => Some(Self::AssocReq),
58            1 => Some(Self::AssocResp),
59            4 => Some(Self::ProbeReq),
60            5 => Some(Self::ProbeResp),
61            8 => Some(Self::Beacon),
62            10 => Some(Self::Disassoc),
63            11 => Some(Self::Auth),
64            12 => Some(Self::Deauth),
65            13 => Some(Self::Action),
66            _ => None,
67        }
68    }
69}
70
71// ============================================================================
72// Frame Control Field
73// ============================================================================
74
75/// IEEE 802.11 Frame Control field (16 bits)
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub struct FrameControl {
78    /// Protocol version (2 bits, always 0)
79    pub protocol_version: u8,
80    /// Frame type
81    pub frame_type: FrameType,
82    /// Frame subtype (4 bits)
83    pub subtype: u8,
84    /// To Distribution System
85    pub to_ds: bool,
86    /// From Distribution System
87    pub from_ds: bool,
88    /// More Fragments flag
89    pub more_fragments: bool,
90    /// Retry flag
91    pub retry: bool,
92    /// Power Management flag
93    pub power_mgmt: bool,
94    /// More Data flag
95    pub more_data: bool,
96    /// Protected Frame flag (WEP/WPA encryption)
97    pub protected_frame: bool,
98    /// Order flag (+HTC/Order)
99    pub order: bool,
100}
101
102impl Default for FrameControl {
103    fn default() -> Self {
104        Self {
105            protocol_version: 0,
106            frame_type: FrameType::Management,
107            subtype: 0,
108            to_ds: false,
109            from_ds: false,
110            more_fragments: false,
111            retry: false,
112            power_mgmt: false,
113            more_data: false,
114            protected_frame: false,
115            order: false,
116        }
117    }
118}
119
120impl FrameControl {
121    /// Parse Frame Control from 2 bytes (little-endian on air)
122    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
123        if bytes.len() < 2 {
124            return None;
125        }
126        let b0 = bytes[0];
127        let b1 = bytes[1];
128
129        let protocol_version = b0 & 0x03;
130        let type_bits = (b0 >> 2) & 0x03;
131        let subtype = (b0 >> 4) & 0x0F;
132
133        let frame_type = FrameType::from_bits(type_bits)?;
134
135        Some(Self {
136            protocol_version,
137            frame_type,
138            subtype,
139            to_ds: (b1 & 0x01) != 0,
140            from_ds: (b1 & 0x02) != 0,
141            more_fragments: (b1 & 0x04) != 0,
142            retry: (b1 & 0x08) != 0,
143            power_mgmt: (b1 & 0x10) != 0,
144            more_data: (b1 & 0x20) != 0,
145            protected_frame: (b1 & 0x40) != 0,
146            order: (b1 & 0x80) != 0,
147        })
148    }
149
150    /// Serialize Frame Control to 2 bytes
151    pub fn to_bytes(&self) -> [u8; 2] {
152        let b0 = (self.protocol_version & 0x03)
153            | ((self.frame_type as u8 & 0x03) << 2)
154            | ((self.subtype & 0x0F) << 4);
155        let b1 = (self.to_ds as u8)
156            | ((self.from_ds as u8) << 1)
157            | ((self.more_fragments as u8) << 2)
158            | ((self.retry as u8) << 3)
159            | ((self.power_mgmt as u8) << 4)
160            | ((self.more_data as u8) << 5)
161            | ((self.protected_frame as u8) << 6)
162            | ((self.order as u8) << 7);
163        [b0, b1]
164    }
165}
166
167// ============================================================================
168// IEEE 802.11 Header
169// ============================================================================
170
171/// IEEE 802.11 MAC header
172#[derive(Debug, Clone)]
173pub struct Ieee80211Header {
174    /// Frame control field
175    pub frame_control: FrameControl,
176    /// Duration/ID field (microseconds or association ID)
177    pub duration_id: u16,
178    /// Address 1: Receiver/Destination
179    pub addr1: MacAddress,
180    /// Address 2: Transmitter/Source
181    pub addr2: MacAddress,
182    /// Address 3: BSSID or other
183    pub addr3: MacAddress,
184    /// Sequence control (fragment + sequence number)
185    pub sequence_control: u16,
186    /// Address 4 (only in WDS/mesh frames: to_ds=1 && from_ds=1)
187    pub addr4: Option<MacAddress>,
188}
189
190impl Ieee80211Header {
191    /// Minimum header size (without addr4): 2+2+6+6+6+2 = 24 bytes
192    pub const MIN_SIZE: usize = 24;
193    /// Header size with addr4: 24 + 6 = 30 bytes
194    pub const WITH_ADDR4_SIZE: usize = 30;
195
196    /// Parse header from raw bytes
197    pub fn from_bytes(data: &[u8]) -> Option<Self> {
198        if data.len() < Self::MIN_SIZE {
199            return None;
200        }
201
202        let frame_control = FrameControl::from_bytes(&data[0..2])?;
203        let duration_id = u16::from_le_bytes([data[2], data[3]]);
204
205        let mut addr1_bytes = [0u8; 6];
206        addr1_bytes.copy_from_slice(&data[4..10]);
207        let mut addr2_bytes = [0u8; 6];
208        addr2_bytes.copy_from_slice(&data[10..16]);
209        let mut addr3_bytes = [0u8; 6];
210        addr3_bytes.copy_from_slice(&data[16..22]);
211
212        let sequence_control = u16::from_le_bytes([data[22], data[23]]);
213
214        // addr4 present only when both to_ds and from_ds are set
215        let addr4 = if frame_control.to_ds && frame_control.from_ds {
216            if data.len() < Self::WITH_ADDR4_SIZE {
217                return None;
218            }
219            let mut addr4_bytes = [0u8; 6];
220            addr4_bytes.copy_from_slice(&data[24..30]);
221            Some(MacAddress::new(addr4_bytes))
222        } else {
223            None
224        };
225
226        Some(Self {
227            frame_control,
228            duration_id,
229            addr1: MacAddress::new(addr1_bytes),
230            addr2: MacAddress::new(addr2_bytes),
231            addr3: MacAddress::new(addr3_bytes),
232            sequence_control,
233            addr4,
234        })
235    }
236
237    /// Serialize header to bytes
238    pub fn to_bytes(&self) -> Vec<u8> {
239        let mut buf = Vec::with_capacity(Self::WITH_ADDR4_SIZE);
240        let fc = self.frame_control.to_bytes();
241        buf.extend_from_slice(&fc);
242        buf.extend_from_slice(&self.duration_id.to_le_bytes());
243        buf.extend_from_slice(&self.addr1.0);
244        buf.extend_from_slice(&self.addr2.0);
245        buf.extend_from_slice(&self.addr3.0);
246        buf.extend_from_slice(&self.sequence_control.to_le_bytes());
247        if let Some(ref a4) = self.addr4 {
248            buf.extend_from_slice(&a4.0);
249        }
250        buf
251    }
252
253    /// Get header length in bytes
254    pub fn header_len(&self) -> usize {
255        if self.addr4.is_some() {
256            Self::WITH_ADDR4_SIZE
257        } else {
258            Self::MIN_SIZE
259        }
260    }
261}
262
263// ============================================================================
264// Information Elements
265// ============================================================================
266
267/// Information Element IDs used in management frames
268#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269#[repr(u8)]
270pub enum InformationElementId {
271    /// SSID (network name, 0-32 bytes)
272    Ssid = 0,
273    /// Supported rates
274    SupportedRates = 1,
275    /// DS Parameter Set (channel number)
276    DsParameterSet = 3,
277    /// RSN (Robust Security Network) - WPA2/WPA3
278    Rsn = 48,
279}
280
281/// Parsed Information Element
282#[derive(Debug, Clone)]
283pub struct InformationElement {
284    /// Element ID
285    pub id: u8,
286    /// Element data
287    pub data: Vec<u8>,
288}
289
290/// Parse all Information Elements from a byte slice
291pub fn parse_information_elements(data: &[u8]) -> Vec<InformationElement> {
292    let mut elements = Vec::new();
293    let mut offset = 0;
294
295    while offset + 2 <= data.len() {
296        let id = data[offset];
297        let len = data[offset + 1] as usize;
298        offset += 2;
299
300        if offset + len > data.len() {
301            break;
302        }
303
304        elements.push(InformationElement {
305            id,
306            data: data[offset..offset + len].to_vec(),
307        });
308
309        offset += len;
310    }
311
312    elements
313}
314
315/// Extract SSID from Information Elements
316pub fn extract_ssid(ies: &[InformationElement]) -> Option<Vec<u8>> {
317    for ie in ies {
318        if ie.id == InformationElementId::Ssid as u8 && ie.data.len() <= 32 {
319            return Some(ie.data.clone());
320        }
321    }
322    None
323}
324
325/// Extract channel from DS Parameter Set IE
326pub fn extract_channel(ies: &[InformationElement]) -> Option<u8> {
327    for ie in ies {
328        if ie.id == InformationElementId::DsParameterSet as u8 && ie.data.len() == 1 {
329            return Some(ie.data[0]);
330        }
331    }
332    None
333}
334
335/// Check if RSN (WPA2) IE is present
336pub fn has_rsn(ies: &[InformationElement]) -> bool {
337    ies.iter()
338        .any(|ie| ie.id == InformationElementId::Rsn as u8)
339}
340
341// ============================================================================
342// Security Type
343// ============================================================================
344
345/// WiFi security type
346#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
347pub enum SecurityType {
348    /// Open (no encryption)
349    #[default]
350    Open,
351    /// WPA2 Personal (PSK)
352    Wpa2Psk,
353    /// WPA2 Enterprise (802.1X)
354    Wpa2Enterprise,
355    /// WPA3 Personal (SAE)
356    Wpa3Sae,
357}
358
359// ============================================================================
360// BSS Information
361// ============================================================================
362
363/// BSS (Basic Service Set) information from scan results
364#[derive(Debug, Clone)]
365pub struct BssInfo {
366    /// BSSID (AP MAC address)
367    pub bssid: MacAddress,
368    /// SSID (network name, up to 32 bytes)
369    pub ssid: Vec<u8>,
370    /// Channel number (1-14 for 2.4GHz, 36-165 for 5GHz)
371    pub channel: u8,
372    /// Beacon interval in TUs (1 TU = 1024 microseconds)
373    pub beacon_interval: u16,
374    /// Capability information
375    pub capability: u16,
376    /// Signal strength in dBm (integer, typically -90 to -20)
377    pub signal_strength: i8,
378    /// Security type detected from IEs
379    pub security_type: SecurityType,
380}
381
382// ============================================================================
383// Station State Machine
384// ============================================================================
385
386/// Station (STA) connection state
387#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
388pub enum StaState {
389    /// Not connected to any BSS
390    #[default]
391    Disconnected,
392    /// Actively scanning for BSSes
393    Scanning,
394    /// Performing 802.11 authentication
395    Authenticating,
396    /// Sending association request
397    Associating,
398    /// Associated but not yet authenticated (WPA)
399    Associated,
400    /// Fully connected (802.11 + WPA handshake complete)
401    Connected,
402}
403
404/// Station configuration for connection
405#[derive(Debug, Clone)]
406pub struct StaConfig {
407    /// Target SSID to connect to
408    pub ssid: Vec<u8>,
409    /// Pre-computed password hash (PMK) for WPA
410    pub password_hash: [u8; 32],
411    /// Preferred BSSID (if any)
412    pub preferred_bssid: Option<MacAddress>,
413}
414
415/// WiFi station (client) state machine
416pub struct WifiStation {
417    /// Current state
418    state: StaState,
419    /// Our MAC address
420    own_addr: MacAddress,
421    /// Current BSS we are associated with
422    current_bss: Option<BssInfo>,
423    /// Scan results from last scan
424    scan_results: Vec<BssInfo>,
425    /// Connection configuration
426    _config: Option<StaConfig>,
427    /// Sequence number counter for outgoing frames
428    sequence_number: u16,
429}
430
431impl WifiStation {
432    /// Create a new WiFi station
433    pub fn new(own_addr: MacAddress) -> Self {
434        Self {
435            state: StaState::Disconnected,
436            own_addr,
437            current_bss: None,
438            scan_results: Vec::new(),
439            _config: None,
440            sequence_number: 0,
441        }
442    }
443
444    /// Get current station state
445    pub fn state(&self) -> StaState {
446        self.state
447    }
448
449    /// Get current BSS info
450    pub fn current_bss(&self) -> Option<&BssInfo> {
451        self.current_bss.as_ref()
452    }
453
454    /// Get scan results
455    pub fn scan_results(&self) -> &[BssInfo] {
456        &self.scan_results
457    }
458
459    /// Start BSS scanning. Transitions to Scanning state.
460    /// Returns a probe request frame to send on each channel.
461    pub fn start_scan(&mut self) -> Vec<u8> {
462        self.state = StaState::Scanning;
463        self.scan_results.clear();
464        self.build_probe_request()
465    }
466
467    /// Process a received beacon frame and extract BSS information.
468    /// Returns the parsed BSS info if the frame is valid.
469    pub fn process_beacon(&mut self, frame_data: &[u8]) -> Option<BssInfo> {
470        // Beacon frame: header(24) + timestamp(8) + interval(2) + capability(2) + IEs
471        let header = Ieee80211Header::from_bytes(frame_data)?;
472
473        if header.frame_control.frame_type != FrameType::Management {
474            return None;
475        }
476        if header.frame_control.subtype != ManagementSubtype::Beacon as u8 {
477            return None;
478        }
479
480        let hdr_len = header.header_len();
481        // Fixed fields: 8 (timestamp) + 2 (interval) + 2 (capability) = 12 bytes
482        if frame_data.len() < hdr_len + 12 {
483            return None;
484        }
485
486        let fixed_start = hdr_len;
487        // Skip timestamp (8 bytes)
488        let beacon_interval =
489            u16::from_le_bytes([frame_data[fixed_start + 8], frame_data[fixed_start + 9]]);
490        let capability =
491            u16::from_le_bytes([frame_data[fixed_start + 10], frame_data[fixed_start + 11]]);
492
493        let ie_start = fixed_start + 12;
494        let ies = parse_information_elements(&frame_data[ie_start..]);
495
496        let ssid = extract_ssid(&ies).unwrap_or_default();
497        let channel = extract_channel(&ies).unwrap_or(0);
498        let security_type = if has_rsn(&ies) {
499            SecurityType::Wpa2Psk
500        } else if capability & 0x0010 != 0 {
501            // Privacy bit set but no RSN -> legacy WEP/WPA (treat as WPA2 for simplicity)
502            SecurityType::Wpa2Psk
503        } else {
504            SecurityType::Open
505        };
506
507        let bss = BssInfo {
508            bssid: header.addr2,
509            ssid,
510            channel,
511            beacon_interval,
512            capability,
513            signal_strength: -50, // Placeholder; real value from PHY RSSI
514            security_type,
515        };
516
517        // Add to scan results if not already present
518        if !self.scan_results.iter().any(|b| b.bssid.0 == bss.bssid.0) {
519            self.scan_results.push(bss.clone());
520        }
521
522        Some(bss)
523    }
524
525    /// Begin 802.11 Open System authentication with the target BSS.
526    /// Returns authentication frame bytes to send.
527    pub fn authenticate(&mut self, bss: &BssInfo) -> Option<Vec<u8>> {
528        if self.state != StaState::Scanning && self.state != StaState::Disconnected {
529            return None;
530        }
531
532        self.current_bss = Some(bss.clone());
533        self.state = StaState::Authenticating;
534
535        Some(self.build_auth_frame(&bss.bssid))
536    }
537
538    /// Process authentication response. On success, transition to Associating.
539    /// Returns true if authentication succeeded.
540    pub fn process_auth_response(&mut self, frame_data: &[u8]) -> bool {
541        let header = match Ieee80211Header::from_bytes(frame_data) {
542            Some(h) => h,
543            None => return false,
544        };
545
546        if header.frame_control.frame_type != FrameType::Management {
547            return false;
548        }
549        if header.frame_control.subtype != ManagementSubtype::Auth as u8 {
550            return false;
551        }
552
553        let hdr_len = header.header_len();
554        // Auth frame body: algo(2) + seq(2) + status(2) = 6 bytes minimum
555        if frame_data.len() < hdr_len + 6 {
556            return false;
557        }
558
559        let status = u16::from_le_bytes([frame_data[hdr_len + 4], frame_data[hdr_len + 5]]);
560
561        if status == 0 {
562            // Success
563            self.state = StaState::Associating;
564            true
565        } else {
566            self.state = StaState::Disconnected;
567            false
568        }
569    }
570
571    /// Send association request to the current BSS.
572    /// Returns association request frame bytes.
573    pub fn associate(&mut self) -> Option<Vec<u8>> {
574        if self.state != StaState::Associating {
575            return None;
576        }
577
578        let bssid = self.current_bss.as_ref()?.bssid;
579        Some(self.build_assoc_request(&bssid))
580    }
581
582    /// Process association response. On success, transition to Associated.
583    /// Returns true if association succeeded.
584    pub fn process_assoc_response(&mut self, frame_data: &[u8]) -> bool {
585        let header = match Ieee80211Header::from_bytes(frame_data) {
586            Some(h) => h,
587            None => return false,
588        };
589
590        if header.frame_control.frame_type != FrameType::Management {
591            return false;
592        }
593        if header.frame_control.subtype != ManagementSubtype::AssocResp as u8 {
594            return false;
595        }
596
597        let hdr_len = header.header_len();
598        // Assoc response body: capability(2) + status(2) + AID(2)
599        if frame_data.len() < hdr_len + 6 {
600            return false;
601        }
602
603        let status = u16::from_le_bytes([frame_data[hdr_len + 2], frame_data[hdr_len + 3]]);
604
605        if status == 0 {
606            self.state = StaState::Associated;
607            true
608        } else {
609            self.state = StaState::Disconnected;
610            false
611        }
612    }
613
614    /// Deauthenticate from the current BSS.
615    /// Returns deauthentication frame bytes.
616    pub fn deauthenticate(&mut self, reason: u16) -> Option<Vec<u8>> {
617        let bssid = self.current_bss.as_ref()?.bssid;
618        let frame = self.build_deauth_frame(&bssid, reason);
619        self.state = StaState::Disconnected;
620        self.current_bss = None;
621        Some(frame)
622    }
623
624    /// Mark station as fully connected (after WPA handshake completes)
625    pub fn set_connected(&mut self) {
626        if self.state == StaState::Associated {
627            self.state = StaState::Connected;
628        }
629    }
630
631    /// Parse a raw 802.11 frame into header + body
632    pub fn parse_frame(data: &[u8]) -> Option<(Ieee80211Header, &[u8])> {
633        let header = Ieee80211Header::from_bytes(data)?;
634        let hdr_len = header.header_len();
635        if data.len() >= hdr_len {
636            Some((header, &data[hdr_len..]))
637        } else {
638            None
639        }
640    }
641
642    /// Get the next sequence number and advance counter
643    fn next_sequence_control(&mut self) -> u16 {
644        let seq = self.sequence_number;
645        self.sequence_number = self.sequence_number.wrapping_add(1) & 0x0FFF;
646        seq << 4 // Fragment number = 0
647    }
648
649    /// Build a Probe Request frame (broadcast)
650    fn build_probe_request(&mut self) -> Vec<u8> {
651        let fc = FrameControl {
652            frame_type: FrameType::Management,
653            subtype: ManagementSubtype::ProbeReq as u8,
654            ..Default::default()
655        };
656        let seq = self.next_sequence_control();
657        let header = Ieee80211Header {
658            frame_control: fc,
659            duration_id: 0,
660            addr1: MacAddress::BROADCAST,
661            addr2: self.own_addr,
662            addr3: MacAddress::BROADCAST,
663            sequence_control: seq,
664            addr4: None,
665        };
666
667        let mut frame = header.to_bytes();
668
669        // SSID IE (wildcard = zero length)
670        frame.push(InformationElementId::Ssid as u8);
671        frame.push(0);
672
673        // Supported Rates IE (basic set)
674        frame.push(InformationElementId::SupportedRates as u8);
675        frame.push(4);
676        frame.extend_from_slice(&[0x82, 0x84, 0x8B, 0x96]); // 1, 2, 5.5, 11 Mbps
677
678        frame
679    }
680
681    /// Build an Authentication frame (Open System, seq 1)
682    fn build_auth_frame(&mut self, bssid: &MacAddress) -> Vec<u8> {
683        let fc = FrameControl {
684            frame_type: FrameType::Management,
685            subtype: ManagementSubtype::Auth as u8,
686            ..Default::default()
687        };
688        let seq = self.next_sequence_control();
689        let header = Ieee80211Header {
690            frame_control: fc,
691            duration_id: 0,
692            addr1: *bssid,
693            addr2: self.own_addr,
694            addr3: *bssid,
695            sequence_control: seq,
696            addr4: None,
697        };
698
699        let mut frame = header.to_bytes();
700
701        // Auth algorithm: Open System (0)
702        frame.extend_from_slice(&0u16.to_le_bytes());
703        // Auth sequence number: 1
704        frame.extend_from_slice(&1u16.to_le_bytes());
705        // Status code: 0 (reserved, should be 0 in request)
706        frame.extend_from_slice(&0u16.to_le_bytes());
707
708        frame
709    }
710
711    /// Build an Association Request frame
712    fn build_assoc_request(&mut self, bssid: &MacAddress) -> Vec<u8> {
713        let fc = FrameControl {
714            frame_type: FrameType::Management,
715            subtype: ManagementSubtype::AssocReq as u8,
716            ..Default::default()
717        };
718        let seq = self.next_sequence_control();
719        let header = Ieee80211Header {
720            frame_control: fc,
721            duration_id: 0,
722            addr1: *bssid,
723            addr2: self.own_addr,
724            addr3: *bssid,
725            sequence_control: seq,
726            addr4: None,
727        };
728
729        let mut frame = header.to_bytes();
730
731        // Capability info: ESS(bit 0) + short preamble(bit 5)
732        let capability: u16 = 0x0021;
733        frame.extend_from_slice(&capability.to_le_bytes());
734        // Listen interval (in beacon intervals)
735        frame.extend_from_slice(&10u16.to_le_bytes());
736
737        // SSID IE
738        if let Some(ref bss) = self.current_bss {
739            frame.push(InformationElementId::Ssid as u8);
740            frame.push(bss.ssid.len() as u8);
741            frame.extend_from_slice(&bss.ssid);
742        }
743
744        // Supported Rates IE
745        frame.push(InformationElementId::SupportedRates as u8);
746        frame.push(4);
747        frame.extend_from_slice(&[0x82, 0x84, 0x8B, 0x96]);
748
749        frame
750    }
751
752    /// Build a Deauthentication frame
753    fn build_deauth_frame(&mut self, bssid: &MacAddress, reason: u16) -> Vec<u8> {
754        let fc = FrameControl {
755            frame_type: FrameType::Management,
756            subtype: ManagementSubtype::Deauth as u8,
757            ..Default::default()
758        };
759        let seq = self.next_sequence_control();
760        let header = Ieee80211Header {
761            frame_control: fc,
762            duration_id: 0,
763            addr1: *bssid,
764            addr2: self.own_addr,
765            addr3: *bssid,
766            sequence_control: seq,
767            addr4: None,
768        };
769
770        let mut frame = header.to_bytes();
771        frame.extend_from_slice(&reason.to_le_bytes());
772        frame
773    }
774}
775
776// ============================================================================
777// Tests
778// ============================================================================
779
780#[cfg(test)]
781mod tests {
782    #[allow(unused_imports)]
783    use alloc::vec;
784
785    use super::*;
786
787    #[test]
788    fn test_frame_type_from_bits() {
789        assert_eq!(FrameType::from_bits(0), Some(FrameType::Management));
790        assert_eq!(FrameType::from_bits(1), Some(FrameType::Control));
791        assert_eq!(FrameType::from_bits(2), Some(FrameType::Data));
792        assert_eq!(FrameType::from_bits(3), None);
793    }
794
795    #[test]
796    fn test_management_subtype_from_bits() {
797        assert_eq!(
798            ManagementSubtype::from_bits(8),
799            Some(ManagementSubtype::Beacon)
800        );
801        assert_eq!(
802            ManagementSubtype::from_bits(11),
803            Some(ManagementSubtype::Auth)
804        );
805        assert_eq!(
806            ManagementSubtype::from_bits(0),
807            Some(ManagementSubtype::AssocReq)
808        );
809        assert_eq!(ManagementSubtype::from_bits(15), None);
810    }
811
812    #[test]
813    fn test_frame_control_roundtrip() {
814        let fc = FrameControl {
815            protocol_version: 0,
816            frame_type: FrameType::Management,
817            subtype: ManagementSubtype::Beacon as u8,
818            to_ds: false,
819            from_ds: false,
820            retry: true,
821            protected_frame: true,
822            ..Default::default()
823        };
824        let bytes = fc.to_bytes();
825        let parsed = FrameControl::from_bytes(&bytes).unwrap();
826        assert_eq!(parsed.frame_type, FrameType::Management);
827        assert_eq!(parsed.subtype, ManagementSubtype::Beacon as u8);
828        assert!(parsed.retry);
829        assert!(parsed.protected_frame);
830        assert!(!parsed.to_ds);
831    }
832
833    #[test]
834    fn test_frame_control_data_frame() {
835        let fc = FrameControl {
836            frame_type: FrameType::Data,
837            subtype: 0,
838            to_ds: true,
839            from_ds: false,
840            ..Default::default()
841        };
842        let bytes = fc.to_bytes();
843        let parsed = FrameControl::from_bytes(&bytes).unwrap();
844        assert_eq!(parsed.frame_type, FrameType::Data);
845        assert!(parsed.to_ds);
846        assert!(!parsed.from_ds);
847    }
848
849    #[test]
850    fn test_frame_control_too_short() {
851        assert!(FrameControl::from_bytes(&[0x80]).is_none());
852        assert!(FrameControl::from_bytes(&[]).is_none());
853    }
854
855    #[test]
856    fn test_header_roundtrip() {
857        let header = Ieee80211Header {
858            frame_control: FrameControl {
859                frame_type: FrameType::Management,
860                subtype: ManagementSubtype::Beacon as u8,
861                ..Default::default()
862            },
863            duration_id: 0x1234,
864            addr1: MacAddress::BROADCAST,
865            addr2: MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]),
866            addr3: MacAddress::new([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]),
867            sequence_control: 0x0010,
868            addr4: None,
869        };
870
871        let bytes = header.to_bytes();
872        assert_eq!(bytes.len(), Ieee80211Header::MIN_SIZE);
873
874        let parsed = Ieee80211Header::from_bytes(&bytes).unwrap();
875        assert_eq!(parsed.duration_id, 0x1234);
876        assert_eq!(parsed.addr1.0, MacAddress::BROADCAST.0);
877        assert_eq!(parsed.addr2.0, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
878        assert_eq!(parsed.sequence_control, 0x0010);
879        assert!(parsed.addr4.is_none());
880    }
881
882    #[test]
883    fn test_header_with_addr4() {
884        let mut fc = FrameControl::default();
885        fc.to_ds = true;
886        fc.from_ds = true;
887
888        let header = Ieee80211Header {
889            frame_control: fc,
890            duration_id: 0,
891            addr1: MacAddress::BROADCAST,
892            addr2: MacAddress::ZERO,
893            addr3: MacAddress::ZERO,
894            sequence_control: 0,
895            addr4: Some(MacAddress::new([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])),
896        };
897
898        let bytes = header.to_bytes();
899        assert_eq!(bytes.len(), Ieee80211Header::WITH_ADDR4_SIZE);
900
901        let parsed = Ieee80211Header::from_bytes(&bytes).unwrap();
902        assert!(parsed.addr4.is_some());
903        assert_eq!(
904            parsed.addr4.unwrap().0,
905            [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]
906        );
907    }
908
909    #[test]
910    fn test_header_too_short() {
911        let data = [0u8; 20];
912        assert!(Ieee80211Header::from_bytes(&data).is_none());
913    }
914
915    #[test]
916    fn test_parse_information_elements() {
917        // SSID IE: id=0, len=4, data="Test"
918        // Channel IE: id=3, len=1, data=6
919        let data = [0, 4, b'T', b'e', b's', b't', 3, 1, 6];
920        let ies = parse_information_elements(&data);
921        assert_eq!(ies.len(), 2);
922        assert_eq!(ies[0].id, 0);
923        assert_eq!(ies[0].data, b"Test");
924        assert_eq!(ies[1].id, 3);
925        assert_eq!(ies[1].data, &[6]);
926    }
927
928    #[test]
929    fn test_extract_ssid() {
930        let ies = vec![
931            InformationElement {
932                id: 0,
933                data: b"MyNetwork".to_vec(),
934            },
935            InformationElement {
936                id: 3,
937                data: vec![11],
938            },
939        ];
940        let ssid = extract_ssid(&ies);
941        assert_eq!(ssid, Some(b"MyNetwork".to_vec()));
942    }
943
944    #[test]
945    fn test_extract_channel() {
946        let ies = vec![InformationElement {
947            id: 3,
948            data: vec![6],
949        }];
950        assert_eq!(extract_channel(&ies), Some(6));
951    }
952
953    #[test]
954    fn test_has_rsn() {
955        let ies_with_rsn = vec![InformationElement {
956            id: 48,
957            data: vec![1, 0],
958        }];
959        assert!(has_rsn(&ies_with_rsn));
960
961        let ies_without = vec![InformationElement {
962            id: 0,
963            data: vec![],
964        }];
965        assert!(!has_rsn(&ies_without));
966    }
967
968    #[test]
969    fn test_wifi_station_initial_state() {
970        let sta = WifiStation::new(MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]));
971        assert_eq!(sta.state(), StaState::Disconnected);
972        assert!(sta.current_bss().is_none());
973        assert!(sta.scan_results().is_empty());
974    }
975
976    #[test]
977    fn test_wifi_station_start_scan() {
978        let mut sta = WifiStation::new(MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]));
979        let probe = sta.start_scan();
980        assert_eq!(sta.state(), StaState::Scanning);
981        // Probe request should have at least a header + SSID IE + Rates IE
982        assert!(probe.len() >= Ieee80211Header::MIN_SIZE + 2 + 6);
983    }
984
985    #[test]
986    fn test_security_type_default() {
987        assert_eq!(SecurityType::default(), SecurityType::Open);
988    }
989}