1use crate::ip::{is_global_ip, is_global_ipv4, is_global_ipv6};
2use crate::mac::MacAddr;
3pub use ipnet::{self, Ipv4Net, Ipv6Net};
4use std::convert::TryFrom;
5use std::io;
6use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
7use std::time::SystemTime;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[cfg(unix)]
13pub const IFF_UP: u32 = nex_sys::IFF_UP as u32;
14#[cfg(windows)]
15pub const IFF_UP: u32 = nex_sys::IFF_UP;
16
17#[cfg(unix)]
18pub const IFF_BROADCAST: u32 = nex_sys::IFF_BROADCAST as u32;
19#[cfg(windows)]
20pub const IFF_BROADCAST: u32 = nex_sys::IFF_BROADCAST;
21
22#[cfg(unix)]
23pub const IFF_LOOPBACK: u32 = nex_sys::IFF_LOOPBACK as u32;
24#[cfg(windows)]
25pub const IFF_LOOPBACK: u32 = nex_sys::IFF_LOOPBACK;
26
27#[cfg(unix)]
28pub const IFF_POINTOPOINT: u32 = nex_sys::IFF_POINTOPOINT as u32;
29#[cfg(windows)]
30pub const IFF_POINTOPOINT: u32 = nex_sys::IFF_POINTOPOINT;
31
32#[cfg(unix)]
33pub const IFF_MULTICAST: u32 = nex_sys::IFF_MULTICAST as u32;
34#[cfg(windows)]
35pub const IFF_MULTICAST: u32 = nex_sys::IFF_MULTICAST;
36
37#[cfg(unix)]
38pub const IFF_RUNNING: u32 = libc::IFF_RUNNING as u32;
39
40#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43pub enum OperState {
44 Unknown,
45 NotPresent,
46 Down,
47 LowerLayerDown,
48 Testing,
49 Dormant,
50 Up,
51}
52
53impl OperState {
54 pub fn as_str(&self) -> &'static str {
55 match self {
56 OperState::Unknown => "unknown",
57 OperState::NotPresent => "notpresent",
58 OperState::Down => "down",
59 OperState::LowerLayerDown => "lowerlayerdown",
60 OperState::Testing => "testing",
61 OperState::Dormant => "dormant",
62 OperState::Up => "up",
63 }
64 }
65
66 pub fn from_if_flags(if_flags: u32) -> Self {
67 #[cfg(unix)]
68 {
69 if if_flags & IFF_UP != 0 {
70 if if_flags & IFF_RUNNING != 0 {
71 OperState::Up
72 } else {
73 OperState::Dormant
74 }
75 } else {
76 OperState::Down
77 }
78 }
79
80 #[cfg(windows)]
81 {
82 if if_flags & IFF_UP != 0 {
83 OperState::Up
84 } else {
85 OperState::Down
86 }
87 }
88 }
89}
90
91impl std::fmt::Display for OperState {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 f.write_str(self.as_str())
94 }
95}
96
97impl std::str::FromStr for OperState {
98 type Err = ();
99
100 fn from_str(s: &str) -> Result<Self, Self::Err> {
101 match s {
102 "unknown" => Ok(OperState::Unknown),
103 "notpresent" => Ok(OperState::NotPresent),
104 "down" => Ok(OperState::Down),
105 "lowerlayerdown" => Ok(OperState::LowerLayerDown),
106 "testing" => Ok(OperState::Testing),
107 "dormant" => Ok(OperState::Dormant),
108 "up" => Ok(OperState::Up),
109 _ => Err(()),
110 }
111 }
112}
113
114impl From<netdev::interface::state::OperState> for OperState {
115 fn from(value: netdev::interface::state::OperState) -> Self {
116 match value {
117 netdev::interface::state::OperState::Unknown => OperState::Unknown,
118 netdev::interface::state::OperState::NotPresent => OperState::NotPresent,
119 netdev::interface::state::OperState::Down => OperState::Down,
120 netdev::interface::state::OperState::LowerLayerDown => OperState::LowerLayerDown,
121 netdev::interface::state::OperState::Testing => OperState::Testing,
122 netdev::interface::state::OperState::Dormant => OperState::Dormant,
123 netdev::interface::state::OperState::Up => OperState::Up,
124 }
125 }
126}
127
128#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
130#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
131pub enum InterfaceType {
132 Unknown,
133 Ethernet,
134 TokenRing,
135 Fddi,
136 BasicIsdn,
137 PrimaryIsdn,
138 Ppp,
139 Loopback,
140 Ethernet3Megabit,
141 Slip,
142 Atm,
143 GenericModem,
144 ProprietaryVirtual,
145 FastEthernetT,
146 Isdn,
147 FastEthernetFx,
148 Wireless80211,
149 AsymmetricDsl,
150 RateAdaptDsl,
151 SymmetricDsl,
152 VeryHighSpeedDsl,
153 IPOverAtm,
154 GigabitEthernet,
155 Tunnel,
156 MultiRateSymmetricDsl,
157 HighPerformanceSerialBus,
158 Wman,
159 Wwanpp,
160 Wwanpp2,
161 Bridge,
162 Can,
163 PeerToPeerWireless,
164 UnknownWithValue(u32),
165}
166
167impl InterfaceType {
168 pub fn name(&self) -> String {
169 match *self {
170 InterfaceType::Unknown => String::from("Unknown"),
171 InterfaceType::Ethernet => String::from("Ethernet"),
172 InterfaceType::TokenRing => String::from("Token Ring"),
173 InterfaceType::Fddi => String::from("FDDI"),
174 InterfaceType::BasicIsdn => String::from("Basic ISDN"),
175 InterfaceType::PrimaryIsdn => String::from("Primary ISDN"),
176 InterfaceType::Ppp => String::from("PPP"),
177 InterfaceType::Loopback => String::from("Loopback"),
178 InterfaceType::Ethernet3Megabit => String::from("Ethernet 3 megabit"),
179 InterfaceType::Slip => String::from("SLIP"),
180 InterfaceType::Atm => String::from("ATM"),
181 InterfaceType::GenericModem => String::from("Generic Modem"),
182 InterfaceType::ProprietaryVirtual => String::from("Proprietary Virtual/Internal"),
183 InterfaceType::FastEthernetT => String::from("Fast Ethernet T"),
184 InterfaceType::Isdn => String::from("ISDN"),
185 InterfaceType::FastEthernetFx => String::from("Fast Ethernet FX"),
186 InterfaceType::Wireless80211 => String::from("Wireless IEEE 802.11"),
187 InterfaceType::AsymmetricDsl => String::from("Asymmetric DSL"),
188 InterfaceType::RateAdaptDsl => String::from("Rate Adaptive DSL"),
189 InterfaceType::SymmetricDsl => String::from("Symmetric DSL"),
190 InterfaceType::VeryHighSpeedDsl => String::from("Very High Data Rate DSL"),
191 InterfaceType::IPOverAtm => String::from("IP over ATM"),
192 InterfaceType::GigabitEthernet => String::from("Gigabit Ethernet"),
193 InterfaceType::Tunnel => String::from("Tunnel"),
194 InterfaceType::MultiRateSymmetricDsl => String::from("Multi-Rate Symmetric DSL"),
195 InterfaceType::HighPerformanceSerialBus => String::from("High Performance Serial Bus"),
196 InterfaceType::Wman => String::from("WMAN"),
197 InterfaceType::Wwanpp => String::from("WWANPP"),
198 InterfaceType::Wwanpp2 => String::from("WWANPP2"),
199 InterfaceType::Bridge => String::from("Bridge"),
200 InterfaceType::Can => String::from("CAN"),
201 InterfaceType::PeerToPeerWireless => String::from("Peer-to-Peer Wireless"),
202 InterfaceType::UnknownWithValue(v) => format!("Unknown ({v})"),
203 }
204 }
205}
206
207impl From<netdev::interface::types::InterfaceType> for InterfaceType {
208 fn from(value: netdev::interface::types::InterfaceType) -> Self {
209 match value {
210 netdev::interface::types::InterfaceType::Unknown => InterfaceType::Unknown,
211 netdev::interface::types::InterfaceType::Ethernet => InterfaceType::Ethernet,
212 netdev::interface::types::InterfaceType::TokenRing => InterfaceType::TokenRing,
213 netdev::interface::types::InterfaceType::Fddi => InterfaceType::Fddi,
214 netdev::interface::types::InterfaceType::BasicIsdn => InterfaceType::BasicIsdn,
215 netdev::interface::types::InterfaceType::PrimaryIsdn => InterfaceType::PrimaryIsdn,
216 netdev::interface::types::InterfaceType::Ppp => InterfaceType::Ppp,
217 netdev::interface::types::InterfaceType::Loopback => InterfaceType::Loopback,
218 netdev::interface::types::InterfaceType::Ethernet3Megabit => {
219 InterfaceType::Ethernet3Megabit
220 }
221 netdev::interface::types::InterfaceType::Slip => InterfaceType::Slip,
222 netdev::interface::types::InterfaceType::Atm => InterfaceType::Atm,
223 netdev::interface::types::InterfaceType::GenericModem => InterfaceType::GenericModem,
224 netdev::interface::types::InterfaceType::ProprietaryVirtual => {
225 InterfaceType::ProprietaryVirtual
226 }
227 netdev::interface::types::InterfaceType::FastEthernetT => InterfaceType::FastEthernetT,
228 netdev::interface::types::InterfaceType::Isdn => InterfaceType::Isdn,
229 netdev::interface::types::InterfaceType::FastEthernetFx => {
230 InterfaceType::FastEthernetFx
231 }
232 netdev::interface::types::InterfaceType::Wireless80211 => InterfaceType::Wireless80211,
233 netdev::interface::types::InterfaceType::AsymmetricDsl => InterfaceType::AsymmetricDsl,
234 netdev::interface::types::InterfaceType::RateAdaptDsl => InterfaceType::RateAdaptDsl,
235 netdev::interface::types::InterfaceType::SymmetricDsl => InterfaceType::SymmetricDsl,
236 netdev::interface::types::InterfaceType::VeryHighSpeedDsl => {
237 InterfaceType::VeryHighSpeedDsl
238 }
239 netdev::interface::types::InterfaceType::IPOverAtm => InterfaceType::IPOverAtm,
240 netdev::interface::types::InterfaceType::GigabitEthernet => {
241 InterfaceType::GigabitEthernet
242 }
243 netdev::interface::types::InterfaceType::Tunnel => InterfaceType::Tunnel,
244 netdev::interface::types::InterfaceType::MultiRateSymmetricDsl => {
245 InterfaceType::MultiRateSymmetricDsl
246 }
247 netdev::interface::types::InterfaceType::HighPerformanceSerialBus => {
248 InterfaceType::HighPerformanceSerialBus
249 }
250 netdev::interface::types::InterfaceType::Wman => InterfaceType::Wman,
251 netdev::interface::types::InterfaceType::Wwanpp => InterfaceType::Wwanpp,
252 netdev::interface::types::InterfaceType::Wwanpp2 => InterfaceType::Wwanpp2,
253 netdev::interface::types::InterfaceType::Bridge => InterfaceType::Bridge,
254 netdev::interface::types::InterfaceType::Can => InterfaceType::Can,
255 netdev::interface::types::InterfaceType::PeerToPeerWireless => {
256 InterfaceType::PeerToPeerWireless
257 }
258 netdev::interface::types::InterfaceType::UnknownWithValue(v) => {
259 InterfaceType::UnknownWithValue(v)
260 }
261 }
262 }
263}
264
265impl TryFrom<u32> for InterfaceType {
266 type Error = ();
267
268 fn try_from(v: u32) -> Result<Self, Self::Error> {
269 Ok(InterfaceType::from(
270 netdev::interface::types::InterfaceType::try_from(v)?,
271 ))
272 }
273}
274
275#[derive(Clone, Eq, PartialEq, Hash, Debug)]
277#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
278pub struct NetworkDevice {
279 pub mac_addr: MacAddr,
280 pub ipv4: Vec<Ipv4Addr>,
281 pub ipv6: Vec<Ipv6Addr>,
282}
283
284impl NetworkDevice {
285 pub fn new() -> NetworkDevice {
286 NetworkDevice {
287 mac_addr: MacAddr::zero(),
288 ipv4: Vec::new(),
289 ipv6: Vec::new(),
290 }
291 }
292}
293
294impl Default for NetworkDevice {
295 fn default() -> Self {
296 Self::new()
297 }
298}
299
300impl From<netdev::NetworkDevice> for NetworkDevice {
301 fn from(value: netdev::NetworkDevice) -> Self {
302 NetworkDevice {
303 mac_addr: value.mac_addr,
304 ipv4: value.ipv4,
305 ipv6: value.ipv6,
306 }
307 }
308}
309
310#[derive(Clone, Eq, PartialEq, Hash, Debug)]
312#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
313pub struct InterfaceStats {
314 pub rx_bytes: u64,
315 pub tx_bytes: u64,
316 pub timestamp: Option<SystemTime>,
317}
318
319impl From<netdev::stats::counters::InterfaceStats> for InterfaceStats {
320 fn from(value: netdev::stats::counters::InterfaceStats) -> Self {
321 InterfaceStats {
322 rx_bytes: value.rx_bytes,
323 tx_bytes: value.tx_bytes,
324 timestamp: value.timestamp,
325 }
326 }
327}
328
329#[derive(Clone, Eq, PartialEq, Hash, Debug)]
331#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
332pub struct Interface {
333 pub index: u32,
334 pub name: String,
335 pub friendly_name: Option<String>,
336 pub description: Option<String>,
337 pub if_type: InterfaceType,
338 pub mac_addr: Option<MacAddr>,
339 pub ipv4: Vec<Ipv4Net>,
340 pub ipv6: Vec<Ipv6Net>,
341 pub ipv6_scope_ids: Vec<u32>,
342 pub flags: u32,
343 pub oper_state: OperState,
344 pub transmit_speed: Option<u64>,
345 pub receive_speed: Option<u64>,
346 pub stats: Option<InterfaceStats>,
347 #[cfg(feature = "gateway")]
348 pub gateway: Option<NetworkDevice>,
349 #[cfg(feature = "gateway")]
350 pub dns_servers: Vec<IpAddr>,
351 pub mtu: Option<u32>,
352 #[cfg(feature = "gateway")]
353 pub default: bool,
354}
355
356impl Interface {
357 #[cfg(feature = "gateway")]
358 #[allow(clippy::should_implement_trait)]
359 pub fn default() -> Result<Interface, String> {
360 get_default_interface()
361 }
362
363 pub fn dummy() -> Interface {
364 Interface {
365 index: 0,
366 name: String::new(),
367 friendly_name: None,
368 description: None,
369 if_type: InterfaceType::Unknown,
370 mac_addr: None,
371 ipv4: Vec::new(),
372 ipv6: Vec::new(),
373 ipv6_scope_ids: Vec::new(),
374 flags: 0,
375 oper_state: OperState::Unknown,
376 transmit_speed: None,
377 receive_speed: None,
378 stats: None,
379 #[cfg(feature = "gateway")]
380 gateway: None,
381 #[cfg(feature = "gateway")]
382 dns_servers: Vec::new(),
383 mtu: None,
384 #[cfg(feature = "gateway")]
385 default: false,
386 }
387 }
388
389 pub fn refresh(&mut self) -> io::Result<()> {
394 let refreshed = lookup_interface(&self.name, self.index).ok_or_else(|| {
395 io::Error::new(io::ErrorKind::NotFound, "interface could not be refreshed")
396 })?;
397 *self = refreshed.into();
398 Ok(())
399 }
400
401 pub fn is_up(&self) -> bool {
402 self.flags & IFF_UP != 0
403 }
404
405 pub fn is_loopback(&self) -> bool {
406 self.flags & IFF_LOOPBACK != 0
407 }
408
409 pub fn is_point_to_point(&self) -> bool {
410 self.flags & IFF_POINTOPOINT != 0
411 }
412
413 pub fn is_multicast(&self) -> bool {
414 self.flags & IFF_MULTICAST != 0
415 }
416
417 pub fn is_broadcast(&self) -> bool {
418 self.flags & IFF_BROADCAST != 0
419 }
420
421 pub fn is_tun(&self) -> bool {
422 self.is_up() && self.is_point_to_point() && !self.is_broadcast() && !self.is_loopback()
423 }
424
425 pub fn is_running(&self) -> bool {
426 #[cfg(unix)]
427 {
428 self.flags & IFF_RUNNING != 0
429 }
430 #[cfg(windows)]
431 {
432 self.is_up()
433 }
434 }
435
436 pub fn is_physical(&self) -> bool {
437 lookup_interface(&self.name, self.index)
438 .map(|iface| iface.is_physical())
439 .unwrap_or_else(|| {
440 self.is_up() && self.is_running() && !self.is_tun() && !self.is_loopback()
441 })
442 }
443
444 pub fn oper_state(&self) -> OperState {
445 self.oper_state
446 }
447
448 pub fn is_oper_up(&self) -> bool {
449 self.oper_state == OperState::Up
450 }
451
452 pub fn refresh_oper_state(&mut self) -> io::Result<()> {
456 if let Some(iface) = lookup_interface(&self.name, self.index) {
457 self.oper_state = iface.oper_state.into();
458 return Ok(());
459 }
460 Err(io::Error::new(
461 io::ErrorKind::NotFound,
462 "interface operational state could not be refreshed",
463 ))
464 }
465
466 pub fn update_oper_state(&mut self) {
470 let _ = self.refresh_oper_state();
471 }
472
473 pub fn ipv4_addr_iter(&self) -> impl Iterator<Item = Ipv4Addr> + '_ {
475 self.ipv4.iter().map(|net| net.addr())
476 }
477
478 pub fn ipv4_addrs(&self) -> Vec<Ipv4Addr> {
479 self.ipv4_addr_iter().collect()
480 }
481
482 pub fn ipv6_addr_iter(&self) -> impl Iterator<Item = Ipv6Addr> + '_ {
484 self.ipv6.iter().map(|net| net.addr())
485 }
486
487 pub fn ipv6_addrs(&self) -> Vec<Ipv6Addr> {
488 self.ipv6_addr_iter().collect()
489 }
490
491 pub fn ip_addr_iter(&self) -> impl Iterator<Item = IpAddr> + '_ {
493 self.ipv4_addr_iter()
494 .map(IpAddr::V4)
495 .chain(self.ipv6_addr_iter().map(IpAddr::V6))
496 }
497
498 pub fn ip_addrs(&self) -> Vec<IpAddr> {
499 self.ip_addr_iter().collect()
500 }
501
502 pub fn has_ipv4(&self) -> bool {
503 !self.ipv4.is_empty()
504 }
505
506 pub fn has_ipv6(&self) -> bool {
507 !self.ipv6.is_empty()
508 }
509
510 pub fn has_global_ipv4(&self) -> bool {
511 self.ipv4_addrs().iter().any(is_global_ipv4)
512 }
513
514 pub fn has_global_ipv6(&self) -> bool {
515 self.ipv6_addrs().iter().any(is_global_ipv6)
516 }
517
518 pub fn has_global_ip(&self) -> bool {
519 self.ip_addrs().iter().any(is_global_ip)
520 }
521
522 pub fn global_ipv4_addrs(&self) -> Vec<Ipv4Addr> {
523 self.ipv4_addr_iter().filter(is_global_ipv4).collect()
524 }
525
526 pub fn global_ipv6_addrs(&self) -> Vec<Ipv6Addr> {
527 self.ipv6_addr_iter().filter(is_global_ipv6).collect()
528 }
529
530 pub fn global_ip_addrs(&self) -> Vec<IpAddr> {
531 self.ip_addr_iter().filter(is_global_ip).collect()
532 }
533
534 pub fn refresh_stats(&mut self) -> io::Result<()> {
538 if let Some(iface) = lookup_interface(&self.name, self.index) {
539 self.stats = iface.stats.map(Into::into);
540 return Ok(());
541 }
542 Err(io::Error::new(
543 io::ErrorKind::NotFound,
544 "interface statistics could not be refreshed",
545 ))
546 }
547
548 pub fn update_stats(&mut self) -> io::Result<()> {
552 self.refresh_stats()
553 }
554}
555
556impl From<netdev::Interface> for Interface {
557 fn from(value: netdev::Interface) -> Self {
558 Interface {
559 index: value.index,
560 name: value.name,
561 friendly_name: value.friendly_name,
562 description: value.description,
563 if_type: value.if_type.into(),
564 mac_addr: value.mac_addr,
565 ipv4: value.ipv4,
566 ipv6: value.ipv6,
567 ipv6_scope_ids: value.ipv6_scope_ids,
568 flags: value.flags,
569 oper_state: value.oper_state.into(),
570 transmit_speed: value.transmit_speed,
571 receive_speed: value.receive_speed,
572 stats: value.stats.map(Into::into),
573 #[cfg(feature = "gateway")]
574 gateway: value.gateway.map(Into::into),
575 #[cfg(feature = "gateway")]
576 dns_servers: value.dns_servers,
577 mtu: value.mtu,
578 #[cfg(feature = "gateway")]
579 default: value.default,
580 }
581 }
582}
583
584pub fn get_interfaces() -> Vec<Interface> {
585 netdev::get_interfaces()
586 .into_iter()
587 .map(Into::into)
588 .collect()
589}
590
591#[cfg(feature = "gateway")]
592pub fn get_default_interface() -> Result<Interface, String> {
593 netdev::get_default_interface().map(Into::into)
594}
595
596#[cfg(feature = "gateway")]
597pub fn get_default_gateway() -> Result<NetworkDevice, String> {
598 netdev::get_default_gateway().map(Into::into)
599}
600
601fn lookup_interface(name: &str, index: u32) -> Option<netdev::Interface> {
602 netdev::get_interfaces()
603 .into_iter()
604 .find(|iface| iface.index == index || iface.name == name)
605}