From 65aa839803690fec56fe120c4a1739a7517e173a Mon Sep 17 00:00:00 2001 From: shellrow <81893184+shellrow@users.noreply.github.com> Date: Sun, 27 Mar 2022 22:06:53 +0900 Subject: [PATCH] Add if_type for macOS and iOS --- Cargo.toml | 3 + src/interface/linux.rs | 22 +++++ src/interface/macos.rs | 23 +++++ src/interface/mod.rs | 6 ++ src/interface/unix.rs | 200 ++++++++++++++++++++++------------------- 5 files changed, 160 insertions(+), 94 deletions(-) create mode 100644 src/interface/linux.rs create mode 100644 src/interface/macos.rs diff --git a/Cargo.toml b/Cargo.toml index 9cda6e1..ccfe21b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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/src/interface/linux.rs b/src/interface/linux.rs new file mode 100644 index 0000000..a576b64 --- /dev/null +++ b/src/interface/linux.rs @@ -0,0 +1,22 @@ +use std::convert::TryFrom; +use std::fs::read_to_string; + +pub fn get_interface_type(if_name: String) -> InterfaceType { + use std::convert::TryFrom; + use std::fs::read_to_string; + + let if_type_path: String = format!("/sys/class/net/{}/type", if_name); + let r = read_to_string(if_type_path); + let if_type_string = match r { + Ok(content) => content.trim().to_string(), + Err(_) => String::from("999"), + }; + match if_type_string.parse::() { + Ok(if_type) => { + InterfaceType::try_from(if_type).unwrap_or(InterfaceType::Unknown) + }, + Err(_) => { + InterfaceType::Unknown + } + } +} diff --git a/src/interface/macos.rs b/src/interface/macos.rs new file mode 100644 index 0000000..b0eaa14 --- /dev/null +++ b/src/interface/macos.rs @@ -0,0 +1,23 @@ +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 = interface.bsd_name().unwrap().to_string(); + let type_id: String = interface.interface_type_string().unwrap().to_string(); + 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 2cf6ca9..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}; diff --git a/src/interface/unix.rs b/src/interface/unix.rs index 59f7d79..87830a8 100644 --- a/src/interface/unix.rs +++ b/src/interface/unix.rs @@ -12,19 +12,52 @@ use std::str::from_utf8_unchecked; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use crate::interface::InterfaceType; -#[cfg(any(target_os = "linux", target_os = "android"))] -use std::convert::TryFrom; -#[cfg(any(target_os = "linux", target_os = "android"))] -use std::fs::read_to_string; +#[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(){ + Some(local_ip) => local_ip, + None => return interfaces, + }; + for iface in &mut interfaces { + 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 = "macos", target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "ios"))] +#[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) { @@ -53,12 +86,15 @@ pub fn interfaces() -> Vec { #[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) { @@ -85,26 +121,72 @@ pub fn interfaces() -> Vec { interfaces } -#[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "ios"))] -pub fn get_interface_type(_if_name: String) -> InterfaceType { - // TODO - InterfaceType::Unknown +#[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 = "linux", target_os = "android"))] -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); - let if_type_string = match r { - Ok(content) => content.trim().to_string(), - Err(_) => String::from("999"), - }; - match if_type_string.parse::() { - Ok(if_type) => { - InterfaceType::try_from(if_type).unwrap_or(InterfaceType::Unknown) - }, - Err(_) => { - InterfaceType::Unknown +#[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), + } } } } @@ -160,7 +242,7 @@ pub fn unix_interfaces() -> Vec { index: 0, name: name.clone(), description: None, - if_type: get_interface_type(name.clone()), + if_type: InterfaceType::Unknown, mac_addr: mac.clone(), ipv4: ini_ipv4, ipv6: ini_ipv6, @@ -219,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::*;