fix(openwrt): use iw phy interface add for scan when no UCI wifi-iface exists
wifi up does nothing without a wifi-iface section in UCI (common on fresh flash). Instead, create a temporary managed interface directly on phy0 via nl80211 (iw phy phy0 interface add scan0 type managed), scan on it, then delete it. No netifd/UCI involvement needed for scanning. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9dc2343b60
commit
5ab569f150
@ -10,42 +10,46 @@ pub struct ScannedNetwork {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn scan_networks(router: &Router) -> Result<Vec<ScannedNetwork>> {
|
pub fn scan_networks(router: &Router) -> Result<Vec<ScannedNetwork>> {
|
||||||
let iface = find_wireless_iface(router)?;
|
let (iface, temp) = find_wireless_iface(router)?;
|
||||||
let output = router.run_ok(&format!("iwinfo {} scan 2>&1", iface))?;
|
let output = router.run_ok(&format!("iwinfo {} scan 2>&1", iface))?;
|
||||||
|
if temp {
|
||||||
|
let _ = router.run(&format!("iw dev {} del 2>/dev/null", iface));
|
||||||
|
}
|
||||||
if output.contains("No scan results") || output.trim().is_empty() {
|
if output.contains("No scan results") || output.trim().is_empty() {
|
||||||
return Ok(vec![]);
|
return Ok(vec![]);
|
||||||
}
|
}
|
||||||
parse_iwinfo_scan(&output)
|
parse_iwinfo_scan(&output)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_wireless_iface(router: &Router) -> Result<String> {
|
/// Returns `(interface_name, is_temporary)`.
|
||||||
// Fast path: interface already up (radio was previously enabled)
|
/// If no interface exists, creates a temporary managed one directly on the PHY
|
||||||
|
/// so we can scan without needing any UCI wifi-iface sections.
|
||||||
|
fn find_wireless_iface(router: &Router) -> Result<(String, bool)> {
|
||||||
|
// Fast path: an interface already exists (radio was enabled previously)
|
||||||
let (out, _) = router.run("iw dev 2>/dev/null | awk '/Interface/{print $2}' | head -1")?;
|
let (out, _) = router.run("iw dev 2>/dev/null | awk '/Interface/{print $2}' | head -1")?;
|
||||||
if !out.trim().is_empty() {
|
if !out.trim().is_empty() {
|
||||||
return Ok(out.trim().to_string());
|
return Ok((out.trim().to_string(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// No interface yet — common on a freshly-flashed OpenWrt where radio0 is
|
// Find the phy — if this is empty the device has no WiFi hardware at all
|
||||||
// disabled by default. Verify the radio PHY exists at all.
|
|
||||||
let (phy_out, _) = router.run("ls /sys/class/ieee80211/ 2>/dev/null | head -1")?;
|
let (phy_out, _) = router.run("ls /sys/class/ieee80211/ 2>/dev/null | head -1")?;
|
||||||
if phy_out.trim().is_empty() {
|
let phy = phy_out.trim().to_string();
|
||||||
|
if phy.is_empty() {
|
||||||
anyhow::bail!("No wireless radio found on this router");
|
anyhow::bail!("No wireless radio found on this router");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable radio0 and bring wifi up so netifd creates the virtual interface.
|
// Create a temporary managed interface directly on the PHY. This bypasses
|
||||||
tracing::info!("[{}] Radio present but no interface — enabling radio0 and running wifi up", router.host);
|
// netifd entirely so it works even when there are no wifi-iface sections in
|
||||||
router.run_ok("uci set wireless.radio0.disabled=0 && uci commit wireless && wifi up 2>&1")?;
|
// UCI (common on a freshly-flashed device).
|
||||||
|
tracing::info!("[{}] Creating temporary scan interface on {}", router.host, phy);
|
||||||
|
// Remove any stale scan0 from a previous attempt, then add fresh
|
||||||
|
let _ = router.run("iw dev scan0 del 2>/dev/null");
|
||||||
|
router.run_ok(&format!(
|
||||||
|
"iw phy {} interface add scan0 type managed 2>&1 && ip link set scan0 up 2>&1",
|
||||||
|
phy
|
||||||
|
))?;
|
||||||
|
|
||||||
// Wait up to 8s for netifd to create the interface (it's asynchronous)
|
Ok(("scan0".to_string(), true))
|
||||||
for _ in 0..8 {
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
|
||||||
let (out2, _) = router.run("iw dev 2>/dev/null | awk '/Interface/{print $2}' | head -1")?;
|
|
||||||
if !out2.trim().is_empty() {
|
|
||||||
return Ok(out2.trim().to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
anyhow::bail!("WiFi radio enabled but no interface appeared — check OpenWrt wireless config")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_iwinfo_scan(output: &str) -> Result<Vec<ScannedNetwork>> {
|
fn parse_iwinfo_scan(output: &str) -> Result<Vec<ScannedNetwork>> {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user