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!(ðernet_frame[0..6], destination.to_vec().as_slice()); // Destination MAC
718 assert_eq!(ðernet_frame[6..12], source.to_vec().as_slice()); // Source MAC
719 assert_eq!(ðernet_frame[12..14], [0x08, 0x00]); // EtherType
720 }
721 }
722