1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::io::Read;
4use std::path::Path;
5use std::{fs::File, u8};
6
7use crate::common::MacAddr;
8use crate::network::refresh_networks_addresses;
9use crate::{NetworkExt, NetworksExt, NetworksIter};
10use std::collections::{hash_map, HashMap};
11
12#[doc = include_str!("../../md_doc/networks.md")]
13pub struct Networks {
14 interfaces: HashMap<String, NetworkData>,
15}
16
17macro_rules! old_and_new {
18 ($ty_:expr, $name:ident, $old:ident) => {{
19 $ty_.$old = $ty_.$name;
20 $ty_.$name = $name;
21 }};
22 ($ty_:expr, $name:ident, $old:ident, $path:expr) => {{
23 let _tmp = $path;
24 $ty_.$old = $ty_.$name;
25 $ty_.$name = _tmp;
26 }};
27}
28
29#[allow(clippy::ptr_arg)]
30fn read<P: AsRef<Path>>(parent: P, path: &str, data: &mut Vec<u8>) -> u64 {
31 if let Ok(mut f: File) = File::open(path:parent.as_ref().join(path)) {
32 if let Ok(size: usize) = f.read(buf:data) {
33 let mut i: usize = 0;
34 let mut ret: u64 = 0;
35
36 while i < size && i < data.len() && data[i] >= b'0' && data[i] <= b'9' {
37 ret *= 10;
38 ret += (data[i] - b'0') as u64;
39 i += 1;
40 }
41 return ret;
42 }
43 }
44 0
45}
46
47impl Networks {
48 pub(crate) fn new() -> Self {
49 Networks {
50 interfaces: HashMap::new(),
51 }
52 }
53}
54
55fn refresh_networks_list_from_sysfs(
56 interfaces: &mut HashMap<String, NetworkData>,
57 sysfs_net: &Path,
58) {
59 if let Ok(dir) = std::fs::read_dir(sysfs_net) {
60 let mut data = vec![0; 30];
61
62 for stats in interfaces.values_mut() {
63 stats.updated = false;
64 }
65
66 for entry in dir.flatten() {
67 let parent = &entry.path().join("statistics");
68 let entry = match entry.file_name().into_string() {
69 Ok(entry) => entry,
70 Err(_) => continue,
71 };
72 let rx_bytes = read(parent, "rx_bytes", &mut data);
73 let tx_bytes = read(parent, "tx_bytes", &mut data);
74 let rx_packets = read(parent, "rx_packets", &mut data);
75 let tx_packets = read(parent, "tx_packets", &mut data);
76 let rx_errors = read(parent, "rx_errors", &mut data);
77 let tx_errors = read(parent, "tx_errors", &mut data);
78 // let rx_compressed = read(parent, "rx_compressed", &mut data);
79 // let tx_compressed = read(parent, "tx_compressed", &mut data);
80 match interfaces.entry(entry) {
81 hash_map::Entry::Occupied(mut e) => {
82 let interface = e.get_mut();
83 old_and_new!(interface, rx_bytes, old_rx_bytes);
84 old_and_new!(interface, tx_bytes, old_tx_bytes);
85 old_and_new!(interface, rx_packets, old_rx_packets);
86 old_and_new!(interface, tx_packets, old_tx_packets);
87 old_and_new!(interface, rx_errors, old_rx_errors);
88 old_and_new!(interface, tx_errors, old_tx_errors);
89 // old_and_new!(e, rx_compressed, old_rx_compressed);
90 // old_and_new!(e, tx_compressed, old_tx_compressed);
91 interface.updated = true;
92 }
93 hash_map::Entry::Vacant(e) => {
94 e.insert(NetworkData {
95 rx_bytes,
96 old_rx_bytes: rx_bytes,
97 tx_bytes,
98 old_tx_bytes: tx_bytes,
99 rx_packets,
100 old_rx_packets: rx_packets,
101 tx_packets,
102 old_tx_packets: tx_packets,
103 rx_errors,
104 old_rx_errors: rx_errors,
105 tx_errors,
106 old_tx_errors: tx_errors,
107 mac_addr: MacAddr::UNSPECIFIED,
108 // rx_compressed,
109 // old_rx_compressed: rx_compressed,
110 // tx_compressed,
111 // old_tx_compressed: tx_compressed,
112 updated: true,
113 });
114 }
115 };
116 }
117
118 // Remove interfaces which are gone.
119 interfaces.retain(|_, d| d.updated);
120 }
121}
122
123impl NetworksExt for Networks {
124 fn iter(&self) -> NetworksIter {
125 NetworksIter::new(self.interfaces.iter())
126 }
127
128 fn refresh(&mut self) {
129 let mut v: Vec = vec![0; 30];
130
131 for (interface_name: &String, data: &mut NetworkData) in self.interfaces.iter_mut() {
132 data.update(path:interface_name, &mut v);
133 }
134 }
135
136 fn refresh_networks_list(&mut self) {
137 refresh_networks_list_from_sysfs(&mut self.interfaces, sysfs_net:Path::new("/sys/class/net/"));
138 refresh_networks_addresses(&mut self.interfaces);
139 }
140}
141
142#[doc = include_str!("../../md_doc/network_data.md")]
143pub struct NetworkData {
144 /// Total number of bytes received over interface.
145 rx_bytes: u64,
146 old_rx_bytes: u64,
147 /// Total number of bytes transmitted over interface.
148 tx_bytes: u64,
149 old_tx_bytes: u64,
150 /// Total number of packets received.
151 rx_packets: u64,
152 old_rx_packets: u64,
153 /// Total number of packets transmitted.
154 tx_packets: u64,
155 old_tx_packets: u64,
156 /// Shows the total number of packets received with error. This includes
157 /// too-long-frames errors, ring-buffer overflow errors, CRC errors,
158 /// frame alignment errors, fifo overruns, and missed packets.
159 rx_errors: u64,
160 old_rx_errors: u64,
161 /// similar to `rx_errors`
162 tx_errors: u64,
163 old_tx_errors: u64,
164 /// MAC address
165 pub(crate) mac_addr: MacAddr,
166 // /// Indicates the number of compressed packets received by this
167 // /// network device. This value might only be relevant for interfaces
168 // /// that support packet compression (e.g: PPP).
169 // rx_compressed: usize,
170 // old_rx_compressed: usize,
171 // /// Indicates the number of transmitted compressed packets. Note
172 // /// this might only be relevant for devices that support
173 // /// compression (e.g: PPP).
174 // tx_compressed: usize,
175 // old_tx_compressed: usize,
176 /// Whether or not the above data has been updated during refresh
177 updated: bool,
178}
179
180impl NetworkData {
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 // old_and_new!(
210 // self,
211 // rx_compressed,
212 // old_rx_compressed,
213 // read(path, "rx_compressed", data)
214 // );
215 // old_and_new!(
216 // self,
217 // tx_compressed,
218 // old_tx_compressed,
219 // read(path, "tx_compressed", data)
220 // );
221 }
222}
223
224impl NetworkExt for NetworkData {
225 fn received(&self) -> u64 {
226 self.rx_bytes.saturating_sub(self.old_rx_bytes)
227 }
228
229 fn total_received(&self) -> u64 {
230 self.rx_bytes
231 }
232
233 fn transmitted(&self) -> u64 {
234 self.tx_bytes.saturating_sub(self.old_tx_bytes)
235 }
236
237 fn total_transmitted(&self) -> u64 {
238 self.tx_bytes
239 }
240
241 fn packets_received(&self) -> u64 {
242 self.rx_packets.saturating_sub(self.old_rx_packets)
243 }
244
245 fn total_packets_received(&self) -> u64 {
246 self.rx_packets
247 }
248
249 fn packets_transmitted(&self) -> u64 {
250 self.tx_packets.saturating_sub(self.old_tx_packets)
251 }
252
253 fn total_packets_transmitted(&self) -> u64 {
254 self.tx_packets
255 }
256
257 fn errors_on_received(&self) -> u64 {
258 self.rx_errors.saturating_sub(self.old_rx_errors)
259 }
260
261 fn total_errors_on_received(&self) -> u64 {
262 self.rx_errors
263 }
264
265 fn errors_on_transmitted(&self) -> u64 {
266 self.tx_errors.saturating_sub(self.old_tx_errors)
267 }
268
269 fn total_errors_on_transmitted(&self) -> u64 {
270 self.tx_errors
271 }
272
273 fn mac_address(&self) -> MacAddr {
274 self.mac_addr
275 }
276}
277
278#[cfg(test)]
279mod test {
280 use super::refresh_networks_list_from_sysfs;
281 use std::collections::HashMap;
282 use std::fs;
283
284 #[test]
285 fn refresh_networks_list_add_interface() {
286 let sys_net_dir = tempfile::tempdir().expect("failed to create temporary directory");
287
288 fs::create_dir(sys_net_dir.path().join("itf1")).expect("failed to create subdirectory");
289
290 let mut interfaces = HashMap::new();
291
292 refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
293 assert_eq!(interfaces.keys().collect::<Vec<_>>(), ["itf1"]);
294
295 fs::create_dir(sys_net_dir.path().join("itf2")).expect("failed to create subdirectory");
296
297 refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
298 let mut itf_names: Vec<String> = interfaces.keys().map(|n| n.to_owned()).collect();
299 itf_names.sort();
300 assert_eq!(itf_names, ["itf1", "itf2"]);
301 }
302
303 #[test]
304 fn refresh_networks_list_remove_interface() {
305 let sys_net_dir = tempfile::tempdir().expect("failed to create temporary directory");
306
307 let itf1_dir = sys_net_dir.path().join("itf1");
308 let itf2_dir = sys_net_dir.path().join("itf2");
309 fs::create_dir(&itf1_dir).expect("failed to create subdirectory");
310 fs::create_dir(itf2_dir).expect("failed to create subdirectory");
311
312 let mut interfaces = HashMap::new();
313
314 refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
315 let mut itf_names: Vec<String> = interfaces.keys().map(|n| n.to_owned()).collect();
316 itf_names.sort();
317 assert_eq!(itf_names, ["itf1", "itf2"]);
318
319 fs::remove_dir(&itf1_dir).expect("failed to remove subdirectory");
320
321 refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
322 assert_eq!(interfaces.keys().collect::<Vec<_>>(), ["itf2"]);
323 }
324}
325