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 crate::wifi::frame::Frame;
16 use crate::wifi::hostapd::Hostapd;
17 use crate::wifi::hwsim_attr_set::HwsimAttrSet;
18 use anyhow::{anyhow, Context};
19 use bytes::Bytes;
20 use log::{debug, info, warn};
21 use netsim_packets::ieee80211::{DataSubType, Ieee80211, MacAddress};
22 use netsim_packets::mac80211_hwsim::{HwsimCmd, HwsimMsg, HwsimMsgHdr, NlMsgHdr};
23 use pdl_runtime::Packet;
24 use std::collections::HashMap;
25 use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
26 use std::sync::{Arc, RwLock};
27
28 const NLMSG_MIN_TYPE: u16 = 0x10;
29 const NL_AUTO_SEQ: u16 = 0;
30 const NL_AUTO_PORT: u32 = 0;
31 // Default values for mac80211_hwsim.
32 const RX_RATE: u32 = 0;
33 const SIGNAL: u32 = 4294967246; // -50
34 const NL_MSG_HDR_LEN: usize = 16;
35
36 pub struct Processor {
37 pub hostapd: bool,
38 pub network: bool,
39 pub wmedium: bool,
40 pub frame: Frame,
41 pub plaintext_ieee80211: Option<Ieee80211>,
42 }
43
44 impl Processor {
45 /// Returns the decrypted IEEE 802.11 frame if available.
46 /// Otherwise, returns the original IEEE 80211 frame.
get_ieee80211(&self) -> &Ieee8021147 pub fn get_ieee80211(&self) -> &Ieee80211 {
48 self.plaintext_ieee80211.as_ref().unwrap_or(&self.frame.ieee80211)
49 }
50
51 /// Returns the decrypted IEEE 802.11 frame as bytes if available.
52 /// Otherwise, returns the original IEEE 80211 frame as bytes.
get_ieee80211_bytes(&self) -> Bytes53 pub fn get_ieee80211_bytes(&self) -> Bytes {
54 if let Some(ieee80211) = self.plaintext_ieee80211.as_ref() {
55 ieee80211.encode_to_vec().unwrap().into()
56 } else {
57 self.frame.data.clone().into()
58 }
59 }
60 }
61
62 #[allow(dead_code)]
63 #[derive(Debug)]
64 pub enum HwsimCmdEnum {
65 Unspec,
66 Register,
67 Frame(Box<Frame>),
68 TxInfoFrame,
69 NewRadio,
70 DelRadio,
71 GetRadio,
72 AddMacAddr,
73 DelMacAddr,
74 }
75
76 #[derive(Clone)]
77 struct Station {
78 client_id: u32,
79 // Ieee80211 source address
80 addr: MacAddress,
81 // Hwsim virtual address from HWSIM_ATTR_ADDR_TRANSMITTER
82 // Used to create the HwsimMsg to stations.
83 hwsim_addr: MacAddress,
84 // Caches the frequency (HWSIM_ATTR_FREQ) from the latest HwsimMsg request.
85 // This value is used to populate the HWSIM_ATTR_FREQ field in HwsimMsg response.
86 freq: Arc<AtomicU32>,
87 }
88
89 impl Station {
new(client_id: u32, addr: MacAddress, hwsim_addr: MacAddress) -> Self90 fn new(client_id: u32, addr: MacAddress, hwsim_addr: MacAddress) -> Self {
91 Self { client_id, addr, hwsim_addr, freq: Arc::new(AtomicU32::new(0)) }
92 }
93
update_freq(&self, freq: u32)94 fn update_freq(&self, freq: u32) {
95 self.freq.store(freq, Ordering::Relaxed);
96 }
97 }
98
99 #[derive(Clone)]
100 pub struct Client {
101 pub enabled: Arc<AtomicBool>,
102 pub tx_count: Arc<AtomicU32>,
103 pub rx_count: Arc<AtomicU32>,
104 }
105
106 impl Client {
new() -> Self107 fn new() -> Self {
108 Self {
109 enabled: Arc::new(AtomicBool::new(true)),
110 tx_count: Arc::new(AtomicU32::new(0)),
111 rx_count: Arc::new(AtomicU32::new(0)),
112 }
113 }
114 }
115
116 pub struct Medium {
117 callback: HwsimCmdCallback,
118 // Ieee80211 source address
119 stations: RwLock<HashMap<MacAddress, Arc<Station>>>,
120 clients: RwLock<HashMap<u32, Client>>,
121 // Simulate the re-transmission of frames sent to hostapd
122 ap_simulation: bool,
123 hostapd: Arc<Hostapd>,
124 }
125
126 type HwsimCmdCallback = fn(u32, &Bytes);
127 impl Medium {
new(callback: HwsimCmdCallback, hostapd: Arc<Hostapd>) -> Medium128 pub fn new(callback: HwsimCmdCallback, hostapd: Arc<Hostapd>) -> Medium {
129 Self {
130 callback,
131 stations: RwLock::new(HashMap::new()),
132 clients: RwLock::new(HashMap::new()),
133 ap_simulation: true,
134 hostapd,
135 }
136 }
137
add(&self, client_id: u32)138 pub fn add(&self, client_id: u32) {
139 let _ = self.clients.write().unwrap().entry(client_id).or_insert_with(|| {
140 info!("Insert client {}", client_id);
141 Client::new()
142 });
143 }
144
remove(&self, client_id: u32)145 pub fn remove(&self, client_id: u32) {
146 self.stations.write().unwrap().retain(|_, s| s.client_id != client_id);
147 self.clients.write().unwrap().remove(&client_id);
148 }
149
reset(&self, client_id: u32)150 pub fn reset(&self, client_id: u32) {
151 if let Some(client) = self.clients.read().unwrap().get(&client_id) {
152 client.enabled.store(true, Ordering::Relaxed);
153 client.tx_count.store(0, Ordering::Relaxed);
154 client.rx_count.store(0, Ordering::Relaxed);
155 }
156 }
157
get(&self, client_id: u32) -> Option<Client>158 pub fn get(&self, client_id: u32) -> Option<Client> {
159 self.clients.read().unwrap().get(&client_id).map(|c| c.to_owned())
160 }
161
contains_client(&self, client_id: u32) -> bool162 fn contains_client(&self, client_id: u32) -> bool {
163 self.clients.read().unwrap().contains_key(&client_id)
164 }
165
stations(&self) -> impl Iterator<Item = Arc<Station>>166 fn stations(&self) -> impl Iterator<Item = Arc<Station>> {
167 self.stations.read().unwrap().clone().into_values()
168 }
169
contains_station(&self, addr: &MacAddress) -> bool170 fn contains_station(&self, addr: &MacAddress) -> bool {
171 self.stations.read().unwrap().contains_key(addr)
172 }
173
get_station(&self, addr: &MacAddress) -> anyhow::Result<Arc<Station>>174 fn get_station(&self, addr: &MacAddress) -> anyhow::Result<Arc<Station>> {
175 self.stations.read().unwrap().get(addr).context("get station").cloned()
176 }
177
upsert_station(&self, client_id: u32, frame: &Frame) -> anyhow::Result<()>178 fn upsert_station(&self, client_id: u32, frame: &Frame) -> anyhow::Result<()> {
179 let src_addr = frame.ieee80211.get_source();
180 let hwsim_addr = frame.transmitter.context("transmitter")?;
181 self.stations.write().unwrap().entry(src_addr).or_insert_with(|| {
182 info!(
183 "Insert station with client id {}, hwsimaddr: {}, \
184 Ieee80211 addr: {}",
185 client_id, hwsim_addr, src_addr
186 );
187 Arc::new(Station::new(client_id, src_addr, hwsim_addr))
188 });
189 if !self.contains_client(client_id) {
190 warn!("Client {} is missing", client_id);
191 self.add(client_id);
192 }
193 Ok(())
194 }
195
ack_frame(&self, client_id: u32, frame: &Frame)196 pub fn ack_frame(&self, client_id: u32, frame: &Frame) {
197 // Send Ack frame back to source
198 self.ack_frame_internal(client_id, frame).unwrap_or_else(move |e| {
199 // TODO: add this error to the netsim_session_stats
200 warn!("error ack frame {e}");
201 });
202 }
203
ack_frame_internal(&self, client_id: u32, frame: &Frame) -> anyhow::Result<()>204 fn ack_frame_internal(&self, client_id: u32, frame: &Frame) -> anyhow::Result<()> {
205 self.send_tx_info_frame(frame)?;
206 self.incr_tx(client_id)?;
207 Ok(())
208 }
209
210 /// Process commands from the kernel's mac80211_hwsim subsystem.
211 ///
212 /// This is the processing that will be implemented:
213 ///
214 /// * The source MacAddress in 802.11 frames is re-mapped to a globally
215 /// unique MacAddress because resumed Emulator AVDs appear with the
216 /// same address.
217 ///
218 /// * 802.11 frames sent between stations
219 ///
220 /// * 802.11 multicast frames are re-broadcast to connected stations.
get_processor(&self, client_id: u32, packet: &Bytes) -> Option<Processor>221 pub fn get_processor(&self, client_id: u32, packet: &Bytes) -> Option<Processor> {
222 let frame = self
223 .validate(client_id, packet)
224 .map_err(|e| warn!("error validate for client {client_id}: {e}"))
225 .ok()?;
226
227 // Creates Stations on the fly when there is no config file.
228 // WiFi Direct will use a randomized mac address for probing
229 // new networks. This block associates the new mac with the station.
230 self.upsert_station(client_id, &frame)
231 .map_err(|e| warn!("error upsert station for client {client_id}: {e}"))
232 .ok()?;
233
234 let plaintext_ieee80211 = self.hostapd.try_decrypt(&frame.ieee80211);
235
236 let mut processor = Processor {
237 hostapd: false,
238 network: false,
239 wmedium: false,
240 frame,
241 plaintext_ieee80211,
242 };
243
244 let dest_addr = processor.frame.ieee80211.get_destination();
245
246 processor.frame.attrs.freq.map(|freq| {
247 self.get_station(&processor.frame.ieee80211.get_source())
248 .map(|sta| sta.update_freq(freq))
249 .map_err(|e| {
250 warn!("Failed to get station for client {client_id} to update freq: {e}")
251 })
252 });
253
254 if self.contains_station(&dest_addr) {
255 processor.wmedium = true;
256 return Some(processor);
257 }
258 if dest_addr.is_multicast() {
259 processor.wmedium = true;
260 }
261
262 let ieee80211: &Ieee80211 = processor.get_ieee80211();
263 // If the BSSID is unicast and does not match the hostapd's BSSID, the packet is not handled by hostapd. Skip further checks.
264 if let Some(bssid) = ieee80211.get_bssid() {
265 if !bssid.is_multicast() && bssid != self.hostapd.get_bssid() {
266 return Some(processor);
267 }
268 }
269 // Data frames
270 if ieee80211.is_data() {
271 // EAPoL is used in Wi-Fi 4-way handshake.
272 let is_eapol = ieee80211.is_eapol().unwrap_or_else(|e| {
273 debug!("Failed to get ether type for is_eapol(): {}", e);
274 false
275 });
276 if is_eapol {
277 processor.hostapd = true;
278 } else if ieee80211.is_to_ap() {
279 // Don't forward Null Data frames to slirp because they are used to maintain an active connection and carry no user data.
280 if ieee80211.stype() != DataSubType::Nodata.into() {
281 processor.network = true;
282 }
283 }
284 } else {
285 // Mgmt or Ctrl frames.
286 // TODO: Refactor this check after verifying all packets sent to hostapd are of ToAP type.
287 let addr1 = ieee80211.get_addr1();
288 if addr1.is_multicast() || addr1.is_broadcast() || addr1 == self.hostapd.get_bssid() {
289 processor.hostapd = true;
290 }
291 }
292 Some(processor)
293 }
294
validate(&self, client_id: u32, packet: &Bytes) -> anyhow::Result<Frame>295 fn validate(&self, client_id: u32, packet: &Bytes) -> anyhow::Result<Frame> {
296 let hwsim_msg = HwsimMsg::decode_full(packet)?;
297
298 // The virtio handler only accepts HWSIM_CMD_FRAME, HWSIM_CMD_TX_INFO_FRAME and HWSIM_CMD_REPORT_PMSR
299 // in https://source.corp.google.com/h/kernel/pub/scm/linux/kernel/git/torvalds/linux/+/master:drivers/net/wireless/virtual/mac80211_hwsim.c
300 match hwsim_msg.hwsim_hdr.hwsim_cmd {
301 HwsimCmd::Frame => {
302 let frame = Frame::parse(&hwsim_msg)?;
303 // Incoming frame must contain transmitter, flag, cookie, and tx_info fields.
304 if frame.transmitter.is_none()
305 || frame.flags.is_none()
306 || frame.cookie.is_none()
307 || frame.tx_info.is_none()
308 {
309 return Err(anyhow!("Missing Hwsim attributes for incoming packet"));
310 }
311 // Use as receiver for outgoing HwsimMsg.
312 let hwsim_addr = frame.transmitter.context("transmitter")?;
313 let flags = frame.flags.context("flags")?;
314 let cookie = frame.cookie.context("cookie")?;
315 debug!(
316 "Frame chip {}, transmitter {}, flags {}, cookie {}, ieee80211 {}",
317 client_id, hwsim_addr, flags, cookie, frame.ieee80211
318 );
319 Ok(frame)
320 }
321 _ => Err(anyhow!("Another command found {:?}", hwsim_msg)),
322 }
323 }
324
325 /// Handle Wi-Fi Ieee802.3 frame from network.
326 /// Convert to HwsimMsg and send to clients.
process_ieee8023_response(&self, packet: &Bytes)327 pub fn process_ieee8023_response(&self, packet: &Bytes) {
328 let result = Ieee80211::from_ieee8023(packet, self.hostapd.get_bssid())
329 .and_then(|ieee80211| self.handle_ieee80211_response(ieee80211));
330
331 if let Err(e) = result {
332 warn!("{}", e);
333 }
334 }
335
336 /// Handle Wi-Fi Ieee802.11 frame from network.
337 /// Convert to HwsimMsg and send to clients.
process_ieee80211_response(&self, packet: &Bytes)338 pub fn process_ieee80211_response(&self, packet: &Bytes) {
339 let result = Ieee80211::decode_full(packet)
340 .context("Ieee80211")
341 .and_then(|ieee80211| self.handle_ieee80211_response(ieee80211));
342
343 if let Err(e) = result {
344 warn!("{}", e);
345 }
346 }
347
348 /// Determine the client id based on destination and send to client.
handle_ieee80211_response(&self, mut ieee80211: Ieee80211) -> anyhow::Result<()>349 fn handle_ieee80211_response(&self, mut ieee80211: Ieee80211) -> anyhow::Result<()> {
350 if let Some(encrypted_ieee80211) = self.hostapd.try_encrypt(&ieee80211) {
351 ieee80211 = encrypted_ieee80211;
352 }
353 let dest_addr = ieee80211.get_destination();
354 if let Ok(destination) = self.get_station(&dest_addr) {
355 self.send_ieee80211_response(&ieee80211, &destination)?;
356 } else if dest_addr.is_multicast() {
357 for destination in self.stations() {
358 self.send_ieee80211_response(&ieee80211, &destination)?;
359 }
360 } else {
361 warn!("Send frame response to unknown destination: {}", dest_addr);
362 }
363 Ok(())
364 }
365
send_ieee80211_response( &self, ieee80211: &Ieee80211, destination: &Station, ) -> anyhow::Result<()>366 fn send_ieee80211_response(
367 &self,
368 ieee80211: &Ieee80211,
369 destination: &Station,
370 ) -> anyhow::Result<()> {
371 let hwsim_msg = self.create_hwsim_msg_from_ieee80211(ieee80211, destination)?;
372 (self.callback)(destination.client_id, &hwsim_msg.encode_to_vec()?.into());
373 self.incr_rx(destination.client_id)?;
374 Ok(())
375 }
376
create_hwsim_msg_from_ieee80211( &self, ieee80211: &Ieee80211, destination: &Station, ) -> anyhow::Result<HwsimMsg>377 fn create_hwsim_msg_from_ieee80211(
378 &self,
379 ieee80211: &Ieee80211,
380 destination: &Station,
381 ) -> anyhow::Result<HwsimMsg> {
382 let attributes = self.create_hwsim_msg_attr(ieee80211, destination)?;
383 let hwsim_hdr = HwsimMsgHdr { hwsim_cmd: HwsimCmd::Frame, hwsim_version: 0, reserved: 0 };
384 let nlmsg_len = (NL_MSG_HDR_LEN + hwsim_hdr.encoded_len() + attributes.len()) as u32;
385 let nl_hdr = NlMsgHdr {
386 nlmsg_len,
387 nlmsg_type: NLMSG_MIN_TYPE,
388 nlmsg_flags: NL_AUTO_SEQ,
389 nlmsg_seq: NL_AUTO_PORT,
390 nlmsg_pid: 0,
391 };
392 Ok(HwsimMsg { nl_hdr, hwsim_hdr, attributes })
393 }
394
create_hwsim_msg_attr( &self, ieee80211: &Ieee80211, destination: &Station, ) -> anyhow::Result<Vec<u8>>395 fn create_hwsim_msg_attr(
396 &self,
397 ieee80211: &Ieee80211,
398 destination: &Station,
399 ) -> anyhow::Result<Vec<u8>> {
400 let mut builder = HwsimAttrSet::builder();
401 // Attributes required by mac80211_hwsim.
402 builder.receiver(&destination.hwsim_addr.to_vec());
403 let frame_bytes = ieee80211.encode_to_vec()?;
404 builder.frame(&frame_bytes);
405 builder.rx_rate(RX_RATE);
406 builder.signal(SIGNAL);
407 builder.freq(destination.freq.load(Ordering::Relaxed));
408 Ok(builder.build()?.attributes)
409 }
410
set_enabled(&self, client_id: u32, enabled: bool)411 pub fn set_enabled(&self, client_id: u32, enabled: bool) {
412 if let Some(client) = self.clients.read().unwrap().get(&client_id) {
413 client.enabled.store(enabled, Ordering::Relaxed);
414 }
415 }
416
enabled(&self, client_id: u32) -> anyhow::Result<bool>417 fn enabled(&self, client_id: u32) -> anyhow::Result<bool> {
418 Ok(self
419 .clients
420 .read()
421 .unwrap()
422 .get(&client_id)
423 .context(format!("client {client_id} is missing"))?
424 .enabled
425 .load(Ordering::Relaxed))
426 }
427
428 /// Create tx info frame to station to ack HwsimMsg.
send_tx_info_frame(&self, frame: &Frame) -> anyhow::Result<()>429 fn send_tx_info_frame(&self, frame: &Frame) -> anyhow::Result<()> {
430 let client_id = self.get_station(&frame.ieee80211.get_source())?.client_id;
431 let hwsim_msg_tx_info = build_tx_info(&frame.hwsim_msg).unwrap().encode_to_vec()?;
432 (self.callback)(client_id, &hwsim_msg_tx_info.into());
433 Ok(())
434 }
435
incr_tx(&self, client_id: u32) -> anyhow::Result<()>436 fn incr_tx(&self, client_id: u32) -> anyhow::Result<()> {
437 self.clients
438 .read()
439 .unwrap()
440 .get(&client_id)
441 .context("incr_tx")?
442 .tx_count
443 .fetch_add(1, Ordering::Relaxed);
444 Ok(())
445 }
446
incr_rx(&self, client_id: u32) -> anyhow::Result<()>447 fn incr_rx(&self, client_id: u32) -> anyhow::Result<()> {
448 self.clients
449 .read()
450 .unwrap()
451 .get(&client_id)
452 .context("incr_rx")?
453 .rx_count
454 .fetch_add(1, Ordering::Relaxed);
455 Ok(())
456 }
457
458 // Send an 802.11 frame from a station to a station after wrapping in HwsimMsg.
459 // The HwsimMsg is processed by mac80211_hwsim framework in the guest OS.
460 //
461 // Simulates transmission through hostapd.
send_from_sta_frame( &self, frame: &Frame, ieee80211: &Ieee80211, source: &Station, destination: &Station, ) -> anyhow::Result<()>462 fn send_from_sta_frame(
463 &self,
464 frame: &Frame,
465 ieee80211: &Ieee80211,
466 source: &Station,
467 destination: &Station,
468 ) -> anyhow::Result<()> {
469 if self.enabled(source.client_id)? && self.enabled(destination.client_id)? {
470 if let Some(packet) = self.create_hwsim_msg(frame, ieee80211, &destination.hwsim_addr) {
471 self.incr_rx(destination.client_id)?;
472 (self.callback)(destination.client_id, &packet.encode_to_vec()?.into());
473 log_hwsim_msg(frame, source.client_id, destination.client_id);
474 }
475 }
476 Ok(())
477 }
478
479 // Broadcast an 802.11 frame to all stations.
480 /// TODO: Compare with the implementations in mac80211_hwsim.c and wmediumd.c.
broadcast_from_sta_frame( &self, frame: &Frame, ieee80211: &Ieee80211, source: &Station, ) -> anyhow::Result<()>481 fn broadcast_from_sta_frame(
482 &self,
483 frame: &Frame,
484 ieee80211: &Ieee80211,
485 source: &Station,
486 ) -> anyhow::Result<()> {
487 for destination in self.stations() {
488 if source.addr != destination.addr {
489 self.send_from_sta_frame(frame, ieee80211, source, &destination)?;
490 }
491 }
492 Ok(())
493 }
494 /// Queues the frame for sending to medium.
495 ///
496 /// The `frame` contains an `ieee80211` field, but it might be encrypted. This function uses the provided `ieee80211` parameter directly, as it's expected to be decrypted if necessary.
queue_frame(&self, frame: Frame, ieee80211: Ieee80211)497 pub fn queue_frame(&self, frame: Frame, ieee80211: Ieee80211) {
498 self.queue_frame_internal(frame, ieee80211).unwrap_or_else(move |e| {
499 // TODO: add this error to the netsim_session_stats
500 warn!("queue frame error {e}");
501 });
502 }
503
queue_frame_internal(&self, frame: Frame, ieee80211: Ieee80211) -> anyhow::Result<()>504 fn queue_frame_internal(&self, frame: Frame, ieee80211: Ieee80211) -> anyhow::Result<()> {
505 let source = self.get_station(&ieee80211.get_source())?;
506 let dest_addr = ieee80211.get_destination();
507 if self.contains_station(&dest_addr) {
508 debug!("Frame deliver from {} to {}", source.addr, dest_addr);
509 let destination = self.get_station(&dest_addr)?;
510 self.send_from_sta_frame(&frame, &ieee80211, &source, &destination)?;
511 return Ok(());
512 } else if dest_addr.is_multicast() {
513 debug!("Frame multicast {}", ieee80211);
514 self.broadcast_from_sta_frame(&frame, &ieee80211, &source)?;
515 return Ok(());
516 }
517
518 Err(anyhow!("Dropped packet {}", ieee80211))
519 }
520
521 // Simulate transmission through hostapd by rewriting frames with 802.11 ToDS
522 // and hostapd_bssid to frames with FromDS set.
create_hwsim_attr( &self, frame: &Frame, ieee80211: &Ieee80211, dest_hwsim_addr: &MacAddress, ) -> anyhow::Result<Vec<u8>>523 fn create_hwsim_attr(
524 &self,
525 frame: &Frame,
526 ieee80211: &Ieee80211,
527 dest_hwsim_addr: &MacAddress,
528 ) -> anyhow::Result<Vec<u8>> {
529 // Encrypt Ieee80211 if needed
530 let attrs = &frame.attrs;
531 let mut ieee80211_response = match self.ap_simulation
532 && ieee80211.is_to_ap()
533 && ieee80211.get_bssid() == Some(self.hostapd.get_bssid())
534 {
535 true => ieee80211.into_from_ap()?.try_into()?,
536 false => ieee80211.clone(),
537 };
538 if let Some(encrypted_ieee80211) = self.hostapd.try_encrypt(&ieee80211_response) {
539 ieee80211_response = encrypted_ieee80211;
540 }
541 let frame = ieee80211_response.encode_to_vec()?;
542
543 let mut builder = HwsimAttrSet::builder();
544
545 // Attributes required by mac80211_hwsim.
546 builder.receiver(&dest_hwsim_addr.to_vec());
547 builder.frame(&frame);
548 // Incoming HwsimMsg don't have rx_rate and signal.
549 builder.rx_rate(attrs.rx_rate_idx.unwrap_or(RX_RATE));
550 builder.signal(attrs.signal.unwrap_or(SIGNAL));
551
552 attrs.flags.map(|v| builder.flags(v));
553 attrs.freq.map(|v| builder.freq(v));
554 attrs.tx_info.as_ref().map(|v| builder.tx_info(v));
555 attrs.tx_info_flags.as_ref().map(|v| builder.tx_info_flags(v));
556
557 Ok(builder.build()?.attributes)
558 }
559
560 // Simulates transmission through hostapd.
create_hwsim_msg( &self, frame: &Frame, ieee80211: &Ieee80211, dest_hwsim_addr: &MacAddress, ) -> Option<HwsimMsg>561 fn create_hwsim_msg(
562 &self,
563 frame: &Frame,
564 ieee80211: &Ieee80211,
565 dest_hwsim_addr: &MacAddress,
566 ) -> Option<HwsimMsg> {
567 let hwsim_msg = &frame.hwsim_msg;
568 assert_eq!(hwsim_msg.hwsim_hdr.hwsim_cmd, HwsimCmd::Frame);
569 let attributes_result = self.create_hwsim_attr(frame, ieee80211, dest_hwsim_addr);
570 let attributes = match attributes_result {
571 Ok(attributes) => attributes,
572 Err(e) => {
573 warn!("Failed to create from_ap attributes. E: {}", e);
574 return None;
575 }
576 };
577
578 let nlmsg_len = hwsim_msg.nl_hdr.nlmsg_len + attributes.len() as u32
579 - hwsim_msg.attributes.len() as u32;
580 let new_hwsim_msg = HwsimMsg {
581 nl_hdr: NlMsgHdr {
582 nlmsg_len,
583 nlmsg_type: NLMSG_MIN_TYPE,
584 nlmsg_flags: hwsim_msg.nl_hdr.nlmsg_flags,
585 nlmsg_seq: 0,
586 nlmsg_pid: 0,
587 },
588 hwsim_hdr: hwsim_msg.hwsim_hdr.clone(),
589 attributes,
590 };
591 Some(new_hwsim_msg)
592 }
593 }
594
log_hwsim_msg(frame: &Frame, client_id: u32, dest_client_id: u32)595 fn log_hwsim_msg(frame: &Frame, client_id: u32, dest_client_id: u32) {
596 debug!(
597 "Sent hwsim_msg from client {} to {}. flags {:?}, ieee80211 {}",
598 client_id, dest_client_id, frame.flags, frame.ieee80211,
599 );
600 }
601
602 /// Build TxInfoFrame HwsimMsg from CmdFrame HwsimMsg.
603 ///
604 /// Reference to ackLocalFrame() in external/qemu/android-qemu2-glue/emulation/VirtioWifiForwarder.cpp
build_tx_info(hwsim_msg: &HwsimMsg) -> anyhow::Result<HwsimMsg>605 fn build_tx_info(hwsim_msg: &HwsimMsg) -> anyhow::Result<HwsimMsg> {
606 let attrs = HwsimAttrSet::parse(&hwsim_msg.attributes).context("HwsimAttrSet").unwrap();
607
608 let hwsim_hdr = &hwsim_msg.hwsim_hdr;
609 let nl_hdr = &hwsim_msg.nl_hdr;
610 let mut new_attr_builder = HwsimAttrSet::builder();
611 const HWSIM_TX_STAT_ACK: u32 = 1 << 2;
612
613 new_attr_builder
614 .transmitter(&attrs.transmitter.context("transmitter")?.into())
615 .flags(attrs.flags.context("flags")? | HWSIM_TX_STAT_ACK)
616 .cookie(attrs.cookie.context("cookie")?)
617 .signal(attrs.signal.unwrap_or(SIGNAL))
618 .tx_info(attrs.tx_info.context("tx_info")?.as_slice());
619
620 let new_attr = new_attr_builder.build().unwrap();
621 let nlmsg_len =
622 nl_hdr.nlmsg_len + new_attr.attributes.len() as u32 - attrs.attributes.len() as u32;
623 let new_hwsim_msg = HwsimMsg {
624 attributes: new_attr.attributes,
625 hwsim_hdr: HwsimMsgHdr {
626 hwsim_cmd: HwsimCmd::TxInfoFrame,
627 hwsim_version: 0,
628 reserved: hwsim_hdr.reserved,
629 },
630 nl_hdr: NlMsgHdr {
631 nlmsg_len,
632 nlmsg_type: NLMSG_MIN_TYPE,
633 nlmsg_flags: nl_hdr.nlmsg_flags,
634 nlmsg_seq: 0,
635 nlmsg_pid: 0,
636 },
637 };
638 Ok(new_hwsim_msg)
639 }
640
641 // It's used by radiotap.rs for packet capture.
parse_hwsim_cmd(packet: &[u8]) -> anyhow::Result<HwsimCmdEnum>642 pub fn parse_hwsim_cmd(packet: &[u8]) -> anyhow::Result<HwsimCmdEnum> {
643 let hwsim_msg = HwsimMsg::decode_full(packet)?;
644 match hwsim_msg.hwsim_hdr.hwsim_cmd {
645 HwsimCmd::Frame => {
646 let frame = Frame::parse(&hwsim_msg)?;
647 Ok(HwsimCmdEnum::Frame(Box::new(frame)))
648 }
649 HwsimCmd::TxInfoFrame => Ok(HwsimCmdEnum::TxInfoFrame),
650 _ => Err(anyhow!("Unknown HwsimMsg cmd={:?}", hwsim_msg.hwsim_hdr.hwsim_cmd)),
651 }
652 }
653
654 #[cfg(test)]
655 mod tests {
656 use super::*;
657 use crate::wifi::hostapd;
658 use netsim_packets::ieee80211::{parse_mac_address, FrameType, Ieee80211, Ieee80211ToAp};
659
660 #[test]
test_get_plaintext_ieee80211()661 fn test_get_plaintext_ieee80211() {
662 // Test Data (802.11 frame with LLC/SNAP)
663 let bssid = parse_mac_address("0:0:0:0:0:0").unwrap();
664 let source = parse_mac_address("1:1:1:1:1:1").unwrap();
665 let destination = parse_mac_address("2:2:2:2:2:2").unwrap();
666 let ieee80211: Ieee80211 = Ieee80211ToAp {
667 duration_id: 0,
668 ftype: FrameType::Data,
669 more_data: 0,
670 more_frags: 0,
671 order: 0,
672 pm: 0,
673 protected: 0,
674 retry: 0,
675 stype: 0,
676 version: 0,
677 bssid,
678 source,
679 destination,
680 seq_ctrl: 0,
681 payload: Vec::new(),
682 }
683 .try_into()
684 .unwrap();
685
686 let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame_mdns.csv");
687 let hwsim_msg = HwsimMsg::decode_full(&packet).unwrap();
688 let frame1 = Frame::parse(&hwsim_msg).unwrap();
689 let frame2 = Frame::parse(&hwsim_msg).unwrap();
690
691 // Case 1: plaintext_ieee80211 is None
692 let processor = Processor {
693 hostapd: false,
694 network: false,
695 wmedium: false,
696 frame: frame1,
697 plaintext_ieee80211: None,
698 };
699 assert_eq!(processor.get_ieee80211(), &processor.frame.ieee80211);
700 assert_eq!(processor.get_ieee80211_bytes(), Bytes::from(processor.frame.data.clone()));
701
702 // Case 2: plaintext_ieee80211 has a value
703 let processor = Processor {
704 hostapd: false,
705 network: false,
706 wmedium: false,
707 frame: frame2,
708 plaintext_ieee80211: Some(ieee80211),
709 };
710 assert_eq!(processor.get_ieee80211(), processor.plaintext_ieee80211.as_ref().unwrap());
711 assert_eq!(
712 processor.get_ieee80211_bytes(),
713 Bytes::from(processor.plaintext_ieee80211.as_ref().unwrap().encode_to_vec().unwrap())
714 );
715 }
716
717 #[test]
test_remove()718 fn test_remove() {
719 let test_client_id: u32 = 1234;
720 let other_client_id: u32 = 5678;
721 let addr: MacAddress = parse_mac_address("00:0b:85:71:20:00").unwrap();
722 let other_addr: MacAddress = parse_mac_address("00:0b:85:71:20:01").unwrap();
723 let hwsim_addr: MacAddress = parse_mac_address("00:0b:85:71:20:ce").unwrap();
724 let other_hwsim_addr: MacAddress = parse_mac_address("00:0b:85:71:20:cf").unwrap();
725
726 let hostapd_options = netsim_proto::config::HostapdOptions::new();
727 let (tx, _rx) = std::sync::mpsc::channel();
728 let hostapd = Arc::new(hostapd::hostapd_run(hostapd_options, tx).unwrap());
729
730 // Create a test Medium object
731 let callback: HwsimCmdCallback = |_, _| {};
732 let medium = Medium {
733 callback,
734 stations: RwLock::new(HashMap::from([
735 (
736 addr,
737 Arc::new(Station {
738 client_id: test_client_id,
739 addr,
740 hwsim_addr,
741 freq: Arc::new(AtomicU32::new(0)),
742 }),
743 ),
744 (
745 other_addr,
746 Arc::new(Station {
747 client_id: other_client_id,
748 addr: other_addr,
749 hwsim_addr: other_hwsim_addr,
750 freq: Arc::new(AtomicU32::new(0)),
751 }),
752 ),
753 ])),
754 clients: RwLock::new(HashMap::from([
755 (test_client_id, Client::new()),
756 (other_client_id, Client::new()),
757 ])),
758 ap_simulation: true,
759 hostapd,
760 };
761
762 medium.remove(test_client_id);
763
764 assert!(!medium.contains_station(&addr));
765 assert!(medium.contains_station(&other_addr));
766 assert!(!medium.contains_client(test_client_id));
767 assert!(medium.contains_client(other_client_id));
768 }
769
770 #[test]
test_netlink_attr()771 fn test_netlink_attr() {
772 let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv");
773 assert!(parse_hwsim_cmd(&packet).is_ok());
774
775 let tx_info_packet: Vec<u8> = include!("test_packets/hwsim_cmd_tx_info.csv");
776 assert!(parse_hwsim_cmd(&tx_info_packet).is_ok());
777 }
778
779 #[test]
test_netlink_attr_response_packet()780 fn test_netlink_attr_response_packet() {
781 // Response packet may not contain transmitter, flags, tx_info, or cookie fields.
782 let response_packet: Vec<u8> =
783 include!("test_packets/hwsim_cmd_frame_response_no_transmitter_flags_tx_info.csv");
784 assert!(parse_hwsim_cmd(&response_packet).is_ok());
785
786 let response_packet2: Vec<u8> =
787 include!("test_packets/hwsim_cmd_frame_response_no_cookie.csv");
788 assert!(parse_hwsim_cmd(&response_packet2).is_ok());
789 }
790
791 #[test]
test_is_mdns_packet()792 fn test_is_mdns_packet() {
793 let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame_mdns.csv");
794 let hwsim_msg = HwsimMsg::decode_full(&packet).unwrap();
795 let mdns_frame = Frame::parse(&hwsim_msg).unwrap();
796 assert!(!mdns_frame.ieee80211.get_source().is_multicast());
797 assert!(mdns_frame.ieee80211.get_destination().is_multicast());
798 }
799
800 #[test]
test_build_tx_info_reconstruct()801 fn test_build_tx_info_reconstruct() {
802 let packet: Vec<u8> = include!("test_packets/hwsim_cmd_tx_info.csv");
803 let hwsim_msg = HwsimMsg::decode_full(&packet).unwrap();
804 assert_eq!(hwsim_msg.hwsim_hdr().hwsim_cmd, HwsimCmd::TxInfoFrame);
805
806 let new_hwsim_msg = build_tx_info(&hwsim_msg).unwrap();
807 assert_eq!(hwsim_msg, new_hwsim_msg);
808 }
809
810 #[test]
test_build_tx_info()811 fn test_build_tx_info() {
812 let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv");
813 let hwsim_msg = HwsimMsg::decode_full(&packet).unwrap();
814 let hwsim_msg_tx_info = build_tx_info(&hwsim_msg).unwrap();
815 assert_eq!(hwsim_msg_tx_info.hwsim_hdr().hwsim_cmd, HwsimCmd::TxInfoFrame);
816 }
817
build_tx_info_and_compare(frame_bytes: &Bytes, tx_info_expected_bytes: &Bytes)818 fn build_tx_info_and_compare(frame_bytes: &Bytes, tx_info_expected_bytes: &Bytes) {
819 let frame = HwsimMsg::decode_full(frame_bytes).unwrap();
820 let tx_info = build_tx_info(&frame).unwrap();
821
822 let tx_info_expected = HwsimMsg::decode_full(tx_info_expected_bytes).unwrap();
823
824 assert_eq!(tx_info.hwsim_hdr(), tx_info_expected.hwsim_hdr());
825 assert_eq!(tx_info.nl_hdr(), tx_info_expected.nl_hdr());
826
827 let attrs = HwsimAttrSet::parse(tx_info.attributes()).context("HwsimAttrSet").unwrap();
828 let attrs_expected =
829 HwsimAttrSet::parse(tx_info_expected.attributes()).context("HwsimAttrSet").unwrap();
830
831 // NOTE: TX info is different and the counts are all zeros in the TX info packet generated by WifiService.
832 // TODO: Confirm if the behavior is intended in WifiService.
833 assert_eq!(attrs.transmitter, attrs_expected.transmitter);
834 assert_eq!(attrs.flags, attrs_expected.flags);
835 assert_eq!(attrs.cookie, attrs_expected.cookie);
836 assert_eq!(attrs.signal, attrs_expected.signal);
837 }
838
839 #[test]
test_build_tx_info_and_compare()840 fn test_build_tx_info_and_compare() {
841 let frame_bytes = Bytes::from(include!("test_packets/hwsim_cmd_frame_request.csv"));
842 let tx_info_expected_bytes =
843 Bytes::from(include!("test_packets/hwsim_cmd_tx_info_response.csv"));
844 build_tx_info_and_compare(&frame_bytes, &tx_info_expected_bytes);
845 }
846
847 #[test]
test_build_tx_info_and_compare_mdns()848 fn test_build_tx_info_and_compare_mdns() {
849 let frame_bytes = Bytes::from(include!("test_packets/hwsim_cmd_frame_request_mdns.csv"));
850 let tx_info_expected_bytes =
851 Bytes::from(include!("test_packets/hwsim_cmd_tx_info_response_mdns.csv"));
852 build_tx_info_and_compare(&frame_bytes, &tx_info_expected_bytes);
853 }
854 }
855