address ordering

This commit is contained in:
John Smith 2022-01-15 22:08:56 -05:00
parent dc9f71a683
commit 268e280914
2 changed files with 56 additions and 34 deletions

View File

@ -223,7 +223,7 @@ impl Network {
} else { } else {
inner inner
.interfaces .interfaces
.default_route_addresses() .best_addresses()
.iter() .iter()
.map(|a| SocketAddr::new(*a, from.port())) .map(|a| SocketAddr::new(*a, from.port()))
.collect() .collect()

View File

@ -96,6 +96,7 @@ pub struct InterfaceAddress {
use core::cmp::Ordering; use core::cmp::Ordering;
// less is less preferable, greater is more preferable
impl Ord for InterfaceAddress { impl Ord for InterfaceAddress {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
match (&self.if_addr, &other.if_addr) { match (&self.if_addr, &other.if_addr) {
@ -155,8 +156,30 @@ impl Ord for InterfaceAddress {
return ret; return ret;
} }
} }
(IfAddr::V4(_), IfAddr::V6(_)) => return Ordering::Less, (IfAddr::V4(a), IfAddr::V6(b)) => {
(IfAddr::V6(_), IfAddr::V4(_)) => return Ordering::Greater, // If the IPv6 address is preferred and not temporary, compare if it is global scope
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;
}
}
// Default, prefer IPv4 because many IPv6 addresses are not actually routed
return Ordering::Greater;
}
(IfAddr::V6(a), IfAddr::V4(b)) => {
// If the IPv6 address is preferred and not temporary, compare if it is global scope
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;
}
}
// Default, prefer IPv4 because many IPv6 addresses are not actually routed
return Ordering::Less;
}
} }
// stable sort // stable sort
let ret = self.if_addr.cmp(&other.if_addr); let ret = self.if_addr.cmp(&other.if_addr);
@ -193,6 +216,14 @@ impl InterfaceAddress {
} }
} }
// #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
// enum NetworkInterfaceType {
// Mobile, // Least preferable, usually metered and slow
// Unknown, // Everything else if we can't detect the type
// Wireless, // Wifi is usually free or cheap and medium speed
// Wired, // Wired is usually free or cheap and high speed
// }
#[derive(PartialEq, Eq, Clone)] #[derive(PartialEq, Eq, Clone)]
pub struct NetworkInterface { pub struct NetworkInterface {
pub name: String, pub name: String,
@ -239,36 +270,24 @@ impl NetworkInterface {
self.flags.has_default_route self.flags.has_default_route
} }
pub fn primary_ipv4(&self) -> Option<Ipv4Addr> { pub fn primary_ipv4(&self) -> Option<InterfaceAddress> {
let mut ipv4addrs: Vec<&InterfaceAddress> = self let mut ipv4addrs: Vec<&InterfaceAddress> = self
.addrs .addrs
.iter() .iter()
.filter(|a| matches!(a.if_addr(), IfAddr::V4(_))) .filter(|a| matches!(a.if_addr(), IfAddr::V4(_)))
.collect(); .collect();
ipv4addrs.sort(); ipv4addrs.sort();
ipv4addrs ipv4addrs.last().cloned().cloned()
.last()
.map(|x| match x.if_addr() {
IfAddr::V4(v4) => Some(v4.ip),
_ => None,
})
.flatten()
} }
pub fn primary_ipv6(&self) -> Option<Ipv6Addr> { pub fn primary_ipv6(&self) -> Option<InterfaceAddress> {
let mut ipv6addrs: Vec<&InterfaceAddress> = self let mut ipv6addrs: Vec<&InterfaceAddress> = self
.addrs .addrs
.iter() .iter()
.filter(|a| matches!(a.if_addr(), IfAddr::V6(_))) .filter(|a| matches!(a.if_addr(), IfAddr::V6(_)))
.collect(); .collect();
ipv6addrs.sort(); ipv6addrs.sort();
ipv6addrs ipv6addrs.last().cloned().cloned()
.last()
.map(|x| match x.if_addr() {
IfAddr::V6(v6) => Some(v6.ip),
_ => None,
})
.flatten()
} }
} }
@ -286,11 +305,7 @@ impl fmt::Debug for NetworkInterfaces {
.finish()?; .finish()?;
if f.alternate() { if f.alternate() {
writeln!(f)?; writeln!(f)?;
writeln!( writeln!(f, "// best_addresses: {:?}", self.best_addresses())?;
f,
"// default_route_addresses: {:?}",
self.default_route_addresses()
)?;
} }
Ok(()) Ok(())
} }
@ -337,18 +352,25 @@ impl NetworkInterfaces {
self.interfaces.iter() self.interfaces.iter()
} }
pub fn default_route_addresses(&self) -> Vec<IpAddr> { pub fn best_addresses(&self) -> Vec<IpAddr> {
let mut out = Vec::new(); // Reduce interfaces to their best routable ip addresses
let mut intf_addrs = Vec::new();
for intf in self.interfaces.values() { for intf in self.interfaces.values() {
if intf.is_running() && intf.has_default_route() && !intf.is_loopback() { if !intf.is_running() || !intf.has_default_route() || intf.is_loopback() {
if let Some(pipv4) = intf.primary_ipv4() { continue;
out.push(IpAddr::V4(pipv4)); }
} if let Some(pipv4) = intf.primary_ipv4() {
if let Some(pipv6) = intf.primary_ipv6() { intf_addrs.push(pipv4);
out.push(IpAddr::V6(pipv6)); }
} if let Some(pipv6) = intf.primary_ipv6() {
intf_addrs.push(pipv6);
} }
} }
out
// Sort one more time to get the best interface addresses overall
intf_addrs.sort();
// Now export just the addresses
intf_addrs.iter().map(|x| x.if_addr().ip()).collect()
} }
} }