diff --git a/src/os/bsd/route/freebsd.rs b/src/os/bsd/route/freebsd.rs new file mode 100644 index 0000000..a5479d2 --- /dev/null +++ b/src/os/bsd/route/freebsd.rs @@ -0,0 +1,56 @@ +use libc::{c_int, c_ulong, c_ushort, pid_t}; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub(in crate::os::bsd) struct rt_metrics { + pub(in crate::os::bsd) rmx_locks: c_ulong, + pub(in crate::os::bsd) rmx_mtu: c_ulong, + pub(in crate::os::bsd) rmx_hopcount: c_ulong, + pub(in crate::os::bsd) rmx_expire: c_ulong, + pub(in crate::os::bsd) rmx_recvpipe: c_ulong, + pub(in crate::os::bsd) rmx_sendpipe: c_ulong, + pub(in crate::os::bsd) rmx_ssthresh: c_ulong, + pub(in crate::os::bsd) rmx_rtt: c_ulong, + pub(in crate::os::bsd) rmx_rttvar: c_ulong, + pub(in crate::os::bsd) rmx_pksent: c_ulong, + pub(in crate::os::bsd) rmx_weight: c_ulong, + pub(in crate::os::bsd) rmx_nhidx: c_ulong, + pub(in crate::os::bsd) rmx_filler: [c_ulong; 2], +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub(in crate::os::bsd) struct rt_msghdr { + pub(in crate::os::bsd) rtm_msglen: c_ushort, + pub(in crate::os::bsd) rtm_version: u8, + pub(in crate::os::bsd) rtm_type: u8, + pub(in crate::os::bsd) rtm_index: c_ushort, + pub(in crate::os::bsd) _rtm_spare1: c_ushort, + pub(in crate::os::bsd) rtm_flags: c_int, + pub(in crate::os::bsd) rtm_addrs: c_int, + pub(in crate::os::bsd) rtm_pid: pid_t, + pub(in crate::os::bsd) rtm_seq: c_int, + pub(in crate::os::bsd) rtm_errno: c_int, + pub(in crate::os::bsd) rtm_fmask: c_int, + pub(in crate::os::bsd) rtm_inits: c_ulong, + pub(in crate::os::bsd) rtm_rmx: rt_metrics, +} + +pub(in crate::os::bsd) const SOCKADDR_ALIGN: usize = core::mem::size_of::(); + +#[inline] +pub(in crate::os::bsd) fn message_header_len(_: &rt_msghdr) -> usize { + core::mem::size_of::() +} + +#[cfg(all( + target_pointer_width = "64", + any(target_arch = "x86_64", target_arch = "aarch64") +))] +const _: [(); 112] = [(); core::mem::size_of::()]; + +#[cfg(all( + target_pointer_width = "64", + any(target_arch = "x86_64", target_arch = "aarch64") +))] +const _: [(); 152] = [(); core::mem::size_of::()]; diff --git a/src/os/bsd/route.rs b/src/os/bsd/route/mod.rs similarity index 92% rename from src/os/bsd/route.rs rename to src/os/bsd/route/mod.rs index da7ed4a..f8eae4a 100644 --- a/src/os/bsd/route.rs +++ b/src/os/bsd/route/mod.rs @@ -1,6 +1,13 @@ #![allow(non_camel_case_types)] -use libc::{c_int, pid_t, size_t}; +#[cfg(target_os = "freebsd")] +mod freebsd; +#[cfg(target_os = "netbsd")] +mod netbsd; +#[cfg(target_os = "openbsd")] +mod openbsd; + +use libc::{c_int, size_t}; use std::{ collections::HashMap, ffi::c_void, @@ -9,6 +16,13 @@ use std::{ ptr, }; +#[cfg(target_os = "freebsd")] +use self::freebsd::{SOCKADDR_ALIGN, message_header_len, rt_msghdr}; +#[cfg(target_os = "netbsd")] +use self::netbsd::{SOCKADDR_ALIGN, message_header_len, rt_msghdr}; +#[cfg(target_os = "openbsd")] +use self::openbsd::{SOCKADDR_ALIGN, message_header_len, rt_msghdr}; + use crate::net::{device::NetworkDevice, mac::MacAddr}; const CTL_NET: c_int = libc::CTL_NET; @@ -21,8 +35,6 @@ const RTM_VERSION: u8 = 5; #[cfg(target_os = "netbsd")] const RTM_VERSION: u8 = 4; -const RTF_WASCLONED: i32 = 0x20000; - const RTAX_DST: usize = 0; const RTAX_GATEWAY: usize = 1; const RTAX_NETMASK: usize = 2; @@ -34,43 +46,6 @@ const RTAX_MAX: usize = 9; #[cfg(target_os = "openbsd")] const RTAX_MAX: usize = 15; -const SA_ALIGN: usize = 4; - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -struct rt_metrics { - rmx_locks: u32, - rmx_mtu: u32, - rmx_hopcount: u32, - rmx_expire: i32, - rmx_recvpipe: u32, - rmx_sendpipe: u32, - rmx_ssthresh: u32, - rmx_rtt: u32, - rmx_rttvar: u32, - rmx_pksent: u32, - rmx_weight: u32, - rmx_nhidx: u32, - rmx_filler: [u32; 2], -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -struct rt_msghdr { - rtm_msglen: u16, - rtm_version: u8, - rtm_type: u8, - rtm_index: u16, - rtm_flags: c_int, - rtm_addrs: c_int, - rtm_pid: pid_t, - rtm_seq: c_int, - rtm_errno: c_int, - rtm_use: c_int, - rtm_inits: u32, - rtm_rmx: rt_metrics, -} - unsafe extern "C" { fn sysctl( name: *mut c_int, @@ -154,9 +129,9 @@ fn sysctl_vec(mib: &mut [c_int]) -> io::Result> { #[inline] fn roundup(len: usize) -> usize { if len == 0 { - SA_ALIGN + SOCKADDR_ALIGN } else { - (len + (SA_ALIGN - 1)) & !(SA_ALIGN - 1) + (len + (SOCKADDR_ALIGN - 1)) & !(SOCKADDR_ALIGN - 1) } } @@ -344,9 +319,8 @@ struct RawRoute { } fn parse_one_route(hdr: &rt_msghdr, addr_block: &[u8]) -> Option { - const MSG_START_INDEX: usize = 60; let mut addrs: [Option<*const libc::sockaddr>; RTAX_MAX] = [None; RTAX_MAX]; - let mut off = MSG_START_INDEX; + let mut off = 0usize; for idx in 0..RTAX_MAX { if (hdr.rtm_addrs & (1 << idx)) != 0 { @@ -432,7 +406,11 @@ fn get_arp_table() -> io::Result> { return Err(code_to_error(hdr.rtm_errno)); } - let addr_block = &buf[off + mem::size_of::()..off + msglen]; + let hdrlen = message_header_len(hdr); + if hdrlen < mem::size_of::() || off + hdrlen > off + msglen { + break; + } + let addr_block = &buf[off + hdrlen..off + msglen]; if let Some((ip, mac)) = message_to_arppair(addr_block) { arp_map.insert(ip, mac); } @@ -469,15 +447,15 @@ fn list_routes() -> io::Result> { off += msglen; continue; } - if (hdr.rtm_flags & RTF_WASCLONED) != 0 { - off += msglen; - continue; - } if hdr.rtm_errno != 0 { return Err(code_to_error(hdr.rtm_errno)); } - let addr_block = &buf[off + mem::size_of::()..off + msglen]; + let hdrlen = message_header_len(hdr); + if hdrlen < mem::size_of::() || off + hdrlen > off + msglen { + break; + } + let addr_block = &buf[off + hdrlen..off + msglen]; if let Some(rr) = parse_one_route(hdr, addr_block) { out.push(rr); } diff --git a/src/os/bsd/route/netbsd.rs b/src/os/bsd/route/netbsd.rs new file mode 100644 index 0000000..cd32cad --- /dev/null +++ b/src/os/bsd/route/netbsd.rs @@ -0,0 +1,43 @@ +use libc::{c_int, pid_t}; + +#[repr(C, align(8))] +#[derive(Debug, Copy, Clone)] +pub(in crate::os::bsd) struct rt_metrics { + pub(in crate::os::bsd) rmx_locks: u64, + pub(in crate::os::bsd) rmx_mtu: u64, + pub(in crate::os::bsd) rmx_hopcount: u64, + pub(in crate::os::bsd) rmx_recvpipe: u64, + pub(in crate::os::bsd) rmx_sendpipe: u64, + pub(in crate::os::bsd) rmx_ssthresh: u64, + pub(in crate::os::bsd) rmx_rtt: u64, + pub(in crate::os::bsd) rmx_rttvar: u64, + pub(in crate::os::bsd) rmx_expire: i64, + pub(in crate::os::bsd) rmx_pksent: i64, +} + +#[repr(C, align(8))] +#[derive(Debug, Copy, Clone)] +pub(in crate::os::bsd) struct rt_msghdr { + pub(in crate::os::bsd) rtm_msglen: u16, + pub(in crate::os::bsd) rtm_version: u8, + pub(in crate::os::bsd) rtm_type: u8, + pub(in crate::os::bsd) rtm_index: u16, + pub(in crate::os::bsd) rtm_flags: c_int, + pub(in crate::os::bsd) rtm_addrs: c_int, + pub(in crate::os::bsd) rtm_pid: pid_t, + pub(in crate::os::bsd) rtm_seq: c_int, + pub(in crate::os::bsd) rtm_errno: c_int, + pub(in crate::os::bsd) rtm_use: c_int, + pub(in crate::os::bsd) rtm_inits: c_int, + pub(in crate::os::bsd) rtm_rmx: rt_metrics, +} + +pub(in crate::os::bsd) const SOCKADDR_ALIGN: usize = core::mem::size_of::(); + +#[inline] +pub(in crate::os::bsd) fn message_header_len(_: &rt_msghdr) -> usize { + core::mem::size_of::() +} + +const _: [(); 80] = [(); core::mem::size_of::()]; +const _: [(); 120] = [(); core::mem::size_of::()]; diff --git a/src/os/bsd/route/openbsd.rs b/src/os/bsd/route/openbsd.rs new file mode 100644 index 0000000..34a94d5 --- /dev/null +++ b/src/os/bsd/route/openbsd.rs @@ -0,0 +1,52 @@ +use libc::{c_int, pid_t}; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub(in crate::os::bsd) struct rt_metrics { + pub(in crate::os::bsd) rmx_pksent: u64, + pub(in crate::os::bsd) rmx_expire: i64, + pub(in crate::os::bsd) rmx_locks: u32, + pub(in crate::os::bsd) rmx_mtu: u32, + pub(in crate::os::bsd) rmx_refcnt: u32, + pub(in crate::os::bsd) rmx_hopcount: u32, + pub(in crate::os::bsd) rmx_recvpipe: u32, + pub(in crate::os::bsd) rmx_sendpipe: u32, + pub(in crate::os::bsd) rmx_ssthresh: u32, + pub(in crate::os::bsd) rmx_rtt: u32, + pub(in crate::os::bsd) rmx_rttvar: u32, + pub(in crate::os::bsd) rmx_pad: u32, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub(in crate::os::bsd) struct rt_msghdr { + pub(in crate::os::bsd) rtm_msglen: u16, + pub(in crate::os::bsd) rtm_version: u8, + pub(in crate::os::bsd) rtm_type: u8, + pub(in crate::os::bsd) rtm_hdrlen: u16, + pub(in crate::os::bsd) rtm_index: u16, + pub(in crate::os::bsd) rtm_tableid: u16, + pub(in crate::os::bsd) rtm_priority: u8, + pub(in crate::os::bsd) rtm_mpls: u8, + pub(in crate::os::bsd) rtm_addrs: c_int, + pub(in crate::os::bsd) rtm_flags: c_int, + pub(in crate::os::bsd) rtm_fmask: c_int, + pub(in crate::os::bsd) rtm_pid: pid_t, + pub(in crate::os::bsd) rtm_seq: c_int, + pub(in crate::os::bsd) rtm_errno: c_int, + pub(in crate::os::bsd) rtm_inits: u32, + pub(in crate::os::bsd) rtm_rmx: rt_metrics, +} + +pub(in crate::os::bsd) const SOCKADDR_ALIGN: usize = core::mem::size_of::(); + +#[inline] +pub(in crate::os::bsd) fn message_header_len(hdr: &rt_msghdr) -> usize { + hdr.rtm_hdrlen as usize +} + +#[cfg(target_pointer_width = "64")] +const _: [(); 56] = [(); core::mem::size_of::()]; + +#[cfg(target_pointer_width = "64")] +const _: [(); 96] = [(); core::mem::size_of::()]; diff --git a/src/os/unix/interface.rs b/src/os/unix/interface.rs index a637f36..51e363f 100644 --- a/src/os/unix/interface.rs +++ b/src/os/unix/interface.rs @@ -1,8 +1,7 @@ -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::mem::MaybeUninit; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::os::raw::c_char; -use std::str::from_utf8_unchecked; use super::sockaddr::{SockaddrRef, compute_sockaddr_len, netmask_ip_autolen, try_mac_from_raw}; use crate::interface::interface::Interface; @@ -10,7 +9,7 @@ use crate::interface::ipv6_addr_flags::get_ipv6_addr_flags; use crate::interface::mtu::get_mtu; use crate::interface::state::OperState; use crate::ipnet::{Ipv4Net, Ipv6Net}; -use crate::os::unix::types::get_interface_type; +use crate::os::unix::types::{get_interface_type, interface_name_from_ptr}; use crate::stats::counters::{InterfaceStats, get_stats}; #[cfg(target_os = "android")] @@ -43,8 +42,7 @@ fn unix_interfaces_inner( let addr_ref: &libc::ifaddrs = unsafe { &*addr }; let if_type = get_interface_type(addr_ref); 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() }; + let name = interface_name_from_ptr(c_str); let if_index = if_nametoindex_or_zero(&name); let cap: libc::socklen_t = super::sockaddr::sockaddr_storage_cap(); let addr_len_opt = unsafe { compute_sockaddr_len(addr_ref.ifa_addr, None, Some(cap)) }; @@ -122,20 +120,25 @@ fn unix_interfaces_inner( iface.stats = stats; } if let Some(ipv4_addr) = ini_ipv4 { - iface.ipv4.push(ipv4_addr); + push_ipv4(&mut iface.ipv4, ipv4_addr); } if let (Some(ipv6_addr), Some(scope_id)) = (ini_ipv6, ipv6_scope_id) { let af = get_ipv6_addr_flags(&iface.name, &ipv6_addr.addr()); - iface.ipv6.push(ipv6_addr); - iface.ipv6_scope_ids.push(scope_id); - iface.ipv6_addr_flags.push(af); + push_ipv6( + &mut iface.ipv6, + &mut iface.ipv6_scope_ids, + &mut iface.ipv6_addr_flags, + ipv6_addr, + scope_id, + af, + ); } } else { let mtu = get_mtu(addr_ref, &name); - let ini_ipv6_flags = ini_ipv6 - .as_ref() - .map(|net| vec![get_ipv6_addr_flags(&name, &net.addr())]) - .unwrap_or_default(); + let ini_ipv6_flags = match ini_ipv6.as_ref() { + Some(ipv6_addr) => vec![get_ipv6_addr_flags(&name, &ipv6_addr.addr())], + None => Vec::new(), + }; let interface: Interface = Interface { index: if_index, name, @@ -184,6 +187,36 @@ fn unix_interfaces_inner( ifaces } +fn push_ipv4(v: &mut Vec, net: Ipv4Net) -> bool { + if v.iter() + .any(|existing| existing.addr() == net.addr() && existing.prefix_len() == net.prefix_len()) + { + return false; + } + v.push(net); + true +} + +fn push_ipv6( + addrs: &mut Vec, + scope_ids: &mut Vec, + addr_flags: &mut Vec, + net: Ipv6Net, + scope_id: u32, + flags: crate::interface::ipv6_addr_flags::Ipv6AddrFlags, +) -> bool { + if addrs + .iter() + .any(|existing| existing.addr() == net.addr() && existing.prefix_len() == net.prefix_len()) + { + return false; + } + addrs.push(net); + scope_ids.push(scope_id); + addr_flags.push(flags); + true +} + fn if_nametoindex_or_zero(name: &str) -> u32 { match CString::new(name.as_bytes()) { Ok(name) => unsafe { libc::if_nametoindex(name.as_ptr()) }, diff --git a/src/os/unix/types.rs b/src/os/unix/types.rs index 8e0a40e..f9f743e 100644 --- a/src/os/unix/types.rs +++ b/src/os/unix/types.rs @@ -1,15 +1,19 @@ #[cfg(any(target_os = "linux", target_os = "android"))] use crate::interface::types::InterfaceType; +use std::ffi::CStr; +use std::os::raw::c_char; + +pub(crate) fn interface_name_from_ptr(c_str: *const c_char) -> String { + unsafe { CStr::from_ptr(c_str) } + .to_string_lossy() + .into_owned() +} + #[cfg(any(target_os = "linux", target_os = "android"))] pub fn get_interface_type(addr_ref: &libc::ifaddrs) -> InterfaceType { - use std::ffi::CStr; - use std::os::raw::c_char; - use std::str::from_utf8_unchecked; - 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() }; + let name = interface_name_from_ptr(c_str); #[cfg(target_os = "linux")] { crate::os::linux::sysfs::get_interface_type(&name) diff --git a/src/os/windows/interface.rs b/src/os/windows/interface.rs index a8ae1df..9ecc5b2 100644 --- a/src/os/windows/interface.rs +++ b/src/os/windows/interface.rs @@ -177,8 +177,13 @@ pub fn interfaces() -> Vec { .to_string_lossy() .into_owned(); // MAC address - let mac_addr_arr: [u8; 6] = cur.PhysicalAddress[..6].try_into().unwrap_or_default(); - let mac_addr: MacAddr = MacAddr::from_octets(mac_addr_arr); + let mac_addr = if cur.PhysicalAddressLength == 6 { + Some(MacAddr::from_octets( + cur.PhysicalAddress[..6].try_into().unwrap(), + )) + } else { + None + }; let mut ipv4_vec: Vec = vec![]; let mut ipv6_vec: Vec = vec![]; let mut ipv6_scope_id_vec: Vec = vec![]; @@ -254,7 +259,7 @@ pub fn interfaces() -> Vec { friendly_name: Some(unsafe { from_wide_string(cur.FriendlyName) }), description: Some(unsafe { from_wide_string(cur.Description) }), if_type, - mac_addr: Some(mac_addr), + mac_addr, ipv4: ipv4_vec, ipv6: ipv6_vec, ipv6_scope_ids: ipv6_scope_id_vec, diff --git a/src/stats/counters.rs b/src/stats/counters.rs index ef50a47..656b63d 100644 --- a/src/stats/counters.rs +++ b/src/stats/counters.rs @@ -55,14 +55,10 @@ pub(crate) fn get_stats_from_name(name: &str) -> Option { use std::fs::read_to_string; let rx_path = format!("/sys/class/net/{}/statistics/rx_bytes", name); let tx_path = format!("/sys/class/net/{}/statistics/tx_bytes", name); - let rx_bytes = match read_to_string(rx_path) { - Ok(s) => s.trim().parse::().unwrap_or(0), - Err(_) => 0, - }; - let tx_bytes = match read_to_string(tx_path) { - Ok(s) => s.trim().parse::().unwrap_or(0), - Err(_) => 0, - }; + + let rx_bytes = read_to_string(rx_path).ok()?.trim().parse::().ok()?; + let tx_bytes = read_to_string(tx_path).ok()?.trim().parse::().ok()?; + Some(InterfaceStats { rx_bytes, tx_bytes,