hermes-android/hermes-pair/src/network.rs

86 lines
2.8 KiB
Rust

pub use crate::models::NetworkInterfaceInfo;
use std::net::Ipv4Addr;
pub fn is_loopback(ip: &Ipv4Addr) -> bool {
ip.is_loopback() || ip.octets()[0] == 127
}
pub fn is_link_local(ip: &Ipv4Addr) -> bool {
let octets = ip.octets();
octets[0] == 169 && octets[1] == 254
}
pub fn is_tailscale_ip(ip: &Ipv4Addr) -> bool {
let octets = ip.octets();
// CGNAT range 100.64.0.0/10 commonly used by Tailscale / WireGuard overlays
octets[0] == 100 && (octets[1] >= 64 && octets[1] <= 127)
}
pub fn is_virtual_adapter(name: &str) -> bool {
let lower = name.to_lowercase();
lower.contains("docker")
|| lower.contains("veth")
|| lower.contains("virbr")
|| lower.contains("vbox")
|| lower.contains("vmnet")
|| lower.contains("hyper-v")
|| lower.contains("wsl")
|| lower.contains("vethernet")
|| lower.contains("virtual")
|| lower.contains("tap")
|| lower.contains("tun")
}
fn interface_priority(info: &NetworkInterfaceInfo) -> u32 {
let is_priv = info.ip.is_private();
let is_ts = is_tailscale_ip(&info.ip);
match (info.is_virtual, is_priv, is_ts) {
(false, true, _) => 100, // Physical LAN (192.168.x.x, 10.x.x.x, 172.16-31.x.x)
(false, false, true) => 80, // Tailscale / Overlay
(false, false, false) => 60, // Other physical (e.g. public or custom)
(true, true, _) => 40, // Virtual LAN (e.g. WSL, Hyper-V virtual switch)
(true, false, true) => 30, // Virtual Tailscale
(true, false, false) => 20, // Other virtual
}
}
pub fn filter_and_sort_interfaces(
interfaces: impl IntoIterator<Item = NetworkInterfaceInfo>,
) -> Vec<NetworkInterfaceInfo> {
let mut filtered: Vec<NetworkInterfaceInfo> = interfaces
.into_iter()
.filter(|iface| !iface.is_loopback && !is_loopback(&iface.ip) && !is_link_local(&iface.ip))
.collect();
filtered.sort_by(|a, b| {
let prio_a = interface_priority(a);
let prio_b = interface_priority(b);
prio_b
.cmp(&prio_a)
.then_with(|| a.name.cmp(&b.name))
.then_with(|| a.ip.cmp(&b.ip))
});
filtered
}
pub fn discover_network_interfaces() -> Result<Vec<NetworkInterfaceInfo>, std::io::Error> {
let if_addrs_list = if_addrs::get_if_addrs()?;
let mut raw_interfaces = Vec::new();
for iface in if_addrs_list {
if let if_addrs::IfAddr::V4(ref v4_addr) = iface.addr {
let is_virt = is_virtual_adapter(&iface.name);
let loopback = iface.is_loopback() || is_loopback(&v4_addr.ip);
raw_interfaces.push(NetworkInterfaceInfo {
name: iface.name,
ip: v4_addr.ip,
is_loopback: loopback,
is_virtual: is_virt,
});
}
}
Ok(filter_and_sort_interfaces(raw_interfaces))
}