Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/interface/flags.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#[cfg(target_family = "unix")]
pub use crate::os::unix::flags::*;

#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(target_os = "linux")]
pub use crate::os::linux::flags::*;

#[cfg(target_os = "android")]
pub use crate::os::android::flags::*;

#[cfg(target_vendor = "apple")]
pub use crate::os::darwin::flags::*;

Expand Down
6 changes: 5 additions & 1 deletion src/interface/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,14 @@ impl FromStr for OperState {
}

pub fn operstate(if_name: &str) -> OperState {
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(target_os = "linux")]
{
crate::os::linux::state::operstate(if_name)
}
#[cfg(target_os = "android")]
{
crate::os::android::state::operstate(if_name)
}
#[cfg(target_vendor = "apple")]
{
crate::os::darwin::state::operstate(if_name)
Expand Down
5 changes: 5 additions & 0 deletions src/os/android/flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use crate::interface::interface::Interface;

pub fn is_physical_interface(interface: &Interface) -> bool {
interface.is_up() && interface.is_running() && !interface.is_tun() && !interface.is_loopback()
}
65 changes: 19 additions & 46 deletions src/os/android/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,17 @@ use crate::interface::interface::Interface;
use crate::interface::state::OperState;
use crate::ipnet::{Ipv4Net, Ipv6Net};
use crate::net::mac::MacAddr;
use crate::os::linux::mtu;
use crate::os::linux::sysfs;
use crate::os::unix::interface::unix_interfaces;
use std::net::{Ipv4Addr, Ipv6Addr};

#[cfg(feature = "gateway")]
use crate::net::device::NetworkDevice;
#[cfg(feature = "gateway")]
use crate::os::linux::procfs;
#[cfg(feature = "gateway")]
use crate::os::unix::dns::get_system_dns_conf;
#[cfg(feature = "gateway")]
use std::collections::HashMap;

use crate::os::unix::interface::unix_interfaces;

fn push_ipv4(v: &mut Vec<Ipv4Net>, add: (Ipv4Addr, u8)) {
if v.iter()
.any(|n| n.addr() == add.0 && n.prefix_len() == add.1)
Expand Down Expand Up @@ -50,9 +47,8 @@ fn calc_v6_scope_id(addr: &Ipv6Addr, ifindex: u32) -> u32 {
}

pub fn interfaces() -> Vec<Interface> {
let mut ifaces = Vec::new();
// Fill ifaces via netlink first
// If netlink fails, fallback to unix_interfaces
let mut ifaces: Vec<Interface> = Vec::new();

match netlink::collect_interfaces() {
Ok(rows) => {
for r in rows {
Expand All @@ -62,7 +58,7 @@ pub fn interfaces() -> Vec<Interface> {
name: name.clone(),
friendly_name: None,
description: None,
if_type: sysfs::get_interface_type(&name),
if_type: super::types::guess_type_by_name(&name).unwrap_or(r.if_type),
mac_addr: r.mac.map(MacAddr::from_octets),
ipv4: Vec::new(),
ipv6: Vec::new(),
Expand All @@ -71,7 +67,7 @@ pub fn interfaces() -> Vec<Interface> {
oper_state: OperState::from_if_flags(r.flags),
transmit_speed: None,
receive_speed: None,
stats: None,
stats: r.stats.clone(),
#[cfg(feature = "gateway")]
gateway: None,
#[cfg(feature = "gateway")]
Expand All @@ -93,63 +89,39 @@ pub fn interfaces() -> Vec<Interface> {
}
}
Err(_) => {
// Fallback: unix ifaddrs
// fallback: unix ifaddrs
ifaces = unix_interfaces();

for iface in &mut ifaces {
if let Some(t) = super::types::guess_type_by_name(&iface.name) {
iface.if_type = t;
}
}
}
}

// Fill gateway info if feature enabled
// Fill gateway info
#[cfg(feature = "gateway")]
match netlink::collect_routes() {
Ok(gmap) => {
{
if let Ok(gmap) = netlink::collect_routes() {
let by_index: HashMap<u32, &netlink::GwRow> =
gmap.iter().map(|(k, v)| (*k, v)).collect();

for iface in &mut ifaces {
if iface.index == 0 {
continue;
}
if let Some(row) = by_index.get(&iface.index) {
let dev = NetworkDevice {
mac_addr: row
.mac
.map(|m| MacAddr::from_octets(m))
.unwrap_or(MacAddr::zero()),
mac_addr: row.mac.map(MacAddr::from_octets).unwrap_or(MacAddr::zero()),
ipv4: row.gw_v4.clone(),
ipv6: row.gw_v6.clone(),
};
iface.gateway = Some(dev);
}
}
}
Err(_) => {
// Fallback: procfs
let gateway_map: HashMap<String, NetworkDevice> = procfs::get_gateway_map();
for iface in &mut ifaces {
if let Some(gateway) = gateway_map.get(&iface.name) {
iface.gateway = Some(gateway.clone());
}
}
}
}

// Fill other info
for iface in &mut ifaces {
iface.if_type = sysfs::get_interface_type(&iface.name);
let if_speed = sysfs::get_interface_speed(&iface.name);
iface.transmit_speed = if_speed;
iface.receive_speed = if_speed;
iface.oper_state = sysfs::operstate(&iface.name);

if iface.stats.is_none() {
iface.stats = crate::stats::counters::get_stats_from_name(&iface.name);
}

if iface.mtu.is_none() {
iface.mtu = mtu::get_mtu(&iface.name);
}
}
#[cfg(feature = "gateway")]
{
if let Some(local_ip) = crate::net::ip::get_local_ipaddr() {
if let Some(idx) = crate::interface::pick_default_iface_index(&ifaces, local_ip) {
if let Some(iface) = ifaces.iter_mut().find(|it| it.index == idx) {
Expand All @@ -159,5 +131,6 @@ pub fn interfaces() -> Vec<Interface> {
}
}
}

ifaces
}
3 changes: 3 additions & 0 deletions src/os/android/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
pub mod flags;
pub mod interface;
pub mod netlink;
pub mod state;
pub mod types;

use once_cell::sync::OnceCell;

Expand Down
58 changes: 58 additions & 0 deletions src/os/android/netlink.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::interface::types::InterfaceType;
use crate::stats::counters::InterfaceStats;
use netlink_packet_core::{NLM_F_DUMP, NLM_F_REQUEST, NetlinkMessage, NetlinkPayload};
use netlink_packet_route::{
RouteNetlinkMessage,
Expand All @@ -7,6 +9,7 @@ use netlink_packet_route::{
use netlink_sys::{Socket, SocketAddr, protocols::NETLINK_ROUTE};
use std::io::ErrorKind;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::time::SystemTime;
use std::{
collections::HashMap,
io, thread,
Expand Down Expand Up @@ -327,6 +330,54 @@ fn mtu_from_link(link: &LinkMessage) -> Option<u32> {
None
}

fn if_type_from_link(link: &LinkMessage, name: &str) -> InterfaceType {
let arphrd = link.header.link_layer_type as u32;
let mut t = InterfaceType::try_from(arphrd).unwrap_or(InterfaceType::UnknownWithValue(arphrd));

// override by name guess
// ARPHRD may be unreliable on some devices
if let Some(guess) = super::types::guess_type_by_name(name) {
t = guess;
}

t
}

fn stats_from_link(link: &LinkMessage) -> Option<InterfaceStats> {
for nla in &link.attributes {
match nla {
LinkAttribute::Stats64(s) => {
return Some(InterfaceStats {
rx_bytes: s.rx_bytes,
tx_bytes: s.tx_bytes,
timestamp: Some(SystemTime::now()),
});
}
LinkAttribute::Stats(s) => {
return Some(InterfaceStats {
rx_bytes: s.rx_bytes as u64,
tx_bytes: s.tx_bytes as u64,
timestamp: Some(SystemTime::now()),
});
}
_ => {}
}
}
None
}

pub fn get_flags_by_name(name: &str) -> io::Result<Option<u32>> {
let links = dump_links()?;
for l in links {
if let Some(ifname) = name_from_link(&l) {
if ifname == name {
return Ok(Some(l.header.flags.bits()));
}
}
}
Ok(None)
}

#[derive(Debug, Clone)]
pub struct IfRow {
pub index: u32,
Expand All @@ -336,6 +387,8 @@ pub struct IfRow {
pub ipv6: Vec<(Ipv6Addr, u8)>,
pub flags: u32,
pub mtu: Option<u32>,
pub if_type: InterfaceType,
pub stats: Option<InterfaceStats>,
}

pub fn collect_interfaces() -> io::Result<Vec<IfRow>> {
Expand All @@ -349,6 +402,9 @@ pub fn collect_interfaces() -> io::Result<Vec<IfRow>> {
let mac = mac_from_link(&l);
let flags = l.header.flags.bits();
let mtu_nl = mtu_from_link(&l);
let if_type = if_type_from_link(&l, &name);
let stats = stats_from_link(&l);

base.insert(
idx,
IfRow {
Expand All @@ -359,6 +415,8 @@ pub fn collect_interfaces() -> io::Result<Vec<IfRow>> {
ipv6: vec![],
flags,
mtu: mtu_nl,
if_type,
stats,
},
);
}
Expand Down
9 changes: 9 additions & 0 deletions src/os/android/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use crate::interface::state::OperState;

pub fn operstate(if_name: &str) -> OperState {
match super::netlink::get_flags_by_name(if_name) {
Ok(Some(flags)) => OperState::from_if_flags(flags),
Ok(None) => OperState::Unknown,
Err(_) => OperState::Unknown,
}
}
39 changes: 39 additions & 0 deletions src/os/android/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use crate::interface::types::InterfaceType;

pub fn guess_type_by_name(name: &str) -> Option<InterfaceType> {
let n = name.as_bytes();

// Loopback: lo
if n == b"lo" {
return Some(InterfaceType::Loopback);
}
// Wi-Fi: wlan0 / wlan1 / wifi0
if n.starts_with(b"wlan") || n.starts_with(b"wifi") {
return Some(InterfaceType::Wireless80211);
}
// Cellular: rmnet_data0 / rmnet0 / ccmni0 / pdp0
if n.starts_with(b"rmnet") || n.starts_with(b"ccmni") || n.starts_with(b"pdp") {
return Some(InterfaceType::Wwanpp);
}
// Wi-Fi Direct: p2p0
if n.starts_with(b"p2p") {
return Some(InterfaceType::PeerToPeerWireless);
}
// Tunnel: tun0 / tap0 / ipsec0 / clat4
if n.starts_with(b"tun")
|| n.starts_with(b"tap")
|| n.starts_with(b"ipsec")
|| n.starts_with(b"clat")
{
return Some(InterfaceType::Tunnel);
}
// Bridge / veth
if n.starts_with(b"br-") || n.starts_with(b"bridge") {
return Some(InterfaceType::Bridge);
}
if n.starts_with(b"veth") {
return Some(InterfaceType::ProprietaryVirtual);
}

None
}
4 changes: 4 additions & 0 deletions src/os/linux/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
pub mod arp;
#[cfg(not(target_os = "android"))]
pub mod flags;
#[cfg(not(target_os = "android"))]
pub mod interface;
pub mod mtu;
#[cfg(not(target_os = "android"))]
pub mod netlink;
#[cfg(not(target_os = "android"))]
#[cfg(feature = "gateway")]
pub mod procfs;
#[cfg(not(target_os = "android"))]
pub mod state;
#[cfg(not(target_os = "android"))]
pub mod sysfs;
9 changes: 8 additions & 1 deletion src/os/unix/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ pub fn get_interface_type(addr_ref: &libc::ifaddrs) -> InterfaceType {
let c_str = addr_ref.ifa_name as *const c_char;
let bytes = unsafe { CStr::from_ptr(c_str).to_bytes() };
let name: String = unsafe { from_utf8_unchecked(bytes).to_owned() };
crate::os::linux::sysfs::get_interface_type(&name)
#[cfg(target_os = "linux")]
{
crate::os::linux::sysfs::get_interface_type(&name)
}
#[cfg(target_os = "android")]
{
crate::os::android::types::guess_type_by_name(&name).unwrap_or(InterfaceType::Unknown)
}
}

#[cfg(target_vendor = "apple")]
Expand Down