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!(ðernet_frame[0..6], destination.to_vec().as_slice()); // Destination MAC
945 assert_eq!(ðernet_frame[6..12], source.to_vec().as_slice()); // Source MAC
946 assert_eq!(ðernet_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