236 lines
7.3 KiB
Rust
236 lines
7.3 KiB
Rust
use std::error::Error;
|
|
use std::result::Result;
|
|
use std::time::Instant;
|
|
|
|
/// # Network Hardware Module
|
|
///
|
|
/// This module provides network information collection for WatcherAgent, including interface enumeration and bandwidth statistics.
|
|
///
|
|
/// ## Responsibilities
|
|
/// - **Interface Detection:** Lists all network interfaces.
|
|
/// - **Bandwidth Monitoring:** Tracks receive/transmit rates using a rolling monitor.
|
|
/// - **Error Handling:** Graceful fallback if metrics are unavailable.
|
|
///
|
|
/// ## Units
|
|
/// - `rx_rate`, `tx_rate`: Network bandwidth in **bytes per second (B/s)**
|
|
///
|
|
/// Network statistics for the host system.
|
|
///
|
|
/// # Fields
|
|
/// - `interfaces`: List of network interface names (strings)
|
|
/// - `rx_rate`: Receive bandwidth in **bytes per second (B/s)**
|
|
/// - `tx_rate`: Transmit bandwidth in **bytes per second (B/s)**
|
|
#[derive(Debug)]
|
|
pub struct NetworkInfo {
|
|
pub interfaces: Option<Vec<String>>,
|
|
pub rx_rate: Option<f64>,
|
|
pub tx_rate: Option<f64>,
|
|
}
|
|
|
|
|
|
/// Rolling monitor for network bandwidth statistics.
|
|
///
|
|
/// # Fields
|
|
/// - `prev_rx`: Previous received bytes
|
|
/// - `prev_tx`: Previous transmitted bytes
|
|
/// - `last_update`: Timestamp of last update
|
|
#[derive(Debug)]
|
|
pub struct NetworkMonitor {
|
|
prev_rx: u64,
|
|
prev_tx: u64,
|
|
last_update: Instant,
|
|
}
|
|
|
|
impl Default for NetworkMonitor {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl NetworkMonitor {
|
|
/// Creates a new `NetworkMonitor` for bandwidth tracking.
|
|
pub fn new() -> Self {
|
|
Self {
|
|
prev_rx: 0,
|
|
prev_tx: 0,
|
|
last_update: Instant::now(),
|
|
}
|
|
}
|
|
|
|
/// Updates the network usage statistics and returns current rx/tx rates.
|
|
///
|
|
/// # Returns
|
|
/// * `Result<(f64, f64), Box<dyn Error>>` - Tuple of (rx_rate, tx_rate) in bytes per second.
|
|
pub fn update_usage(&mut self) -> Result<(f64, f64), Box<dyn Error>> {
|
|
let (current_rx, current_tx) = get_network_bytes()?;
|
|
let elapsed = self.last_update.elapsed().as_secs_f64();
|
|
self.last_update = Instant::now();
|
|
|
|
let rx_rate = if current_rx >= self.prev_rx {
|
|
(current_rx - self.prev_rx) as f64 / elapsed
|
|
} else {
|
|
0.0
|
|
};
|
|
|
|
let tx_rate = if current_tx >= self.prev_tx {
|
|
(current_tx - self.prev_tx) as f64 / elapsed
|
|
} else {
|
|
0.0
|
|
};
|
|
|
|
self.prev_rx = current_rx;
|
|
self.prev_tx = current_tx;
|
|
|
|
Ok((rx_rate, tx_rate))
|
|
}
|
|
}
|
|
|
|
/// Collects network information (interfaces, rx/tx rates) using a monitor.
|
|
///
|
|
/// # Arguments
|
|
/// * `monitor` - Mutable reference to a `NetworkMonitor`
|
|
///
|
|
/// # Returns
|
|
/// * `Result<NetworkInfo, Box<dyn Error>>` - Network statistics or error if unavailable.
|
|
pub async fn get_network_info(monitor: &mut NetworkMonitor) -> Result<NetworkInfo, Box<dyn Error>> {
|
|
let (rx_rate, tx_rate) = monitor.update_usage()?;
|
|
Ok(NetworkInfo {
|
|
interfaces: Some(get_network_interfaces()),
|
|
rx_rate: Some(rx_rate),
|
|
tx_rate: Some(tx_rate),
|
|
})
|
|
}
|
|
|
|
fn get_network_bytes() -> Result<(u64, u64), Box<dyn Error>> {
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
use std::ptr::null_mut;
|
|
use winapi::shared::ifmib::MIB_IFTABLE;
|
|
use winapi::um::iphlpapi::GetIfTable;
|
|
|
|
unsafe {
|
|
// Erste Abfrage zur Bestimmung der benötigten Puffergröße
|
|
let mut buffer_size = 0u32;
|
|
if GetIfTable(null_mut(), &mut buffer_size, 0)
|
|
!= winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER
|
|
{
|
|
return Err(
|
|
anyhow::anyhow!("Failed to get buffer size for network interfaces").into(),
|
|
);
|
|
}
|
|
|
|
// Puffer allozieren
|
|
let mut buffer = vec![0u8; buffer_size as usize];
|
|
let if_table = buffer.as_mut_ptr() as *mut MIB_IFTABLE;
|
|
|
|
// Tatsächliche Daten abrufen
|
|
if GetIfTable(if_table, &mut buffer_size, 0) != 0 {
|
|
return Err(anyhow::anyhow!("Failed to get network interface table").into());
|
|
}
|
|
|
|
// Daten auswerten
|
|
let mut rx_total = 0u64;
|
|
let mut tx_total = 0u64;
|
|
|
|
for i in 0..(*if_table).dwNumEntries {
|
|
let row = &*((*if_table).table.as_ptr().offset(i as isize));
|
|
rx_total += row.dwInOctets as u64;
|
|
tx_total += row.dwOutOctets as u64;
|
|
}
|
|
|
|
if rx_total == 0 && tx_total == 0 {
|
|
Err(anyhow::anyhow!("No network data available").into())
|
|
} else {
|
|
Ok((rx_total, tx_total))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
use std::fs;
|
|
|
|
let mut rx_total = 0u64;
|
|
let mut tx_total = 0u64;
|
|
if let Ok(dir) = fs::read_dir("/sys/class/net") {
|
|
for entry in dir.flatten() {
|
|
let iface = entry.file_name();
|
|
let iface_name = iface.to_string_lossy();
|
|
|
|
// Ignoriere virtuelle Interfaces
|
|
if !iface_name.starts_with("lo") && !iface_name.starts_with("virbr") {
|
|
if let (Ok(rx), Ok(tx)) = (
|
|
fs::read_to_string(entry.path().join("statistics/rx_bytes")),
|
|
fs::read_to_string(entry.path().join("statistics/tx_bytes")),
|
|
) {
|
|
rx_total += rx.trim().parse::<u64>().unwrap_or(0);
|
|
tx_total += tx.trim().parse::<u64>().unwrap_or(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if rx_total == 0 && tx_total == 0 {
|
|
return Err(anyhow::anyhow!("No network data available").into());
|
|
} else {
|
|
return Ok((rx_total, tx_total));
|
|
}
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
|
Err("No network data available for this OS".into())
|
|
}
|
|
|
|
fn get_network_interfaces() -> Vec<String> {
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
use std::ffi::CStr;
|
|
use std::ptr::null_mut;
|
|
use winapi::shared::ifmib::MIB_IFTABLE;
|
|
use winapi::um::iphlpapi::GetIfTable;
|
|
|
|
unsafe {
|
|
let mut buffer_size = 0u32;
|
|
if GetIfTable(null_mut(), &mut buffer_size, 0)
|
|
!= winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER
|
|
{
|
|
return vec![];
|
|
}
|
|
|
|
let mut buffer = vec![0u8; buffer_size as usize];
|
|
let if_table = buffer.as_mut_ptr() as *mut MIB_IFTABLE;
|
|
|
|
if GetIfTable(if_table, &mut buffer_size, 0) != 0 {
|
|
return vec![];
|
|
}
|
|
|
|
(0..(*if_table).dwNumEntries)
|
|
.map(|i| {
|
|
let row = &*((*if_table).table.as_ptr().offset(i as isize));
|
|
let descr = CStr::from_ptr(row.bDescr.as_ptr() as *const i8)
|
|
.to_string_lossy()
|
|
.into_owned();
|
|
descr.trim().to_string()
|
|
})
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
use std::fs;
|
|
|
|
let mut interfaces = vec![];
|
|
if let Ok(dir) = fs::read_dir("/sys/class/net") {
|
|
for entry in dir.flatten() {
|
|
let iface = entry.file_name();
|
|
interfaces.push(iface.to_string_lossy().to_string());
|
|
}
|
|
}
|
|
interfaces
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
|
vec![]
|
|
}
|