diff --git a/Cargo.toml b/Cargo.toml index 9cda6e1..1c3187b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "default-net" -version = "0.8.2" +version = "0.9.0" authors = ["shellrow "] edition = "2018" description = "Cross-platform library for network interface and gateway" @@ -16,3 +16,6 @@ libc = "0.2" [target.'cfg(windows)'.dependencies.windows] version = "0.29.0" features = ["Win32_Foundation","Win32_NetworkManagement_IpHelper"] + +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +system-configuration = "0.5.0" diff --git a/README.md b/README.md index be8852e..0be2d5b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Add `default-net` to your dependencies ```toml:Cargo.toml [dependencies] -default-net = "0.8.2" +default-net = "0.9.0" ``` ## Example @@ -33,6 +33,7 @@ fn main(){ println!("\tIndex: {}", default_interface.index); println!("\tName: {}", default_interface.name); println!("\tDescription: {:?}", default_interface.description); + println!("\tType: {}", default_interface.if_type.name()); if let Some(mac_addr) = default_interface.mac_addr { println!("\tMAC: {}", mac_addr); }else{ @@ -40,6 +41,7 @@ fn main(){ } println!("\tIPv4: {:?}", default_interface.ipv4); println!("\tIPv6: {:?}", default_interface.ipv6); + println!("\tFlags: {:?}", default_interface.flags); if let Some(gateway) = default_interface.gateway { println!("Default Gateway"); println!("\tMAC: {}", gateway.mac_addr); @@ -58,13 +60,15 @@ fn main(){ ## Tested on - Linux - Ubuntu - - 21.10 - - 20.04 - - 18.04 - - Kali 2021.1 (VM) + - 21.10 + - 20.04 + - 18.04 + - Kali + - 2022.1 (VM) + - 2021.1 (VM) - macOS 11.6 - Windows - - Windows 10 21H2 19044.1526 + - Windows 10 21H2 19044.1586 - Windows 11 21H2 22000.493 (VM) For more details, see [examples][examples-url] or doc. diff --git a/examples/default_interface.rs b/examples/default_interface.rs index 3868df8..4aaddcd 100644 --- a/examples/default_interface.rs +++ b/examples/default_interface.rs @@ -7,6 +7,7 @@ fn main(){ println!("\tIndex: {}", default_interface.index); println!("\tName: {}", default_interface.name); println!("\tDescription: {:?}", default_interface.description); + println!("\tType: {}", default_interface.if_type.name()); if let Some(mac_addr) = default_interface.mac_addr { println!("\tMAC: {}", mac_addr); }else{ @@ -14,6 +15,7 @@ fn main(){ } println!("\tIPv4: {:?}", default_interface.ipv4); println!("\tIPv6: {:?}", default_interface.ipv6); + println!("\tFlags: {:?}", default_interface.flags); if let Some(gateway) = default_interface.gateway { println!("Default Gateway"); println!("\tMAC: {}", gateway.mac_addr); diff --git a/examples/list_interfaces.rs b/examples/list_interfaces.rs index e035159..37a27d0 100644 --- a/examples/list_interfaces.rs +++ b/examples/list_interfaces.rs @@ -7,6 +7,7 @@ fn main(){ println!("\tIndex: {}", interface.index); println!("\tName: {}", interface.name); println!("\tDescription: {:?}", interface.description); + println!("\tType: {}", interface.if_type.name()); if let Some(mac_addr) = interface.mac_addr { println!("\tMAC: {}", mac_addr); }else{ @@ -14,6 +15,7 @@ fn main(){ } println!("\tIPv4: {:?}", interface.ipv4); println!("\tIPv6: {:?}", interface.ipv6); + println!("\tFlags: {:?}", interface.flags); if let Some(gateway) = interface.gateway { println!("Gateway"); println!("\tMAC: {}", gateway.mac_addr); diff --git a/src/interface/linux.rs b/src/interface/linux.rs new file mode 100644 index 0000000..bd9fae9 --- /dev/null +++ b/src/interface/linux.rs @@ -0,0 +1,24 @@ +use std::convert::TryFrom; +use std::fs::read_to_string; +use crate::interface::InterfaceType; + +pub fn get_interface_type(if_name: String) -> InterfaceType { + let if_type_path: String = format!("/sys/class/net/{}/type", if_name); + let r = read_to_string(if_type_path); + match r { + Ok(content) => { + let if_type_string = content.trim().to_string(); + match if_type_string.parse::() { + Ok(if_type) => { + return InterfaceType::try_from(if_type).unwrap_or(InterfaceType::Unknown); + }, + Err(_) => { + return InterfaceType::Unknown; + } + } + }, + Err(_) => { + return InterfaceType::Unknown; + } + }; +} diff --git a/src/interface/macos.rs b/src/interface/macos.rs new file mode 100644 index 0000000..89be874 --- /dev/null +++ b/src/interface/macos.rs @@ -0,0 +1,31 @@ +use std::collections::HashMap; +use system_configuration::network_configuration; +use crate::interface::InterfaceType; + +fn get_if_type_from_id(type_id: String) -> InterfaceType { + match type_id.as_str() { + "Ethernet" => InterfaceType::Ethernet, + "IEEE80211" => InterfaceType::Wireless80211, + "PPP" => InterfaceType::Ppp, + _ => InterfaceType::Unknown, + } +} + +pub fn get_if_type_map() -> HashMap { + let mut map: HashMap = HashMap::new(); + let interfaces = network_configuration::get_interfaces(); + for interface in &interfaces { + let if_name: String = if let Some(bsd_name) = interface.bsd_name() { + bsd_name.to_string() + }else{ + continue; + }; + let type_id: String = if let Some(type_string) = interface.interface_type_string() { + type_string.to_string() + }else{ + continue; + }; + map.insert(if_name, get_if_type_from_id(type_id)); + } + return map; +} diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 129eb54..67688c2 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -14,6 +14,12 @@ mod windows; #[cfg(target_os = "windows")] use self::windows::*; +#[cfg(any(target_os = "linux", target_os = "android"))] +mod linux; + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod macos; + use std::net::IpAddr; use crate::ip::{Ipv4Net, Ipv6Net}; use crate::gateway::{Gateway}; @@ -73,12 +79,16 @@ pub struct Interface { /// /// On Windows, this field is the adapter name pub description: Option, + /// Interface Type + pub if_type: InterfaceType, /// MAC address of network interface pub mac_addr: Option, /// List of Ipv4Net for the network interface pub ipv4: Vec, /// List of Ipv6Net for the network interface pub ipv6: Vec, + /// Flags for the network interface (OS Specific) + pub flags: u32, /// Default gateway for the network interface pub gateway: Option, } diff --git a/src/interface/types.rs b/src/interface/types.rs index 368448c..076fe88 100644 --- a/src/interface/types.rs +++ b/src/interface/types.rs @@ -1,5 +1,6 @@ use std::convert::TryFrom; +/// Type of Network Interface #[derive(Clone, Copy, Debug, PartialEq)] pub enum InterfaceType { Unknown, @@ -69,9 +70,18 @@ impl InterfaceType { #[cfg(any(target_os = "linux", target_os = "android"))] pub fn value(&self) -> u32 { - // TODO match *self { - _ => 0, + InterfaceType::Ethernet => 1, + InterfaceType::TokenRing => 4, + InterfaceType::Fddi => 774, + InterfaceType::Ppp => 512, + InterfaceType::Loopback => 772, + InterfaceType::Ethernet3Megabit => 2, + InterfaceType::Slip => 256, + InterfaceType::Atm => 19, + InterfaceType::Wireless80211 => 801, + InterfaceType::Tunnel => 768, + _ => u32::MAX, } } diff --git a/src/interface/unix.rs b/src/interface/unix.rs index a646664..87830a8 100644 --- a/src/interface/unix.rs +++ b/src/interface/unix.rs @@ -10,8 +10,9 @@ use std::mem::{self, MaybeUninit}; use std::os::raw::c_char; use std::str::from_utf8_unchecked; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use crate::interface::InterfaceType; -#[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "ios"))] +#[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "netbsd"))] pub fn interfaces() -> Vec { let mut interfaces: Vec = unix_interfaces(); let local_ip: IpAddr = match super::get_local_ipaddr(){ @@ -45,14 +46,55 @@ pub fn interfaces() -> Vec { interfaces } +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn interfaces() -> Vec { + use super::macos; + + let type_map = macos::get_if_type_map(); + let mut interfaces: Vec = unix_interfaces(); + let local_ip: IpAddr = match super::get_local_ipaddr(){ + Some(local_ip) => local_ip, + None => return interfaces, + }; + for iface in &mut interfaces { + iface.if_type = *type_map.get(&iface.name).unwrap_or(&InterfaceType::Unknown); + match local_ip { + IpAddr::V4(local_ipv4) => { + if iface.ipv4.iter().any(|x| x.addr == local_ipv4) { + match gateway::unix::get_default_gateway(iface.name.clone()) { + Ok(gateway) => { + iface.gateway = Some(gateway); + }, + Err(_) => {}, + } + } + }, + IpAddr::V6(local_ipv6) => { + if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { + match gateway::unix::get_default_gateway(iface.name.clone()) { + Ok(gateway) => { + iface.gateway = Some(gateway); + }, + Err(_) => {}, + } + } + }, + } + } + interfaces +} + #[cfg(any(target_os = "linux", target_os = "android"))] pub fn interfaces() -> Vec { + use super::linux; + let mut interfaces: Vec = unix_interfaces(); let local_ip: IpAddr = match super::get_local_ipaddr(){ Some(local_ip) => local_ip, None => return interfaces, }; for iface in &mut interfaces { + iface.if_type = linux::get_interface_type(iface.name.clone()); match local_ip { IpAddr::V4(local_ipv4) => { if iface.ipv4.iter().any(|x| x.addr == local_ipv4) { @@ -79,6 +121,76 @@ pub fn interfaces() -> Vec { interfaces } +#[cfg(any(target_os = "linux", target_os = "android"))] +fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Option) { + use std::net::SocketAddr; + + unsafe { + if sa.is_null() { + (None, None) + } else if (*sa).sa_family as libc::c_int == libc::AF_PACKET { + let sll: *const libc::sockaddr_ll = mem::transmute(sa); + let mac = MacAddr( + (*sll).sll_addr[0], + (*sll).sll_addr[1], + (*sll).sll_addr[2], + (*sll).sll_addr[3], + (*sll).sll_addr[4], + (*sll).sll_addr[5], + ); + + (Some(mac), None) + } else { + let addr = sys::sockaddr_to_addr( + mem::transmute(sa), + mem::size_of::(), + ); + + match addr { + Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))), + Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))), + Err(_) => (None, None), + } + } + } +} + +#[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "macos", target_os = "ios"))] +fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Option) { + use crate::bpf; + use std::net::SocketAddr; + + unsafe { + if sa.is_null() { + (None, None) + } else if (*sa).sa_family as libc::c_int == bpf::AF_LINK { + let sdl: *const bpf::sockaddr_dl = mem::transmute(sa); + let nlen = (*sdl).sdl_nlen as usize; + let mac = MacAddr( + (*sdl).sdl_data[nlen] as u8, + (*sdl).sdl_data[nlen + 1] as u8, + (*sdl).sdl_data[nlen + 2] as u8, + (*sdl).sdl_data[nlen + 3] as u8, + (*sdl).sdl_data[nlen + 4] as u8, + (*sdl).sdl_data[nlen + 5] as u8, + ); + + (Some(mac), None) + } else { + let addr = sys::sockaddr_to_addr( + mem::transmute(sa), + mem::size_of::(), + ); + + match addr { + Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))), + Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))), + Err(_) => (None, None), + } + } + } +} + pub fn unix_interfaces() -> Vec { let mut ifaces: Vec = vec![]; let mut addrs: MaybeUninit<*mut libc::ifaddrs> = MaybeUninit::uninit(); @@ -130,9 +242,11 @@ pub fn unix_interfaces() -> Vec { index: 0, name: name.clone(), description: None, + if_type: InterfaceType::Unknown, mac_addr: mac.clone(), ipv4: ini_ipv4, ipv6: ini_ipv6, + flags: addr_ref.ifa_flags, gateway: None, }; let mut found: bool = false; @@ -187,76 +301,6 @@ pub fn unix_interfaces() -> Vec { ifaces } -#[cfg(any(target_os = "linux", target_os = "android"))] -fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Option) { - use std::net::SocketAddr; - - unsafe { - if sa.is_null() { - (None, None) - } else if (*sa).sa_family as libc::c_int == libc::AF_PACKET { - let sll: *const libc::sockaddr_ll = mem::transmute(sa); - let mac = MacAddr( - (*sll).sll_addr[0], - (*sll).sll_addr[1], - (*sll).sll_addr[2], - (*sll).sll_addr[3], - (*sll).sll_addr[4], - (*sll).sll_addr[5], - ); - - (Some(mac), None) - } else { - let addr = sys::sockaddr_to_addr( - mem::transmute(sa), - mem::size_of::(), - ); - - match addr { - Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))), - Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))), - Err(_) => (None, None), - } - } - } -} - -#[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "macos", target_os = "ios"))] -fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Option) { - use crate::bpf; - use std::net::SocketAddr; - - unsafe { - if sa.is_null() { - (None, None) - } else if (*sa).sa_family as libc::c_int == bpf::AF_LINK { - let sdl: *const bpf::sockaddr_dl = mem::transmute(sa); - let nlen = (*sdl).sdl_nlen as usize; - let mac = MacAddr( - (*sdl).sdl_data[nlen] as u8, - (*sdl).sdl_data[nlen + 1] as u8, - (*sdl).sdl_data[nlen + 2] as u8, - (*sdl).sdl_data[nlen + 3] as u8, - (*sdl).sdl_data[nlen + 4] as u8, - (*sdl).sdl_data[nlen + 5] as u8, - ); - - (Some(mac), None) - } else { - let addr = sys::sockaddr_to_addr( - mem::transmute(sa), - mem::size_of::(), - ); - - match addr { - Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))), - Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))), - Err(_) => (None, None), - } - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/interface/windows.rs b/src/interface/windows.rs index 6aec6d6..c6a513e 100644 --- a/src/interface/windows.rs +++ b/src/interface/windows.rs @@ -178,9 +178,11 @@ pub fn interfaces() -> Vec { index: adapter.Index, name: adapter_name, description: Some(adapter_desc), + if_type: InterfaceType::try_from(adapter.Type).unwrap_or(InterfaceType::Unknown), mac_addr: Some(MacAddr::new(mac_addr)), ipv4: ipv4_vec, ipv6: ipv6_vec, + flags: adapter.Type, gateway: default_gateway, }; interfaces.push(interface);