blob: bad2f32d38937bf08c245b6e45feff4428dd30e3 [file] [log] [blame]
// 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::common::MacAddr;
use crate::network::refresh_networks_addresses;
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<String, NetworkData>,
}
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);
refresh_networks_addresses(&mut self.interfaces);
}
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 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,
mac_addr: MacAddr::UNSPECIFIED,
});
}
}
}
}
}
}
#[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,
/// MAC address
pub(crate) mac_addr: MacAddr,
}
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
}
fn mac_address(&self) -> MacAddr {
self.mac_addr
}
}