1use std::collections::{hash_map, HashMap};
4use std::fs::File;
5use std::io::Read;
6use std::path::Path;
7
8use crate::network::refresh_networks_addresses;
9use crate::{IpNetwork, MacAddr, NetworkData};
10
11macro_rules! old_and_new {
12 ($ty_:expr, $name:ident, $old:ident) => {{
13 $ty_.$old = $ty_.$name;
14 $ty_.$name = $name;
15 }};
16 ($ty_:expr, $name:ident, $old:ident, $path:expr) => {{
17 let _tmp = $path;
18 $ty_.$old = $ty_.$name;
19 $ty_.$name = _tmp;
20 }};
21}
22
23#[allow(clippy::ptr_arg)]
24fn read<P: AsRef<Path>>(parent: P, path: &str, data: &mut Vec<u8>) -> u64 {
25 if let Ok(mut f) = File::open(parent.as_ref().join(path)) {
26 if let Ok(size) = f.read(data) {
27 let mut i = 0;
28 let mut ret = 0;
29
30 while i < size && i < data.len() && data[i] >= b'0' && data[i] <= b'9' {
31 ret *= 10;
32 ret += (data[i] - b'0') as u64;
33 i += 1;
34 }
35 return ret;
36 }
37 }
38 0
39}
40
41fn refresh_networks_list_from_sysfs(
42 interfaces: &mut HashMap<String, NetworkData>,
43 remove_not_listed_interfaces: bool,
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_path = &entry.path();
56 let entry = match entry.file_name().into_string() {
57 Ok(entry) => entry,
58 Err(_) => continue,
59 };
60 let rx_bytes = read(parent, "rx_bytes", &mut data);
61 let tx_bytes = read(parent, "tx_bytes", &mut data);
62 let rx_packets = read(parent, "rx_packets", &mut data);
63 let tx_packets = read(parent, "tx_packets", &mut data);
64 let rx_errors = read(parent, "rx_errors", &mut data);
65 let tx_errors = read(parent, "tx_errors", &mut data);
66 let mtu = read(entry_path, "mtu", &mut data);
69
70 match interfaces.entry(entry) {
71 hash_map::Entry::Occupied(mut e) => {
72 let interface = e.get_mut();
73 let interface = &mut interface.inner;
74
75 old_and_new!(interface, rx_bytes, old_rx_bytes);
76 old_and_new!(interface, tx_bytes, old_tx_bytes);
77 old_and_new!(interface, rx_packets, old_rx_packets);
78 old_and_new!(interface, tx_packets, old_tx_packets);
79 old_and_new!(interface, rx_errors, old_rx_errors);
80 old_and_new!(interface, tx_errors, old_tx_errors);
81 if interface.mtu != mtu {
84 interface.mtu = mtu;
85 }
86 interface.updated = true;
87 }
88 hash_map::Entry::Vacant(e) => {
89 e.insert(NetworkData {
90 inner: NetworkDataInner {
91 rx_bytes,
92 old_rx_bytes: rx_bytes,
93 tx_bytes,
94 old_tx_bytes: tx_bytes,
95 rx_packets,
96 old_rx_packets: rx_packets,
97 tx_packets,
98 old_tx_packets: tx_packets,
99 rx_errors,
100 old_rx_errors: rx_errors,
101 tx_errors,
102 old_tx_errors: tx_errors,
103 mac_addr: MacAddr::UNSPECIFIED,
104 ip_networks: vec![],
105 mtu,
110 updated: true,
111 },
112 });
113 }
114 };
115 }
116 }
117 if remove_not_listed_interfaces {
120 interfaces.retain(|_, i| {
122 if !i.inner.updated {
123 return false;
124 }
125 i.inner.updated = false;
126 true
127 });
128 }
129}
130
131pub(crate) struct NetworksInner {
132 pub(crate) interfaces: HashMap<String, NetworkData>,
133}
134
135impl NetworksInner {
136 pub(crate) fn new() -> Self {
137 Self {
138 interfaces: HashMap::new(),
139 }
140 }
141
142 pub(crate) fn list(&self) -> &HashMap<String, NetworkData> {
143 &self.interfaces
144 }
145
146 pub(crate) fn refresh(&mut self, remove_not_listed_interfaces: bool) {
147 refresh_networks_list_from_sysfs(
148 &mut self.interfaces,
149 remove_not_listed_interfaces,
150 Path::new("/sys/class/net/"),
151 );
152 refresh_networks_addresses(&mut self.interfaces);
153 }
154}
155
156pub(crate) struct NetworkDataInner {
157 rx_bytes: u64,
159 old_rx_bytes: u64,
160 tx_bytes: u64,
162 old_tx_bytes: u64,
163 rx_packets: u64,
165 old_rx_packets: u64,
166 tx_packets: u64,
168 old_tx_packets: u64,
169 rx_errors: u64,
173 old_rx_errors: u64,
174 tx_errors: u64,
176 old_tx_errors: u64,
177 pub(crate) mac_addr: MacAddr,
179 pub(crate) ip_networks: Vec<IpNetwork>,
180 mtu: u64,
182 updated: bool,
194}
195
196impl NetworkDataInner {
197 pub(crate) fn received(&self) -> u64 {
198 self.rx_bytes.saturating_sub(self.old_rx_bytes)
199 }
200
201 pub(crate) fn total_received(&self) -> u64 {
202 self.rx_bytes
203 }
204
205 pub(crate) fn transmitted(&self) -> u64 {
206 self.tx_bytes.saturating_sub(self.old_tx_bytes)
207 }
208
209 pub(crate) fn total_transmitted(&self) -> u64 {
210 self.tx_bytes
211 }
212
213 pub(crate) fn packets_received(&self) -> u64 {
214 self.rx_packets.saturating_sub(self.old_rx_packets)
215 }
216
217 pub(crate) fn total_packets_received(&self) -> u64 {
218 self.rx_packets
219 }
220
221 pub(crate) fn packets_transmitted(&self) -> u64 {
222 self.tx_packets.saturating_sub(self.old_tx_packets)
223 }
224
225 pub(crate) fn total_packets_transmitted(&self) -> u64 {
226 self.tx_packets
227 }
228
229 pub(crate) fn errors_on_received(&self) -> u64 {
230 self.rx_errors.saturating_sub(self.old_rx_errors)
231 }
232
233 pub(crate) fn total_errors_on_received(&self) -> u64 {
234 self.rx_errors
235 }
236
237 pub(crate) fn errors_on_transmitted(&self) -> u64 {
238 self.tx_errors.saturating_sub(self.old_tx_errors)
239 }
240
241 pub(crate) fn total_errors_on_transmitted(&self) -> u64 {
242 self.tx_errors
243 }
244
245 pub(crate) fn mac_address(&self) -> MacAddr {
246 self.mac_addr
247 }
248
249 pub(crate) fn ip_networks(&self) -> &[IpNetwork] {
250 &self.ip_networks
251 }
252
253 pub(crate) fn mtu(&self) -> u64 {
254 self.mtu
255 }
256}
257
258#[cfg(test)]
259mod test {
260 use super::refresh_networks_list_from_sysfs;
261 use std::collections::HashMap;
262 use std::fs;
263
264 #[test]
265 fn refresh_networks_list_add_interface() {
266 let sys_net_dir = tempfile::tempdir().expect("failed to create temporary directory");
267
268 fs::create_dir(sys_net_dir.path().join("itf1")).expect("failed to create subdirectory");
269
270 let mut interfaces = HashMap::new();
271
272 refresh_networks_list_from_sysfs(&mut interfaces, false, sys_net_dir.path());
273 assert_eq!(interfaces.keys().collect::<Vec<_>>(), ["itf1"]);
274
275 fs::create_dir(sys_net_dir.path().join("itf2")).expect("failed to create subdirectory");
276
277 refresh_networks_list_from_sysfs(&mut interfaces, false, sys_net_dir.path());
278 let mut itf_names: Vec<String> = interfaces.keys().map(|n| n.to_owned()).collect();
279 itf_names.sort();
280 assert_eq!(itf_names, ["itf1", "itf2"]);
281 }
282
283 #[test]
284 fn refresh_networks_list_remove_interface() {
285 let sys_net_dir = tempfile::tempdir().expect("failed to create temporary directory");
286
287 let itf1_dir = sys_net_dir.path().join("itf1");
288 let itf2_dir = sys_net_dir.path().join("itf2");
289 fs::create_dir(&itf1_dir).expect("failed to create subdirectory");
290 fs::create_dir(itf2_dir).expect("failed to create subdirectory");
291
292 let mut interfaces = HashMap::new();
293
294 refresh_networks_list_from_sysfs(&mut interfaces, false, sys_net_dir.path());
295 let mut itf_names: Vec<String> = interfaces.keys().map(|n| n.to_owned()).collect();
296 itf_names.sort();
297 assert_eq!(itf_names, ["itf1", "itf2"]);
298
299 fs::remove_dir(&itf1_dir).expect("failed to remove subdirectory");
300
301 refresh_networks_list_from_sysfs(&mut interfaces, true, sys_net_dir.path());
302 assert_eq!(interfaces.keys().collect::<Vec<_>>(), ["itf2"]);
303 }
304}