1use async_process::Command;
2use std::{
3 collections::HashMap,
4 fmt::Display,
5 io::BufRead,
6 process::{ExitStatus, Stdio},
7};
8
9mod ap_info;
10mod err;
11mod iw_res;
12
13pub use ap_info::{BandWidth, SecondChannel, Security, Standard, BSS};
14pub use err::IWError;
15use err::Result;
16use iw_res::*;
17
18pub struct IW {
19 can_sudo: bool,
20}
21
22#[derive(Debug, Clone, Copy)]
23pub enum Mode {
24 Managed,
25 Ibss,
26 Monitor,
27 Mesh,
28 Wds,
29}
30impl Mode {
31 fn name(&self) -> &'static str {
32 match self {
33 Mode::Managed => "managed",
34 Mode::Ibss => "ibss",
35 Mode::Monitor => "monitor",
36 Mode::Mesh => "mesh",
37 Mode::Wds => "wds",
38 }
39 }
40}
41
42#[derive(Clone)]
43pub struct Interface {
44 ifname: String,
45}
46impl Display for Interface {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 write!(f, "{}", self.ifname)
49 }
50}
51impl From<String> for Interface {
52 fn from(ifname: String) -> Self {
53 Self { ifname }
54 }
55}
56impl From<&str> for Interface {
57 fn from(value: &str) -> Self {
58 Self {
59 ifname: value.to_string(),
60 }
61 }
62}
63impl AsRef<str> for Interface {
64 fn as_ref(&self) -> &str {
65 self.ifname.as_str()
66 }
67}
68
69impl IW {
70 pub async fn new() -> Result<Self> {
71 let can_sudo = false;
73 check_command("iw").await?;
74 check_command("ifconfig").await?;
75 Ok(IW { can_sudo })
76 }
77
78 pub async fn interface_list(&self) -> Result<Vec<Interface>> {
79 let output = Command::new("sh")
80 .arg("-c")
81 .arg("iw dev | grep Interface")
82 .output()
83 .await
84 .map_err(|e| IWError::Unknown(format!("Failed to execute command: {}", e)))?;
85
86 let lines = output.stdout.lines();
87
88 let mut interfaces = Vec::new();
90 for line in lines.map_while(|i| i.ok()) {
91 if let Some(interface_name) = line.split_whitespace().nth(1) {
92 interfaces.push(interface_name.into());
93 }
94 }
95 Ok(interfaces)
96 }
97
98 fn command(&self, program: &str) -> Command {
99 if self.can_sudo {
100 let mut cmd = Command::new("sudo");
101 cmd.stdout(Stdio::inherit());
102 cmd.stdin(Stdio::inherit());
103 cmd.arg(program);
104 cmd
105 } else {
106 Command::new(program)
107 }
108 }
109
110 async fn info(&self, interface: &Interface) -> anyhow::Result<Info> {
111 let output = self
112 .command("iw")
113 .args(["dev", interface.as_ref(), "info"])
114 .output()
115 .await?;
116
117 let result = String::from_utf8_lossy(&output.stdout);
118
119 Info::parse(&result)
120 }
121
122 pub async fn freq_max_bandwidth(
123 &self,
124 interface: &Interface,
125 ) -> anyhow::Result<HashMap<i32, BandWidth>> {
126 let mut out = HashMap::new();
127 let info = self.info(interface).await?;
128 let output = self
129 .command("iw")
130 .arg("phy")
131 .arg(format!("phy{}", info.wiphy))
132 .arg("info")
133 .output()
134 .await?;
135
136 let result = String::from_utf8_lossy(&output.stdout);
137
138 let info = PhyInfo::parse(&result)?;
139
140 for band in info.bands {
141 let mut max_width = BandWidth::HT20;
142 if band.capabilities.rx_ht40_sgi {
143 max_width = BandWidth::HT40;
144 }
145 if band.vht_capabilities.mhz80 {
146 max_width = BandWidth::MHz80;
147 }
148 if band.vht_capabilities.mhz160 {
149 max_width = BandWidth::MHz160;
150 }
151
152 for freq in band.frequencies {
153 out.insert(freq, max_width);
154 }
155 }
156
157 Ok(out)
158 }
159
160 pub async fn ifdown(&self, interface: &Interface) -> Result<()> {
161 let status = self
162 .command("ifconfig")
163 .arg(interface.as_ref())
164 .arg("down")
165 .status()
166 .await?;
167
168 status.check(format!(
169 "Failed to bring down interface {}",
170 interface.as_ref()
171 ))
172 }
173 pub async fn ifup(&self, interface: &Interface) -> Result<()> {
174 let status = self
175 .command("ifconfig")
176 .arg(interface.as_ref())
177 .arg("up")
178 .status()
179 .await?;
180
181 status.check(format!(
182 "Failed to bring up interface {}",
183 interface.as_ref()
184 ))
185 }
186 pub async fn set_mode(&self, interface: &Interface, mode: Mode) -> Result<()> {
187 self.ifdown(interface).await?;
188 let status = self
189 .command("iw")
190 .arg("dev")
191 .arg(interface.as_ref())
192 .arg("set")
193 .arg("type")
194 .arg(mode.name())
195 .status()
196 .await?;
197
198 status.check(format!(
199 "Failed to set mode for interface {}",
200 interface.as_ref()
201 ))?;
202
203 self.ifup(interface).await?;
204 Ok(())
205 }
206
207 pub async fn scan(&self, interface: &Interface) -> Result<Vec<BSS>> {
208 self.set_mode(interface, Mode::Managed).await?;
209
210 let output = self
211 .command("iw")
212 .args(["dev", interface.as_ref(), "scan"])
213 .output()
214 .await?;
215
216 output.status.check(format!(
217 "scan fail: {}",
218 String::from_utf8_lossy(&output.stderr)
219 ))?;
220
221 let result = String::from_utf8_lossy(&output.stdout);
222
223 let out = BSS::parse_all(result.as_ref()).unwrap();
224
225 Ok(out)
226 }
227
228 pub async fn set_channel(
229 &self,
230 interface: &Interface,
231 channel: i32,
232 band_width: Option<BandWidth>,
233 second: Option<SecondChannel>,
234 ) -> Result<()> {
235 let mut cmd = self.command("iw");
236 let c = cmd
237 .arg("dev")
238 .arg(interface.as_ref())
239 .arg("set")
240 .arg("channel")
241 .arg(format!("{}", channel));
242
243 if let Some(b) = band_width {
244 match b {
245 BandWidth::HT40 => {
246 if let Some(second) = second {
247 c.arg(match second {
248 SecondChannel::Below => "HT40-",
249 SecondChannel::Above => "HT40+",
250 });
251 }
252 }
253 _ => {
254 c.arg(b.cmd());
255 }
256 }
257 }
258
259 let status = c.status().await?;
260
261 status.check(format!(
262 "Failed to set channel for interface {}",
263 interface.as_ref()
264 ))
265 }
266
267 pub async fn set_channel_by_bss(&self, interface: &Interface, bss: &BSS) -> Result<()> {
268 self.set_channel(interface, bss.channel, Some(bss.width), bss.second)
269 .await
270 }
271 pub async fn set_freq_by_bss(&self, interface: &Interface, bss: &BSS) -> Result<()> {
272 self.set_freq(interface, bss.freq_mhz, Some(bss.width), bss.second)
273 .await
274 }
275 pub async fn set_freq(
276 &self,
277 interface: &Interface,
278 freq_mhz: i32,
279 band_width: Option<BandWidth>,
280 second: Option<SecondChannel>,
281 ) -> Result<()> {
282 let mut cmd = self.command("iw");
283 let c = cmd
284 .arg("dev")
285 .arg(interface.as_ref())
286 .arg("set")
287 .arg("freq")
288 .arg(format!("{}", freq_mhz));
289
290 if let Some(b) = band_width {
291 match b {
292 BandWidth::HT40 => {
293 if let Some(second) = second {
294 c.arg(match second {
295 SecondChannel::Below => "HT40-",
296 SecondChannel::Above => "HT40+",
297 });
298 }
299 }
300 _ => {
301 c.arg(b.cmd());
302 }
303 }
304 }
305
306 let status = c.status().await?;
307
308 status.check(format!(
309 "Failed to set channel for interface {}",
310 interface.as_ref()
311 ))
312 }
313}
314
315async fn check_command(cmd: &str) -> Result<()> {
325 Command::new(cmd)
326 .arg("--help")
327 .output()
328 .await
329 .map_err(|e| IWError::NotSupport(format!("command [{}] fail: {:?}", cmd, e)))?;
330 Ok(())
331}
332
333trait ExitStatusExt {
334 fn check(&self, fail_msg: String) -> Result<()>;
335}
336
337impl ExitStatusExt for ExitStatus {
338 fn check(&self, fail_msg: String) -> Result<()> {
339 if !self.success() {
340 return Err(IWError::Unknown(fail_msg));
341 }
342 Ok(())
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[tokio::test]
351 async fn test_iw_ifconfig() {
352 check_command("iw").await.unwrap();
353 check_command("ifconfig").await.unwrap();
354 }
355
356 #[tokio::test]
357 async fn test_check() {
358 assert!(check_command("asfsadasd").await.is_err());
359 }
360
361 #[tokio::test]
362 async fn test_info() {
363 let iw = IW::new().await.unwrap();
364
365 let list = iw.interface_list().await.expect("无可用网卡");
366 for card in &list {
367 println!("ifname: {}", card);
368 let info = iw.info(card).await.unwrap();
369 println!("{:?}", info)
370 }
371 }
372
373 #[tokio::test]
374 async fn test_max_band() {
375 let iw = IW::new().await.unwrap();
376
377 let list = iw.interface_list().await.expect("无可用网卡");
378 for card in &list {
379 println!("ifname: {}", card);
380 let info = iw.freq_max_bandwidth(card).await.unwrap();
381 println!("{:?}", info)
382 }
383 }
384}