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}