// Take a look at the license at the top of the repository in the LICENSE file. use std::collections::{hash_map, HashMap}; use std::mem::MaybeUninit; use super::utils; use crate::{NetworkExt, NetworksExt, NetworksIter}; macro_rules! old_and_new { ($ty_:expr, $name:ident, $old:ident, $data:expr) => {{ $ty_.$old = $ty_.$name; $ty_.$name = $data.$name; }}; } #[doc = include_str!("../../md_doc/networks.md")] pub struct Networks { interfaces: HashMap, } impl Networks { pub(crate) fn new() -> Networks { Networks { interfaces: HashMap::new(), } } } impl NetworksExt for Networks { fn iter(&self) -> NetworksIter { NetworksIter::new(self.interfaces.iter()) } fn refresh_networks_list(&mut self) { unsafe { self.refresh_interfaces(true); } // Remove interfaces which are gone. self.interfaces.retain(|_, n| n.updated); } fn refresh(&mut self) { unsafe { self.refresh_interfaces(false); } } } impl Networks { unsafe fn refresh_interfaces(&mut self, refresh_all: bool) { let mut nb_interfaces: libc::c_int = 0; if !utils::get_sys_value( &[ libc::CTL_NET, libc::PF_LINK, libc::NETLINK_GENERIC, libc::IFMIB_SYSTEM, libc::IFMIB_IFCOUNT, ], &mut nb_interfaces, ) { return; } if refresh_all { // We don't need to update this value if we're not updating all interfaces. for interface in self.interfaces.values_mut() { interface.updated = false; } } let mut data: libc::ifmibdata = MaybeUninit::zeroed().assume_init(); for row in 1..nb_interfaces { let mib = [ libc::CTL_NET, libc::PF_LINK, libc::NETLINK_GENERIC, libc::IFMIB_IFDATA, row, libc::IFDATA_GENERAL, ]; if !utils::get_sys_value(&mib, &mut data) { continue; } if let Some(name) = utils::c_buf_to_string(&data.ifmd_name) { let data = &data.ifmd_data; match self.interfaces.entry(name) { hash_map::Entry::Occupied(mut e) => { let mut interface = e.get_mut(); old_and_new!(interface, ifi_ibytes, old_ifi_ibytes, data); old_and_new!(interface, ifi_obytes, old_ifi_obytes, data); old_and_new!(interface, ifi_ipackets, old_ifi_ipackets, data); old_and_new!(interface, ifi_opackets, old_ifi_opackets, data); old_and_new!(interface, ifi_ierrors, old_ifi_ierrors, data); old_and_new!(interface, ifi_oerrors, old_ifi_oerrors, data); interface.updated = true; } hash_map::Entry::Vacant(e) => { if !refresh_all { // This is simply a refresh, we don't want to add new interfaces! continue; } e.insert(NetworkData { ifi_ibytes: data.ifi_ibytes, old_ifi_ibytes: 0, ifi_obytes: data.ifi_obytes, old_ifi_obytes: 0, ifi_ipackets: data.ifi_ipackets, old_ifi_ipackets: 0, ifi_opackets: data.ifi_opackets, old_ifi_opackets: 0, ifi_ierrors: data.ifi_ierrors, old_ifi_ierrors: 0, ifi_oerrors: data.ifi_oerrors, old_ifi_oerrors: 0, updated: true, }); } } } } } } #[doc = include_str!("../../md_doc/network_data.md")] pub struct NetworkData { /// Total number of bytes received over interface. ifi_ibytes: u64, old_ifi_ibytes: u64, /// Total number of bytes transmitted over interface. ifi_obytes: u64, old_ifi_obytes: u64, /// Total number of packets received. ifi_ipackets: u64, old_ifi_ipackets: u64, /// Total number of packets transmitted. ifi_opackets: u64, old_ifi_opackets: u64, /// Shows the total number of packets received with error. This includes /// too-long-frames errors, ring-buffer overflow errors, CRC errors, /// frame alignment errors, fifo overruns, and missed packets. ifi_ierrors: u64, old_ifi_ierrors: u64, /// similar to `ifi_ierrors` ifi_oerrors: u64, old_ifi_oerrors: u64, /// Whether or not the above data has been updated during refresh updated: bool, } impl NetworkExt for NetworkData { fn received(&self) -> u64 { self.ifi_ibytes.saturating_sub(self.old_ifi_ibytes) } fn total_received(&self) -> u64 { self.ifi_ibytes } fn transmitted(&self) -> u64 { self.ifi_obytes.saturating_sub(self.old_ifi_obytes) } fn total_transmitted(&self) -> u64 { self.ifi_obytes } fn packets_received(&self) -> u64 { self.ifi_ipackets.saturating_sub(self.old_ifi_ipackets) } fn total_packets_received(&self) -> u64 { self.ifi_ipackets } fn packets_transmitted(&self) -> u64 { self.ifi_opackets.saturating_sub(self.old_ifi_opackets) } fn total_packets_transmitted(&self) -> u64 { self.ifi_opackets } fn errors_on_received(&self) -> u64 { self.ifi_ierrors.saturating_sub(self.old_ifi_ierrors) } fn total_errors_on_received(&self) -> u64 { self.ifi_ierrors } fn errors_on_transmitted(&self) -> u64 { self.ifi_oerrors.saturating_sub(self.old_ifi_oerrors) } fn total_errors_on_transmitted(&self) -> u64 { self.ifi_oerrors } }