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