1use std::collections::{hash_map, HashMap};
4use std::fs::File;
5use std::io::Read;
6use std::path::Path;
7
8use crate::common::MacAddr;
9use crate::network::refresh_networks_addresses;
10use crate::NetworkData;
11
12macro_rules! old_and_new {
13 ($ty_:expr, $name:ident, $old:ident) => {{
14 $ty_.$old = $ty_.$name;
15 $ty_.$name = $name;
16 }};
17 ($ty_:expr, $name:ident, $old:ident, $path:expr) => {{
18 let _tmp = $path;
19 $ty_.$old = $ty_.$name;
20 $ty_.$name = _tmp;
21 }};
22}
23
24#[allow(clippy::ptr_arg)]
25fn read<P: AsRef<Path>>(parent: P, path: &str, data: &mut Vec<u8>) -> u64 {
26 if let Ok(mut f) = File::open(parent.as_ref().join(path)) {
27 if let Ok(size) = f.read(data) {
28 let mut i = 0;
29 let mut ret = 0;
30
31 while i < size && i < data.len() && data[i] >= b'0' && data[i] <= b'9' {
32 ret *= 10;
33 ret += (data[i] - b'0') as u64;
34 i += 1;
35 }
36 return ret;
37 }
38 }
39 0
40}
41
42fn refresh_networks_list_from_sysfs(
43 interfaces: &mut HashMap<String, NetworkData>,
44 sysfs_net: &Path,
45) {
46 if let Ok(dir) = std::fs::read_dir(sysfs_net) {
47 let mut data = vec![0; 30];
48
49 for stats in interfaces.values_mut() {
50 stats.inner.updated = false;
51 }
52
53 for entry in dir.flatten() {
54 let parent = &entry.path().join("statistics");
55 let entry = match entry.file_name().into_string() {
56 Ok(entry) => entry,
57 Err(_) => continue,
58 };
59 let rx_bytes = read(parent, "rx_bytes", &mut data);
60 let tx_bytes = read(parent, "tx_bytes", &mut data);
61 let rx_packets = read(parent, "rx_packets", &mut data);
62 let tx_packets = read(parent, "tx_packets", &mut data);
63 let rx_errors = read(parent, "rx_errors", &mut data);
64 let tx_errors = read(parent, "tx_errors", &mut data);
65 match interfaces.entry(entry) {
68 hash_map::Entry::Occupied(mut e) => {
69 let interface = e.get_mut();
70 let interface = &mut interface.inner;
71
72 old_and_new!(interface, rx_bytes, old_rx_bytes);
73 old_and_new!(interface, tx_bytes, old_tx_bytes);
74 old_and_new!(interface, rx_packets, old_rx_packets);
75 old_and_new!(interface, tx_packets, old_tx_packets);
76 old_and_new!(interface, rx_errors, old_rx_errors);
77 old_and_new!(interface, tx_errors, old_tx_errors);
78 interface.updated = true;
81 }
82 hash_map::Entry::Vacant(e) => {
83 e.insert(NetworkData {
84 inner: NetworkDataInner {
85 rx_bytes,
86 old_rx_bytes: rx_bytes,
87 tx_bytes,
88 old_tx_bytes: tx_bytes,
89 rx_packets,
90 old_rx_packets: rx_packets,
91 tx_packets,
92 old_tx_packets: tx_packets,
93 rx_errors,
94 old_rx_errors: rx_errors,
95 tx_errors,
96 old_tx_errors: tx_errors,
97 mac_addr: MacAddr::UNSPECIFIED,
98 updated: true,
103 },
104 });
105 }
106 };
107 }
108
109 interfaces.retain(|_, d| d.inner.updated);
111 }
112}
113
114pub(crate) struct NetworksInner {
115 pub(crate) interfaces: HashMap<String, NetworkData>,
116}
117
118impl NetworksInner {
119 pub(crate) fn new() -> Self {
120 Self {
121 interfaces: HashMap::new(),
122 }
123 }
124
125 pub(crate) fn list(&self) -> &HashMap<String, NetworkData> {
126 &self.interfaces
127 }
128
129 pub(crate) fn refresh(&mut self) {
130 let mut v = vec![0; 30];
131
132 for (interface_name, data) in self.interfaces.iter_mut() {
133 data.inner.update(interface_name, &mut v);
134 }
135 }
136
137 pub(crate) fn refresh_list(&mut self) {
138 refresh_networks_list_from_sysfs(&mut self.interfaces, Path::new("/sys/class/net/"));
139 refresh_networks_addresses(&mut self.interfaces);
140 }
141}
142
143pub(crate) struct NetworkDataInner {
144 rx_bytes: u64,
146 old_rx_bytes: u64,
147 tx_bytes: u64,
149 old_tx_bytes: u64,
150 rx_packets: u64,
152 old_rx_packets: u64,
153 tx_packets: u64,
155 old_tx_packets: u64,
156 rx_errors: u64,
160 old_rx_errors: u64,
161 tx_errors: u64,
163 old_tx_errors: u64,
164 pub(crate) mac_addr: MacAddr,
166 updated: bool,
178}
179
180impl NetworkDataInner {
181 fn update(&mut self, path: &str, data: &mut Vec<u8>) {
182 let path = &Path::new("/sys/class/net/").join(path).join("statistics");
183 old_and_new!(self, rx_bytes, old_rx_bytes, read(path, "rx_bytes", data));
184 old_and_new!(self, tx_bytes, old_tx_bytes, read(path, "tx_bytes", data));
185 old_and_new!(
186 self,
187 rx_packets,
188 old_rx_packets,
189 read(path, "rx_packets", data)
190 );
191 old_and_new!(
192 self,
193 tx_packets,
194 old_tx_packets,
195 read(path, "tx_packets", data)
196 );
197 old_and_new!(
198 self,
199 rx_errors,
200 old_rx_errors,
201 read(path, "rx_errors", data)
202 );
203 old_and_new!(
204 self,
205 tx_errors,
206 old_tx_errors,
207 read(path, "tx_errors", data)
208 );
209 }
222
223 pub(crate) fn received(&self) -> u64 {
224 self.rx_bytes.saturating_sub(self.old_rx_bytes)
225 }
226
227 pub(crate) fn total_received(&self) -> u64 {
228 self.rx_bytes
229 }
230
231 pub(crate) fn transmitted(&self) -> u64 {
232 self.tx_bytes.saturating_sub(self.old_tx_bytes)
233 }
234
235 pub(crate) fn total_transmitted(&self) -> u64 {
236 self.tx_bytes
237 }
238
239 pub(crate) fn packets_received(&self) -> u64 {
240 self.rx_packets.saturating_sub(self.old_rx_packets)
241 }
242
243 pub(crate) fn total_packets_received(&self) -> u64 {
244 self.rx_packets
245 }
246
247 pub(crate) fn packets_transmitted(&self) -> u64 {
248 self.tx_packets.saturating_sub(self.old_tx_packets)
249 }
250
251 pub(crate) fn total_packets_transmitted(&self) -> u64 {
252 self.tx_packets
253 }
254
255 pub(crate) fn errors_on_received(&self) -> u64 {
256 self.rx_errors.saturating_sub(self.old_rx_errors)
257 }
258
259 pub(crate) fn total_errors_on_received(&self) -> u64 {
260 self.rx_errors
261 }
262
263 pub(crate) fn errors_on_transmitted(&self) -> u64 {
264 self.tx_errors.saturating_sub(self.old_tx_errors)
265 }
266
267 pub(crate) fn total_errors_on_transmitted(&self) -> u64 {
268 self.tx_errors
269 }
270
271 pub(crate) fn mac_address(&self) -> MacAddr {
272 self.mac_addr
273 }
274}
275
276#[cfg(test)]
277mod test {
278 use super::refresh_networks_list_from_sysfs;
279 use std::collections::HashMap;
280 use std::fs;
281
282 #[test]
283 fn refresh_networks_list_add_interface() {
284 let sys_net_dir = tempfile::tempdir().expect("failed to create temporary directory");
285
286 fs::create_dir(sys_net_dir.path().join("itf1")).expect("failed to create subdirectory");
287
288 let mut interfaces = HashMap::new();
289
290 refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
291 assert_eq!(interfaces.keys().collect::<Vec<_>>(), ["itf1"]);
292
293 fs::create_dir(sys_net_dir.path().join("itf2")).expect("failed to create subdirectory");
294
295 refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
296 let mut itf_names: Vec<String> = interfaces.keys().map(|n| n.to_owned()).collect();
297 itf_names.sort();
298 assert_eq!(itf_names, ["itf1", "itf2"]);
299 }
300
301 #[test]
302 fn refresh_networks_list_remove_interface() {
303 let sys_net_dir = tempfile::tempdir().expect("failed to create temporary directory");
304
305 let itf1_dir = sys_net_dir.path().join("itf1");
306 let itf2_dir = sys_net_dir.path().join("itf2");
307 fs::create_dir(&itf1_dir).expect("failed to create subdirectory");
308 fs::create_dir(itf2_dir).expect("failed to create subdirectory");
309
310 let mut interfaces = HashMap::new();
311
312 refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
313 let mut itf_names: Vec<String> = interfaces.keys().map(|n| n.to_owned()).collect();
314 itf_names.sort();
315 assert_eq!(itf_names, ["itf1", "itf2"]);
316
317 fs::remove_dir(&itf1_dir).expect("failed to remove subdirectory");
318
319 refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
320 assert_eq!(interfaces.keys().collect::<Vec<_>>(), ["itf2"]);
321 }
322}