#[cfg(not(wasm_browser))]
use std::net::IpAddr;
use std::net::Ipv6Addr;
#[cfg(not(wasm_browser))]
const IFF_UP: u32 = 0x1;
#[cfg(not(wasm_browser))]
const IFF_LOOPBACK: u32 = 0x8;
#[cfg(not(wasm_browser))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LocalAddresses {
pub loopback: Vec<IpAddr>,
pub regular: Vec<IpAddr>,
}
#[cfg(not(wasm_browser))]
impl Default for LocalAddresses {
fn default() -> Self {
Self::new()
}
}
#[cfg(not(wasm_browser))]
impl LocalAddresses {
pub fn new() -> Self {
let ifaces = netdev::interface::get_interfaces();
Self::from_raw_interfaces(&ifaces)
}
pub(crate) fn from_raw_interfaces(ifaces: &[netdev::Interface]) -> Self {
let mut loopback = Vec::new();
let mut regular4 = Vec::new();
let mut regular6 = Vec::new();
let mut linklocal4 = Vec::new();
let mut ula6 = Vec::new();
for iface in ifaces {
if !is_up(iface) {
continue;
}
let ifc_is_loopback = is_loopback(iface);
let addrs = iface
.ipv4
.iter()
.map(|a| IpAddr::V4(a.addr()))
.chain(iface.ipv6.iter().map(|a| IpAddr::V6(a.addr())));
for ip in addrs {
let ip = ip.to_canonical();
if ip.is_loopback() || ifc_is_loopback {
loopback.push(ip);
} else if is_link_local(ip) {
if ip.is_ipv4() {
linklocal4.push(ip);
}
} else if ip.is_ipv6() && is_private(&ip) {
ula6.push(ip);
} else if ip.is_ipv4() {
regular4.push(ip);
} else {
regular6.push(ip);
}
}
}
if regular4.is_empty() && regular6.is_empty() {
regular4 = linklocal4;
regular6 = ula6;
}
let mut regular = regular4;
regular.extend(regular6);
regular.sort();
loopback.sort();
LocalAddresses { loopback, regular }
}
}
#[cfg(not(wasm_browser))]
pub(crate) const fn is_up(interface: &netdev::Interface) -> bool {
interface.flags & IFF_UP != 0
}
#[cfg(not(wasm_browser))]
pub(crate) const fn is_loopback(interface: &netdev::Interface) -> bool {
interface.flags & IFF_LOOPBACK != 0
}
#[cfg(not(wasm_browser))]
pub(crate) fn is_private(ip: &IpAddr) -> bool {
match ip {
IpAddr::V4(ip) => {
let octets = ip.octets();
octets[0] == 10
|| (octets[0] == 172 && octets[1] & 0xf0 == 16)
|| (octets[0] == 192 && octets[1] == 168)
}
IpAddr::V6(ip) => is_private_v6(ip),
}
}
#[cfg(not(wasm_browser))]
pub(crate) fn is_private_v6(ip: &Ipv6Addr) -> bool {
ip.octets()[0] & 0xfe == 0xfc
}
#[cfg(not(wasm_browser))]
pub(super) fn is_link_local(ip: IpAddr) -> bool {
match ip {
IpAddr::V4(ip) => ip.is_link_local(),
IpAddr::V6(ip) => is_unicast_link_local(ip),
}
}
pub const fn is_unicast_link_local(addr: Ipv6Addr) -> bool {
(addr.segments()[0] & 0xffc0) == 0xfe80
}
#[cfg(test)]
mod tests {
#[cfg(not(wasm_browser))]
#[test]
fn test_local_addresses() {
let addrs = super::LocalAddresses::new();
dbg!(&addrs);
assert!(!addrs.loopback.is_empty());
assert!(!addrs.regular.is_empty());
}
}