network interfaces work
This commit is contained in:
@@ -1061,7 +1061,9 @@ impl Network {
|
||||
info!("starting network");
|
||||
|
||||
// initialize interfaces
|
||||
self.inner.lock().interfaces.refresh()?;
|
||||
let mut interfaces = NetworkInterfaces::new();
|
||||
interfaces.refresh().await?;
|
||||
self.inner.lock().interfaces = interfaces;
|
||||
|
||||
// get protocol config
|
||||
let protocol_config = {
|
||||
|
@@ -95,8 +95,11 @@ impl TableStore {
|
||||
let dbpath = Self::get_dbpath(&inner, &table_name)?;
|
||||
let cfg = DatabaseConfig::with_columns(column_count);
|
||||
let db =
|
||||
Database::open(dbpath, cfg).map_err(|e| format!("failed to open tabledb: {}", e))?;
|
||||
|
||||
Database::open(&dbpath, cfg).map_err(|e| format!("failed to open tabledb: {}", e))?;
|
||||
info!(
|
||||
"opened table store '{}' at path '{:?}' with {} columns",
|
||||
name, dbpath, column_count
|
||||
);
|
||||
let table_db = TableDB::new(table_name.clone(), self.clone(), db);
|
||||
|
||||
inner.opened.insert(table_name, table_db.weak_inner());
|
||||
|
@@ -1,6 +1,8 @@
|
||||
mod android_get_if_addrs;
|
||||
// xxx : support for android older than API 24, if we need it someday
|
||||
//mod android_get_if_addrs;
|
||||
//pub use android_get_if_addrs::*;
|
||||
|
||||
mod get_directories;
|
||||
pub use android_get_if_addrs::*;
|
||||
pub use get_directories::*;
|
||||
|
||||
use crate::xx::*;
|
||||
|
@@ -1,108 +0,0 @@
|
||||
#[cfg(target_os = "android")]
|
||||
pub use super::android::*;
|
||||
use crate::xx::*;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
pub use if_addrs::*;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct NetworkInterface {
|
||||
name: String,
|
||||
is_loopback: bool,
|
||||
addrs: Vec<IfAddr>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl NetworkInterface {
|
||||
pub fn new(name: String, is_loopback: bool) -> Self {
|
||||
Self {
|
||||
name,
|
||||
is_loopback,
|
||||
addrs: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
pub fn is_loopback(&self) -> bool {
|
||||
self.is_loopback
|
||||
}
|
||||
pub fn primary_ipv4(&self) -> Option<Ipv4Addr> {
|
||||
for x in self.addrs.iter() {
|
||||
if let IfAddr::V4(a) = x {
|
||||
return Some(a.ip);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn primary_ipv6(&self) -> Option<Ipv6Addr> {
|
||||
for x in self.addrs.iter() {
|
||||
if let IfAddr::V6(a) = x {
|
||||
return Some(a.ip);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NetworkInterfaces {
|
||||
valid: bool,
|
||||
interfaces: BTreeMap<String, NetworkInterface>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl NetworkInterfaces {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
valid: false,
|
||||
interfaces: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.valid
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
self.interfaces.clear();
|
||||
self.valid = false;
|
||||
}
|
||||
// returns Ok(false) if refresh had no changes, Ok(true) if changes were present
|
||||
pub fn refresh(&mut self) -> Result<bool, String> {
|
||||
self.valid = false;
|
||||
|
||||
let last_interfaces = core::mem::take(&mut self.interfaces);
|
||||
|
||||
let mut intfs = match get_if_addrs() {
|
||||
Err(e) => {
|
||||
return Err(format!("failed to refresh network interfaces: {}", e));
|
||||
}
|
||||
Ok(v) => v,
|
||||
};
|
||||
intfs.sort();
|
||||
|
||||
// debug!("{} interfaces found", intfs.len());
|
||||
for intf in intfs {
|
||||
// trace!("interface {} at {}", &intf.name, &intf.addr.ip());
|
||||
let ni = match self.interfaces.get_mut(&intf.name) {
|
||||
None => {
|
||||
self.interfaces.insert(
|
||||
intf.name.clone(),
|
||||
NetworkInterface::new(intf.name.clone(), intf.is_loopback()),
|
||||
);
|
||||
self.interfaces.get_mut(&intf.name).unwrap()
|
||||
}
|
||||
Some(v) => v,
|
||||
};
|
||||
|
||||
ni.addrs.push(intf.addr.clone());
|
||||
}
|
||||
|
||||
self.valid = true;
|
||||
|
||||
Ok(last_interfaces != self.interfaces)
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.interfaces.len()
|
||||
}
|
||||
pub fn iter(&self) -> std::collections::btree_map::Iter<String, NetworkInterface> {
|
||||
self.interfaces.iter()
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
use super::*;
|
||||
|
||||
pub struct PlatformSupportApple {
|
||||
//
|
||||
}
|
||||
|
||||
impl PlatformSupportApple {
|
||||
pub fn new() -> Result<Self, String> {
|
||||
Ok(PlatformSupportApple {})
|
||||
}
|
||||
|
||||
pub async fn get_interfaces(
|
||||
&mut self,
|
||||
interfaces: &mut BTreeMap<String, NetworkInterface>,
|
||||
) -> Result<(), String> {
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
}
|
@@ -1,54 +1,8 @@
|
||||
use super::*;
|
||||
use crate::xx::*;
|
||||
pub use if_addrs::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
|
||||
use jni::objects::JValue;
|
||||
use std::io;
|
||||
|
||||
fn get_netmask_from_prefix_length_v4(out: &mut [u8; 4], mut plen: i16) {
|
||||
for n in 0..4 {
|
||||
out[n] = if plen >= 8 {
|
||||
plen -= 8;
|
||||
255u8
|
||||
} else if plen <= 0 {
|
||||
0u8
|
||||
} else {
|
||||
let v = 255u8 << (8 - plen);
|
||||
plen = 0;
|
||||
v
|
||||
}
|
||||
}
|
||||
}
|
||||
fn get_netmask_from_prefix_length_v6(out: &mut [u8; 16], mut plen: i16) {
|
||||
for n in 0..16 {
|
||||
out[n] = if plen >= 8 {
|
||||
plen -= 8;
|
||||
255u8
|
||||
} else if plen == 0 {
|
||||
0u8
|
||||
} else {
|
||||
let v = 255u8 << (8 - plen);
|
||||
plen = 0;
|
||||
v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_unsigned_4(x: [i8; 4]) -> [u8; 4] {
|
||||
let mut out: [u8; 4] = [0u8; 4];
|
||||
for i in 0..4 {
|
||||
out[i] = x[i] as u8;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn convert_to_unsigned_16(x: [i8; 16]) -> [u8; 16] {
|
||||
let mut out: [u8; 16] = [0u8; 16];
|
||||
for i in 0..16 {
|
||||
out[i] = x[i] as u8;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
macro_rules! call_method_checked {
|
||||
($env:expr, $obj:expr, $name:expr, $sig:expr, $args:expr, $kind:ident) => {
|
||||
$env.call_method($obj, $name, $sig, $args)
|
@@ -0,0 +1,232 @@
|
||||
#![allow(dead_code)]
|
||||
use super::*;
|
||||
use crate::xx::*;
|
||||
use hex::FromHex;
|
||||
use if_addrs::{IfAddr, Ifv4Addr, Ifv6Addr};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Error, ErrorKind};
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ProcNetIpv6RouteEntry {
|
||||
dest_network: Ipv6Addr,
|
||||
dest_prefix: u8,
|
||||
src_network: Ipv6Addr,
|
||||
src_prefix: u8,
|
||||
next_hop: Ipv6Addr,
|
||||
metric: u32,
|
||||
ref_count: u32,
|
||||
use_count: u32,
|
||||
flags: u32,
|
||||
intf_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ProcNetRouteEntry {
|
||||
iface: String,
|
||||
destination: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
flags: u16,
|
||||
ref_count: u32,
|
||||
use_count: u32,
|
||||
metric: u32,
|
||||
mask: Ipv4Addr,
|
||||
mtu: u32,
|
||||
window: u32,
|
||||
irtt: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PlatformSupport {
|
||||
proc_net_ipv6_route: Vec<ProcNetIpv6RouteEntry>,
|
||||
proc_net_route: Vec<ProcNetRouteEntry>,
|
||||
}
|
||||
|
||||
impl PlatformSupport {
|
||||
fn parse_proc_net_ipv6_route() -> Result<Vec<ProcNetIpv6RouteEntry>, Error> {
|
||||
let file = File::open("/proc/net/ipv6_route")?;
|
||||
let reader = BufReader::new(file);
|
||||
let mut ipv6_route: Vec<ProcNetIpv6RouteEntry> = Vec::new();
|
||||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
let line: Vec<&str> = line.split_ascii_whitespace().collect();
|
||||
if line.len() != 10 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Unexpected number of columns in /proc/net/ipv6_route",
|
||||
));
|
||||
}
|
||||
|
||||
let entry =
|
||||
ProcNetIpv6RouteEntry {
|
||||
dest_network: Ipv6Addr::from(<[u8; 16]>::from_hex(line[0]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse dest_network")
|
||||
})?),
|
||||
dest_prefix: <[u8; 1]>::from_hex(line[1]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse dest_prefix")
|
||||
})?[0],
|
||||
src_network: Ipv6Addr::from(<[u8; 16]>::from_hex(line[2]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse src_network")
|
||||
})?),
|
||||
src_prefix: <[u8; 1]>::from_hex(line[3]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse src_prefix")
|
||||
})?[0],
|
||||
next_hop: Ipv6Addr::from(<[u8; 16]>::from_hex(line[4]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse next_hop")
|
||||
})?),
|
||||
metric: u32::from_be_bytes(<[u8; 4]>::from_hex(line[5]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse metric")
|
||||
})?),
|
||||
ref_count: u32::from_be_bytes(<[u8; 4]>::from_hex(line[6]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse ref_count")
|
||||
})?),
|
||||
use_count: u32::from_be_bytes(<[u8; 4]>::from_hex(line[7]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse use_count")
|
||||
})?),
|
||||
flags: u32::from_be_bytes(<[u8; 4]>::from_hex(line[8]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse flags")
|
||||
})?),
|
||||
intf_name: String::from(line[9]),
|
||||
};
|
||||
|
||||
ipv6_route.push(entry)
|
||||
}
|
||||
|
||||
Ok(ipv6_route)
|
||||
}
|
||||
|
||||
fn parse_proc_net_route() -> Result<Vec<ProcNetRouteEntry>, Error> {
|
||||
let file = File::open("/proc/net/route")?;
|
||||
let reader = BufReader::new(file);
|
||||
let mut route: Vec<ProcNetRouteEntry> = Vec::new();
|
||||
let mut first = false;
|
||||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
let line: Vec<&str> = line.split_ascii_whitespace().collect();
|
||||
if line.len() != 11 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Unexpected number of columns in /proc/net/route",
|
||||
));
|
||||
}
|
||||
if first {
|
||||
if line
|
||||
!= [
|
||||
"Iface",
|
||||
"Destination",
|
||||
"Gateway",
|
||||
"Flags",
|
||||
"RefCnt",
|
||||
"Use",
|
||||
"Metric",
|
||||
"Mask",
|
||||
"MTU",
|
||||
"Window",
|
||||
"IRTT",
|
||||
]
|
||||
{
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
format!("Unexpected columns in /proc/net/route: {:?}", line),
|
||||
));
|
||||
}
|
||||
first = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
let entry =
|
||||
ProcNetRouteEntry {
|
||||
iface: String::from(line[0]),
|
||||
|
||||
destination: Ipv4Addr::from(u32::from_le_bytes(
|
||||
<[u8; 4]>::from_hex(line[0]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse destination")
|
||||
})?,
|
||||
)),
|
||||
gateway: Ipv4Addr::from(u32::from_le_bytes(
|
||||
<[u8; 4]>::from_hex(line[0]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse gateway")
|
||||
})?,
|
||||
)),
|
||||
flags: u16::from_be_bytes(<[u8; 2]>::from_hex(line[8]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse flags")
|
||||
})?),
|
||||
ref_count: u32::from_be_bytes(<[u8; 4]>::from_hex(line[6]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse ref_count")
|
||||
})?),
|
||||
use_count: u32::from_be_bytes(<[u8; 4]>::from_hex(line[7]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse use_count")
|
||||
})?),
|
||||
metric: u32::from_be_bytes(<[u8; 4]>::from_hex(line[5]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse metric")
|
||||
})?),
|
||||
mask: Ipv4Addr::from(u32::from_le_bytes(
|
||||
<[u8; 4]>::from_hex(line[0]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse mask")
|
||||
})?,
|
||||
)),
|
||||
mtu: u32::from_be_bytes(
|
||||
<[u8; 4]>::from_hex(line[5]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse mtu")
|
||||
})?,
|
||||
),
|
||||
window: u32::from_be_bytes(<[u8; 4]>::from_hex(line[5]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse window")
|
||||
})?),
|
||||
irtt: u32::from_be_bytes(
|
||||
<[u8; 4]>::from_hex(line[5]).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "Unable to parse irtt")
|
||||
})?,
|
||||
),
|
||||
};
|
||||
|
||||
route.push(entry)
|
||||
}
|
||||
|
||||
Ok(route)
|
||||
}
|
||||
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
// Read /proc/net/ipv6_route
|
||||
let proc_net_ipv6_route = Self::parse_proc_net_ipv6_route().unwrap_or_default();
|
||||
// Read /proc/net/route
|
||||
let proc_net_route = Self::parse_proc_net_route().unwrap_or_default();
|
||||
|
||||
trace!("proc_net_ipv6_route: {:#?}", proc_net_ipv6_route);
|
||||
trace!("proc_net_route: {:#?}", proc_net_route);
|
||||
|
||||
// At least one routing table must be available
|
||||
if proc_net_ipv6_route.is_empty() && proc_net_route.is_empty() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"No routing tables available",
|
||||
));
|
||||
}
|
||||
Ok(Self {
|
||||
proc_net_ipv6_route,
|
||||
proc_net_route,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_default_route(&self, name: &str) -> bool {
|
||||
for e in &self.proc_net_ipv6_route {
|
||||
if e.intf_name == name && e.dest_prefix == 0u8 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for e in &self.proc_net_route {
|
||||
if e.iface == name && e.mask == Ipv4Addr::new(0, 0, 0, 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_address_flags(&self, _addr: &IfAddr) -> AddressFlags {
|
||||
AddressFlags {
|
||||
is_temporary: false,
|
||||
is_dynamic: false,
|
||||
is_deprecated: false,
|
||||
}
|
||||
}
|
||||
}
|
216
veilid-core/src/intf/native/utils/network_interfaces/mod.rs
Normal file
216
veilid-core/src/intf/native/utils/network_interfaces/mod.rs
Normal file
@@ -0,0 +1,216 @@
|
||||
use crate::xx::*;
|
||||
use crate::*;
|
||||
mod tools;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(target_os = "linux", target_os = "android"))] {
|
||||
mod netlink;
|
||||
use netlink::PlatformSupportNetlink as PlatformSupport;
|
||||
} else if #[cfg(target_os = "windows")] {
|
||||
mod windows;
|
||||
use windows::PlatformSupportWindows as PlatformSupport;
|
||||
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
|
||||
mod apple;
|
||||
use 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),
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Details about the ipv4 address of an interface on this host.
|
||||
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone)]
|
||||
pub struct Ifv4Addr {
|
||||
/// The IP address of the interface.
|
||||
pub ip: Ipv4Addr,
|
||||
/// The netmask of the interface.
|
||||
pub netmask: Ipv4Addr,
|
||||
/// The broadcast address of the interface.
|
||||
pub broadcast: Option<Ipv4Addr>,
|
||||
}
|
||||
|
||||
/// Details about the ipv6 address of an interface on this host.
|
||||
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone)]
|
||||
pub struct Ifv6Addr {
|
||||
/// The IP address of the interface.
|
||||
pub ip: Ipv6Addr,
|
||||
/// The netmask of the interface.
|
||||
pub netmask: Ipv6Addr,
|
||||
/// The broadcast address of the interface.
|
||||
pub broadcast: Option<Ipv6Addr>,
|
||||
}
|
||||
|
||||
/// Some of the flags associated with an interface.
|
||||
#[derive(Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct InterfaceFlags {
|
||||
pub is_loopback: bool,
|
||||
pub is_running: bool,
|
||||
pub has_default_route: bool,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
pub is_dynamic: bool,
|
||||
pub is_deprecated: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct InterfaceAddress {
|
||||
if_addr: IfAddr,
|
||||
flags: AddressFlags,
|
||||
}
|
||||
|
||||
#[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_deprecated(&self) -> bool {
|
||||
self.flags.is_deprecated
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct NetworkInterface {
|
||||
pub name: String,
|
||||
pub flags: InterfaceFlags,
|
||||
pub addrs: Vec<InterfaceAddress>,
|
||||
}
|
||||
|
||||
#[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_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<Ipv4Addr> {
|
||||
// see if we have a non-dynamic address to use first
|
||||
let mut best_dynamic: Option<Ipv4Addr> = 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
|
||||
}
|
||||
pub fn primary_ipv6(&self) -> Option<Ipv6Addr> {
|
||||
let mut best_dynamic: Option<Ipv6Addr> = 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
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct NetworkInterfaces {
|
||||
valid: bool,
|
||||
interfaces: BTreeMap<String, NetworkInterface>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl NetworkInterfaces {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
valid: false,
|
||||
interfaces: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.valid
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
self.interfaces.clear();
|
||||
self.valid = false;
|
||||
}
|
||||
// returns Ok(false) if refresh had no changes, Ok(true) if changes were present
|
||||
pub async fn refresh(&mut self) -> Result<bool, String> {
|
||||
self.valid = false;
|
||||
|
||||
let last_interfaces = core::mem::take(&mut self.interfaces);
|
||||
|
||||
let mut platform_support = PlatformSupport::new().map_err(logthru_net!())?;
|
||||
platform_support
|
||||
.get_interfaces(&mut self.interfaces)
|
||||
.await?;
|
||||
|
||||
self.valid = true;
|
||||
|
||||
Ok(last_interfaces != self.interfaces)
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.interfaces.len()
|
||||
}
|
||||
pub fn iter(&self) -> std::collections::btree_map::Iter<String, NetworkInterface> {
|
||||
self.interfaces.iter()
|
||||
}
|
||||
}
|
288
veilid-core/src/intf/native/utils/network_interfaces/netlink.rs
Normal file
288
veilid-core/src/intf/native/utils/network_interfaces/netlink.rs
Normal file
@@ -0,0 +1,288 @@
|
||||
use super::*;
|
||||
|
||||
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,
|
||||
SOCK_DGRAM,
|
||||
};
|
||||
use rtnetlink::packet::{nlas::address::Nla, AddressMessage, AF_INET, AF_INET6};
|
||||
use rtnetlink::{new_connection_with_socket, sys::SmolSocket, Handle, IpVersion};
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::CStr;
|
||||
use std::io;
|
||||
use std::os::raw::c_int;
|
||||
use tools::*;
|
||||
|
||||
fn get_interface_name(index: u32) -> Result<String, String> {
|
||||
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());
|
||||
}
|
||||
let ifnamebuflen = ifnamebuf
|
||||
.iter()
|
||||
.position(|c| *c == 0u8)
|
||||
.ok_or_else(|| "null not found in interface name".to_owned())?;
|
||||
let ifname_str = CStr::from_bytes_with_nul(&ifnamebuf[0..=ifnamebuflen])
|
||||
.map_err(map_to_string)?
|
||||
.to_str()
|
||||
.map_err(map_to_string)?;
|
||||
Ok(ifname_str.to_owned())
|
||||
}
|
||||
|
||||
fn flags_to_address_flags(flags: u32) -> AddressFlags {
|
||||
AddressFlags {
|
||||
is_temporary: (flags & IFA_F_TEMPORARY) != 0,
|
||||
is_dynamic: (flags & IFA_F_PERMANENT) == 0,
|
||||
is_deprecated: (flags & IFA_F_DEPRECATED) != 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PlatformSupportNetlink {
|
||||
_connection_jh: intf::JoinHandle<()>,
|
||||
handle: Handle,
|
||||
default_route_interfaces: BTreeSet<u32>,
|
||||
}
|
||||
|
||||
impl PlatformSupportNetlink {
|
||||
pub fn new() -> Result<Self, String> {
|
||||
// Get the netlink connection
|
||||
let (connection, handle, _) = new_connection_with_socket::<SmolSocket>()
|
||||
.map_err(map_to_string)
|
||||
.map_err(logthru_net!(error))?;
|
||||
|
||||
// Spawn a connection handler
|
||||
let _connection_jh = intf::spawn(connection);
|
||||
|
||||
Ok(PlatformSupportNetlink {
|
||||
_connection_jh,
|
||||
handle,
|
||||
default_route_interfaces: BTreeSet::new(),
|
||||
})
|
||||
}
|
||||
|
||||
// Figure out which interfaces have default routes
|
||||
async fn refresh_default_route_interfaces(&mut self) -> Result<(), String> {
|
||||
self.default_route_interfaces.clear();
|
||||
let mut routesv4 = self.handle.route().get(IpVersion::V4).execute();
|
||||
while let Some(routev4) = routesv4.try_next().await.unwrap_or_default() {
|
||||
if let Some(index) = routev4.input_interface() {
|
||||
if let Some((dest, prefix)) = routev4.destination_prefix() {
|
||||
if prefix == 0 && dest.is_unspecified() {
|
||||
self.default_route_interfaces.insert(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut routesv6 = self.handle.route().get(IpVersion::V6).execute();
|
||||
while let Some(routev6) = routesv6.try_next().await.unwrap_or_default() {
|
||||
if let Some(index) = routev6.input_interface() {
|
||||
if let Some((dest, prefix)) = routev6.destination_prefix() {
|
||||
if prefix == 0 && dest.is_unspecified() {
|
||||
self.default_route_interfaces.insert(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_interface_flags(&self, index: u32, ifname: &str) -> Result<InterfaceFlags, String> {
|
||||
let mut req = ifreq::from_name(ifname).map_err(map_to_string)?;
|
||||
|
||||
let sock = unsafe { socket(AF_INET as i32, SOCK_DGRAM, 0) };
|
||||
if sock < 0 {
|
||||
return Err(io::Error::last_os_error()).map_err(map_to_string);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
let flags = req.get_flags() as c_int;
|
||||
|
||||
Ok(InterfaceFlags {
|
||||
is_loopback: (flags & IFF_LOOPBACK) != 0,
|
||||
is_running: (flags & IFF_RUNNING) != 0,
|
||||
has_default_route: self.default_route_interfaces.contains(&index),
|
||||
})
|
||||
}
|
||||
|
||||
fn process_address_message_v4(msg: AddressMessage) -> Option<InterfaceAddress> {
|
||||
// Get ip address
|
||||
let ip = msg.nlas.iter().find_map(|nla| {
|
||||
if let Nla::Address(a) = nla {
|
||||
let a: Option<[u8; 4]> = a.clone().try_into().ok();
|
||||
a.map(Ipv4Addr::from)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})?;
|
||||
|
||||
// get netmask
|
||||
let plen = msg.header.prefix_len as i16;
|
||||
let mut netmask = [0u8; 4];
|
||||
get_netmask_from_prefix_length_v4(&mut netmask, plen);
|
||||
let netmask = Ipv4Addr::from(netmask);
|
||||
|
||||
// get broadcast address
|
||||
let broadcast = msg.nlas.iter().find_map(|nla| {
|
||||
if let Nla::Broadcast(b) = nla {
|
||||
let b: Option<[u8; 4]> = b.clone().try_into().ok();
|
||||
b.map(Ipv4Addr::from)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
// get address flags
|
||||
let flags = msg
|
||||
.nlas
|
||||
.iter()
|
||||
.find_map(|nla| {
|
||||
if let Nla::Flags(f) = nla {
|
||||
Some(*f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or(msg.header.flags as u32);
|
||||
|
||||
Some(InterfaceAddress::new(
|
||||
IfAddr::V4(Ifv4Addr {
|
||||
ip,
|
||||
/// The netmask of the interface.
|
||||
netmask,
|
||||
/// The broadcast address of the interface.
|
||||
broadcast,
|
||||
}),
|
||||
flags_to_address_flags(flags),
|
||||
))
|
||||
}
|
||||
|
||||
fn process_address_message_v6(msg: AddressMessage) -> Option<InterfaceAddress> {
|
||||
// Get ip address
|
||||
let ip = msg.nlas.iter().find_map(|nla| {
|
||||
if let Nla::Address(a) = nla {
|
||||
let a: Option<[u8; 16]> = a.clone().try_into().ok();
|
||||
a.map(Ipv6Addr::from)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})?;
|
||||
|
||||
// get netmask
|
||||
let plen = msg.header.prefix_len as i16;
|
||||
let mut netmask = [0u8; 16];
|
||||
get_netmask_from_prefix_length_v6(&mut netmask, plen);
|
||||
let netmask = Ipv6Addr::from(netmask);
|
||||
|
||||
// get address flags
|
||||
let flags = msg
|
||||
.nlas
|
||||
.iter()
|
||||
.find_map(|nla| {
|
||||
if let Nla::Flags(f) = nla {
|
||||
Some(*f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or(msg.header.flags as u32);
|
||||
|
||||
// Skip addresses going through duplicate address detection, or ones that have failed it
|
||||
if ((flags & IFA_F_TENTATIVE) != 0) || ((flags & IFA_F_DADFAILED) != 0) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(InterfaceAddress::new(
|
||||
IfAddr::V6(Ifv6Addr {
|
||||
ip,
|
||||
/// The netmask of the interface.
|
||||
netmask,
|
||||
/// The broadcast address of the interface.
|
||||
broadcast: None,
|
||||
}),
|
||||
flags_to_address_flags(flags),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_interfaces(
|
||||
&mut self,
|
||||
interfaces: &mut BTreeMap<String, NetworkInterface>,
|
||||
) -> Result<(), String> {
|
||||
// Refresh the routes
|
||||
self.refresh_default_route_interfaces().await?;
|
||||
|
||||
// If we have no routes, this isn't going to work
|
||||
if self.default_route_interfaces.is_empty() {
|
||||
return Err("no routes available for NetworkInterfaces".to_owned());
|
||||
}
|
||||
|
||||
// Ask for all the addresses we have
|
||||
let mut names = BTreeMap::<u32, String>::new();
|
||||
let mut addresses = self.handle.address().get().execute();
|
||||
while let Some(msg) = addresses
|
||||
.try_next()
|
||||
.await
|
||||
.map_err(map_to_string)
|
||||
.map_err(logthru_net!(error))?
|
||||
{
|
||||
// Have we seen this interface index yet?
|
||||
// Get the name from the index, cached, if we can
|
||||
let ifname = match names.entry(msg.header.index) {
|
||||
Entry::Vacant(v) => {
|
||||
// If not, get the name for the index if we can
|
||||
let ifname = match get_interface_name(msg.header.index) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
log_net!(
|
||||
"couldn't get interface name for index {}: {}",
|
||||
msg.header.index,
|
||||
e
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
v.insert(ifname).clone()
|
||||
}
|
||||
Entry::Occupied(o) => o.get().clone(),
|
||||
};
|
||||
|
||||
// Map the name to a NetworkInterface
|
||||
if !interfaces.contains_key(&ifname) {
|
||||
// If we have no NetworkInterface yet, make one
|
||||
let flags = self.get_interface_flags(msg.header.index, &ifname)?;
|
||||
interfaces.insert(ifname.clone(), NetworkInterface::new(ifname.clone(), flags));
|
||||
}
|
||||
let intf = interfaces.get_mut(&ifname).unwrap();
|
||||
|
||||
// Process the address
|
||||
let intf_addr = match msg.header.family as u16 {
|
||||
AF_INET => match Self::process_address_message_v4(msg) {
|
||||
Some(ia) => ia,
|
||||
None => {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
AF_INET6 => match Self::process_address_message_v6(msg) {
|
||||
Some(ia) => ia,
|
||||
None => {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
intf.addrs.push(intf_addr);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
pub fn convert_to_unsigned_4(x: [i8; 4]) -> [u8; 4] {
|
||||
let mut out: [u8; 4] = [0u8; 4];
|
||||
for i in 0..4 {
|
||||
out[i] = x[i] as u8;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn convert_to_unsigned_16(x: [i8; 16]) -> [u8; 16] {
|
||||
let mut out: [u8; 16] = [0u8; 16];
|
||||
for i in 0..16 {
|
||||
out[i] = x[i] as u8;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn get_netmask_from_prefix_length_v4(out: &mut [u8; 4], mut plen: i16) {
|
||||
for outb in out.iter_mut() {
|
||||
*outb = if plen >= 8 {
|
||||
plen -= 8;
|
||||
255u8
|
||||
} else if plen <= 0 {
|
||||
0u8
|
||||
} else {
|
||||
let v = 255u8 << (8 - plen);
|
||||
plen = 0;
|
||||
v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_netmask_from_prefix_length_v6(out: &mut [u8; 16], mut plen: i16) {
|
||||
for outb in out.iter_mut() {
|
||||
*outb = if plen >= 8 {
|
||||
plen -= 8;
|
||||
255u8
|
||||
} else if plen == 0 {
|
||||
0u8
|
||||
} else {
|
||||
let v = 255u8 << (8 - plen);
|
||||
plen = 0;
|
||||
v
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
use super::*;
|
||||
|
||||
pub struct PlatformSupportWindows {
|
||||
//
|
||||
}
|
||||
|
||||
impl PlatformSupportWindows {
|
||||
pub fn new() -> Result<Self, String> {
|
||||
Ok(PlatformSupportWindows {})
|
||||
}
|
||||
|
||||
pub async fn get_interfaces(
|
||||
&mut self,
|
||||
interfaces: &mut BTreeMap<String, NetworkInterface>,
|
||||
) -> Result<(), String> {
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
}
|
@@ -80,6 +80,7 @@ impl TableStore {
|
||||
let db = Database::open(table_name.clone(), column_count)
|
||||
.await
|
||||
.map_err(|e| format!("failed to open tabledb at: {} ({})", table_name, e))?;
|
||||
info!("opened table store '{}' with table name '{:?}' with {} columns", name, table_name, column_count);
|
||||
|
||||
let table_db = TableDB::new(table_name.clone(), self.clone(), db);
|
||||
|
||||
|
Reference in New Issue
Block a user