xref: /aosp_15_r20/tools/netsim/rust/daemon/src/wireless/wifi.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
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::devices::chip::ChipIdentifier;
16 use crate::wifi::hostapd;
17 use crate::wifi::libslirp;
18 #[cfg(not(feature = "cuttlefish"))]
19 use crate::wifi::mdns_forwarder;
20 use crate::wifi::medium::Medium;
21 use crate::wireless::{packet::handle_response, WirelessAdaptor, WirelessAdaptorImpl};
22 use anyhow;
23 use bytes::Bytes;
24 use log::{info, warn};
25 use netsim_proto::config::WiFi as WiFiConfig;
26 use netsim_proto::model::Chip as ProtoChip;
27 use netsim_proto::stats::{netsim_radio_stats, NetsimRadioStats as ProtoRadioStats};
28 use protobuf::MessageField;
29 use std::sync::atomic::Ordering;
30 use std::sync::{mpsc, Arc, OnceLock};
31 use std::thread;
32 use std::time::{Duration, Instant};
33 
34 /// Parameters for creating Wifi chips
35 /// allow(dead_code) due to not being used in unit tests
36 #[allow(dead_code)]
37 pub struct CreateParams {}
38 
39 /// Wifi struct will keep track of chip_id
40 pub struct Wifi {
41     chip_id: ChipIdentifier,
42 }
43 
44 pub struct WifiManager {
45     medium: Medium,
46     tx_request: mpsc::Sender<(u32, Bytes)>,
47     slirp: libslirp::LibSlirp,
48     hostapd: Arc<hostapd::Hostapd>,
49 }
50 
51 impl WifiManager {
new( tx_request: mpsc::Sender<(u32, Bytes)>, slirp: libslirp::LibSlirp, hostapd: hostapd::Hostapd, ) -> WifiManager52     pub fn new(
53         tx_request: mpsc::Sender<(u32, Bytes)>,
54         slirp: libslirp::LibSlirp,
55         hostapd: hostapd::Hostapd,
56     ) -> WifiManager {
57         let hostapd = Arc::new(hostapd);
58         WifiManager {
59             medium: Medium::new(medium_callback, hostapd.clone()),
60             tx_request,
61             slirp,
62             hostapd,
63         }
64     }
65 
66     /// Starts background threads:
67     /// * One to handle requests from medium.
68     /// * One to handle IEEE802.3 responses from network.
69     /// * One to handle IEEE802.11 responses from hostapd.
start( &self, rx_request: mpsc::Receiver<(u32, Bytes)>, rx_ieee8023_response: mpsc::Receiver<Bytes>, rx_ieee80211_response: mpsc::Receiver<Bytes>, tx_ieee8023_response: mpsc::Sender<Bytes>, forward_host_mdns: bool, ) -> anyhow::Result<()>70     pub fn start(
71         &self,
72         rx_request: mpsc::Receiver<(u32, Bytes)>,
73         rx_ieee8023_response: mpsc::Receiver<Bytes>,
74         rx_ieee80211_response: mpsc::Receiver<Bytes>,
75         tx_ieee8023_response: mpsc::Sender<Bytes>,
76         forward_host_mdns: bool,
77     ) -> anyhow::Result<()> {
78         self.start_request_thread(rx_request)?;
79         self.start_ieee8023_response_thread(rx_ieee8023_response)?;
80         self.start_ieee80211_response_thread(rx_ieee80211_response)?;
81         if forward_host_mdns {
82             self.start_mdns_forwarder_thread(tx_ieee8023_response)?;
83         }
84         Ok(())
85     }
86 
start_request_thread(&self, rx_request: mpsc::Receiver<(u32, Bytes)>) -> anyhow::Result<()>87     fn start_request_thread(&self, rx_request: mpsc::Receiver<(u32, Bytes)>) -> anyhow::Result<()> {
88         thread::Builder::new().name("Wi-Fi HwsimMsg request".to_string()).spawn(move || {
89             const POLL_INTERVAL: Duration = Duration::from_millis(1);
90             let mut next_instant = Instant::now() + POLL_INTERVAL;
91 
92             loop {
93                 let this_instant = Instant::now();
94                 let timeout = if next_instant > this_instant {
95                     next_instant - this_instant
96                 } else {
97                     Duration::ZERO
98                 };
99                 match rx_request.recv_timeout(timeout) {
100                     Ok((chip_id, packet)) => {
101                         if let Some(processor) =
102                             get_wifi_manager().medium.get_processor(chip_id, &packet)
103                         {
104                             get_wifi_manager().medium.ack_frame(chip_id, &processor.frame);
105                             if processor.hostapd {
106                                 let ieee80211: Bytes = processor.get_ieee80211_bytes();
107                                 if let Err(err) = get_wifi_manager().hostapd.input(ieee80211) {
108                                     warn!("Failed to call hostapd input: {:?}", err);
109                                 };
110                             }
111                             if processor.network {
112                                 match processor.get_ieee80211().to_ieee8023() {
113                                     Ok(ethernet_frame) => {
114                                         get_wifi_manager().slirp.input(ethernet_frame.into())
115                                     }
116                                     Err(err) => {
117                                         warn!("Failed to convert 802.11 to 802.3: {}", err)
118                                     }
119                                 }
120                             }
121                             if processor.wmedium {
122                                 // Decrypt the frame using the sender's key and re-encrypt it using the receiver's key for peer-to-peer communication through hostapd (broadcast or unicast).
123                                 let ieee80211 = processor.get_ieee80211().clone();
124                                 get_wifi_manager().medium.queue_frame(processor.frame, ieee80211);
125                             }
126                         }
127                     }
128                     _ => {
129                         next_instant = Instant::now() + POLL_INTERVAL;
130                     }
131                 };
132             }
133         })?;
134         Ok(())
135     }
136 
137     /// Starts a dedicated thread to process IEEE 802.3 (Ethernet) responses from the network.
138     ///
139     /// This thread continuously receives IEEE 802.3 response packets from the `rx_ieee8023_response` channel
140     /// and forwards them to the Wi-Fi manager's medium.
start_ieee8023_response_thread( &self, rx_ieee8023_response: mpsc::Receiver<Bytes>, ) -> anyhow::Result<()>141     fn start_ieee8023_response_thread(
142         &self,
143         rx_ieee8023_response: mpsc::Receiver<Bytes>,
144     ) -> anyhow::Result<()> {
145         thread::Builder::new().name("Wi-Fi IEEE802.3 response".to_string()).spawn(move || {
146             for packet in rx_ieee8023_response {
147                 get_wifi_manager().medium.process_ieee8023_response(&packet);
148             }
149         })?;
150         Ok(())
151     }
152 
153     /// Starts a dedicated thread to process IEEE 802.11 responses from hostapd.
154     ///
155     /// This thread continuously receives IEEE 802.11 response packets from the hostapd response channel
156     /// and forwards them to the Wi-Fi manager's medium.
start_ieee80211_response_thread( &self, rx_ieee80211_response: mpsc::Receiver<Bytes>, ) -> anyhow::Result<()>157     fn start_ieee80211_response_thread(
158         &self,
159         rx_ieee80211_response: mpsc::Receiver<Bytes>,
160     ) -> anyhow::Result<()> {
161         thread::Builder::new().name("Wi-Fi IEEE802.11 response".to_string()).spawn(move || {
162             for packet in rx_ieee80211_response {
163                 get_wifi_manager().medium.process_ieee80211_response(&packet);
164             }
165         })?;
166         Ok(())
167     }
168 
169     #[cfg(feature = "cuttlefish")]
start_mdns_forwarder_thread( &self, _tx_ieee8023_response: mpsc::Sender<Bytes>, ) -> anyhow::Result<()>170     fn start_mdns_forwarder_thread(
171         &self,
172         _tx_ieee8023_response: mpsc::Sender<Bytes>,
173     ) -> anyhow::Result<()> {
174         Ok(())
175     }
176 
177     #[cfg(not(feature = "cuttlefish"))]
start_mdns_forwarder_thread( &self, tx_ieee8023_response: mpsc::Sender<Bytes>, ) -> anyhow::Result<()>178     fn start_mdns_forwarder_thread(
179         &self,
180         tx_ieee8023_response: mpsc::Sender<Bytes>,
181     ) -> anyhow::Result<()> {
182         info!("Start mDNS forwarder thread");
183         thread::Builder::new().name("Wi-Fi mDNS forwarder".to_string()).spawn(move || {
184             if let Err(e) = mdns_forwarder::run_mdns_forwarder(tx_ieee8023_response) {
185                 warn!("Failed to start mDNS forwarder: {}", e);
186             }
187         })?;
188         Ok(())
189     }
190 }
191 
192 // Allocator for chip identifiers.
193 static WIFI_MANAGER: OnceLock<WifiManager> = OnceLock::new();
194 
get_wifi_manager() -> &'static WifiManager195 fn get_wifi_manager() -> &'static WifiManager {
196     WIFI_MANAGER.get().expect("WifiManager not initialized")
197 }
198 
199 impl Drop for Wifi {
drop(&mut self)200     fn drop(&mut self) {
201         get_wifi_manager().medium.remove(self.chip_id.0);
202     }
203 }
204 
205 impl WirelessAdaptor for Wifi {
handle_request(&self, packet: &Bytes)206     fn handle_request(&self, packet: &Bytes) {
207         if let Err(e) = get_wifi_manager().tx_request.send((self.chip_id.0, packet.clone())) {
208             warn!("Failed wifi handle_request: {:?}", e);
209         }
210     }
211 
reset(&self)212     fn reset(&self) {
213         get_wifi_manager().medium.reset(self.chip_id.0);
214     }
215 
get(&self) -> ProtoChip216     fn get(&self) -> ProtoChip {
217         let mut chip_proto = ProtoChip::new();
218         if let Some(client) = get_wifi_manager().medium.get(self.chip_id.0) {
219             chip_proto.mut_wifi().state = Some(client.enabled.load(Ordering::Relaxed));
220             chip_proto.mut_wifi().tx_count = client.tx_count.load(Ordering::Relaxed) as i32;
221             chip_proto.mut_wifi().rx_count = client.rx_count.load(Ordering::Relaxed) as i32;
222         }
223         chip_proto
224     }
225 
patch(&self, patch: &ProtoChip)226     fn patch(&self, patch: &ProtoChip) {
227         if patch.wifi().state.is_some() {
228             get_wifi_manager().medium.set_enabled(self.chip_id.0, patch.wifi().state.unwrap());
229         }
230     }
231 
get_stats(&self, duration_secs: u64) -> Vec<ProtoRadioStats>232     fn get_stats(&self, duration_secs: u64) -> Vec<ProtoRadioStats> {
233         let mut stats_proto = ProtoRadioStats::new();
234         stats_proto.set_duration_secs(duration_secs);
235         stats_proto.set_kind(netsim_radio_stats::Kind::WIFI);
236         let chip_proto = self.get();
237         if chip_proto.has_wifi() {
238             stats_proto.set_tx_count(chip_proto.wifi().tx_count);
239             stats_proto.set_rx_count(chip_proto.wifi().rx_count);
240         }
241         vec![stats_proto]
242     }
243 }
244 
medium_callback(id: u32, packet: &Bytes)245 fn medium_callback(id: u32, packet: &Bytes) {
246     handle_response(ChipIdentifier(id), packet);
247 }
248 
249 /// Create a new Emulated Wifi Chip
250 /// allow(dead_code) due to not being used in unit tests
251 #[allow(dead_code)]
new(_params: &CreateParams, chip_id: ChipIdentifier) -> WirelessAdaptorImpl252 pub fn new(_params: &CreateParams, chip_id: ChipIdentifier) -> WirelessAdaptorImpl {
253     get_wifi_manager().medium.add(chip_id.0);
254     info!("WiFi WirelessAdaptor created chip_id: {chip_id}");
255     let wifi = Wifi { chip_id };
256     Box::new(wifi)
257 }
258 
259 /// Starts the WiFi service.
wifi_start(config: &MessageField<WiFiConfig>, forward_host_mdns: bool)260 pub fn wifi_start(config: &MessageField<WiFiConfig>, forward_host_mdns: bool) {
261     let (tx_request, rx_request) = mpsc::channel::<(u32, Bytes)>();
262     let (tx_ieee8023_response, rx_ieee8023_response) = mpsc::channel::<Bytes>();
263     let (tx_ieee80211_response, rx_ieee80211_response) = mpsc::channel::<Bytes>();
264     let tx_ieee8023_response_clone = tx_ieee8023_response.clone();
265     let wifi_config = config.clone().unwrap_or_default();
266     let slirp_opt = wifi_config.slirp_options.as_ref().unwrap_or_default().clone();
267     let slirp = libslirp::slirp_run(slirp_opt, tx_ieee8023_response_clone)
268         .map_err(|e| warn!("Failed to run libslirp. {e}"))
269         .unwrap();
270 
271     let hostapd_opt = wifi_config.hostapd_options.as_ref().unwrap_or_default().clone();
272     let hostapd = hostapd::hostapd_run(hostapd_opt, tx_ieee80211_response)
273         .map_err(|e| warn!("Failed to run hostapd. {e}"))
274         .unwrap();
275 
276     let _ = WIFI_MANAGER.set(WifiManager::new(tx_request, slirp, hostapd));
277 
278     if let Err(e) = get_wifi_manager().start(
279         rx_request,
280         rx_ieee8023_response,
281         rx_ieee80211_response,
282         tx_ieee8023_response,
283         forward_host_mdns,
284     ) {
285         warn!("Failed to start Wi-Fi manager: {}", e);
286     }
287 }
288 
289 /// Stops the WiFi service.
wifi_stop()290 pub fn wifi_stop() {
291     // TODO: stop hostapd
292 }
293