xref: /aosp_15_r20/tools/netsim/rust/packets/src/ieee80211.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! ieee80211 frames
16 
17 // TODO: only allow the warnings for the included code
18 #![allow(clippy::all)]
19 #![allow(missing_docs)]
20 #![allow(unused)]
21 include!(concat!(env!("OUT_DIR"), "/ieee80211_packets.rs"));
22 
23 use crate::llc::{EtherType, LlcCtrl, LlcSap, LlcSnapHeader};
24 use anyhow::anyhow;
25 
26 const ETHERTYPE_LEN: usize = 2;
27 
28 /// A Ieee80211 MAC address
29 
30 impl MacAddress {
to_vec(&self) -> [u8; 6]31     pub fn to_vec(&self) -> [u8; 6] {
32         u64::to_le_bytes(self.0)[0..6].try_into().expect("slice with incorrect length")
33     }
34 }
35 
36 // TODO: Add unit tests.
37 impl fmt::Display for MacAddress {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result38     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39         let bytes = u64::to_le_bytes(self.0);
40         write!(
41             f,
42             "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
43             bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5],
44         )
45     }
46 }
47 
48 impl fmt::Display for Ieee80211 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result49     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50         write!(
51             f,
52             "{{ds: {}, src: {}, dst: {}}}",
53             self.get_ds(),
54             self.get_source(),
55             self.get_destination()
56         )
57     }
58 }
59 
60 impl From<&[u8; 6]> for MacAddress {
from(bytes: &[u8; 6]) -> Self61     fn from(bytes: &[u8; 6]) -> Self {
62         Self(u64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], 0, 0]))
63     }
64 }
65 
66 impl From<MacAddress> for [u8; 6] {
from(MacAddress(addr): MacAddress) -> Self67     fn from(MacAddress(addr): MacAddress) -> Self {
68         let bytes = u64::to_le_bytes(addr);
69         bytes[0..6].try_into().unwrap()
70     }
71 }
72 
73 impl MacAddress {
74     pub const LEN: usize = 6;
75 
is_multicast(&self) -> bool76     pub fn is_multicast(&self) -> bool {
77         let addr = u64::to_le_bytes(self.0);
78         (addr[0] & 0x1) == 1
79     }
80 
is_broadcast(&self) -> bool81     pub fn is_broadcast(&self) -> bool {
82         self.0 == u64::MAX
83     }
84 }
85 
86 struct Ieee8023<'a> {
87     destination: MacAddress,
88     source: MacAddress,
89     ethertype: EtherType,
90     payload: &'a [u8],
91 }
92 
93 impl<'a> Ieee8023<'a> {
94     pub const HDR_LEN: usize = 14;
95 
96     /// Creates an `Ieee8023` instance from packet slice.
from(packet: &'a [u8]) -> anyhow::Result<Self>97     fn from(packet: &'a [u8]) -> anyhow::Result<Self> {
98         // Ensure the packet has enough bytes for the header
99         anyhow::ensure!(
100             packet.len() >= Self::HDR_LEN,
101             "Packet (len: {}) too short for IEEE 802.3 header",
102             packet.len()
103         );
104         let dest_slice: &[u8; 6] = packet[..MacAddress::LEN].try_into()?;
105         let src_slice: &[u8; 6] = packet[MacAddress::LEN..2 * MacAddress::LEN].try_into()?;
106         let ethertype_bytes = packet[2 * MacAddress::LEN..Self::HDR_LEN].try_into()?;
107         let ethertype = EtherType::try_from(u16::from_be_bytes(ethertype_bytes))
108             .map_err(|e| anyhow::anyhow!("invalid EtherType: {e}"))?;
109 
110         Ok(Ieee8023 {
111             destination: MacAddress::from(dest_slice),
112             source: MacAddress::from(src_slice),
113             ethertype,
114             payload: &packet[Self::HDR_LEN..],
115         })
116     }
117 
to_vec(self) -> anyhow::Result<Vec<u8>>118     fn to_vec(self) -> anyhow::Result<Vec<u8>> {
119         // Build 802.3 frame
120         let mut ethernet_frame =
121             Vec::with_capacity(MacAddress::LEN * 2 + ETHERTYPE_LEN + self.payload.len());
122 
123         ethernet_frame.extend_from_slice(&self.destination.to_vec());
124         ethernet_frame.extend_from_slice(&self.source.to_vec());
125         // Add extracted EtherType
126         ethernet_frame.extend_from_slice(&u16::from(self.ethertype).to_be_bytes());
127         // Actually data is after 802.2 LLC/SNAP header
128         ethernet_frame.extend_from_slice(self.payload);
129         Ok(ethernet_frame)
130     }
131 }
132 
133 impl Ieee80211 {
134     // Create Ieee80211 from Ieee8023 frame.
from_ieee8023(packet: &[u8], bssid: MacAddress) -> anyhow::Result<Ieee80211>135     pub fn from_ieee8023(packet: &[u8], bssid: MacAddress) -> anyhow::Result<Ieee80211> {
136         let ieee8023 = Ieee8023::from(packet)?;
137 
138         let llc_snap_header = LlcSnapHeader {
139             dsap: LlcSap::Snap,
140             ssap: LlcSap::Snap,
141             ctrl: LlcCtrl::UiCmd,
142             oui: 0,
143             ethertype: ieee8023.ethertype,
144         };
145         // IEEE80211 payload: LLC/SNAP Header + IEEE8023 payload
146         let mut payload = Vec::with_capacity(LlcSnapHeader::LEN + ieee8023.payload.len());
147         llc_snap_header.encode(&mut payload)?;
148         payload.extend_from_slice(ieee8023.payload);
149 
150         Ok(Ieee80211FromAp {
151             duration_id: 0,
152             ftype: FrameType::Data,
153             more_data: 0,
154             more_frags: 0,
155             order: 0,
156             pm: 0,
157             protected: 0,
158             retry: 0,
159             stype: 0,
160             version: 0,
161             bssid,
162             source: ieee8023.source,
163             destination: ieee8023.destination,
164             seq_ctrl: 0,
165             payload,
166         }
167         .try_into()?)
168     }
169 
170     // Frame has addr4 field
has_a4(&self) -> bool171     pub fn has_a4(&self) -> bool {
172         self.to_ds == 1 || self.from_ds == 1
173     }
174 
is_to_ap(&self) -> bool175     pub fn is_to_ap(&self) -> bool {
176         self.to_ds == 1 && self.from_ds == 0
177     }
178 
179     // Frame type is management
is_mgmt(&self) -> bool180     pub fn is_mgmt(&self) -> bool {
181         self.ftype == FrameType::Mgmt
182     }
183 
184     // Frame is (management) beacon frame
is_beacon(&self) -> bool185     pub fn is_beacon(&self) -> bool {
186         self.ftype == FrameType::Mgmt && self.stype == (ManagementSubType::Beacon as u8)
187     }
188 
189     // Frame type is data
is_data(&self) -> bool190     pub fn is_data(&self) -> bool {
191         self.ftype == FrameType::Data
192     }
193 
194     // Frame is probe request
is_probe_req(&self) -> bool195     pub fn is_probe_req(&self) -> bool {
196         self.ftype == FrameType::Ctl && self.stype == (ManagementSubType::ProbeReq as u8)
197     }
198 
199     // Frame type is EAPoL
is_eapol(&self) -> anyhow::Result<bool>200     pub fn is_eapol(&self) -> anyhow::Result<bool> {
201         Ok(self.get_ethertype()? == EtherType::Eapol)
202     }
203 
get_ds(&self) -> String204     pub fn get_ds(&self) -> String {
205         match self.specialize().unwrap() {
206             Ieee80211Child::Ieee80211ToAp(hdr) => "ToAp",
207             Ieee80211Child::Ieee80211FromAp(hdr) => "FromAp",
208             Ieee80211Child::Ieee80211Ibss(hdr) => "Ibss",
209             Ieee80211Child::Ieee80211Wds(hdr) => "Wds",
210             _ => panic!("unexpected specialized header"),
211         }
212         .to_string()
213     }
214 
get_source(&self) -> MacAddress215     pub fn get_source(&self) -> MacAddress {
216         match self.specialize().unwrap() {
217             Ieee80211Child::Ieee80211ToAp(hdr) => hdr.source,
218             Ieee80211Child::Ieee80211FromAp(hdr) => hdr.source,
219             Ieee80211Child::Ieee80211Ibss(hdr) => hdr.source,
220             Ieee80211Child::Ieee80211Wds(hdr) => hdr.source,
221             _ => panic!("unexpected specialized header"),
222         }
223     }
224 
225     /// Ieee80211 packets have 3-4 addresses in different positions based
226     /// on the FromDS and ToDS flags. This function gets the destination
227     /// address depending on the FromDS+ToDS packet subtypes.
get_destination(&self) -> MacAddress228     pub fn get_destination(&self) -> MacAddress {
229         match self.specialize().unwrap() {
230             Ieee80211Child::Ieee80211ToAp(hdr) => hdr.destination,
231             Ieee80211Child::Ieee80211FromAp(hdr) => hdr.destination,
232             Ieee80211Child::Ieee80211Ibss(hdr) => hdr.destination,
233             Ieee80211Child::Ieee80211Wds(hdr) => hdr.destination,
234             _ => panic!("unexpected specialized header"),
235         }
236     }
237 
get_bssid(&self) -> Option<MacAddress>238     pub fn get_bssid(&self) -> Option<MacAddress> {
239         match self.specialize().unwrap() {
240             Ieee80211Child::Ieee80211ToAp(hdr) => Some(hdr.bssid),
241             Ieee80211Child::Ieee80211FromAp(hdr) => Some(hdr.bssid),
242             Ieee80211Child::Ieee80211Ibss(hdr) => Some(hdr.bssid),
243             Ieee80211Child::Ieee80211Wds(hdr) => None,
244             _ => panic!("unexpected specialized header"),
245         }
246     }
247 
get_addr1(&self) -> MacAddress248     pub fn get_addr1(&self) -> MacAddress {
249         match self.specialize().unwrap() {
250             Ieee80211Child::Ieee80211ToAp(hdr) => hdr.bssid,
251             Ieee80211Child::Ieee80211FromAp(hdr) => hdr.destination,
252             Ieee80211Child::Ieee80211Ibss(hdr) => hdr.destination,
253             Ieee80211Child::Ieee80211Wds(hdr) => hdr.receiver,
254             _ => panic!("unexpected specialized header"),
255         }
256     }
257 
get_ssid_from_beacon_frame(&self) -> anyhow::Result<String>258     pub fn get_ssid_from_beacon_frame(&self) -> anyhow::Result<String> {
259         // Verify packet is a beacon frame
260         if !self.is_beacon() {
261             return Err(anyhow!("Frame is not beacon frame."));
262         };
263 
264         // SSID field starts after the first 36 bytes. Ieee80211 payload starts after 4 bytes.
265         let pos = 36 - 4;
266 
267         // Check for SSID element ID (0) and extract the SSID
268         let payload = &self.payload;
269         if payload[pos] == 0 {
270             let ssid_len = payload[pos + 1] as usize;
271             let ssid_bytes = &payload[pos + 2..pos + 2 + ssid_len];
272             return Ok(String::from_utf8(ssid_bytes.to_vec())?);
273         }
274 
275         Err(anyhow!("SSID not found."))
276     }
277 
get_payload(&self) -> Vec<u8>278     fn get_payload(&self) -> Vec<u8> {
279         match self.specialize().unwrap() {
280             Ieee80211Child::Ieee80211ToAp(hdr) => hdr.payload,
281             Ieee80211Child::Ieee80211FromAp(hdr) => hdr.payload,
282             Ieee80211Child::Ieee80211Ibss(hdr) => hdr.payload,
283             Ieee80211Child::Ieee80211Wds(hdr) => hdr.payload,
284             _ => panic!("unexpected specialized header"),
285         }
286     }
287 
get_ethertype(&self) -> anyhow::Result<EtherType>288     fn get_ethertype(&self) -> anyhow::Result<EtherType> {
289         if !self.is_data() {
290             return Err(anyhow!("Not an 802.2 LLC/SNAP frame"));
291         }
292 
293         // Extract and validate LLC/SNAP header
294         let payload = self.get_payload();
295         if payload.len() < LlcSnapHeader::LEN {
296             return Err(anyhow!("Payload too short for LLC/SNAP header"));
297         }
298         let llc_snap_header = LlcSnapHeader::decode_full(&payload[..LlcSnapHeader::LEN])?;
299         Ok(llc_snap_header.ethertype())
300     }
301 
with_address( &self, source: Option<MacAddress>, destination: Option<MacAddress>, ) -> Ieee80211302     pub fn with_address(
303         &self,
304         source: Option<MacAddress>,
305         destination: Option<MacAddress>,
306     ) -> Ieee80211 {
307         match self.specialize().unwrap() {
308             Ieee80211Child::Ieee80211ToAp(frame) => {
309                 frame.with_address(source, destination).try_into().unwrap()
310             }
311             Ieee80211Child::Ieee80211FromAp(frame) => {
312                 frame.with_address(source, destination).try_into().unwrap()
313             }
314             Ieee80211Child::Ieee80211Ibss(frame) => {
315                 frame.with_address(source, destination).try_into().unwrap()
316             }
317             Ieee80211Child::Ieee80211Wds(frame) => {
318                 frame.with_address(source, destination).try_into().unwrap()
319             }
320             _ => panic!("Unknown Ieee80211Child type"),
321         }
322     }
323 
324     /// Covert Ieee80211ToAp to Ieee80211FromAp packet.
into_from_ap(&self) -> anyhow::Result<Ieee80211FromAp>325     pub fn into_from_ap(&self) -> anyhow::Result<Ieee80211FromAp> {
326         match self.specialize().unwrap() {
327             Ieee80211Child::Ieee80211ToAp(frame_to_ap) => {
328                 // Flip from_ap and to_ap bits.
329                 // TODO: Investigate if there is a way to copy frame_control flags at once.
330                 // The header struct only has 7 fields, not 15. Most fields come from le16 frame_control.
331                 Ok(Ieee80211FromAp {
332                     duration_id: frame_to_ap.duration_id,
333                     ftype: frame_to_ap.ftype,
334                     more_data: frame_to_ap.more_data,
335                     more_frags: frame_to_ap.more_frags,
336                     order: frame_to_ap.order,
337                     pm: frame_to_ap.pm,
338                     protected: frame_to_ap.protected,
339                     retry: frame_to_ap.retry,
340                     stype: frame_to_ap.stype,
341                     version: frame_to_ap.version,
342                     bssid: frame_to_ap.bssid,
343                     source: frame_to_ap.source,
344                     destination: frame_to_ap.destination,
345                     seq_ctrl: frame_to_ap.seq_ctrl,
346                     payload: frame_to_ap.payload.to_vec(),
347                 })
348             }
349             _ => Err(anyhow!(
350                 "Invalid Ieee80211Child packet. from_ds: {}, to_ds: {}",
351                 self.from_ds,
352                 self.to_ds
353             )),
354         }
355     }
356 
357     // Convert to ieee802.3
to_ieee8023(&self) -> anyhow::Result<Vec<u8>>358     pub fn to_ieee8023(&self) -> anyhow::Result<Vec<u8>> {
359         let ethertype = self.get_ethertype()?;
360         let payload = self.get_payload();
361         Ieee8023 {
362             destination: self.get_destination(),
363             source: self.get_source(),
364             ethertype,
365             payload: &payload[LlcSnapHeader::LEN..],
366         }
367         .to_vec()
368     }
369 }
370 
371 impl Ieee80211FromAp {
with_address( &self, source: Option<MacAddress>, destination: Option<MacAddress>, ) -> Ieee80211FromAp372     pub fn with_address(
373         &self,
374         source: Option<MacAddress>,
375         destination: Option<MacAddress>,
376     ) -> Ieee80211FromAp {
377         Ieee80211FromAp {
378             source: source.unwrap_or(self.source),
379             destination: destination.unwrap_or(self.destination),
380             ..self.clone()
381         }
382     }
383 }
384 
385 impl Ieee80211ToAp {
with_address( &self, source: Option<MacAddress>, destination: Option<MacAddress>, ) -> Ieee80211ToAp386     pub fn with_address(
387         &self,
388         source: Option<MacAddress>,
389         destination: Option<MacAddress>,
390     ) -> Ieee80211ToAp {
391         Ieee80211ToAp {
392             source: source.unwrap_or(self.source),
393             destination: destination.unwrap_or(self.destination),
394             ..self.clone()
395         }
396     }
397 }
398 
399 impl Ieee80211Ibss {
with_address( &self, source: Option<MacAddress>, destination: Option<MacAddress>, ) -> Ieee80211Ibss400     pub fn with_address(
401         &self,
402         source: Option<MacAddress>,
403         destination: Option<MacAddress>,
404     ) -> Ieee80211Ibss {
405         Ieee80211Ibss {
406             source: source.unwrap_or(self.source),
407             destination: destination.unwrap_or(self.destination),
408             ..self.clone()
409         }
410     }
411 }
412 
413 impl Ieee80211Wds {
with_address( &self, source: Option<MacAddress>, destination: Option<MacAddress>, ) -> Ieee80211Wds414     pub fn with_address(
415         &self,
416         source: Option<MacAddress>,
417         destination: Option<MacAddress>,
418     ) -> Ieee80211Wds {
419         Ieee80211Wds {
420             source: source.unwrap_or(self.source),
421             destination: destination.unwrap_or(self.destination),
422             ..self.clone()
423         }
424     }
425 }
426 
parse_mac_address(s: &str) -> Option<MacAddress>427 pub fn parse_mac_address(s: &str) -> Option<MacAddress> {
428     let parts: Vec<&str> = s.split(':').collect();
429     if parts.len() != 6 {
430         return None;
431     }
432     let mut bytes = [0u8; 6];
433     for (i, part) in parts.iter().enumerate() {
434         match u8::from_str_radix(part, 16) {
435             Ok(n) => bytes[i] = n,
436             Err(e) => return None,
437         }
438     }
439     Some(MacAddress::from(&bytes))
440 }
441 
442 #[cfg(test)]
443 mod tests {
444     use super::*;
445 
446     #[test]
test_mac_address_len()447     fn test_mac_address_len() {
448         let mac_address: MacAddress = parse_mac_address("00:0b:85:71:20:ce").unwrap();
449         assert_eq!(mac_address.encoded_len(), MacAddress::LEN);
450     }
451 
452     #[test]
test_mac_address_to_vec()453     fn test_mac_address_to_vec() {
454         let mac_address: MacAddress = parse_mac_address("00:0b:85:71:20:ce").unwrap();
455         let mac_address_bytes = mac_address.to_vec();
456         let reconstructed_mac_address = MacAddress::from(&mac_address_bytes);
457         assert_eq!(mac_address, reconstructed_mac_address);
458     }
459 
460     // These tests use the packets available here
461     // https://community.cisco.com/t5/wireless-mobility-knowledge-base/802-11-frames-a-starter-guide-to-learn-wireless-sniffer-traces/ta-p/3110019
462 
463     #[test]
test_frame_qos()464     fn test_frame_qos() {
465         let frame: Vec<u8> = vec![
466             0x88, 0x02, 0x2c, 0x00, 0x00, 0x13, 0xe8, 0xeb, 0xd6, 0x03, 0x00, 0x0b, 0x85, 0x71,
467             0x20, 0xce, 0x00, 0x0b, 0x85, 0x71, 0x20, 0xce, 0x00, 0x26, 0x00, 0x00,
468         ];
469         let hdr = Ieee80211::decode_full(&frame).unwrap();
470         assert!(hdr.is_data());
471         assert_eq!(hdr.stype, DataSubType::Qos as u8);
472         assert_eq!(hdr.from_ds, 1);
473         assert_eq!(hdr.to_ds, 0);
474         assert_eq!(hdr.duration_id, 44);
475         // Source address: Cisco_71:20:ce (00:0b:85:71:20:ce)
476         let a = format!("{}", hdr.get_source());
477         let b = format!("{}", parse_mac_address("00:0b:85:71:20:ce").unwrap());
478         assert_eq!(a, b);
479     }
480 
481     #[test]
test_beacon_frame()482     fn test_beacon_frame() {
483         // Example from actual beacon frame from Hostapd with "AndroidWifi" SSID
484         let frame: Vec<u8> = vec![
485             0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0x10, 0x85,
486             0xfe, 0x01, 0x00, 0x13, 0x10, 0x85, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487             0x00, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x01, 0x04, 0x00, 0x0b, 0x41, 0x6e, 0x64, 0x72,
488             0x6f, 0x69, 0x64, 0x57, 0x69, 0x66, 0x69, 0x01, 0x04, 0x82, 0x84, 0x8b, 0x96, 0x03,
489             0x01, 0x08, 0x2a, 0x01, 0x07, 0x2d, 0x1a, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491             0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x16, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493             0x00, 0x7f, 0x04, 0x00, 0x00, 0x00, 0x02,
494         ];
495         let decoded_frame = Ieee80211::decode_full(&frame).unwrap();
496         assert!(decoded_frame.is_mgmt());
497         assert!(decoded_frame.is_beacon());
498         let ssid = decoded_frame.get_ssid_from_beacon_frame();
499         assert!(ssid.is_ok());
500         assert_eq!(ssid.unwrap(), "AndroidWifi");
501     }
502 
503     #[test]
test_is_multicast()504     fn test_is_multicast() {
505         // Multicast MAC address: 01:00:5E:00:00:FB
506         let mdns_mac_address = parse_mac_address("01:00:5e:00:00:fb").unwrap();
507         assert!(mdns_mac_address.is_multicast());
508         // Broadcast MAC address: ff:ff:ff:ff:ff:ff
509         let broadcast_mac_address = parse_mac_address("ff:ff:ff:ff:ff:ff").unwrap();
510         assert!(broadcast_mac_address.is_multicast());
511         // Source address: Cisco_71:20:ce (00:0b:85:71:20:ce)
512         let non_mdns_mac_address = parse_mac_address("00:0b:85:71:20:ce").unwrap();
513         assert!(!non_mdns_mac_address.is_multicast());
514     }
515 
test_is_broadcast()516     fn test_is_broadcast() {
517         // Multicast MAC address: 01:00:5E:00:00:FB
518         let mdns_mac_address = parse_mac_address("01:00:5e:00:00:fb").unwrap();
519         assert!(!mdns_mac_address.is_broadcast());
520         // Broadcast MAC address: ff:ff:ff:ff:ff:ff
521         let broadcast_mac_address = parse_mac_address("ff:ff:ff:ff:ff:ff").unwrap();
522         assert!(broadcast_mac_address.is_broadcast());
523         // Source address: Cisco_71:20:ce (00:0b:85:71:20:ce)
524         let non_mdns_mac_address = parse_mac_address("00:0b:85:71:20:ce").unwrap();
525         assert!(!non_mdns_mac_address.is_broadcast());
526     }
527 
528     #[test]
test_ieee8023_from_valid_packet()529     fn test_ieee8023_from_valid_packet() {
530         let packet: [u8; 20] = [
531             0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // Destination MAC
532             0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, // Source MAC
533             0x08, 0x00, // EtherType (IPv4)
534             0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, // Data
535         ];
536 
537         let result = Ieee8023::from(&packet);
538         assert!(result.is_ok());
539 
540         let ieee8023 = result.unwrap();
541         assert_eq!(ieee8023.destination.to_vec(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
542         assert_eq!(ieee8023.source.to_vec(), [0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB]);
543         assert_eq!(ieee8023.ethertype, EtherType::IPv4);
544         assert_eq!(ieee8023.payload, &[0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11]);
545     }
546 
547     #[test]
test_ieee8023_from_short_packet()548     fn test_ieee8023_from_short_packet() {
549         let packet: [u8; 10] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99];
550 
551         let result = Ieee8023::from(&packet);
552         assert!(result.is_err());
553     }
554 
create_test_from_ap_ieee80211( source: MacAddress, destination: MacAddress, bssid: MacAddress, ) -> Ieee80211555     fn create_test_from_ap_ieee80211(
556         source: MacAddress,
557         destination: MacAddress,
558         bssid: MacAddress,
559     ) -> Ieee80211 {
560         Ieee80211FromAp {
561             duration_id: 0,
562             ftype: FrameType::Mgmt,
563             more_data: 0,
564             more_frags: 0,
565             order: 0,
566             pm: 0,
567             protected: 0,
568             retry: 0,
569             stype: 0,
570             version: 0,
571             bssid,
572             source,
573             destination,
574             seq_ctrl: 0,
575             payload: Vec::new(),
576         }
577         .try_into()
578         .unwrap()
579     }
580 
create_test_ibss_ieee80211( source: MacAddress, destination: MacAddress, bssid: MacAddress, ) -> Ieee80211581     fn create_test_ibss_ieee80211(
582         source: MacAddress,
583         destination: MacAddress,
584         bssid: MacAddress,
585     ) -> Ieee80211 {
586         Ieee80211Ibss {
587             duration_id: 0,
588             ftype: FrameType::Mgmt,
589             more_data: 0,
590             more_frags: 0,
591             order: 0,
592             pm: 0,
593             protected: 0,
594             retry: 0,
595             stype: 0,
596             version: 0,
597             bssid,
598             source,
599             destination,
600             seq_ctrl: 0,
601             payload: Vec::new(),
602         }
603         .try_into()
604         .unwrap()
605     }
606 
create_test_to_ap_ieee80211( source: MacAddress, destination: MacAddress, bssid: MacAddress, ) -> Ieee80211607     fn create_test_to_ap_ieee80211(
608         source: MacAddress,
609         destination: MacAddress,
610         bssid: MacAddress,
611     ) -> Ieee80211 {
612         Ieee80211ToAp {
613             duration_id: 0,
614             ftype: FrameType::Mgmt,
615             more_data: 0,
616             more_frags: 0,
617             order: 0,
618             pm: 0,
619             protected: 0,
620             retry: 0,
621             stype: 0,
622             version: 0,
623             bssid,
624             source,
625             destination,
626             seq_ctrl: 0,
627             payload: Vec::new(),
628         }
629         .try_into()
630         .unwrap()
631     }
632 
test_with_address( create_test_ieee80211: fn(MacAddress, MacAddress, MacAddress) -> Ieee80211, )633     fn test_with_address(
634         create_test_ieee80211: fn(MacAddress, MacAddress, MacAddress) -> Ieee80211,
635     ) {
636         let source = parse_mac_address("01:02:03:00:00:01").unwrap();
637         let destination = parse_mac_address("01:02:03:00:00:02").unwrap();
638         let bssid = parse_mac_address("00:13:10:85:fe:01").unwrap();
639         let ieee80211 = create_test_ieee80211(source, destination, bssid);
640 
641         let new_source = parse_mac_address("01:02:03:00:00:03").unwrap();
642         let new_destination = parse_mac_address("01:02:03:00:00:04").unwrap();
643 
644         let new_ieee80211 = ieee80211.with_address(Some(new_source), Some(new_destination));
645         assert!(new_ieee80211.get_source() == new_source);
646         assert!(new_ieee80211.get_destination() == new_destination);
647 
648         let new_ieee80211 = ieee80211.with_address(Some(new_source), None);
649         assert!(new_ieee80211.get_source() == new_source);
650         assert!(new_ieee80211.get_destination() == destination);
651 
652         let new_ieee80211 = ieee80211.with_address(None, Some(new_destination));
653         assert!(new_ieee80211.get_source() == source);
654         assert!(new_ieee80211.get_destination() == new_destination);
655     }
656 
657     #[test]
test_with_address_from_ap()658     fn test_with_address_from_ap() {
659         test_with_address(create_test_from_ap_ieee80211);
660     }
661 
662     #[test]
test_with_address_to_ap()663     fn test_with_address_to_ap() {
664         test_with_address(create_test_to_ap_ieee80211);
665     }
666     #[test]
test_with_address_ibss()667     fn test_with_address_ibss() {
668         test_with_address(create_test_ibss_ieee80211);
669     }
670 
671     #[test]
test_to_ieee8023()672     fn test_to_ieee8023() {
673         let source = parse_mac_address("01:02:03:00:00:01").unwrap();
674         let destination = parse_mac_address("01:02:03:00:00:02").unwrap();
675         let bssid = parse_mac_address("00:13:10:85:fe:01").unwrap();
676 
677         // Test Data (802.11 frame with LLC/SNAP)
678         let ieee80211: Ieee80211 = Ieee80211ToAp {
679             duration_id: 0,
680             ftype: FrameType::Data,
681             more_data: 0,
682             more_frags: 0,
683             order: 0,
684             pm: 0,
685             protected: 0,
686             retry: 0,
687             stype: 0,
688             version: 0,
689             bssid,
690             source,
691             destination,
692             seq_ctrl: 0,
693             payload: vec![
694                 // LLC/SNAP Header
695                 LlcSap::Snap as u8,
696                 LlcSap::Snap as u8,
697                 LlcCtrl::UiCmd as u8,
698                 // OUI
699                 0x00,
700                 0x00,
701                 0x00,
702                 // EtherType
703                 0x08,
704                 0x00,
705             ],
706         }
707         .try_into()
708         .unwrap();
709 
710         // Call the function under test
711         let result = ieee80211.to_ieee8023();
712         // Assert
713         assert!(result.is_ok());
714         let ethernet_frame = result.unwrap();
715 
716         // Verify ethernet frame
717         assert_eq!(&ethernet_frame[0..6], destination.to_vec().as_slice()); // Destination MAC
718         assert_eq!(&ethernet_frame[6..12], source.to_vec().as_slice()); // Source MAC
719         assert_eq!(&ethernet_frame[12..14], [0x08, 0x00]); // EtherType
720     }
721 }
722