1use anyhow::{Result, anyhow};
24use serde::{Deserialize, Serialize};
25use std::{
26 fs::{read_dir, read_to_string},
27 path::{Path, PathBuf},
28};
29
30use crate::traits::ToJson;
31
32const NET_DIR: &str = "/sys/class/net/";
33
34#[derive(Debug, Clone, Deserialize, Serialize)]
35pub struct Networks {
36 pub networks: Vec<Network>,
37}
38
39impl Networks {
40 pub fn new() -> Result<Self> {
41 let net_dirs = read_dir(NET_DIR)?;
42 let mut networks = Vec::new();
43 for dir in net_dirs {
44 let dir = dir?.path();
45 networks.push(Network::new(&dir)?);
46 }
47 Ok(Self { networks })
48 }
49}
50
51impl ToJson for Networks {}
52
53#[derive(Debug, Clone, Deserialize, Serialize)]
54pub struct Network {
55 pub name: String,
56 pub address: String,
57 pub broadcast: String,
58 pub mtu: u64,
59 pub operstate: String,
60 pub statistics: Statistics,
61}
62
63impl Network {
64 pub fn new(path: &PathBuf) -> Result<Self> {
65 let name = path.strip_prefix(NET_DIR)?.display().to_string();
66 let address = read_to_string(path.join("address"))
67 .and_then(|address| Ok(address.trim().to_string()))?;
68 let broadcast = read_to_string(path.join("broadcast"))
69 .and_then(|broadcast| Ok(broadcast.trim().to_string()))?;
70 let mtu = read_to_string(path.join("mtu"))
71 .and_then(|mtu| Ok(mtu.trim().parse::<u64>().unwrap_or(0)))?;
72 let operstate = read_to_string(path.join("operstate"))
73 .and_then(|opstate| Ok(opstate.trim().to_string()))?;
74 let statistics = Statistics::new(&name)?;
75
76 Ok(Self {
77 name,
78 address,
79 broadcast,
80 mtu,
81 operstate,
82 statistics,
83 })
84 }
85}
86
87#[derive(Debug, Clone, Deserialize, Serialize)]
88pub struct Statistics {
89 pub collisions: u64,
90 pub multicast: u64,
91
92 pub rx_bytes: u64,
93 pub rx_compressed: u64,
94 pub rx_crc_errors: u64,
95 pub rx_dropped: u64,
96 pub rx_errors: u64,
97 pub rx_fifo_errors: u64,
98 pub rx_frame_errors: u64,
99 pub rx_length_erorrs: u64,
100 pub rx_missed_errors: u64,
101 pub rx_nohandler: u64,
102 pub rx_over_errors: u64,
103 pub rx_packets: u64,
104
105 pub tx_aborted_errors: u64,
106 pub tx_bytes: u64,
107 pub tx_carrier_errors: u64,
108 pub tx_compressed: u64,
109 pub tx_dropped: u64,
110 pub tx_errors: u64,
111 pub tx_fifo_errors: u64,
112 pub tx_heartbeat_errors: u64,
113 pub tx_packets: u64,
114 pub tx_window_errors: u64,
115}
116
117impl Statistics {
118 pub fn new(interface: &str) -> Result<Self> {
119 let dir = Path::new(NET_DIR).join(interface).join("statistics");
120 if !dir.is_dir() {
121 return Err(anyhow!(
122 "Failed to open '{}' directory: not found",
123 dir.display(),
124 ));
125 }
126
127 let read = |file: &str| -> Result<u64> {
128 let file = dir.join(file);
129 let contents = read_to_string(&file)
130 .map_err(|err| anyhow!("Failed to read '{}' file: {err}", file.display()))?;
131
132 contents
133 .trim()
134 .parse::<u64>()
135 .map_err(|err| anyhow!("Failed to parse '{interface}' value: {err}"))
136 };
137
138 let collisions = read("collisions")?;
139 let multicast = read("multicast")?;
140
141 let rx_bytes = read("rx_bytes")?;
142 let rx_compressed = read("rx_compressed")?;
143 let rx_crc_errors = read("rx_crc_errors")?;
144 let rx_dropped = read("rx_dropped")?;
145 let rx_errors = read("rx_errors")?;
146 let rx_fifo_errors = read("rx_fifo_errors")?;
147 let rx_frame_errors = read("rx_frame_errors")?;
148 let rx_length_erorrs = read("rx_length_errors")?;
149 let rx_missed_errors = read("rx_missed_errors")?;
150 let rx_nohandler = read("rx_nohandler")?;
151 let rx_over_errors = read("rx_over_errors")?;
152 let rx_packets = read("rx_packets")?;
153
154 let tx_aborted_errors = read("tx_aborted_errors")?;
155 let tx_bytes = read("tx_bytes")?;
156 let tx_carrier_errors = read("tx_carrier_errors")?;
157 let tx_compressed = read("tx_compressed")?;
158 let tx_dropped = read("tx_dropped")?;
159 let tx_errors = read("tx_errors")?;
160 let tx_fifo_errors = read("tx_fifo_errors")?;
161 let tx_heartbeat_errors = read("tx_heartbeat_errors")?;
162 let tx_packets = read("tx_packets")?;
163 let tx_window_errors = read("tx_window_errors")?;
164
165 Ok(Self {
166 collisions,
167 multicast,
168 rx_bytes,
169 rx_compressed,
170 rx_crc_errors,
171 rx_dropped,
172 rx_errors,
173 rx_fifo_errors,
174 rx_frame_errors,
175 rx_length_erorrs,
176 rx_missed_errors,
177 rx_nohandler,
178 rx_over_errors,
179 rx_packets,
180 tx_aborted_errors,
181 tx_bytes,
182 tx_carrier_errors,
183 tx_compressed,
184 tx_dropped,
185 tx_errors,
186 tx_fifo_errors,
187 tx_heartbeat_errors,
188 tx_packets,
189 tx_window_errors,
190 })
191 }
192}
193
194#[derive(Debug, Clone, Deserialize, Serialize)]
195pub struct ARP {
196 pub tables: Vec<ARPTable>,
197}
198
199impl ToJson for ARP {}
200
201impl ARP {
202 pub fn new() -> Result<Self> {
203 let contents = read_to_string("/proc/net/arp")?;
204 let lines = contents.lines().skip(1);
205 let mut tables = vec![];
206
207 for line in lines {
208 tables.push(ARPTable::try_from(line)?);
209 }
210 tables.shrink_to_fit();
211
212 Ok(Self { tables })
213 }
214}
215
216#[derive(Debug, Clone, Deserialize, Serialize)]
217pub struct ARPTable {
218 pub ip_addr: String,
219 pub hw_type: String,
220 pub flags: String,
221 pub hw_addr: String,
222 pub mask: String,
223 pub device: String,
224}
225
226impl TryFrom<&str> for ARPTable {
227 type Error = anyhow::Error;
228
229 fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
230 let chunks = value.split_whitespace().collect::<Vec<_>>();
231 if chunks.len() != 6 {
232 return Err(anyhow!(
233 "ARP Table parsing failed: String '{value}' is incorrect!"
234 ));
235 }
236
237 let ip_addr = chunks[0].to_string();
238 let hw_type = chunks[1].to_string();
239 let flags = chunks[2].to_string();
240 let hw_addr = chunks[3].to_string();
241 let mask = chunks[4].to_string();
242 let device = chunks[5].to_string();
243
244 Ok(Self {
245 ip_addr,
246 hw_type,
247 flags,
248 hw_addr,
249 mask,
250 device,
251 })
252 }
253}