From 185a473bac6f9a3586582b9ab7916c77615f3239 Mon Sep 17 00:00:00 2001 From: Chiara Seim Date: Thu, 31 Oct 2024 14:27:05 -0700 Subject: [PATCH] Fix missing wifi device data Getting wifi iface names from 'uci show wireless' is no longer an option since these do not always get saved to config- and routers that were missing hard coded ifname config were not populating wifi devices to report to ops. Instead, 'iw dev' lists all interfaces for wifi and those ifnames can be more reliably accessed from there. --- althea_kernel_interface/src/hardware_info.rs | 103 ++++++++++++------- althea_kernel_interface/src/lib.rs | 2 + 2 files changed, 68 insertions(+), 37 deletions(-) diff --git a/althea_kernel_interface/src/hardware_info.rs b/althea_kernel_interface/src/hardware_info.rs index 79a0b2e81..9b8897773 100644 --- a/althea_kernel_interface/src/hardware_info.rs +++ b/althea_kernel_interface/src/hardware_info.rs @@ -1,7 +1,5 @@ use crate::file_io::get_lines; -use crate::is_openwrt::is_openwrt; use crate::manipulate_uci::get_uci_var; -use crate::run_command; use crate::KernelInterfaceError as Error; use althea_types::extract_wifi_station_data; use althea_types::extract_wifi_survey_data; @@ -19,7 +17,6 @@ use std::io::BufRead; use std::io::BufReader; use std::process::Command; use std::process::Stdio; -use std::str::from_utf8; use std::time::Duration; /// Gets the load average and memory of the system from /proc should be plenty @@ -405,44 +402,42 @@ fn get_conntrack_info() -> Option { /// Device names are in the form wlan0, wlan1 etc fn parse_wifi_device_names() -> Result, Error> { - // We parse /etc/config/wireless which is an openwrt config. We return an error if not openwrt - if is_openwrt() { - let mut ret = Vec::new(); - - let lines = run_command("uci", &["show", "wireless"])?; - let lines: Vec<&str> = from_utf8(&lines.stdout)?.lines().collect(); - - // trying to get lines 'wireless.default_radio1.ifname='wlan1'' - for line in lines { - if line.contains("wireless.default_radio") && line.contains("device") { - let name = match line.split('=').collect::>().last() { - Some(a) => *a, - None => { - error!("Cannot parse wifi string {}", line); - continue; - } - }; - let name = name.replace('\'', ""); - ret.push(name) + // Call iw dev to get a list of wifi interfaces + let res = Command::new("iw") + .args(["dev"]) + .stdout(Stdio::piped()) + .output(); + match res { + Ok(a) => match String::from_utf8(a.stdout) { + Ok(a) => Ok(extract_wifi_ifnames(&a)), + Err(e) => { + error!("Unable to parse iw dev output {:?}", e); + Err(Error::FromUtf8Error) } - } - Ok(ret) - } else { - // Fallback to /proc/ parsing if no openwrt - let mut ret = Vec::new(); - let path = "/proc/net/wireless"; - let lines = get_lines(path)?; - for line in lines { - if line.contains(':') { - let name: Vec<&str> = line.split(':').collect(); - let name = name[0]; - let name = name.replace(' ', ""); - ret.push(name.to_string()); + }, + Err(e) => Err(Error::ParseError(e.to_string())), + } +} + +fn extract_wifi_ifnames(dev_output: &str) -> Vec { + let mut ret: Vec = vec![]; + + // we are looking for the line "Interface [ifname]" + let mut iter = dev_output.split_ascii_whitespace(); + loop { + let to_struct = iter.next(); + if let Some(to_struct) = to_struct { + if to_struct == "Interface" { + let ifname = iter.next(); + if let Some(ifname) = ifname { + ret.push(ifname.to_string()); + } } + } else { + break; } - - Ok(ret) } + ret } fn get_wifi_survey_info(dev: &str) -> Vec { @@ -708,4 +703,38 @@ mod test { .count() ); } + + #[test] + fn test_parse_wifi_device_names() { + // sample output from iw dev + let iw_dev_output = "phy#1 + Interface wlan1 + ifindex 12 + wdev 0x100000002 + addr 12:23:34:45:56:67 + ssid altheahome-5 + type AP + channel 36 (5180 MHz), width: 80 MHz, center1: 5210 MHz + txpower 23.00 dBm + multicast TXQ: + qsz-byt qsz-pkt flows drops marks overlmt hashcol tx-bytes tx-packets + 0 0 3991833 0 0 0 0 1112061710 3991837 +phy#0 + Interface wlan0 + ifindex 11 + wdev 0x2 + addr 76:65:54:43:32:21 + ssid altheahome-2.4 + type AP + channel 11 (2462 MHz), width: 20 MHz, center1: 2462 MHz + txpower 30.00 dBm + multicast TXQ: + qsz-byt qsz-pkt flows drops marks overlmt hashcol tx-bytes tx-packets + 0 0 3991759 0 0 0 3 1112047714 3991791 +"; + let res = extract_wifi_ifnames(iw_dev_output); + assert!(res.len() == 2); + assert!(res.contains(&"wlan0".to_string())); + assert!(res.contains(&"wlan1".to_string())); + } } diff --git a/althea_kernel_interface/src/lib.rs b/althea_kernel_interface/src/lib.rs index 28b9c946f..7751cd392 100644 --- a/althea_kernel_interface/src/lib.rs +++ b/althea_kernel_interface/src/lib.rs @@ -84,6 +84,7 @@ pub enum KernelInterfaceError { FailedToGetSystemTime, FailedToGetSystemKernelVersion, ParseError(String), + FromUtf8Error, } impl fmt::Display for KernelInterfaceError { @@ -126,6 +127,7 @@ impl fmt::Display for KernelInterfaceError { KernelInterfaceError::FailedToGetSystemKernelVersion => { write!(f, "Failed to get system kernel version!") } + KernelInterfaceError::FromUtf8Error => write!(f, "Could not parse from utf8 output"), } } }