1use std::fmt;
7use std::net::{IpAddr, SocketAddr};
8use std::time::Duration;
9
10use serde::{Deserialize, Serialize};
11
12use crate::process::ProcessInfo;
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub struct Link {
17 pub name: String,
18 pub index: u32,
19 pub kind: LinkKind,
20 pub mac: Option<MacAddr>,
21 pub mtu: u32,
22 pub state: OperState,
23 pub linkmode: LinkMode,
25 pub flags: LinkFlags,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31#[serde(rename_all = "snake_case")]
32pub enum LinkKind {
33 Ethernet,
34 Wifi,
35 Loopback,
36 Bridge,
37 Veth,
38 Tun,
39 Tap,
40 Wireguard,
41 Vlan,
42 Bond,
43 Other(String),
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
49#[serde(rename_all = "lowercase")]
50pub enum OperState {
51 Up,
53 Down,
55 Dormant,
57 Unknown,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
64#[serde(rename_all = "lowercase")]
65pub enum LinkMode {
66 Default,
68 Dormant,
70}
71
72#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
75pub struct LinkFlags(pub Vec<String>);
76
77impl LinkFlags {
78 pub fn has(&self, flag: &str) -> bool {
80 self.0.iter().any(|f| f.eq_ignore_ascii_case(flag))
81 }
82 pub fn is_loopback(&self) -> bool {
84 self.has("LOOPBACK")
85 }
86 pub fn lower_up(&self) -> bool {
88 self.has("LOWER_UP")
89 }
90 pub fn no_carrier(&self) -> bool {
92 self.has("NO-CARRIER")
93 }
94}
95
96#[derive(Clone, Copy, PartialEq, Eq, Hash)]
98pub struct MacAddr(pub [u8; 6]);
99
100impl fmt::Debug for MacAddr {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 fmt::Display::fmt(self, f)
103 }
104}
105
106impl fmt::Display for MacAddr {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 let b = self.0;
109 write!(
110 f,
111 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
112 b[0], b[1], b[2], b[3], b[4], b[5]
113 )
114 }
115}
116
117impl std::str::FromStr for MacAddr {
118 type Err = MacAddrParseError;
119 fn from_str(s: &str) -> Result<Self, Self::Err> {
120 let mut out = [0u8; 6];
121 let parts: Vec<&str> = s.split([':', '-']).collect();
122 if parts.len() != 6 {
123 return Err(MacAddrParseError);
124 }
125 for (i, p) in parts.iter().enumerate() {
126 out[i] = u8::from_str_radix(p, 16).map_err(|_| MacAddrParseError)?;
127 }
128 Ok(MacAddr(out))
129 }
130}
131
132impl Serialize for MacAddr {
133 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
134 s.collect_str(self)
135 }
136}
137
138impl<'de> Deserialize<'de> for MacAddr {
139 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
140 let s = String::deserialize(d)?;
141 s.parse().map_err(serde::de::Error::custom)
142 }
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
146#[error("invalid MAC address")]
147pub struct MacAddrParseError;
148
149#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
151pub struct Addr {
152 pub ip: IpAddr,
154 pub prefix: u8,
156 pub scope: AddrScope,
157 pub dynamic: bool,
159 pub temporary: bool,
161 pub deprecated: bool,
163 pub mngtmpaddr: bool,
166 pub noprefixroute: bool,
168 pub valid_lft: Lifetime,
170 pub preferred_lft: Lifetime,
172 pub label: Option<String>,
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
178#[serde(rename_all = "lowercase")]
179pub enum AddrScope {
180 Global,
182 Link,
184 Host,
186 Site,
188 Nowhere,
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
195#[serde(untagged)]
196pub enum Lifetime {
197 Forever,
198 Seconds(u32),
199}
200
201impl Lifetime {
202 pub fn is_expired(self) -> bool {
204 matches!(self, Lifetime::Seconds(0))
205 }
206 pub fn as_duration(self) -> Option<Duration> {
208 match self {
209 Lifetime::Forever => None,
210 Lifetime::Seconds(s) => Some(Duration::from_secs(u64::from(s))),
211 }
212 }
213}
214
215#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
217pub struct Route {
218 pub dst: RouteDst,
220 pub gateway: Option<IpAddr>,
222 pub oif: Option<String>,
224 pub metric: Option<u32>,
226 pub table: u32,
228 pub protocol: String,
230 pub scope: RouteScope,
231 pub prefsrc: Option<IpAddr>,
233 pub flags: Vec<String>,
235}
236
237#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
239pub enum RouteDst {
240 Default,
242 Prefix { ip: IpAddr, prefix: u8 },
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
248#[serde(rename_all = "lowercase")]
249pub enum RouteScope {
250 Global,
252 Universe,
254 Site,
256 Link,
258 Host,
260 Nowhere,
262}
263
264#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
266pub struct Neighbor {
267 pub ip: IpAddr,
268 pub lladdr: Option<MacAddr>,
269 pub oif: String,
270 pub state: NeighState,
271 pub is_router: bool,
274}
275
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
278#[serde(rename_all = "UPPERCASE")]
279pub enum NeighState {
280 Incomplete,
282 Reachable,
284 Stale,
286 Delay,
288 Probe,
290 Failed,
292 Permanent,
294 Noarp,
296 None,
298}
299
300#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
302pub struct Socket {
303 pub proto: L4Proto,
304 pub local: SocketAddr,
305 pub remote: Option<SocketAddr>,
306 pub state: TcpState,
307 pub process: ProcessInfo,
308 pub bound_iface: Option<String>,
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
314#[serde(rename_all = "lowercase")]
315pub enum L4Proto {
316 Tcp,
317 Udp,
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
322#[serde(rename_all = "UPPERCASE")]
323pub enum TcpState {
324 Listen,
325 Established,
326 SynSent,
327 SynRecv,
328 FinWait1,
329 FinWait2,
330 TimeWait,
331 Close,
332 CloseWait,
333 LastAck,
334 Closing,
335 Unconn,
336 Unknown,
337}