use super::serde_ext::*;
use bincode::{deserialize, serialize, Error};
use get_if_addrs::{get_if_addrs, IfAddr};
use pnet::datalink;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::IpAddr;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct InterfaceSnapshot {
pub name: String,
pub is_up: bool,
pub is_loopback: bool,
pub is_multicast: bool,
pub is_broadcast: bool,
pub mac_address: Option<String>,
pub interface_index: Option<u32>,
#[serde(with = "serde_ipaddr_vec")]
pub ip_addresses: Vec<IpAddr>,
#[serde(with = "serde_ipaddr_option")]
pub subnet_mask: Option<IpAddr>,
#[serde(with = "serde_ipaddr_option")]
pub gateway: Option<IpAddr>,
}
impl InterfaceSnapshot {
pub fn serialize_snapshot(snapshot: &Vec<InterfaceSnapshot>) -> Result<Vec<u8>, Error> {
serialize(snapshot)
}
pub fn deserialize_snapshot(data: &[u8]) -> Result<Vec<InterfaceSnapshot>, Error> {
deserialize(data)
}
pub fn take_all() -> Vec<InterfaceSnapshot> {
let interfaces = datalink::interfaces();
let mut iface_map: HashMap<String, InterfaceSnapshot> = HashMap::new();
for iface in interfaces {
iface_map.insert(
iface.name.clone(),
InterfaceSnapshot {
name: iface.name.clone(),
is_up: iface.is_up(),
is_loopback: iface.is_loopback(),
is_multicast: iface.is_multicast(),
is_broadcast: iface.is_broadcast(),
mac_address: iface.mac.as_ref().map(|mac| mac.to_string()),
interface_index: Some(iface.index),
ip_addresses: Vec::new(),
subnet_mask: None,
gateway: None,
},
);
}
if let Ok(if_addrs) = get_if_addrs() {
for iface in if_addrs {
if let Some(entry) = iface_map.get_mut(&iface.name) {
match iface.addr {
IfAddr::V4(ipv4) => {
entry.ip_addresses.push(IpAddr::V4(ipv4.ip));
entry.subnet_mask = Some(IpAddr::V4(ipv4.netmask));
}
IfAddr::V6(ipv6) => {
entry.ip_addresses.push(IpAddr::V6(ipv6.ip));
}
}
}
}
}
iface_map.into_values().collect()
}
}
#[cfg(test)]
mod tests {
use super::InterfaceSnapshot;
use bincode;
#[test]
fn test_interface_snapshot_serialize_deserialize() {
let snapshot = vec![InterfaceSnapshot {
name: "eth0".to_string(),
is_up: true,
is_loopback: false,
is_multicast: true,
is_broadcast: true,
mac_address: Some("00:1A:2B:3C:4D:5E".to_string()),
interface_index: Some(1),
ip_addresses: vec![
"192.168.1.100".parse().unwrap(),
"10.0.0.1".parse().unwrap(),
],
subnet_mask: Some("255.255.255.0".parse().unwrap()),
gateway: Some("192.168.1.1".parse().unwrap()),
}];
let serialized = InterfaceSnapshot::serialize_snapshot(&snapshot).unwrap();
assert!(!serialized.is_empty(), "Serialization should produce data");
let deserialized = InterfaceSnapshot::deserialize_snapshot(&serialized).unwrap();
assert_eq!(
deserialized, snapshot,
"Deserialized snapshot should match original"
);
}
#[test]
fn test_empty_interface_snapshot_serialize_deserialize() {
let snapshot: Vec<InterfaceSnapshot> = vec![];
let serialized = InterfaceSnapshot::serialize_snapshot(&snapshot).unwrap();
assert!(!serialized.is_empty(), "Serialization should produce data");
let deserialized = InterfaceSnapshot::deserialize_snapshot(&serialized).unwrap();
assert!(
deserialized.is_empty(),
"Deserialized snapshot should be empty"
);
}
#[test]
fn test_take_all_interfaces() {
let snapshot = InterfaceSnapshot::take_all();
assert!(
!snapshot.is_empty(),
"At least one network interface should be detected"
);
}
#[test]
fn test_serde_ipaddr_option_fields() {
let snapshot = InterfaceSnapshot {
name: "eth0".to_string(),
is_up: true,
is_loopback: false,
is_multicast: true,
is_broadcast: true,
mac_address: Some("00:1A:2B:3C:4D:5E".to_string()),
interface_index: Some(1),
ip_addresses: vec!["192.168.1.100".parse().unwrap()],
subnet_mask: None,
gateway: Some("192.168.1.1".parse().unwrap()),
};
let serialized = bincode::serialize(&snapshot).unwrap();
let deserialized: InterfaceSnapshot = bincode::deserialize(&serialized).unwrap();
assert_eq!(
deserialized.subnet_mask, None,
"Subnet mask should remain `None` after deserialization"
);
assert_eq!(
deserialized.gateway,
Some("192.168.1.1".parse().unwrap()),
"Gateway should match after deserialization"
);
}
#[test]
fn test_serde_ipaddr_vec_fields() {
let snapshot = InterfaceSnapshot {
name: "eth0".to_string(),
is_up: true,
is_loopback: false,
is_multicast: true,
is_broadcast: true,
mac_address: Some("00:1A:2B:3C:4D:5E".to_string()),
interface_index: Some(1),
ip_addresses: vec![
"192.168.1.100".parse().unwrap(),
"10.0.0.1".parse().unwrap(),
],
subnet_mask: Some("255.255.255.0".parse().unwrap()),
gateway: None,
};
let serialized = bincode::serialize(&snapshot).unwrap();
let deserialized: InterfaceSnapshot = bincode::deserialize(&serialized).unwrap();
assert_eq!(
deserialized.ip_addresses.len(),
2,
"There should be exactly 2 IP addresses"
);
assert_eq!(
deserialized.ip_addresses, snapshot.ip_addresses,
"IP addresses should match after deserialization"
);
}
}