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