mod apple;
mod netlink;
mod sockaddr_tools;
mod tools;
mod windows;
use crate::*;
cfg_if::cfg_if! {
if #[cfg(any(target_os = "linux", target_os = "android"))] {
use self::netlink::PlatformSupportNetlink as PlatformSupport;
} else if #[cfg(target_os = "windows")] {
use self::windows::PlatformSupportWindows as PlatformSupport;
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
use self::apple::PlatformSupportApple as PlatformSupport;
} else {
compile_error!("No network interfaces support for this platform!");
}
}
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone)]
pub enum IfAddr {
V4(Ifv4Addr),
V6(Ifv6Addr),
}
#[allow(dead_code)]
impl IfAddr {
pub fn ip(&self) -> IpAddr {
match *self {
IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.ip),
IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.ip),
}
}
pub fn netmask(&self) -> IpAddr {
match *self {
IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.netmask),
IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.netmask),
}
}
pub fn broadcast(&self) -> Option<IpAddr> {
match *self {
IfAddr::V4(ref ifv4_addr) => ifv4_addr.broadcast.map(IpAddr::V4),
IfAddr::V6(ref ifv6_addr) => ifv6_addr.broadcast.map(IpAddr::V6),
}
}
}
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone)]
pub struct Ifv4Addr {
pub ip: Ipv4Addr,
pub netmask: Ipv4Addr,
pub broadcast: Option<Ipv4Addr>,
}
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone)]
pub struct Ifv6Addr {
pub ip: Ipv6Addr,
pub netmask: Ipv6Addr,
pub broadcast: Option<Ipv6Addr>,
}
#[derive(Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy)]
pub struct InterfaceFlags {
pub is_loopback: bool,
pub is_running: bool,
pub is_point_to_point: bool,
pub has_default_route: bool,
}
#[derive(Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy)]
pub struct AddressFlags {
pub is_dynamic: bool,
pub is_temporary: bool,
pub is_preferred: bool,
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct InterfaceAddress {
if_addr: IfAddr,
flags: AddressFlags,
}
use core::cmp::Ordering;
impl Ord for InterfaceAddress {
fn cmp(&self, other: &Self) -> Ordering {
match (&self.if_addr, &other.if_addr) {
(IfAddr::V4(a), IfAddr::V4(b)) => {
let ret = ipv4addr_is_global(&a.ip).cmp(&ipv4addr_is_global(&b.ip));
if ret != Ordering::Equal {
return ret;
}
let ret = ipv4addr_is_private(&a.ip).cmp(&ipv4addr_is_private(&b.ip));
if ret != Ordering::Equal {
return ret;
}
let ret = (!self.flags.is_dynamic).cmp(&!other.flags.is_dynamic);
if ret != Ordering::Equal {
return ret;
}
}
(IfAddr::V6(a), IfAddr::V6(b)) => {
let ret = self.flags.is_preferred.cmp(&other.flags.is_preferred);
if ret != Ordering::Equal {
return ret;
}
let ret = (!self.flags.is_temporary).cmp(&!other.flags.is_temporary);
if ret != Ordering::Equal {
return ret;
}
let ret = ipv6addr_is_global(&a.ip).cmp(&ipv6addr_is_global(&b.ip));
if ret != Ordering::Equal {
return ret;
}
let ret = ipv6addr_is_unique_local(&a.ip).cmp(&ipv6addr_is_unique_local(&b.ip));
if ret != Ordering::Equal {
return ret;
}
let ret = ipv6addr_is_unicast_site_local(&a.ip)
.cmp(&ipv6addr_is_unicast_site_local(&b.ip));
if ret != Ordering::Equal {
return ret;
}
let ret = ipv6addr_is_unicast_link_local(&a.ip)
.cmp(&ipv6addr_is_unicast_link_local(&b.ip));
if ret != Ordering::Equal {
return ret;
}
let ret = (!self.flags.is_dynamic).cmp(&!other.flags.is_dynamic);
if ret != Ordering::Equal {
return ret;
}
}
(IfAddr::V4(a), IfAddr::V6(b)) => {
if other.flags.is_preferred && !other.flags.is_temporary {
let ret = ipv4addr_is_global(&a.ip).cmp(&ipv6addr_is_global(&b.ip));
if ret != Ordering::Equal {
return ret;
}
}
return Ordering::Greater;
}
(IfAddr::V6(a), IfAddr::V4(b)) => {
if self.flags.is_preferred && !self.flags.is_temporary {
let ret = ipv6addr_is_global(&a.ip).cmp(&ipv4addr_is_global(&b.ip));
if ret != Ordering::Equal {
return ret;
}
}
return Ordering::Less;
}
}
let ret = self.if_addr.cmp(&other.if_addr);
if ret != Ordering::Equal {
return ret;
}
self.flags.cmp(&other.flags)
}
}
impl PartialOrd for InterfaceAddress {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[allow(dead_code)]
impl InterfaceAddress {
pub fn new(if_addr: IfAddr, flags: AddressFlags) -> Self {
Self { if_addr, flags }
}
pub fn if_addr(&self) -> &IfAddr {
&self.if_addr
}
pub fn is_temporary(&self) -> bool {
self.flags.is_temporary
}
pub fn is_dynamic(&self) -> bool {
self.flags.is_dynamic
}
pub fn is_preferred(&self) -> bool {
self.flags.is_preferred
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct NetworkInterface {
pub name: String,
pub flags: InterfaceFlags,
pub addrs: Vec<InterfaceAddress>,
}
impl fmt::Debug for NetworkInterface {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NetworkInterface")
.field("name", &self.name)
.field("flags", &self.flags)
.field("addrs", &self.addrs)
.finish()?;
if f.alternate() {
writeln!(f)?;
writeln!(f, "// primary_ipv4: {:?}", self.primary_ipv4())?;
writeln!(f, "// primary_ipv6: {:?}", self.primary_ipv6())?;
}
Ok(())
}
}
#[allow(dead_code)]
impl NetworkInterface {
pub fn new(name: String, flags: InterfaceFlags) -> Self {
Self {
name,
flags,
addrs: Vec::new(),
}
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn is_loopback(&self) -> bool {
self.flags.is_loopback
}
pub fn is_point_to_point(&self) -> bool {
self.flags.is_point_to_point
}
pub fn is_running(&self) -> bool {
self.flags.is_running
}
pub fn has_default_route(&self) -> bool {
self.flags.has_default_route
}
pub fn primary_ipv4(&self) -> Option<InterfaceAddress> {
let mut ipv4addrs: Vec<&InterfaceAddress> = self
.addrs
.iter()
.filter(|a| matches!(a.if_addr(), IfAddr::V4(_)))
.collect();
ipv4addrs.sort();
ipv4addrs.last().cloned().cloned()
}
pub fn primary_ipv6(&self) -> Option<InterfaceAddress> {
let mut ipv6addrs: Vec<&InterfaceAddress> = self
.addrs
.iter()
.filter(|a| matches!(a.if_addr(), IfAddr::V6(_)))
.collect();
ipv6addrs.sort();
ipv6addrs.last().cloned().cloned()
}
}
pub struct NetworkInterfacesInner {
valid: bool,
interfaces: BTreeMap<String, NetworkInterface>,
interface_address_cache: Vec<IpAddr>,
}
#[derive(Clone)]
pub struct NetworkInterfaces {
inner: Arc<Mutex<NetworkInterfacesInner>>,
}
impl fmt::Debug for NetworkInterfaces {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let inner = self.inner.lock();
f.debug_struct("NetworkInterfaces")
.field("valid", &inner.valid)
.field("interfaces", &inner.interfaces)
.finish()?;
if f.alternate() {
writeln!(f)?;
writeln!(
f,
"// stable_addresses: {:?}",
inner.interface_address_cache
)?;
}
Ok(())
}
}
impl Default for NetworkInterfaces {
fn default() -> Self {
Self::new()
}
}
impl NetworkInterfaces {
pub fn new() -> Self {
Self {
inner: Arc::new(Mutex::new(NetworkInterfacesInner {
valid: false,
interfaces: BTreeMap::new(),
interface_address_cache: Vec::new(),
})),
}
}
pub fn is_valid(&self) -> bool {
let inner = self.inner.lock();
inner.valid
}
pub fn clear(&self) {
let mut inner = self.inner.lock();
inner.interfaces.clear();
inner.interface_address_cache.clear();
inner.valid = false;
}
pub async fn refresh(&self) -> std::io::Result<bool> {
let mut last_interfaces = {
let mut last_interfaces = BTreeMap::<String, NetworkInterface>::new();
let mut platform_support = PlatformSupport::new();
platform_support
.get_interfaces(&mut last_interfaces)
.await?;
last_interfaces
};
let mut inner = self.inner.lock();
core::mem::swap(&mut inner.interfaces, &mut last_interfaces);
inner.valid = true;
if last_interfaces != inner.interfaces {
let old_stable_addresses = inner.interface_address_cache.clone();
Self::cache_stable_addresses(&mut inner);
if old_stable_addresses != inner.interface_address_cache {
debug!(
"Network interface addresses changed: \nFrom: {:?}\n To: {:?}\n",
old_stable_addresses, inner.interface_address_cache
);
return Ok(true);
}
}
Ok(false)
}
pub fn with_interfaces<F, R>(&self, f: F) -> R
where
F: FnOnce(&BTreeMap<String, NetworkInterface>) -> R,
{
let inner = self.inner.lock();
f(&inner.interfaces)
}
pub fn stable_addresses(&self) -> Vec<IpAddr> {
let inner = self.inner.lock();
inner.interface_address_cache.clone()
}
fn cache_stable_addresses(inner: &mut NetworkInterfacesInner) {
let mut intf_addrs = Vec::new();
for intf in inner.interfaces.values() {
if !intf.is_running()
|| !intf.has_default_route()
|| intf.is_loopback()
|| intf.is_point_to_point()
{
continue;
}
if let Some(pipv4) = intf.primary_ipv4() {
if !pipv4.is_temporary() {
intf_addrs.push(pipv4);
}
}
if let Some(pipv6) = intf.primary_ipv6() {
if !pipv6.is_temporary() {
intf_addrs.push(pipv6);
}
}
}
intf_addrs.sort();
inner.interface_address_cache = intf_addrs.iter().map(|x| x.if_addr().ip()).collect()
}
}