use crate::interface::types::InterfaceType;
use std::{ffi::CString, mem};
pub fn get_interface_type(addr_ref: &libc::ifaddrs) -> InterfaceType {
if !addr_ref.ifa_data.is_null() {
let if_data = unsafe { &*(addr_ref.ifa_data as *const libc::if_data) };
InterfaceType::try_from(if_data.ifi_type as u32).unwrap_or(InterfaceType::Unknown)
} else {
InterfaceType::Unknown
}
}
const IOC_INOUT: u32 = 0xC000_0000;
const IOCPARM_MASK: u32 = 0x1fff;
const fn ioc(inout: u32, group: u8, num: u8, len: u32) -> u32 {
inout | ((len & IOCPARM_MASK) << 16) | ((group as u32) << 8) | (num as u32)
}
fn siocgiffunctionaltype() -> u64 {
let len = mem::size_of::<libc::ifreq>() as u32;
ioc(IOC_INOUT, b'i', 173, len) as u64
}
const IFRTYPE_FUNCTIONAL_UNKNOWN: u32 = 0;
const IFRTYPE_FUNCTIONAL_LOOPBACK: u32 = 1;
const IFRTYPE_FUNCTIONAL_WIRED: u32 = 2;
const IFRTYPE_FUNCTIONAL_WIFI_INFRA: u32 = 3;
const IFRTYPE_FUNCTIONAL_WIFI_AWDL: u32 = 4;
const IFRTYPE_FUNCTIONAL_CELLULAR: u32 = 5;
const IFRTYPE_FUNCTIONAL_INTCOPROC: u32 = 6;
const IFRTYPE_FUNCTIONAL_COMPANIONLINK: u32 = 7;
fn map_to_interface_type(ft: u32) -> InterfaceType {
match ft {
IFRTYPE_FUNCTIONAL_UNKNOWN => InterfaceType::Unknown,
IFRTYPE_FUNCTIONAL_LOOPBACK => InterfaceType::Loopback,
IFRTYPE_FUNCTIONAL_WIRED => InterfaceType::Ethernet,
IFRTYPE_FUNCTIONAL_WIFI_INFRA => InterfaceType::Wireless80211,
IFRTYPE_FUNCTIONAL_WIFI_AWDL => InterfaceType::PeerToPeerWireless,
IFRTYPE_FUNCTIONAL_CELLULAR => InterfaceType::Wwanpp,
IFRTYPE_FUNCTIONAL_INTCOPROC => InterfaceType::Unknown,
IFRTYPE_FUNCTIONAL_COMPANIONLINK => InterfaceType::Unknown,
_ => InterfaceType::Unknown,
}
}
pub(crate) fn get_functional_type(name: &str) -> InterfaceType {
let fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
if fd < 0 {
return InterfaceType::Unknown;
}
let c_name = match CString::new(name) {
Ok(v) => v,
Err(_) => {
unsafe { libc::close(fd) };
return InterfaceType::Unknown;
}
};
let mut ifr: libc::ifreq = unsafe { mem::zeroed() };
unsafe {
let dst = ifr.ifr_name.as_mut_ptr() as *mut u8;
let src = c_name.as_bytes_with_nul();
let copy_len = std::cmp::min(ifr.ifr_name.len() - 1, src.len() - 1);
std::ptr::copy_nonoverlapping(src.as_ptr(), dst, copy_len);
*dst.add(copy_len) = 0;
}
let req = siocgiffunctionaltype();
let ok = unsafe { libc::ioctl(fd, req, &mut ifr) } >= 0;
unsafe { libc::close(fd) };
if !ok {
return InterfaceType::Unknown;
}
#[allow(clippy::unnecessary_cast)]
let type_id = unsafe { ifr.ifr_ifru.ifru_functional_type as u32 };
map_to_interface_type(type_id)
}
pub(crate) fn interface_type_by_name(name: &str) -> Option<InterfaceType> {
let n = name.as_bytes();
if n.starts_with(b"awdl") {
return Some(InterfaceType::PeerToPeerWireless);
}
if n.starts_with(b"utun") || n.starts_with(b"gif") || n.starts_with(b"stf") {
return Some(InterfaceType::Tunnel);
}
if n.starts_with(b"bridge") {
return Some(InterfaceType::Bridge);
}
None
}