• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // Constants for field lengths
27 const ETHERTYPE_LEN: usize = 2;
28 pub const CCMP_HDR_LEN: usize = 8;
29 
30 // Constants for Ieee80211 definitions.
31 // Reference: external/wpa_supplicant_8/src/common/ieee802_11_defs.h
32 const WLAN_FC_RETRY: u16 = 0x0800;
33 const WLAN_FC_PWRMGT: u16 = 0x1000;
34 const WLAN_FC_MOREDATA: u16 = 0x2000;
35 const WLAN_FC_ISWEP: u16 = 0x4000;
36 const WLAN_ACTION_PUBLIC: u8 = 4;
37 const WLAN_ACTION_HT: u8 = 7;
38 const WLAN_ACTION_SELF_PROTECTED: u8 = 15;
39 const WLAN_ACTION_VENDOR_SPECIFIC: u8 = 127;
40 
41 /// A Ieee80211 MAC address
42 
43 impl MacAddress {
to_vec(&self) -> [u8; 6]44     pub fn to_vec(&self) -> [u8; 6] {
45         u64::to_le_bytes(self.0)[0..6].try_into().expect("slice with incorrect length")
46     }
47 }
48 
49 // TODO: Add unit tests.
50 impl fmt::Display for MacAddress {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result51     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52         let bytes = u64::to_le_bytes(self.0);
53         write!(
54             f,
55             "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
56             bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5],
57         )
58     }
59 }
60 
61 impl fmt::Display for Ieee80211 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result62     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63         write!(
64             f,
65             "{{ds: {}, src: {}, dst: {}}}",
66             self.get_ds(),
67             self.get_source(),
68             self.get_destination()
69         )
70     }
71 }
72 
73 impl From<&[u8; 6]> for MacAddress {
from(bytes: &[u8; 6]) -> Self74     fn from(bytes: &[u8; 6]) -> Self {
75         Self(u64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], 0, 0]))
76     }
77 }
78 
79 impl From<MacAddress> for [u8; 6] {
from(MacAddress(addr): MacAddress) -> Self80     fn from(MacAddress(addr): MacAddress) -> Self {
81         let bytes = u64::to_le_bytes(addr);
82         bytes[0..6].try_into().unwrap()
83     }
84 }
85 
86 impl MacAddress {
87     pub const LEN: usize = 6;
88 
is_multicast(&self) -> bool89     pub fn is_multicast(&self) -> bool {
90         let addr = u64::to_le_bytes(self.0);
91         (addr[0] & 0x1) == 1
92     }
93 
is_broadcast(&self) -> bool94     pub fn is_broadcast(&self) -> bool {
95         self.0 == u64::MAX
96     }
97 }
98 
99 struct Ieee8023<'a> {
100     destination: MacAddress,
101     source: MacAddress,
102     ethertype: EtherType,
103     payload: &'a [u8],
104 }
105 
106 impl<'a> Ieee8023<'a> {
107     pub const HDR_LEN: usize = 14;
108 
109     /// Creates an `Ieee8023` instance from packet slice.
from(packet: &'a [u8]) -> anyhow::Result<Self>110     fn from(packet: &'a [u8]) -> anyhow::Result<Self> {
111         // Ensure the packet has enough bytes for the header
112         anyhow::ensure!(
113             packet.len() >= Self::HDR_LEN,
114             "Packet (len: {}) too short for IEEE 802.3 header",
115             packet.len()
116         );
117         let dest_slice: &[u8; 6] = packet[..MacAddress::LEN].try_into()?;
118         let src_slice: &[u8; 6] = packet[MacAddress::LEN..2 * MacAddress::LEN].try_into()?;
119         let ethertype_bytes = packet[2 * MacAddress::LEN..Self::HDR_LEN].try_into()?;
120         let ethertype = EtherType::try_from(u16::from_be_bytes(ethertype_bytes))
121             .map_err(|e| anyhow::anyhow!("invalid EtherType: {e}"))?;
122 
123         Ok(Ieee8023 {
124             destination: MacAddress::from(dest_slice),
125             source: MacAddress::from(src_slice),
126             ethertype,
127             payload: &packet[Self::HDR_LEN..],
128         })
129     }
130 
to_vec(self) -> anyhow::Result<Vec<u8>>131     fn to_vec(self) -> anyhow::Result<Vec<u8>> {
132         // Build 802.3 frame
133         let mut ethernet_frame =
134             Vec::with_capacity(MacAddress::LEN * 2 + ETHERTYPE_LEN + self.payload.len());
135 
136         ethernet_frame.extend_from_slice(&self.destination.to_vec());
137         ethernet_frame.extend_from_slice(&self.source.to_vec());
138         // Add extracted EtherType
139         ethernet_frame.extend_from_slice(&u16::from(self.ethertype).to_be_bytes());
140         // Actually data is after 802.2 LLC/SNAP header
141         ethernet_frame.extend_from_slice(self.payload);
142         Ok(ethernet_frame)
143     }
144 }
145 
146 impl Ieee80211 {
147     /// Create Ieee80211 from Ieee8023 frame.
from_ieee8023(packet: &[u8], bssid: MacAddress) -> anyhow::Result<Ieee80211>148     pub fn from_ieee8023(packet: &[u8], bssid: MacAddress) -> anyhow::Result<Ieee80211> {
149         let ieee8023 = Ieee8023::from(packet)?;
150 
151         let llc_snap_header = LlcSnapHeader {
152             dsap: LlcSap::Snap,
153             ssap: LlcSap::Snap,
154             ctrl: LlcCtrl::UiCmd,
155             oui: 0,
156             ethertype: ieee8023.ethertype,
157         };
158         // IEEE80211 payload: LLC/SNAP Header + IEEE8023 payload
159         let mut payload = Vec::with_capacity(LlcSnapHeader::LEN + ieee8023.payload.len());
160         llc_snap_header.encode(&mut payload)?;
161         payload.extend_from_slice(ieee8023.payload);
162 
163         Ok(Ieee80211FromAp {
164             duration_id: 0,
165             ftype: FrameType::Data,
166             more_data: 0,
167             more_frags: 0,
168             order: 0,
169             pm: 0,
170             protected: 0,
171             retry: 0,
172             stype: 0,
173             version: 0,
174             bssid,
175             source: ieee8023.source,
176             destination: ieee8023.destination,
177             seq_ctrl: 0,
178             payload,
179         }
180         .try_into()?)
181     }
182 
183     /// Frame has addr4 field
has_a4(&self) -> bool184     pub fn has_a4(&self) -> bool {
185         self.to_ds == 1 && self.from_ds == 1
186     }
187 
188     /// Frame is sent to ap
is_to_ap(&self) -> bool189     pub fn is_to_ap(&self) -> bool {
190         self.to_ds == 1 && self.from_ds == 0
191     }
192 
193     /// Frame type is management
is_mgmt(&self) -> bool194     pub fn is_mgmt(&self) -> bool {
195         self.ftype == FrameType::Mgmt
196     }
197 
198     /// Generates the Additional Authentication Data (AAD) for CCMP encryption.
199     ///
200     /// Reference Linux kernel net/mac80211/wpa.c
get_aad(&self) -> Vec<u8>201     pub fn get_aad(&self) -> Vec<u8> {
202         // Initialize AAD with header length - 2 bytes (no duration id)
203         let hdr_len = self.hdr_length();
204         let mut aad = vec![0u8; hdr_len - 2];
205 
206         // Construct the Frame Control bytes for the AAD:
207         aad[0] = (self.version as u8) | (self.ftype as u8) << 2 | (self.stype as u8) << 4;
208 
209         if !self.is_mgmt() {
210             // Clear the first three bits of stype (bits 4, 5, and 6)
211             aad[0] &= !(0x07 << 4);
212         }
213 
214         aad[1] = (self.to_ds as u8) << 0
215         | (self.from_ds as u8) << 1
216         | (self.more_frags as u8) << 2
217         | (0 << 3) // Clear Retry bit
218         | (0 << 4) // Clear Power Management bit
219         | (0 << 5) // Clear More Data bit
220         | (1 << 6) // Set Protected Frame bit
221         | (self.order as u8) << 7;
222 
223         // Insert 3 MAC Addresses ( 3 * 6 = 18 bytes):
224         aad[2..20].copy_from_slice(&self.payload[..18]);
225         // Insert Masked Sequence Control.
226         aad[20] = (self.payload[18] & 0x0f) as u8;
227         // aad[21] is set to 0 by default
228 
229         // Handle Address 4 and QoS Control field (TID) as applicable
230         if self.has_a4() {
231             aad[22..28].copy_from_slice(&self.payload[20..26]);
232             if self.is_qos_data() {
233                 aad[28] = self.get_qos_tid();
234             }
235         } else if self.is_qos_data() {
236             aad[22] = self.get_qos_tid();
237         }
238 
239         aad
240     }
241 
242     /// Calculates the length of the IEEE 802.11 frame header.
hdr_length(&self) -> usize243     pub fn hdr_length(&self) -> usize {
244         // Base header length is 24. +6 if Addr4 is used. +2 for QoS Data
245         24 + (6 * self.has_a4() as usize) + (2 * self.is_qos_data() as usize)
246     }
247 
248     /// Frame is a QoS Data frame
is_qos_data(&self) -> bool249     pub fn is_qos_data(&self) -> bool {
250         self.is_data() && self.stype == DataSubType::Qos as u8
251     }
252 
253     /// Retrieves the QoS TID (Traffic Identifier) from the IEEE 802.11 frame
get_qos_tid(&self) -> u8254     pub fn get_qos_tid(&self) -> u8 {
255         if !self.is_qos_data() {
256             return 0; // No QoS Control field, return default TID 0
257         }
258 
259         // QOS TID is last 2 bytes of header
260         let qos_offset = self.hdr_length() - 2;
261         // Extract the QoS TID
262         let qos_control = u16::from_be_bytes(
263             self.payload[qos_offset..qos_offset + 2]
264                 .try_into()
265                 .expect("Failed to convert QoS control bytes"),
266         );
267 
268         (qos_control >> 8) as u8
269     }
270 
271     /// Retrieves the QoS Control field from the IEEE 802.11 frame
get_qos_control(&self) -> u16272     pub fn get_qos_control(&self) -> u16 {
273         if !self.is_qos_data() {
274             return 0;
275         }
276         u16::from_be_bytes(
277             self.get_payload()[2..4].try_into().expect("Failed to convert QoS control bytes"),
278         )
279     }
280 
281     /// Extracts the Packet Number (PN) from the IEEE 802.11 frame
get_packet_number(&self) -> [u8; 6]282     pub fn get_packet_number(&self) -> [u8; 6] {
283         let body_pos = self.hdr_length() - 4;
284         let frame_body = &self.payload[body_pos..(body_pos + 8)]; // Get the packet num from frame
285 
286         // Extract the PN bytes in the specified order
287         [frame_body[7], frame_body[6], frame_body[5], frame_body[4], frame_body[1], frame_body[0]]
288     }
289 
290     /// Generates the Nonce for CCMP encryption
291     ///
292     /// Reference Linux kernel net/mac80211/wpa.c
get_nonce(&self, pn: &[u8]) -> [u8; 13]293     pub fn get_nonce(&self, pn: &[u8]) -> [u8; 13] {
294         let qos_tid = self.get_qos_tid();
295         let mgmt_flag = self.is_mgmt() as u8;
296         let addr2 = self.get_addr2().to_vec();
297         let mut nonce = [0u8; 13];
298         // Construct the nonce using qos_tid, mgmt bit, addr2, and pn
299         nonce[0] = qos_tid | (mgmt_flag << 4);
300         nonce[1..7].copy_from_slice(&addr2);
301         nonce[7..].copy_from_slice(pn);
302         nonce
303     }
304 
305     /// Check if the frame is multicast based on the destination address
is_multicast(&self) -> bool306     pub fn is_multicast(&self) -> bool {
307         self.get_addr1().is_multicast()
308     }
309 
310     /// Check if the frame is broadcast based on the destination address
is_broadcast(&self) -> bool311     pub fn is_broadcast(&self) -> bool {
312         self.get_addr1().is_broadcast()
313     }
314 
315     /// Frame is Robust Management frame
316     ///
317     /// Reference Linux kernel include/linux/ieee80211.h
is_robust_mgmt(&self) -> bool318     pub fn is_robust_mgmt(&self) -> bool {
319         if self.payload.len() < 21 || !self.is_mgmt() {
320             // 25 - 4 (fc and duration id)
321             return false;
322         }
323 
324         match ManagementSubType::try_from(self.stype).unwrap() {
325             // Disassoc and Deauth are robust mgmt
326             ManagementSubType::Disassoc | ManagementSubType::Deauth => true,
327             /*
328              * Action frames, excluding Public Action frames, are Robust
329              * Management Frames. However, if we are looking at a Protected
330              * frame, skip the check since the data may be encrypted and
331              * the frame has already been found to be a Robust Management
332              * Frame (by the other end).
333              */
334             ManagementSubType::Action => {
335                 if self.is_protected() {
336                     return true; // Assume protected Action frames are robust
337                 }
338                 // Access category at offset 20 (24 - 2 frame control - 2 dutation id)
339                 let category = u8::from_be_bytes([self.payload[20]]);
340 
341                 !matches!(
342                     category,
343                     WLAN_ACTION_PUBLIC
344                         | WLAN_ACTION_HT
345                         | WLAN_ACTION_SELF_PROTECTED
346                         | WLAN_ACTION_VENDOR_SPECIFIC
347                 )
348             }
349             _ => false, // Other management frames are not robust by default
350         }
351     }
352 
353     /// Frame is (management) beacon frame
is_beacon(&self) -> bool354     pub fn is_beacon(&self) -> bool {
355         self.ftype == FrameType::Mgmt && self.stype == (ManagementSubType::Beacon as u8)
356     }
357 
358     /// Frame type is data
is_data(&self) -> bool359     pub fn is_data(&self) -> bool {
360         self.ftype == FrameType::Data
361     }
362 
363     /// Frame is probe request
is_probe_req(&self) -> bool364     pub fn is_probe_req(&self) -> bool {
365         self.ftype == FrameType::Ctl && self.stype == (ManagementSubType::ProbeReq as u8)
366     }
367 
368     /// Frame is protected
is_protected(&self) -> bool369     pub fn is_protected(&self) -> bool {
370         self.protected != 0u8
371     }
372 
373     /// Frame type is EAPoL
is_eapol(&self) -> anyhow::Result<bool>374     pub fn is_eapol(&self) -> anyhow::Result<bool> {
375         Ok(self.get_ethertype()? == EtherType::Eapol)
376     }
377 
378     /// Whether frame needs to be encrypted
needs_encryption(&self) -> bool379     pub fn needs_encryption(&self) -> bool {
380         !self.is_protected() && (self.is_data() || self.is_robust_mgmt())
381     }
382 
383     /// Whether frame needs to be decrypted
needs_decryption(&self) -> bool384     pub fn needs_decryption(&self) -> bool {
385         self.is_protected() && (self.is_data() || self.is_robust_mgmt())
386     }
387 
388     /// Set whether frame is protected
set_protected(&mut self, protected: bool)389     pub fn set_protected(&mut self, protected: bool) {
390         self.protected = protected.into();
391     }
392 
get_ds(&self) -> String393     pub fn get_ds(&self) -> String {
394         match self.specialize().unwrap() {
395             Ieee80211Child::Ieee80211ToAp(hdr) => "ToAp",
396             Ieee80211Child::Ieee80211FromAp(hdr) => "FromAp",
397             Ieee80211Child::Ieee80211Ibss(hdr) => "Ibss",
398             Ieee80211Child::Ieee80211Wds(hdr) => "Wds",
399             _ => panic!("unexpected specialized header"),
400         }
401         .to_string()
402     }
403 
get_source(&self) -> MacAddress404     pub fn get_source(&self) -> MacAddress {
405         match self.specialize().unwrap() {
406             Ieee80211Child::Ieee80211ToAp(hdr) => hdr.source,
407             Ieee80211Child::Ieee80211FromAp(hdr) => hdr.source,
408             Ieee80211Child::Ieee80211Ibss(hdr) => hdr.source,
409             Ieee80211Child::Ieee80211Wds(hdr) => hdr.source,
410             _ => panic!("unexpected specialized header"),
411         }
412     }
413 
414     /// Ieee80211 packets have 3-4 addresses in different positions based
415     /// on the FromDS and ToDS flags. This function gets the destination
416     /// address depending on the FromDS+ToDS packet subtypes.
get_destination(&self) -> MacAddress417     pub fn get_destination(&self) -> MacAddress {
418         match self.specialize().unwrap() {
419             Ieee80211Child::Ieee80211ToAp(hdr) => hdr.destination,
420             Ieee80211Child::Ieee80211FromAp(hdr) => hdr.destination,
421             Ieee80211Child::Ieee80211Ibss(hdr) => hdr.destination,
422             Ieee80211Child::Ieee80211Wds(hdr) => hdr.destination,
423             _ => panic!("unexpected specialized header"),
424         }
425     }
426 
get_bssid(&self) -> Option<MacAddress>427     pub fn get_bssid(&self) -> Option<MacAddress> {
428         match self.specialize().unwrap() {
429             Ieee80211Child::Ieee80211ToAp(hdr) => Some(hdr.bssid),
430             Ieee80211Child::Ieee80211FromAp(hdr) => Some(hdr.bssid),
431             Ieee80211Child::Ieee80211Ibss(hdr) => Some(hdr.bssid),
432             Ieee80211Child::Ieee80211Wds(hdr) => None,
433             _ => panic!("unexpected specialized header"),
434         }
435     }
436 
get_addr1(&self) -> MacAddress437     pub fn get_addr1(&self) -> MacAddress {
438         match self.specialize().unwrap() {
439             Ieee80211Child::Ieee80211ToAp(hdr) => hdr.bssid,
440             Ieee80211Child::Ieee80211FromAp(hdr) => hdr.destination,
441             Ieee80211Child::Ieee80211Ibss(hdr) => hdr.destination,
442             Ieee80211Child::Ieee80211Wds(hdr) => hdr.receiver,
443             _ => panic!("unexpected specialized header"),
444         }
445     }
446 
get_addr2(&self) -> MacAddress447     pub fn get_addr2(&self) -> MacAddress {
448         match self.specialize().unwrap() {
449             Ieee80211Child::Ieee80211Ibss(hdr) => hdr.source,
450             Ieee80211Child::Ieee80211FromAp(hdr) => hdr.bssid,
451             Ieee80211Child::Ieee80211ToAp(hdr) => hdr.source,
452             Ieee80211Child::Ieee80211Wds(hdr) => hdr.transmitter,
453             _ => panic!("unexpected specialized header"),
454         }
455     }
456 
get_ssid_from_beacon_frame(&self) -> anyhow::Result<String>457     pub fn get_ssid_from_beacon_frame(&self) -> anyhow::Result<String> {
458         // Verify packet is a beacon frame
459         if !self.is_beacon() {
460             return Err(anyhow!("Frame is not beacon frame."));
461         };
462 
463         // SSID field starts after the first 36 bytes. Ieee80211 payload starts after 4 bytes.
464         let pos = 36 - 4;
465 
466         // Check for SSID element ID (0) and extract the SSID
467         let payload = &self.payload;
468         if payload[pos] == 0 {
469             let ssid_len = payload[pos + 1] as usize;
470             let ssid_bytes = &payload[pos + 2..pos + 2 + ssid_len];
471             return Ok(String::from_utf8(ssid_bytes.to_vec())?);
472         }
473 
474         Err(anyhow!("SSID not found."))
475     }
476 
get_payload(&self) -> Vec<u8>477     fn get_payload(&self) -> Vec<u8> {
478         match self.specialize().unwrap() {
479             Ieee80211Child::Ieee80211ToAp(hdr) => hdr.payload,
480             Ieee80211Child::Ieee80211FromAp(hdr) => hdr.payload,
481             Ieee80211Child::Ieee80211Ibss(hdr) => hdr.payload,
482             Ieee80211Child::Ieee80211Wds(hdr) => hdr.payload,
483             _ => panic!("unexpected specialized header"),
484         }
485     }
486 
get_ethertype(&self) -> anyhow::Result<EtherType>487     fn get_ethertype(&self) -> anyhow::Result<EtherType> {
488         if !self.is_data() {
489             return Err(anyhow!("Not an 802.2 LLC/SNAP frame"));
490         }
491 
492         // Extract and validate LLC/SNAP header
493         let payload = self.get_payload();
494         if payload.len() < LlcSnapHeader::LEN {
495             return Err(anyhow!("Payload too short for LLC/SNAP header"));
496         }
497         let llc_snap_header = LlcSnapHeader::decode_full(&payload[..LlcSnapHeader::LEN])?;
498         Ok(llc_snap_header.ethertype())
499     }
500 
with_address( &self, source: Option<MacAddress>, destination: Option<MacAddress>, ) -> Ieee80211501     pub fn with_address(
502         &self,
503         source: Option<MacAddress>,
504         destination: Option<MacAddress>,
505     ) -> Ieee80211 {
506         match self.specialize().unwrap() {
507             Ieee80211Child::Ieee80211ToAp(frame) => {
508                 frame.with_address(source, destination).try_into().unwrap()
509             }
510             Ieee80211Child::Ieee80211FromAp(frame) => {
511                 frame.with_address(source, destination).try_into().unwrap()
512             }
513             Ieee80211Child::Ieee80211Ibss(frame) => {
514                 frame.with_address(source, destination).try_into().unwrap()
515             }
516             Ieee80211Child::Ieee80211Wds(frame) => {
517                 frame.with_address(source, destination).try_into().unwrap()
518             }
519             _ => panic!("Unknown Ieee80211Child type"),
520         }
521     }
522 
523     /// Covert Ieee80211ToAp to Ieee80211FromAp packet.
into_from_ap(&self) -> anyhow::Result<Ieee80211FromAp>524     pub fn into_from_ap(&self) -> anyhow::Result<Ieee80211FromAp> {
525         match self.specialize().unwrap() {
526             Ieee80211Child::Ieee80211ToAp(frame_to_ap) => {
527                 // Flip from_ap and to_ap bits.
528                 // TODO: Investigate if there is a way to copy frame_control flags at once.
529                 // The header struct only has 7 fields, not 15. Most fields come from le16 frame_control.
530                 Ok(Ieee80211FromAp {
531                     duration_id: frame_to_ap.duration_id,
532                     ftype: frame_to_ap.ftype,
533                     more_data: frame_to_ap.more_data,
534                     more_frags: frame_to_ap.more_frags,
535                     order: frame_to_ap.order,
536                     pm: frame_to_ap.pm,
537                     protected: frame_to_ap.protected,
538                     retry: frame_to_ap.retry,
539                     stype: frame_to_ap.stype,
540                     version: frame_to_ap.version,
541                     bssid: frame_to_ap.bssid,
542                     source: frame_to_ap.source,
543                     destination: frame_to_ap.destination,
544                     seq_ctrl: frame_to_ap.seq_ctrl,
545                     payload: frame_to_ap.payload.to_vec(),
546                 })
547             }
548             _ => Err(anyhow!(
549                 "Invalid Ieee80211Child packet. from_ds: {}, to_ds: {}",
550                 self.from_ds,
551                 self.to_ds
552             )),
553         }
554     }
555 
556     // Convert to ieee802.3
to_ieee8023(&self) -> anyhow::Result<Vec<u8>>557     pub fn to_ieee8023(&self) -> anyhow::Result<Vec<u8>> {
558         let ethertype = self.get_ethertype()?;
559         let payload = self.get_payload();
560         Ieee8023 {
561             destination: self.get_destination(),
562             source: self.get_source(),
563             ethertype,
564             payload: &payload[LlcSnapHeader::LEN..],
565         }
566         .to_vec()
567     }
568 }
569 
570 impl Ieee80211FromAp {
with_address( &self, source: Option<MacAddress>, destination: Option<MacAddress>, ) -> Ieee80211FromAp571     pub fn with_address(
572         &self,
573         source: Option<MacAddress>,
574         destination: Option<MacAddress>,
575     ) -> Ieee80211FromAp {
576         Ieee80211FromAp {
577             source: source.unwrap_or(self.source),
578             destination: destination.unwrap_or(self.destination),
579             ..self.clone()
580         }
581     }
582 }
583 
584 impl Ieee80211ToAp {
with_address( &self, source: Option<MacAddress>, destination: Option<MacAddress>, ) -> Ieee80211ToAp585     pub fn with_address(
586         &self,
587         source: Option<MacAddress>,
588         destination: Option<MacAddress>,
589     ) -> Ieee80211ToAp {
590         Ieee80211ToAp {
591             source: source.unwrap_or(self.source),
592             destination: destination.unwrap_or(self.destination),
593             ..self.clone()
594         }
595     }
596 }
597 
598 impl Ieee80211Ibss {
with_address( &self, source: Option<MacAddress>, destination: Option<MacAddress>, ) -> Ieee80211Ibss599     pub fn with_address(
600         &self,
601         source: Option<MacAddress>,
602         destination: Option<MacAddress>,
603     ) -> Ieee80211Ibss {
604         Ieee80211Ibss {
605             source: source.unwrap_or(self.source),
606             destination: destination.unwrap_or(self.destination),
607             ..self.clone()
608         }
609     }
610 }
611 
612 impl Ieee80211Wds {
with_address( &self, source: Option<MacAddress>, destination: Option<MacAddress>, ) -> Ieee80211Wds613     pub fn with_address(
614         &self,
615         source: Option<MacAddress>,
616         destination: Option<MacAddress>,
617     ) -> Ieee80211Wds {
618         Ieee80211Wds {
619             source: source.unwrap_or(self.source),
620             destination: destination.unwrap_or(self.destination),
621             ..self.clone()
622         }
623     }
624 }
625 
parse_mac_address(s: &str) -> Option<MacAddress>626 pub fn parse_mac_address(s: &str) -> Option<MacAddress> {
627     let parts: Vec<&str> = s.split(':').collect();
628     if parts.len() != 6 {
629         return None;
630     }
631     let mut bytes = [0u8; 6];
632     for (i, part) in parts.iter().enumerate() {
633         match u8::from_str_radix(part, 16) {
634             Ok(n) => bytes[i] = n,
635             Err(e) => return None,
636         }
637     }
638     Some(MacAddress::from(&bytes))
639 }
640 
641 #[cfg(test)]
642 mod tests {
643     use super::*;
644 
645     #[test]
test_mac_address_len()646     fn test_mac_address_len() {
647         let mac_address: MacAddress = parse_mac_address("00:0b:85:71:20:ce").unwrap();
648         assert_eq!(mac_address.encoded_len(), MacAddress::LEN);
649     }
650 
651     #[test]
test_mac_address_to_vec()652     fn test_mac_address_to_vec() {
653         let mac_address: MacAddress = parse_mac_address("00:0b:85:71:20:ce").unwrap();
654         let mac_address_bytes = mac_address.to_vec();
655         let reconstructed_mac_address = MacAddress::from(&mac_address_bytes);
656         assert_eq!(mac_address, reconstructed_mac_address);
657     }
658 
659     // These tests use the packets available here
660     // https://community.cisco.com/t5/wireless-mobility-knowledge-base/802-11-frames-a-starter-guide-to-learn-wireless-sniffer-traces/ta-p/3110019
661 
662     #[test]
test_frame_qos()663     fn test_frame_qos() {
664         let frame: Vec<u8> = vec![
665             0x88, 0x02, 0x2c, 0x00, 0x00, 0x13, 0xe8, 0xeb, 0xd6, 0x03, 0x00, 0x0b, 0x85, 0x71,
666             0x20, 0xce, 0x00, 0x0b, 0x85, 0x71, 0x20, 0xce, 0x00, 0x26, 0x00, 0x00,
667         ];
668         let hdr = Ieee80211::decode_full(&frame).unwrap();
669         assert!(hdr.is_data());
670         assert_eq!(hdr.stype, DataSubType::Qos as u8);
671         assert_eq!(hdr.from_ds, 1);
672         assert_eq!(hdr.to_ds, 0);
673         assert_eq!(hdr.duration_id, 44);
674         // Source address: Cisco_71:20:ce (00:0b:85:71:20:ce)
675         let a = format!("{}", hdr.get_source());
676         let b = format!("{}", parse_mac_address("00:0b:85:71:20:ce").unwrap());
677         assert_eq!(a, b);
678     }
679 
680     #[test]
test_beacon_frame()681     fn test_beacon_frame() {
682         // Example from actual beacon frame from Hostapd with "AndroidWifi" SSID
683         let frame: Vec<u8> = vec![
684             0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0x10, 0x85,
685             0xfe, 0x01, 0x00, 0x13, 0x10, 0x85, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
686             0x00, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x01, 0x04, 0x00, 0x0b, 0x41, 0x6e, 0x64, 0x72,
687             0x6f, 0x69, 0x64, 0x57, 0x69, 0x66, 0x69, 0x01, 0x04, 0x82, 0x84, 0x8b, 0x96, 0x03,
688             0x01, 0x08, 0x2a, 0x01, 0x07, 0x2d, 0x1a, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
689             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
690             0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x16, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
691             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
692             0x00, 0x7f, 0x04, 0x00, 0x00, 0x00, 0x02,
693         ];
694         let decoded_frame = Ieee80211::decode_full(&frame).unwrap();
695         assert!(decoded_frame.is_mgmt());
696         assert!(decoded_frame.is_beacon());
697         let ssid = decoded_frame.get_ssid_from_beacon_frame();
698         assert!(ssid.is_ok());
699         assert_eq!(ssid.unwrap(), "AndroidWifi");
700     }
701 
702     #[test]
test_is_multicast()703     fn test_is_multicast() {
704         // Multicast MAC address: 01:00:5E:00:00:FB
705         let mdns_mac_address = parse_mac_address("01:00:5e:00:00:fb").unwrap();
706         assert!(mdns_mac_address.is_multicast());
707         // Broadcast MAC address: ff:ff:ff:ff:ff:ff
708         let broadcast_mac_address = parse_mac_address("ff:ff:ff:ff:ff:ff").unwrap();
709         assert!(broadcast_mac_address.is_multicast());
710         // Source address: Cisco_71:20:ce (00:0b:85:71:20:ce)
711         let non_mdns_mac_address = parse_mac_address("00:0b:85:71:20:ce").unwrap();
712         assert!(!non_mdns_mac_address.is_multicast());
713     }
714 
test_is_broadcast()715     fn test_is_broadcast() {
716         // Multicast MAC address: 01:00:5E:00:00:FB
717         let mdns_mac_address = parse_mac_address("01:00:5e:00:00:fb").unwrap();
718         assert!(!mdns_mac_address.is_broadcast());
719         // Broadcast MAC address: ff:ff:ff:ff:ff:ff
720         let broadcast_mac_address = parse_mac_address("ff:ff:ff:ff:ff:ff").unwrap();
721         assert!(broadcast_mac_address.is_broadcast());
722         // Source address: Cisco_71:20:ce (00:0b:85:71:20:ce)
723         let non_mdns_mac_address = parse_mac_address("00:0b:85:71:20:ce").unwrap();
724         assert!(!non_mdns_mac_address.is_broadcast());
725     }
726 
727     #[test]
test_ieee8023_from_valid_packet()728     fn test_ieee8023_from_valid_packet() {
729         let packet: [u8; 20] = [
730             0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // Destination MAC
731             0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, // Source MAC
732             0x08, 0x00, // EtherType (IPv4)
733             0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, // Data
734         ];
735 
736         let result = Ieee8023::from(&packet);
737         assert!(result.is_ok());
738 
739         let ieee8023 = result.unwrap();
740         assert_eq!(ieee8023.destination.to_vec(), [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
741         assert_eq!(ieee8023.source.to_vec(), [0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB]);
742         assert_eq!(ieee8023.ethertype, EtherType::IPv4);
743         assert_eq!(ieee8023.payload, &[0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11]);
744     }
745 
746     #[test]
test_ieee8023_from_short_packet()747     fn test_ieee8023_from_short_packet() {
748         let packet: [u8; 10] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99];
749 
750         let result = Ieee8023::from(&packet);
751         assert!(result.is_err());
752     }
753 
create_test_from_ap_ieee80211( source: MacAddress, destination: MacAddress, bssid: MacAddress, ) -> Ieee80211754     fn create_test_from_ap_ieee80211(
755         source: MacAddress,
756         destination: MacAddress,
757         bssid: MacAddress,
758     ) -> Ieee80211 {
759         Ieee80211FromAp {
760             duration_id: 0,
761             ftype: FrameType::Mgmt,
762             more_data: 0,
763             more_frags: 0,
764             order: 0,
765             pm: 0,
766             protected: 0,
767             retry: 0,
768             stype: 0,
769             version: 0,
770             bssid,
771             source,
772             destination,
773             seq_ctrl: 0,
774             payload: Vec::new(),
775         }
776         .try_into()
777         .unwrap()
778     }
779 
create_test_ibss_ieee80211( source: MacAddress, destination: MacAddress, bssid: MacAddress, ) -> Ieee80211780     fn create_test_ibss_ieee80211(
781         source: MacAddress,
782         destination: MacAddress,
783         bssid: MacAddress,
784     ) -> Ieee80211 {
785         Ieee80211Ibss {
786             duration_id: 0,
787             ftype: FrameType::Mgmt,
788             more_data: 0,
789             more_frags: 0,
790             order: 0,
791             pm: 0,
792             protected: 0,
793             retry: 0,
794             stype: 0,
795             version: 0,
796             bssid,
797             source,
798             destination,
799             seq_ctrl: 0,
800             payload: Vec::new(),
801         }
802         .try_into()
803         .unwrap()
804     }
805 
create_test_to_ap_ieee80211( source: MacAddress, destination: MacAddress, bssid: MacAddress, ) -> Ieee80211806     fn create_test_to_ap_ieee80211(
807         source: MacAddress,
808         destination: MacAddress,
809         bssid: MacAddress,
810     ) -> Ieee80211 {
811         Ieee80211ToAp {
812             duration_id: 0,
813             ftype: FrameType::Mgmt,
814             more_data: 0,
815             more_frags: 0,
816             order: 0,
817             pm: 0,
818             protected: 0,
819             retry: 0,
820             stype: 0,
821             version: 0,
822             bssid,
823             source,
824             destination,
825             seq_ctrl: 0,
826             payload: Vec::new(),
827         }
828         .try_into()
829         .unwrap()
830     }
831 
create_test_wds_ieee80211( receiver: MacAddress, transmitter: MacAddress, destination: MacAddress, source: MacAddress, ) -> Ieee80211832     fn create_test_wds_ieee80211(
833         receiver: MacAddress,
834         transmitter: MacAddress,
835         destination: MacAddress,
836         source: MacAddress,
837     ) -> Ieee80211 {
838         Ieee80211Wds {
839             duration_id: 0,
840             ftype: FrameType::Mgmt,
841             more_data: 0,
842             more_frags: 0,
843             order: 0,
844             pm: 0,
845             protected: 0,
846             retry: 0,
847             stype: 0,
848             version: 0,
849             receiver,
850             transmitter,
851             destination,
852             seq_ctrl: 0,
853             source,
854             payload: Vec::new(),
855         }
856         .try_into()
857         .unwrap()
858     }
859 
test_with_address( create_test_ieee80211: fn(MacAddress, MacAddress, MacAddress) -> Ieee80211, )860     fn test_with_address(
861         create_test_ieee80211: fn(MacAddress, MacAddress, MacAddress) -> Ieee80211,
862     ) {
863         let source = parse_mac_address("01:02:03:00:00:01").unwrap();
864         let destination = parse_mac_address("01:02:03:00:00:02").unwrap();
865         let bssid = parse_mac_address("00:13:10:85:fe:01").unwrap();
866         let ieee80211 = create_test_ieee80211(source, destination, bssid);
867 
868         let new_source = parse_mac_address("01:02:03:00:00:03").unwrap();
869         let new_destination = parse_mac_address("01:02:03:00:00:04").unwrap();
870 
871         let new_ieee80211 = ieee80211.with_address(Some(new_source), Some(new_destination));
872         assert!(new_ieee80211.get_source() == new_source);
873         assert!(new_ieee80211.get_destination() == new_destination);
874 
875         let new_ieee80211 = ieee80211.with_address(Some(new_source), None);
876         assert!(new_ieee80211.get_source() == new_source);
877         assert!(new_ieee80211.get_destination() == destination);
878 
879         let new_ieee80211 = ieee80211.with_address(None, Some(new_destination));
880         assert!(new_ieee80211.get_source() == source);
881         assert!(new_ieee80211.get_destination() == new_destination);
882     }
883 
884     #[test]
test_with_address_from_ap()885     fn test_with_address_from_ap() {
886         test_with_address(create_test_from_ap_ieee80211);
887     }
888 
889     #[test]
test_with_address_to_ap()890     fn test_with_address_to_ap() {
891         test_with_address(create_test_to_ap_ieee80211);
892     }
893     #[test]
test_with_address_ibss()894     fn test_with_address_ibss() {
895         test_with_address(create_test_ibss_ieee80211);
896     }
897 
898     #[test]
test_to_ieee8023()899     fn test_to_ieee8023() {
900         let source = parse_mac_address("01:02:03:00:00:01").unwrap();
901         let destination = parse_mac_address("01:02:03:00:00:02").unwrap();
902         let bssid = parse_mac_address("00:13:10:85:fe:01").unwrap();
903 
904         // Test Data (802.11 frame with LLC/SNAP)
905         let ieee80211: Ieee80211 = Ieee80211ToAp {
906             duration_id: 0,
907             ftype: FrameType::Data,
908             more_data: 0,
909             more_frags: 0,
910             order: 0,
911             pm: 0,
912             protected: 0,
913             retry: 0,
914             stype: 0,
915             version: 0,
916             bssid,
917             source,
918             destination,
919             seq_ctrl: 0,
920             payload: vec![
921                 // LLC/SNAP Header
922                 LlcSap::Snap as u8,
923                 LlcSap::Snap as u8,
924                 LlcCtrl::UiCmd as u8,
925                 // OUI
926                 0x00,
927                 0x00,
928                 0x00,
929                 // EtherType
930                 0x08,
931                 0x00,
932             ],
933         }
934         .try_into()
935         .unwrap();
936 
937         // Call the function under test
938         let result = ieee80211.to_ieee8023();
939         // Assert
940         assert!(result.is_ok());
941         let ethernet_frame = result.unwrap();
942 
943         // Verify ethernet frame
944         assert_eq!(&ethernet_frame[0..6], destination.to_vec().as_slice()); // Destination MAC
945         assert_eq!(&ethernet_frame[6..12], source.to_vec().as_slice()); // Source MAC
946         assert_eq!(&ethernet_frame[12..14], [0x08, 0x00]); // EtherType
947     }
948 
949     #[test]
test_has_a4()950     fn test_has_a4() {
951         let addr1 = parse_mac_address("01:02:03:00:00:01").unwrap();
952         let addr2 = parse_mac_address("01:02:03:00:00:02").unwrap();
953         let addr3 = parse_mac_address("01:02:03:00:00:03").unwrap();
954         let addr4 = parse_mac_address("01:02:03:00:00:04").unwrap();
955 
956         // Only WDS has addr4
957         assert!(!create_test_from_ap_ieee80211(addr1, addr2, addr3).has_a4());
958         assert!(!create_test_ibss_ieee80211(addr1, addr2, addr3).has_a4());
959         assert!(!create_test_to_ap_ieee80211(addr1, addr2, addr3).has_a4());
960         assert!(create_test_wds_ieee80211(addr1, addr2, addr3, addr4).has_a4());
961     }
962 }
963