use crate::sys::ffi;
use libc::{self, c_char, CTL_NET, NET_RT_IFLIST2, PF_ROUTE, RTM_IFINFO2};
use std::collections::HashMap;
use std::ptr::null_mut;
use crate::{NetworkExt, NetworksExt, NetworksIter};
macro_rules! old_and_new {
($ty_:expr, $name:ident, $old:ident, $new_val:expr) => {{
$ty_.$old = $ty_.$name;
$ty_.$name = $new_val;
}};
}
pub struct Networks {
interfaces: HashMap<String, NetworkData>,
}
impl Networks {
pub(crate) fn new() -> Self {
Networks {
interfaces: HashMap::new(),
}
}
#[allow(clippy::cast_ptr_alignment)]
fn update_networks(&mut self) {
let mib = &mut [CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0];
let mut len = 0;
if unsafe { libc::sysctl(mib.as_mut_ptr(), 6, null_mut(), &mut len, null_mut(), 0) } < 0 {
return;
}
let mut buf = Vec::with_capacity(len);
unsafe {
buf.set_len(len);
if libc::sysctl(
mib.as_mut_ptr(),
6,
buf.as_mut_ptr(),
&mut len,
null_mut(),
0,
) < 0
{
return;
}
}
let buf = buf.as_ptr() as *const c_char;
let lim = unsafe { buf.add(len) };
let mut next = buf;
while next < lim {
unsafe {
let ifm = next as *const libc::if_msghdr;
next = next.offset((*ifm).ifm_msglen as isize);
if (*ifm).ifm_type == RTM_IFINFO2 as u8 {
let mut name = vec![0u8; libc::IFNAMSIZ + 6];
let if2m: *const ffi::if_msghdr2 = ifm as *const ffi::if_msghdr2;
let pname =
libc::if_indextoname((*if2m).ifm_index as _, name.as_mut_ptr() as _);
if pname.is_null() {
continue;
}
name.set_len(libc::strlen(pname));
let name = String::from_utf8_unchecked(name);
let interface = self.interfaces.entry(name).or_insert_with(|| NetworkData {
current_in: (*if2m).ifm_data.ifi_ibytes,
old_in: 0,
current_out: (*if2m).ifm_data.ifi_obytes,
old_out: 0,
packets_in: (*if2m).ifm_data.ifi_ipackets,
old_packets_in: 0,
packets_out: (*if2m).ifm_data.ifi_opackets,
old_packets_out: 0,
errors_in: (*if2m).ifm_data.ifi_ierrors,
old_errors_in: 0,
errors_out: (*if2m).ifm_data.ifi_oerrors,
old_errors_out: 0,
updated: true,
});
old_and_new!(interface, current_out, old_out, (*if2m).ifm_data.ifi_obytes);
old_and_new!(interface, current_in, old_in, (*if2m).ifm_data.ifi_ibytes);
old_and_new!(
interface,
packets_in,
old_packets_in,
(*if2m).ifm_data.ifi_ipackets
);
old_and_new!(
interface,
packets_out,
old_packets_out,
(*if2m).ifm_data.ifi_opackets
);
old_and_new!(
interface,
errors_in,
old_errors_in,
(*if2m).ifm_data.ifi_ierrors
);
old_and_new!(
interface,
errors_out,
old_errors_out,
(*if2m).ifm_data.ifi_oerrors
);
interface.updated = true;
}
}
}
}
}
impl NetworksExt for Networks {
#[allow(clippy::needless_lifetimes)]
fn iter<'a>(&'a self) -> NetworksIter<'a> {
NetworksIter::new(self.interfaces.iter())
}
fn refresh_networks_list(&mut self) {
for (_, data) in self.interfaces.iter_mut() {
data.updated = false;
}
self.update_networks();
self.interfaces.retain(|_, data| data.updated);
}
fn refresh(&mut self) {
self.update_networks();
}
}
#[derive(PartialEq, Eq)]
pub struct NetworkData {
current_in: u64,
old_in: u64,
current_out: u64,
old_out: u64,
packets_in: u64,
old_packets_in: u64,
packets_out: u64,
old_packets_out: u64,
errors_in: u64,
old_errors_in: u64,
errors_out: u64,
old_errors_out: u64,
updated: bool,
}
impl NetworkExt for NetworkData {
fn get_received(&self) -> u64 {
self.current_in - self.old_in
}
fn get_total_received(&self) -> u64 {
self.current_in
}
fn get_transmitted(&self) -> u64 {
self.current_out - self.old_out
}
fn get_total_transmitted(&self) -> u64 {
self.current_out
}
fn get_packets_received(&self) -> u64 {
self.packets_in - self.old_packets_in
}
fn get_total_packets_received(&self) -> u64 {
self.packets_in
}
fn get_packets_transmitted(&self) -> u64 {
self.packets_out - self.old_packets_out
}
fn get_total_packets_transmitted(&self) -> u64 {
self.packets_out
}
fn get_errors_on_received(&self) -> u64 {
self.errors_in - self.old_errors_in
}
fn get_total_errors_on_received(&self) -> u64 {
self.errors_in
}
fn get_errors_on_transmitted(&self) -> u64 {
self.errors_out - self.old_errors_out
}
fn get_total_errors_on_transmitted(&self) -> u64 {
self.errors_out
}
}