diff --git a/.gitmodules b/.gitmodules index 644e6d96..a6695daa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "external/keyvaluedb"] path = external/keyvaluedb url = ../keyvaluedb.git +[submodule "external/netlink"] + path = external/netlink + url = git@gitlab.hackers.town:veilid/netlink.git diff --git a/Cargo.lock b/Cargo.lock index 6430c816..193a62c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2126,8 +2126,6 @@ dependencies = [ [[package]] name = "netlink-packet-core" version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8349128e95f5dabcb8a18587ad06b3ca7993e90c0c360b4a2abac0313ebce727" dependencies = [ "anyhow", "byteorder", @@ -2138,8 +2136,6 @@ dependencies = [ [[package]] name = "netlink-packet-route" version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb5d54077de7c0904111e1d19b661b8cfccbc23d9ce5b6dbcc7362721e6e552" dependencies = [ "anyhow", "bitflags", @@ -2152,8 +2148,6 @@ dependencies = [ [[package]] name = "netlink-packet-utils" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a008a56eceb0cab06739c7f37f15bda27f1147a14d0e7136e8c913b94f1441d" dependencies = [ "anyhow", "byteorder", @@ -2164,8 +2158,6 @@ dependencies = [ [[package]] name = "netlink-proto" version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "073885f70c1d54fdc6148075e8e38a5e8a28179f59de5bd0fc6277cae4fec95a" dependencies = [ "bytes 1.1.0", "futures", @@ -2178,8 +2170,6 @@ dependencies = [ [[package]] name = "netlink-sys" version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed51a4602bb956eefef0ebc15f478bf9732fa3cc706e0a37112e654f41c5b92c" dependencies = [ "async-io", "bytes 1.1.0", @@ -2938,8 +2928,6 @@ dependencies = [ [[package]] name = "rtnetlink" version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa584f57f271d3fbd9f59503b090a0410a531c8cc272143669bf136c62ef409d" dependencies = [ "async-global-executor", "futures", diff --git a/Cargo.toml b/Cargo.toml index 46c2cff0..b6f10cc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,19 @@ exclude = [ "./external/cursive" ] cursive = { path = "./external/cursive/cursive" } cursive_core = { path = "./external/cursive/cursive-core" } +netlink-sys = { path = "./external/netlink/netlink-sys" } +netlink-packet-core = { path = "./external/netlink/netlink-packet-core" } +netlink-packet-utils = { path = "./external/netlink/netlink-packet-utils" } +#netlink-packet-generic = { path = "./external/netlink/netlink-packet-generic" } +netlink-packet-route = { path = "./external/netlink/netlink-packet-route" } +#netlink-packet-audit = { path = "./external/netlink/netlink-packet-audit" } +#netlink-packet-sock-diag = { path = "./external/netlink/netlink-packet-sock-diag" } +netlink-proto = { path = "./external/netlink/netlink-proto" } +#genetlink = { path = "./external/netlink/genetlink" } +rtnetlink = { path = "./external/netlink/rtnetlink" } +#audit = { path = "./external/netlink/audit" } +#ethtool = { path = "./external/netlink/ethtool" } + [profile.release] opt-level = "s" lto = true diff --git a/external/netlink b/external/netlink new file mode 160000 index 00000000..bf542d21 --- /dev/null +++ b/external/netlink @@ -0,0 +1 @@ +Subproject commit bf542d210ec3c3f1c359b49036dc938ddfb3fdf9 diff --git a/veilid-core/src/intf/native/utils/network_interfaces/mod.rs b/veilid-core/src/intf/native/utils/network_interfaces/mod.rs index 88b35167..c1ce3541 100644 --- a/veilid-core/src/intf/native/utils/network_interfaces/mod.rs +++ b/veilid-core/src/intf/native/utils/network_interfaces/mod.rs @@ -79,8 +79,10 @@ pub struct InterfaceFlags { /// Some of the flags associated with an address. #[derive(Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy)] pub struct AddressFlags { - pub is_temporary: bool, + // common flags pub is_dynamic: bool, + // ipv6 flags + pub is_temporary: bool, pub is_deprecated: bool, } @@ -90,6 +92,84 @@ pub struct InterfaceAddress { 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)) => { + // global scope addresses are better + let ret = ipv4addr_is_global(&a.ip).cmp(&ipv4addr_is_global(&b.ip)); + if ret != Ordering::Equal { + return ret; + } + // local scope addresses are better + let ret = ipv4addr_is_private(&a.ip).cmp(&ipv4addr_is_private(&b.ip)); + if ret != Ordering::Equal { + return ret; + } + // non-dynamic addresses are better + let ret = (!self.flags.is_dynamic).cmp(&!other.flags.is_dynamic); + if ret != Ordering::Equal { + return ret; + } + } + (IfAddr::V6(a), IfAddr::V6(b)) => { + // non-deprecated addresses are better + let ret = (!self.flags.is_deprecated).cmp(&!other.flags.is_deprecated); + if ret != Ordering::Equal { + return ret; + } + // non-temporary address are better + let ret = (!self.flags.is_temporary).cmp(&!other.flags.is_temporary); + if ret != Ordering::Equal { + return ret; + } + // global scope addresses are better + let ret = ipv6addr_is_global(&a.ip).cmp(&ipv6addr_is_global(&b.ip)); + if ret != Ordering::Equal { + return ret; + } + // unique local unicast addresses are better + let ret = ipv6addr_is_unique_local(&a.ip).cmp(&ipv6addr_is_unique_local(&b.ip)); + if ret != Ordering::Equal { + return ret; + } + // unicast site local addresses are better + let ret = ipv6addr_is_unicast_site_local(&a.ip) + .cmp(&ipv6addr_is_unicast_site_local(&b.ip)); + if ret != Ordering::Equal { + return ret; + } + // unicast link local addresses are better + let ret = ipv6addr_is_unicast_link_local(&a.ip) + .cmp(&ipv6addr_is_unicast_link_local(&b.ip)); + if ret != Ordering::Equal { + return ret; + } + // non-dynamic addresses are better + let ret = (!self.flags.is_dynamic).cmp(&!other.flags.is_dynamic); + if ret != Ordering::Equal { + return ret; + } + } + (IfAddr::V4(_), IfAddr::V6(_)) => return Ordering::Less, + (IfAddr::V6(_), IfAddr::V4(_)) => return Ordering::Greater, + } + // stable sort + 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 { + Some(self.cmp(other)) + } +} + #[allow(dead_code)] impl InterfaceAddress { pub fn new(if_addr: IfAddr, flags: AddressFlags) -> Self { @@ -158,33 +238,35 @@ impl NetworkInterface { } pub fn primary_ipv4(&self) -> Option { - // see if we have a non-dynamic address to use first - let mut best_dynamic: Option = None; - for x in self.addrs.iter() { - if let IfAddr::V4(a) = x.if_addr() { - if !x.is_dynamic() { - return Some(a.ip); - } else if best_dynamic.is_none() { - best_dynamic = Some(a.ip); - } - } - } - best_dynamic + let mut ipv4addrs: Vec<&InterfaceAddress> = self + .addrs + .iter() + .filter(|a| matches!(a.if_addr(), IfAddr::V4(_))) + .collect(); + ipv4addrs.sort(); + ipv4addrs + .last() + .map(|x| match x.if_addr() { + IfAddr::V4(v4) => Some(v4.ip), + _ => None, + }) + .flatten() } + pub fn primary_ipv6(&self) -> Option { - let mut best_dynamic: Option = None; - for x in self.addrs.iter() { - if let IfAddr::V6(a) = x.if_addr() { - if x.is_temporary() || x.is_deprecated() { - if !x.is_dynamic() { - return Some(a.ip); - } else if best_dynamic.is_none() { - best_dynamic = Some(a.ip); - } - } - } - } - best_dynamic + let mut ipv6addrs: Vec<&InterfaceAddress> = self + .addrs + .iter() + .filter(|a| matches!(a.if_addr(), IfAddr::V6(_))) + .collect(); + ipv6addrs.sort(); + ipv6addrs + .last() + .map(|x| match x.if_addr() { + IfAddr::V6(v6) => Some(v6.ip), + _ => None, + }) + .flatten() } } diff --git a/veilid-core/src/intf/native/utils/network_interfaces/netlink.rs b/veilid-core/src/intf/native/utils/network_interfaces/netlink.rs index 2d47a29a..a99a239c 100644 --- a/veilid-core/src/intf/native/utils/network_interfaces/netlink.rs +++ b/veilid-core/src/intf/native/utils/network_interfaces/netlink.rs @@ -4,11 +4,13 @@ use alloc::collections::btree_map::Entry; use futures_util::stream::TryStreamExt; use ifstructs::ifreq; use libc::{ - close, if_indextoname, ioctl, socket, IFA_F_DADFAILED, IFA_F_DEPRECATED, IFA_F_PERMANENT, - IFA_F_TEMPORARY, IFA_F_TENTATIVE, IFF_LOOPBACK, IFF_RUNNING, IF_NAMESIZE, SIOCGIFFLAGS, + close, if_indextoname, ioctl, socket, IFF_LOOPBACK, IFF_RUNNING, IF_NAMESIZE, SIOCGIFFLAGS, SOCK_DGRAM, }; -use rtnetlink::packet::{nlas::address::Nla, AddressMessage, AF_INET, AF_INET6}; +use rtnetlink::packet::{ + nlas::address::Nla, AddressMessage, AF_INET, AF_INET6, IFA_F_DADFAILED, IFA_F_DEPRECATED, + IFA_F_PERMANENT, IFA_F_TEMPORARY, IFA_F_TENTATIVE, +}; use rtnetlink::{new_connection_with_socket, sys::SmolSocket, Handle, IpVersion}; use std::convert::TryInto; use std::ffi::CStr; @@ -18,9 +20,18 @@ use tools::*; fn get_interface_name(index: u32) -> Result { let mut ifnamebuf = [0u8; (IF_NAMESIZE + 1)]; - if unsafe { if_indextoname(index, ifnamebuf.as_mut_ptr() as *mut i8) }.is_null() { - return Err("if_indextoname returned null".to_owned()); + cfg_if! { + if #[cfg(all(target_os = "android", target_arch = "aarch64"))] { + if unsafe { if_indextoname(index, ifnamebuf.as_mut_ptr()) }.is_null() { + return Err("if_indextoname returned null".to_owned()); + } + } else { + if unsafe { if_indextoname(index, ifnamebuf.as_mut_ptr() as *mut i8) }.is_null() { + return Err("if_indextoname returned null".to_owned()); + } + } } + let ifnamebuflen = ifnamebuf .iter() .position(|c| *c == 0u8) @@ -95,7 +106,13 @@ impl PlatformSupportNetlink { return Err(io::Error::last_os_error()).map_err(map_to_string); } - let res = unsafe { ioctl(sock, SIOCGIFFLAGS, &mut req) }; + cfg_if! { + if #[cfg(target_os = "android")] { + let res = unsafe { ioctl(sock, SIOCGIFFLAGS as i32, &mut req) }; + } else { + let res = unsafe { ioctl(sock, SIOCGIFFLAGS, &mut req) }; + } + } unsafe { close(sock) }; if res < 0 { return Err(io::Error::last_os_error()).map_err(map_to_string);