nullnet_libconfmon/interface_snapshot/
snapshot.rs1use super::serde_ext::*;
2use bincode::{deserialize, serialize, Error};
3use get_if_addrs::{get_if_addrs, IfAddr};
4use pnet::datalink;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::net::IpAddr;
8
9#[derive(Debug, Serialize, Deserialize, PartialEq)]
11pub struct InterfaceSnapshot {
12 pub name: String,
13 pub is_up: bool,
14 pub is_loopback: bool,
15 pub is_multicast: bool,
16 pub is_broadcast: bool,
17 pub mac_address: Option<String>,
18 pub interface_index: Option<u32>,
19 #[serde(with = "serde_ipaddr_vec")]
20 pub ip_addresses: Vec<IpAddr>,
21 #[serde(with = "serde_ipaddr_option")]
22 pub subnet_mask: Option<IpAddr>,
23 #[serde(with = "serde_ipaddr_option")]
24 pub gateway: Option<IpAddr>,
25}
26
27impl InterfaceSnapshot {
28 pub fn serialize_snapshot(snapshot: &Vec<InterfaceSnapshot>) -> Result<Vec<u8>, Error> {
37 serialize(snapshot)
38 }
39
40 pub fn deserialize_snapshot(data: &[u8]) -> Result<Vec<InterfaceSnapshot>, Error> {
49 deserialize(data)
50 }
51
52 pub fn take_all() -> Vec<InterfaceSnapshot> {
60 let interfaces = datalink::interfaces();
61 let mut iface_map: HashMap<String, InterfaceSnapshot> = HashMap::new();
62
63 for iface in interfaces {
64 iface_map.insert(
65 iface.name.clone(),
66 InterfaceSnapshot {
67 name: iface.name.clone(),
68 is_up: iface.is_up(),
69 is_loopback: iface.is_loopback(),
70 is_multicast: iface.is_multicast(),
71 is_broadcast: iface.is_broadcast(),
72 mac_address: iface.mac.as_ref().map(|mac| mac.to_string()),
73 interface_index: Some(iface.index),
74 ip_addresses: Vec::new(),
75 subnet_mask: None,
76 gateway: None,
77 },
78 );
79 }
80
81 if let Ok(if_addrs) = get_if_addrs() {
82 for iface in if_addrs {
83 if let Some(entry) = iface_map.get_mut(&iface.name) {
84 match iface.addr {
85 IfAddr::V4(ipv4) => {
86 entry.ip_addresses.push(IpAddr::V4(ipv4.ip));
87 entry.subnet_mask = Some(IpAddr::V4(ipv4.netmask));
88 }
89 IfAddr::V6(ipv6) => {
90 entry.ip_addresses.push(IpAddr::V6(ipv6.ip));
91 }
92 }
93 }
94 }
95 }
96
97 iface_map.into_values().collect()
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::InterfaceSnapshot;
104 use bincode;
105
106 #[test]
107 fn test_interface_snapshot_serialize_deserialize() {
108 let snapshot = vec![InterfaceSnapshot {
109 name: "eth0".to_string(),
110 is_up: true,
111 is_loopback: false,
112 is_multicast: true,
113 is_broadcast: true,
114 mac_address: Some("00:1A:2B:3C:4D:5E".to_string()),
115 interface_index: Some(1),
116 ip_addresses: vec![
117 "192.168.1.100".parse().unwrap(),
118 "10.0.0.1".parse().unwrap(),
119 ],
120 subnet_mask: Some("255.255.255.0".parse().unwrap()),
121 gateway: Some("192.168.1.1".parse().unwrap()),
122 }];
123
124 let serialized = InterfaceSnapshot::serialize_snapshot(&snapshot).unwrap();
125 assert!(!serialized.is_empty(), "Serialization should produce data");
126
127 let deserialized = InterfaceSnapshot::deserialize_snapshot(&serialized).unwrap();
128 assert_eq!(
129 deserialized, snapshot,
130 "Deserialized snapshot should match original"
131 );
132 }
133
134 #[test]
135 fn test_empty_interface_snapshot_serialize_deserialize() {
136 let snapshot: Vec<InterfaceSnapshot> = vec![];
137
138 let serialized = InterfaceSnapshot::serialize_snapshot(&snapshot).unwrap();
139 assert!(!serialized.is_empty(), "Serialization should produce data");
140
141 let deserialized = InterfaceSnapshot::deserialize_snapshot(&serialized).unwrap();
142 assert!(
143 deserialized.is_empty(),
144 "Deserialized snapshot should be empty"
145 );
146 }
147
148 #[test]
149 fn test_take_all_interfaces() {
150 let snapshot = InterfaceSnapshot::take_all();
151 assert!(
152 !snapshot.is_empty(),
153 "At least one network interface should be detected"
154 );
155 }
156
157 #[test]
158 fn test_serde_ipaddr_option_fields() {
159 let snapshot = InterfaceSnapshot {
160 name: "eth0".to_string(),
161 is_up: true,
162 is_loopback: false,
163 is_multicast: true,
164 is_broadcast: true,
165 mac_address: Some("00:1A:2B:3C:4D:5E".to_string()),
166 interface_index: Some(1),
167 ip_addresses: vec!["192.168.1.100".parse().unwrap()],
168 subnet_mask: None,
169 gateway: Some("192.168.1.1".parse().unwrap()),
170 };
171
172 let serialized = bincode::serialize(&snapshot).unwrap();
173 let deserialized: InterfaceSnapshot = bincode::deserialize(&serialized).unwrap();
174
175 assert_eq!(
176 deserialized.subnet_mask, None,
177 "Subnet mask should remain `None` after deserialization"
178 );
179 assert_eq!(
180 deserialized.gateway,
181 Some("192.168.1.1".parse().unwrap()),
182 "Gateway should match after deserialization"
183 );
184 }
185
186 #[test]
188 fn test_serde_ipaddr_vec_fields() {
189 let snapshot = InterfaceSnapshot {
190 name: "eth0".to_string(),
191 is_up: true,
192 is_loopback: false,
193 is_multicast: true,
194 is_broadcast: true,
195 mac_address: Some("00:1A:2B:3C:4D:5E".to_string()),
196 interface_index: Some(1),
197 ip_addresses: vec![
198 "192.168.1.100".parse().unwrap(),
199 "10.0.0.1".parse().unwrap(),
200 ],
201 subnet_mask: Some("255.255.255.0".parse().unwrap()),
202 gateway: None,
203 };
204
205 let serialized = bincode::serialize(&snapshot).unwrap();
206 let deserialized: InterfaceSnapshot = bincode::deserialize(&serialized).unwrap();
207
208 assert_eq!(
209 deserialized.ip_addresses.len(),
210 2,
211 "There should be exactly 2 IP addresses"
212 );
213 assert_eq!(
214 deserialized.ip_addresses, snapshot.ip_addresses,
215 "IP addresses should match after deserialization"
216 );
217 }
218}