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