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