• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use super::packets::ieee80211::MacAddress;
16 use super::packets::mac80211_hwsim::{HwsimCmd, HwsimMsg, HwsimMsgHdr, NlMsgHdr};
17 use crate::wifi::frame::Frame;
18 use crate::wifi::hwsim_attr_set::HwsimAttrSet;
19 use anyhow::{anyhow, Context};
20 use bytes::Bytes;
21 use log::{debug, info, warn};
22 use pdl_runtime::Packet;
23 use std::collections::{HashMap, HashSet};
24 use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
25 use std::sync::{Arc, RwLock};
26 const NLMSG_MIN_TYPE: u16 = 0x10;
27 // Default values for mac80211_hwsim.
28 const RX_RATE: u32 = 0;
29 const SIGNAL: u32 = 4294967246; // -50
30 
31 #[allow(dead_code)]
32 #[derive(Debug)]
33 pub enum HwsimCmdEnum {
34     Unspec,
35     Register,
36     Frame(Box<Frame>),
37     TxInfoFrame,
38     NewRadio,
39     DelRadio,
40     GetRadio,
41     AddMacAddr,
42     DelMacAddr,
43 }
44 
45 #[derive(Clone)]
46 struct Station {
47     client_id: u32,
48     // Ieee80211 source address
49     addr: MacAddress,
50     // Hwsim virtual address from HWSIM_ATTR_ADDR_TRANSMITTER
51     // Used to create the HwsimMsg to stations.
52     hwsim_addr: MacAddress,
53 }
54 
55 impl Station {
new(client_id: u32, addr: MacAddress, hwsim_addr: MacAddress) -> Self56     fn new(client_id: u32, addr: MacAddress, hwsim_addr: MacAddress) -> Self {
57         Self { client_id, addr, hwsim_addr }
58     }
59 }
60 
61 #[derive(Clone)]
62 pub struct Client {
63     pub enabled: Arc<AtomicBool>,
64     pub tx_count: Arc<AtomicU32>,
65     pub rx_count: Arc<AtomicU32>,
66 }
67 
68 impl Client {
new() -> Self69     fn new() -> Self {
70         Self {
71             enabled: Arc::new(AtomicBool::new(true)),
72             tx_count: Arc::new(AtomicU32::new(0)),
73             rx_count: Arc::new(AtomicU32::new(0)),
74         }
75     }
76 }
77 
78 pub struct Medium {
79     callback: HwsimCmdCallback,
80     // Ieee80211 source address
81     stations: RwLock<HashMap<MacAddress, Arc<Station>>>,
82     clients: RwLock<HashMap<u32, Client>>,
83     // BSSID. MAC address of the access point in WiFi Service.
84     hostapd_bssid: MacAddress,
85     // Simulate the re-transmission of frames sent to hostapd
86     ap_simulation: bool,
87 }
88 
89 type HwsimCmdCallback = fn(u32, &Bytes);
90 impl Medium {
new(callback: HwsimCmdCallback) -> Medium91     pub fn new(callback: HwsimCmdCallback) -> Medium {
92         // Defined in external/qemu/android-qemu2-glue/emulation/WifiService.cpp
93         // TODO: Use hostapd_bssid to initialize hostapd.
94         let bssid_bytes: [u8; 6] = [0x00, 0x13, 0x10, 0x85, 0xfe, 0x01];
95         Self {
96             callback,
97             stations: RwLock::new(HashMap::new()),
98             clients: RwLock::new(HashMap::new()),
99             hostapd_bssid: MacAddress::from(&bssid_bytes),
100             ap_simulation: true,
101         }
102     }
103 
add(&self, client_id: u32)104     pub fn add(&self, client_id: u32) {
105         let _ = self.clients.write().unwrap().entry(client_id).or_insert_with(|| {
106             info!("Insert client {}", client_id);
107             Client::new()
108         });
109     }
110 
remove(&self, client_id: u32)111     pub fn remove(&self, client_id: u32) {
112         self.stations.write().unwrap().retain(|_, s| s.client_id != client_id);
113         self.clients.write().unwrap().remove(&client_id);
114     }
115 
reset(&self, client_id: u32)116     pub fn reset(&self, client_id: u32) {
117         if let Some(client) = self.clients.read().unwrap().get(&client_id) {
118             client.enabled.store(true, Ordering::Relaxed);
119             client.tx_count.store(0, Ordering::Relaxed);
120             client.rx_count.store(0, Ordering::Relaxed);
121         }
122     }
123 
get(&self, client_id: u32) -> Option<Client>124     pub fn get(&self, client_id: u32) -> Option<Client> {
125         self.clients.read().unwrap().get(&client_id).map(|c| c.to_owned())
126     }
127 
contains_client(&self, client_id: u32) -> bool128     fn contains_client(&self, client_id: u32) -> bool {
129         self.clients.read().unwrap().contains_key(&client_id)
130     }
131 
stations(&self) -> impl Iterator<Item = Arc<Station>>132     fn stations(&self) -> impl Iterator<Item = Arc<Station>> {
133         self.stations.read().unwrap().clone().into_values()
134     }
135 
contains_station(&self, addr: &MacAddress) -> bool136     fn contains_station(&self, addr: &MacAddress) -> bool {
137         self.stations.read().unwrap().contains_key(addr)
138     }
139 
get_station(&self, addr: &MacAddress) -> anyhow::Result<Arc<Station>>140     fn get_station(&self, addr: &MacAddress) -> anyhow::Result<Arc<Station>> {
141         self.stations.read().unwrap().get(addr).context("get station").cloned()
142     }
143 
144     /// Process commands from the kernel's mac80211_hwsim subsystem.
145     ///
146     /// This is the processing that will be implemented:
147     ///
148     /// * The source MacAddress in 802.11 frames is re-mapped to a globally
149     /// unique MacAddress because resumed Emulator AVDs appear with the
150     /// same address.
151     ///
152     /// * 802.11 frames sent between stations
153     ///
154     /// * 802.11 multicast frames are re-broadcast to connected stations.
155     ///
process(&self, client_id: u32, packet: &Bytes) -> bool156     pub fn process(&self, client_id: u32, packet: &Bytes) -> bool {
157         self.process_internal(client_id, packet).unwrap_or_else(move |e| {
158             // TODO: add this error to the netsim_session_stats
159             warn!("error processing wifi {e}");
160             false
161         })
162     }
163 
process_internal(&self, client_id: u32, packet: &Bytes) -> anyhow::Result<bool>164     fn process_internal(&self, client_id: u32, packet: &Bytes) -> anyhow::Result<bool> {
165         let hwsim_msg = HwsimMsg::decode_full(packet)?;
166 
167         // The virtio handler only accepts HWSIM_CMD_FRAME, HWSIM_CMD_TX_INFO_FRAME and HWSIM_CMD_REPORT_PMSR
168         // in https://source.corp.google.com/h/kernel/pub/scm/linux/kernel/git/torvalds/linux/+/master:drivers/net/wireless/virtual/mac80211_hwsim.c
169         match hwsim_msg.hwsim_hdr.hwsim_cmd {
170             HwsimCmd::Frame => {
171                 let frame = Frame::parse(&hwsim_msg)?;
172                 // Incoming frame must contain transmitter, flag, cookie, and tx_info fields.
173                 if frame.transmitter.is_none()
174                     || frame.flags.is_none()
175                     || frame.cookie.is_none()
176                     || frame.tx_info.is_none()
177                 {
178                     return Err(anyhow!("Missing Hwsim attributes for incoming packet"));
179                 }
180                 // Use as receiver for outgoing HwsimMsg.
181                 let hwsim_addr = frame.transmitter.context("transmitter")?;
182                 let flags = frame.flags.context("flags")?;
183                 let cookie = frame.cookie.context("cookie")?;
184                 debug!(
185                     "Frame chip {}, transmitter {}, flags {}, cookie {}, ieee80211 {}",
186                     client_id, hwsim_addr, flags, cookie, frame.ieee80211
187                 );
188                 let src_addr = frame.ieee80211.get_source();
189                 // Creates Stations on the fly when there is no config file.
190                 // WiFi Direct will use a randomized mac address for probing
191                 // new networks. This block associates the new mac with the station.
192                 let source = self
193                     .stations
194                     .write()
195                     .unwrap()
196                     .entry(src_addr)
197                     .or_insert_with(|| {
198                         info!(
199                             "Insert station with client id {}, hwsimaddr: {}, \
200                         Ieee80211 addr: {}",
201                             client_id, hwsim_addr, src_addr
202                         );
203                         Arc::new(Station::new(client_id, src_addr, hwsim_addr))
204                     })
205                     .clone();
206                 if !self.contains_client(client_id) {
207                     warn!("Client {} is missing", client_id);
208                     self.add(client_id);
209                 }
210                 self.queue_frame(frame, &source)
211             }
212             _ => {
213                 info!("Another command found {:?}", hwsim_msg);
214                 Ok(false)
215             }
216         }
217     }
218 
219     /// Handle Wi-Fi MwsimMsg from libslirp and hostapd.
220     /// Send it to clients.
process_response(&self, packet: &Bytes)221     pub fn process_response(&self, packet: &Bytes) {
222         if let Err(e) = self.send_response(packet) {
223             warn!("{}", e);
224         }
225     }
226 
227     /// Determine the client id based on Ieee80211 destination and send to client.
send_response(&self, packet: &Bytes) -> anyhow::Result<()>228     fn send_response(&self, packet: &Bytes) -> anyhow::Result<()> {
229         // When Wi-Fi P2P is disabled, send all packets from WifiService to all clients.
230         if crate::config::get_disable_wifi_p2p() {
231             for client_id in self.clients.read().unwrap().keys() {
232                 (self.callback)(*client_id, packet);
233             }
234             return Ok(());
235         }
236         let hwsim_msg = HwsimMsg::decode_full(packet)?;
237         let hwsim_cmd = hwsim_msg.hwsim_hdr.hwsim_cmd;
238         match hwsim_cmd {
239             HwsimCmd::Frame => self.send_frame_response(packet, &hwsim_msg)?,
240             // TODO: Handle sending TxInfo frame for WifiService so we don't have to
241             // send duplicate HwsimMsg for all clients with the same Hwsim addr.
242             HwsimCmd::TxInfoFrame => self.send_tx_info_response(packet, &hwsim_msg)?,
243             _ => return Err(anyhow!("Invalid HwsimMsg cmd={:?}", hwsim_cmd)),
244         };
245         Ok(())
246     }
247 
send_frame_response(&self, packet: &Bytes, hwsim_msg: &HwsimMsg) -> anyhow::Result<()>248     fn send_frame_response(&self, packet: &Bytes, hwsim_msg: &HwsimMsg) -> anyhow::Result<()> {
249         let frame = Frame::parse(hwsim_msg)?;
250         let dest_addr = frame.ieee80211.get_destination();
251         if let Ok(destination) = self.get_station(&dest_addr) {
252             self.send_from_ds_frame(packet, &frame, &destination)?;
253         } else if dest_addr.is_multicast() {
254             for destination in self.stations() {
255                 self.send_from_ds_frame(packet, &frame, &destination)?;
256             }
257         } else {
258             warn!("Send frame response to unknown destination: {}", dest_addr);
259         }
260         Ok(())
261     }
262 
263     /// Send frame from DS to STA.
send_from_ds_frame( &self, packet: &Bytes, frame: &Frame, destination: &Station, ) -> anyhow::Result<()>264     fn send_from_ds_frame(
265         &self,
266         packet: &Bytes,
267         frame: &Frame,
268         destination: &Station,
269     ) -> anyhow::Result<()> {
270         if frame.attrs.receiver.context("receiver")? == destination.hwsim_addr {
271             (self.callback)(destination.client_id, packet);
272         } else {
273             // Broadcast: replace HwsimMsg destination but keep other attributes
274             let hwsim_msg = self
275                 .create_hwsim_msg(frame, &destination.hwsim_addr)
276                 .context("Create HwsimMsg from WifiService")?;
277             (self.callback)(destination.client_id, &hwsim_msg.encode_to_vec()?.into());
278         }
279         self.incr_rx(destination.client_id)?;
280         Ok(())
281     }
282 
send_tx_info_response(&self, packet: &Bytes, hwsim_msg: &HwsimMsg) -> anyhow::Result<()>283     fn send_tx_info_response(&self, packet: &Bytes, hwsim_msg: &HwsimMsg) -> anyhow::Result<()> {
284         let attrs = HwsimAttrSet::parse(&hwsim_msg.attributes).context("HwsimAttrSet")?;
285         let hwsim_addr = attrs.transmitter.context("missing transmitter")?;
286         let client_ids = self
287             .stations()
288             .filter(|v| v.hwsim_addr == hwsim_addr)
289             .map(|v| v.client_id)
290             .collect::<HashSet<_>>();
291         if client_ids.len() > 1 {
292             warn!("multiple clients found for TxInfo frame");
293         }
294         for client_id in client_ids {
295             if self.enabled(client_id)? {
296                 (self.callback)(client_id, packet);
297             }
298         }
299         Ok(())
300     }
301 
set_enabled(&self, client_id: u32, enabled: bool)302     pub fn set_enabled(&self, client_id: u32, enabled: bool) {
303         if let Some(client) = self.clients.read().unwrap().get(&client_id) {
304             client.enabled.store(enabled, Ordering::Relaxed);
305         }
306     }
307 
enabled(&self, client_id: u32) -> anyhow::Result<bool>308     fn enabled(&self, client_id: u32) -> anyhow::Result<bool> {
309         Ok(self
310             .clients
311             .read()
312             .unwrap()
313             .get(&client_id)
314             .context(format!("client {client_id} is missing"))?
315             .enabled
316             .load(Ordering::Relaxed))
317     }
318 
319     /// Create tx info frame to station to ack HwsimMsg.
send_tx_info_frame(&self, frame: &Frame) -> anyhow::Result<()>320     fn send_tx_info_frame(&self, frame: &Frame) -> anyhow::Result<()> {
321         let client_id = self.get_station(&frame.ieee80211.get_source())?.client_id;
322         let hwsim_msg_tx_info = build_tx_info(&frame.hwsim_msg).unwrap().encode_to_vec()?;
323         (self.callback)(client_id, &hwsim_msg_tx_info.into());
324         Ok(())
325     }
326 
incr_tx(&self, client_id: u32) -> anyhow::Result<()>327     fn incr_tx(&self, client_id: u32) -> anyhow::Result<()> {
328         self.clients
329             .read()
330             .unwrap()
331             .get(&client_id)
332             .context("incr_tx")?
333             .tx_count
334             .fetch_add(1, Ordering::Relaxed);
335         Ok(())
336     }
337 
incr_rx(&self, client_id: u32) -> anyhow::Result<()>338     fn incr_rx(&self, client_id: u32) -> anyhow::Result<()> {
339         self.clients
340             .read()
341             .unwrap()
342             .get(&client_id)
343             .context("incr_rx")?
344             .rx_count
345             .fetch_add(1, Ordering::Relaxed);
346         Ok(())
347     }
348 
349     // Send an 802.11 frame from a station to a station after wrapping in HwsimMsg.
350     // The HwsimMsg is processed by mac80211_hwsim framework in the guest OS.
351     //
352     // Simulates transmission through hostapd.
send_from_sta_frame( &self, frame: &Frame, source: &Station, destination: &Station, ) -> anyhow::Result<()>353     fn send_from_sta_frame(
354         &self,
355         frame: &Frame,
356         source: &Station,
357         destination: &Station,
358     ) -> anyhow::Result<()> {
359         if self.enabled(source.client_id)? && self.enabled(destination.client_id)? {
360             if let Some(packet) = self.create_hwsim_msg(frame, &destination.hwsim_addr) {
361                 self.incr_tx(source.client_id)?;
362                 self.incr_rx(destination.client_id)?;
363                 (self.callback)(destination.client_id, &packet.encode_to_vec()?.into());
364                 log_hwsim_msg(frame, source.client_id, destination.client_id);
365             }
366         }
367         Ok(())
368     }
369 
370     // Broadcast an 802.11 frame to all stations.
371     /// TODO: Compare with the implementations in mac80211_hwsim.c and wmediumd.c.
broadcast_from_sta_frame(&self, frame: &Frame, source: &Station) -> anyhow::Result<()>372     fn broadcast_from_sta_frame(&self, frame: &Frame, source: &Station) -> anyhow::Result<()> {
373         for destination in self.stations() {
374             if source.addr != destination.addr {
375                 self.send_from_sta_frame(frame, source, &destination)?;
376             }
377         }
378         Ok(())
379     }
380 
queue_frame(&self, frame: Frame, source: &Station) -> anyhow::Result<bool>381     fn queue_frame(&self, frame: Frame, source: &Station) -> anyhow::Result<bool> {
382         let dest_addr = frame.ieee80211.get_destination();
383         if self.contains_station(&dest_addr) {
384             debug!("Frame deliver from {} to {}", source.addr, dest_addr);
385             self.send_tx_info_frame(&frame)?;
386             let destination = self.get_station(&dest_addr)?;
387             self.send_from_sta_frame(&frame, source, &destination)?;
388             Ok(true)
389         } else if dest_addr.is_multicast() {
390             debug!("Frame multicast {}", frame.ieee80211);
391             self.send_tx_info_frame(&frame)?;
392             self.broadcast_from_sta_frame(&frame, source)?;
393             // Forward multicast packets to WifiService:
394             // 1. Stations send probe Request management frame scan network actively,
395             //    so hostapd will send Probe Response for AndroidWiFi.
396             // 2. DNS packets.
397             // TODO: Only pass necessary packets to hostapd and libslirp. (e.g.: Don't forward mDNS packet.)
398             self.incr_tx(source.client_id)?;
399             Ok(false)
400         } else {
401             // pass to libslirp
402             self.incr_tx(source.client_id)?;
403             Ok(false)
404         }
405     }
406 
407     // Simulate transmission through hostapd by rewriting frames with 802.11 ToDS
408     // and hostapd_bssid to frames with FromDS set.
create_hwsim_attr( &self, frame: &Frame, dest_hwsim_addr: &MacAddress, ) -> anyhow::Result<Vec<u8>>409     fn create_hwsim_attr(
410         &self,
411         frame: &Frame,
412         dest_hwsim_addr: &MacAddress,
413     ) -> anyhow::Result<Vec<u8>> {
414         let attrs = &frame.attrs;
415         let frame = match self.ap_simulation
416             && frame.ieee80211.is_to_ap()
417             && frame.ieee80211.get_bssid() == Some(self.hostapd_bssid)
418         {
419             true => frame.ieee80211.into_from_ap()?.encode_to_vec()?,
420             false => attrs.frame.clone().unwrap(),
421         };
422 
423         let mut builder = HwsimAttrSet::builder();
424 
425         // Attributes required by mac80211_hwsim.
426         builder.receiver(&dest_hwsim_addr.to_vec());
427         builder.frame(&frame);
428         // Incoming HwsimMsg don't have rx_rate and signal.
429         builder.rx_rate(attrs.rx_rate_idx.unwrap_or(RX_RATE));
430         builder.signal(attrs.signal.unwrap_or(SIGNAL));
431 
432         attrs.flags.map(|v| builder.flags(v));
433         attrs.freq.map(|v| builder.freq(v));
434         attrs.tx_info.as_ref().map(|v| builder.tx_info(v));
435         attrs.tx_info_flags.as_ref().map(|v| builder.tx_info_flags(v));
436 
437         Ok(builder.build()?.attributes)
438     }
439 
440     // Simulates transmission through hostapd.
create_hwsim_msg(&self, frame: &Frame, dest_hwsim_addr: &MacAddress) -> Option<HwsimMsg>441     fn create_hwsim_msg(&self, frame: &Frame, dest_hwsim_addr: &MacAddress) -> Option<HwsimMsg> {
442         let hwsim_msg = &frame.hwsim_msg;
443         assert_eq!(hwsim_msg.hwsim_hdr.hwsim_cmd, HwsimCmd::Frame);
444         let attributes_result = self.create_hwsim_attr(frame, dest_hwsim_addr);
445         let attributes = match attributes_result {
446             Ok(attributes) => attributes,
447             Err(e) => {
448                 warn!("Failed to create from_ap attributes. E: {}", e);
449                 return None;
450             }
451         };
452 
453         let nlmsg_len = hwsim_msg.nl_hdr.nlmsg_len + attributes.len() as u32
454             - hwsim_msg.attributes.len() as u32;
455         let new_hwsim_msg = HwsimMsg {
456             nl_hdr: NlMsgHdr {
457                 nlmsg_len,
458                 nlmsg_type: NLMSG_MIN_TYPE,
459                 nlmsg_flags: hwsim_msg.nl_hdr.nlmsg_flags,
460                 nlmsg_seq: 0,
461                 nlmsg_pid: 0,
462             },
463             hwsim_hdr: hwsim_msg.hwsim_hdr.clone(),
464             attributes,
465         };
466         Some(new_hwsim_msg)
467     }
468 }
469 
log_hwsim_msg(frame: &Frame, client_id: u32, dest_client_id: u32)470 fn log_hwsim_msg(frame: &Frame, client_id: u32, dest_client_id: u32) {
471     debug!(
472         "Sent hwsim_msg from client {} to {}. flags {:?}, ieee80211 {}",
473         client_id, dest_client_id, frame.flags, frame.ieee80211,
474     );
475 }
476 
477 /// Build TxInfoFrame HwsimMsg from CmdFrame HwsimMsg.
478 ///
479 /// Reference to ackLocalFrame() in external/qemu/android-qemu2-glue/emulation/VirtioWifiForwarder.cpp
build_tx_info(hwsim_msg: &HwsimMsg) -> anyhow::Result<HwsimMsg>480 fn build_tx_info(hwsim_msg: &HwsimMsg) -> anyhow::Result<HwsimMsg> {
481     let attrs = HwsimAttrSet::parse(&hwsim_msg.attributes).context("HwsimAttrSet").unwrap();
482 
483     let hwsim_hdr = &hwsim_msg.hwsim_hdr;
484     let nl_hdr = &hwsim_msg.nl_hdr;
485     let mut new_attr_builder = HwsimAttrSet::builder();
486     const HWSIM_TX_STAT_ACK: u32 = 1 << 2;
487 
488     new_attr_builder
489         .transmitter(&attrs.transmitter.context("transmitter")?.into())
490         .flags(attrs.flags.context("flags")? | HWSIM_TX_STAT_ACK)
491         .cookie(attrs.cookie.context("cookie")?)
492         .signal(attrs.signal.unwrap_or(SIGNAL))
493         .tx_info(attrs.tx_info.context("tx_info")?.as_slice());
494 
495     let new_attr = new_attr_builder.build().unwrap();
496     let nlmsg_len =
497         nl_hdr.nlmsg_len + new_attr.attributes.len() as u32 - attrs.attributes.len() as u32;
498     let new_hwsim_msg = HwsimMsg {
499         attributes: new_attr.attributes,
500         hwsim_hdr: HwsimMsgHdr {
501             hwsim_cmd: HwsimCmd::TxInfoFrame,
502             hwsim_version: 0,
503             reserved: hwsim_hdr.reserved,
504         },
505         nl_hdr: NlMsgHdr {
506             nlmsg_len,
507             nlmsg_type: NLMSG_MIN_TYPE,
508             nlmsg_flags: nl_hdr.nlmsg_flags,
509             nlmsg_seq: 0,
510             nlmsg_pid: 0,
511         },
512     };
513     Ok(new_hwsim_msg)
514 }
515 
516 // It's used by radiotap.rs for packet capture.
parse_hwsim_cmd(packet: &[u8]) -> anyhow::Result<HwsimCmdEnum>517 pub fn parse_hwsim_cmd(packet: &[u8]) -> anyhow::Result<HwsimCmdEnum> {
518     let hwsim_msg = HwsimMsg::decode_full(packet)?;
519     match hwsim_msg.hwsim_hdr.hwsim_cmd {
520         HwsimCmd::Frame => {
521             let frame = Frame::parse(&hwsim_msg)?;
522             Ok(HwsimCmdEnum::Frame(Box::new(frame)))
523         }
524         HwsimCmd::TxInfoFrame => Ok(HwsimCmdEnum::TxInfoFrame),
525         _ => Err(anyhow!("Unknown HwsimMsg cmd={:?}", hwsim_msg.hwsim_hdr.hwsim_cmd)),
526     }
527 }
528 
529 #[cfg(test)]
530 mod tests {
531     use super::*;
532     use crate::wifi::packets::ieee80211::parse_mac_address;
533     #[test]
test_remove()534     fn test_remove() {
535         let hostapd_bssid: MacAddress = parse_mac_address("00:13:10:85:fe:01").unwrap();
536 
537         let test_client_id: u32 = 1234;
538         let other_client_id: u32 = 5678;
539         let addr: MacAddress = parse_mac_address("00:0b:85:71:20:00").unwrap();
540         let other_addr: MacAddress = parse_mac_address("00:0b:85:71:20:01").unwrap();
541         let hwsim_addr: MacAddress = parse_mac_address("00:0b:85:71:20:ce").unwrap();
542         let other_hwsim_addr: MacAddress = parse_mac_address("00:0b:85:71:20:cf").unwrap();
543 
544         // Create a test Medium object
545         let callback: HwsimCmdCallback = |_, _| {};
546         let medium = Medium {
547             callback,
548             stations: RwLock::new(HashMap::from([
549                 (addr, Arc::new(Station { client_id: test_client_id, addr, hwsim_addr })),
550                 (
551                     other_addr,
552                     Arc::new(Station {
553                         client_id: other_client_id,
554                         addr: other_addr,
555                         hwsim_addr: other_hwsim_addr,
556                     }),
557                 ),
558             ])),
559             clients: RwLock::new(HashMap::from([
560                 (test_client_id, Client::new()),
561                 (other_client_id, Client::new()),
562             ])),
563             hostapd_bssid,
564             ap_simulation: true,
565         };
566 
567         medium.remove(test_client_id);
568 
569         assert!(!medium.contains_station(&addr));
570         assert!(medium.contains_station(&other_addr));
571         assert!(!medium.contains_client(test_client_id));
572         assert!(medium.contains_client(other_client_id));
573     }
574 
575     #[test]
test_netlink_attr()576     fn test_netlink_attr() {
577         let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv");
578         assert!(parse_hwsim_cmd(&packet).is_ok());
579 
580         let tx_info_packet: Vec<u8> = include!("test_packets/hwsim_cmd_tx_info.csv");
581         assert!(parse_hwsim_cmd(&tx_info_packet).is_ok());
582     }
583 
584     #[test]
test_netlink_attr_response_packet()585     fn test_netlink_attr_response_packet() {
586         // Response packet may not contain transmitter, flags, tx_info, or cookie fields.
587         let response_packet: Vec<u8> =
588             include!("test_packets/hwsim_cmd_frame_response_no_transmitter_flags_tx_info.csv");
589         assert!(parse_hwsim_cmd(&response_packet).is_ok());
590 
591         let response_packet2: Vec<u8> =
592             include!("test_packets/hwsim_cmd_frame_response_no_cookie.csv");
593         assert!(parse_hwsim_cmd(&response_packet2).is_ok());
594     }
595 
596     #[test]
test_is_mdns_packet()597     fn test_is_mdns_packet() {
598         let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame_mdns.csv");
599         let hwsim_msg = HwsimMsg::decode_full(&packet).unwrap();
600         let mdns_frame = Frame::parse(&hwsim_msg).unwrap();
601         assert!(!mdns_frame.ieee80211.get_source().is_multicast());
602         assert!(mdns_frame.ieee80211.get_destination().is_multicast());
603     }
604 
605     #[test]
test_build_tx_info_reconstruct()606     fn test_build_tx_info_reconstruct() {
607         let packet: Vec<u8> = include!("test_packets/hwsim_cmd_tx_info.csv");
608         let hwsim_msg = HwsimMsg::decode_full(&packet).unwrap();
609         assert_eq!(hwsim_msg.hwsim_hdr().hwsim_cmd, HwsimCmd::TxInfoFrame);
610 
611         let new_hwsim_msg = build_tx_info(&hwsim_msg).unwrap();
612         assert_eq!(hwsim_msg, new_hwsim_msg);
613     }
614 
615     #[test]
test_build_tx_info()616     fn test_build_tx_info() {
617         let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv");
618         let hwsim_msg = HwsimMsg::decode_full(&packet).unwrap();
619         let hwsim_msg_tx_info = build_tx_info(&hwsim_msg).unwrap();
620         assert_eq!(hwsim_msg_tx_info.hwsim_hdr().hwsim_cmd, HwsimCmd::TxInfoFrame);
621     }
622 
build_tx_info_and_compare(frame_bytes: &Bytes, tx_info_expected_bytes: &Bytes)623     fn build_tx_info_and_compare(frame_bytes: &Bytes, tx_info_expected_bytes: &Bytes) {
624         let frame = HwsimMsg::decode_full(frame_bytes).unwrap();
625         let tx_info = build_tx_info(&frame).unwrap();
626 
627         let tx_info_expected = HwsimMsg::decode_full(tx_info_expected_bytes).unwrap();
628 
629         assert_eq!(tx_info.hwsim_hdr(), tx_info_expected.hwsim_hdr());
630         assert_eq!(tx_info.nl_hdr(), tx_info_expected.nl_hdr());
631 
632         let attrs = HwsimAttrSet::parse(tx_info.attributes()).context("HwsimAttrSet").unwrap();
633         let attrs_expected =
634             HwsimAttrSet::parse(tx_info_expected.attributes()).context("HwsimAttrSet").unwrap();
635 
636         // NOTE: TX info is different and the counts are all zeros in the TX info packet generated by WifiService.
637         // TODO: Confirm if the behavior is intended in WifiService.
638         assert_eq!(attrs.transmitter, attrs_expected.transmitter);
639         assert_eq!(attrs.flags, attrs_expected.flags);
640         assert_eq!(attrs.cookie, attrs_expected.cookie);
641         assert_eq!(attrs.signal, attrs_expected.signal);
642     }
643 
644     #[test]
test_build_tx_info_and_compare()645     fn test_build_tx_info_and_compare() {
646         let frame_bytes = Bytes::from(include!("test_packets/hwsim_cmd_frame_request.csv"));
647         let tx_info_expected_bytes =
648             Bytes::from(include!("test_packets/hwsim_cmd_tx_info_response.csv"));
649         build_tx_info_and_compare(&frame_bytes, &tx_info_expected_bytes);
650     }
651 
652     #[test]
test_build_tx_info_and_compare_mdns()653     fn test_build_tx_info_and_compare_mdns() {
654         let frame_bytes = Bytes::from(include!("test_packets/hwsim_cmd_frame_request_mdns.csv"));
655         let tx_info_expected_bytes =
656             Bytes::from(include!("test_packets/hwsim_cmd_tx_info_response_mdns.csv"));
657         build_tx_info_and_compare(&frame_bytes, &tx_info_expected_bytes);
658     }
659 }
660