Skip to main content

getifs/
ifnet.rs

1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
2
3use ipnet::{IpNet, Ipv4Net, Ipv6Net, PrefixLenError};
4
5macro_rules! if_net {
6  ($kind:literal) => {
7    paste::paste! {
8      #[doc = "An interface IP" $kind " network."]
9      #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
10      pub struct [<If $kind Net>] {
11        index: u32,
12        addr: [<Ip $kind Net>],
13      }
14
15      impl core::fmt::Display for [<If $kind Net>] {
16        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17          write!(f, "{} ({})", self.addr, self.index)
18        }
19      }
20
21      impl core::ops::Deref for [<If $kind Net>] {
22        type Target = [<Ip $kind Net>];
23
24        #[inline]
25        fn deref(&self) -> &Self::Target {
26          &self.addr
27        }
28      }
29
30      impl [<If $kind Net>] {
31        #[doc = "Creates a new `If" $kind "Net` from an [`Ip" $kind "Net`]."]
32        #[inline]
33        pub const fn new(index: u32, addr: [<Ip $kind Net>]) -> Self {
34          Self {
35            index,
36            addr,
37          }
38        }
39
40        #[doc = "Creates a new IP" $kind "interface address from an index, [`Ip" $kind "Addr`] and prefix length."]
41        #[inline]
42        pub const fn with_prefix_len(index: u32, addr: [<Ip $kind Addr>], prefix_len: u8) -> Result<Self, PrefixLenError> {
43          match [<Ip $kind Net>]::new(addr, prefix_len) {
44            Ok(net) => Ok(Self::new(index, net)),
45            Err(err) => Err(err),
46          }
47        }
48
49        #[doc = "Creates a new IP" $kind " interface address from an index, [`Ip" $kind "Addr`] and prefix length."]
50        /// If called from a const context it will verify prefix length at compile time.
51        /// Otherwise it will panic at runtime if prefix length is not less then or equal to 32.
52        #[inline]
53        pub const fn with_prefix_len_assert(index: u32, addr: [<Ip $kind Addr>], prefix_len: u8) -> Self {
54          Self { index, addr: [<Ip $kind Net>]::new_assert(addr, prefix_len) }
55        }
56
57        /// Returns the index of the interface.
58        #[inline]
59        pub const fn index(&self) -> u32 {
60          self.index
61        }
62
63        /// Returns the name of the interface.
64        ///
65        /// This method will invoke the `if_indextoname` function to get the name of the interface internally.
66        pub fn name(&self) -> std::io::Result<smol_str::SmolStr> {
67          crate::idx_to_name::ifindex_to_name(self.index)
68        }
69
70        /// Returns the address of the interface.
71        #[inline]
72        pub const fn addr(&self) -> [<Ip $kind Addr>] {
73          self.addr.addr()
74        }
75
76        /// Returns the net of the interface.
77        #[inline]
78        pub const fn net(&self) -> &[<Ip $kind Net>] {
79          &self.addr
80        }
81
82        /// Returns the prefix length of the interface address.
83        #[inline]
84        pub const fn prefix_len(&self) -> u8 {
85          self.addr.prefix_len()
86        }
87
88        /// Returns the maximum prefix length of the interface address.
89        #[inline]
90        pub const fn max_prefix_len(&self) -> u8 {
91          self.addr.max_prefix_len()
92        }
93      }
94    }
95  };
96}
97
98if_net!("v4");
99if_net!("v6");
100
101/// An interface network.
102#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
103pub enum IfNet {
104  /// An IPv4 interface address.
105  V4(Ifv4Net),
106  /// An IPv6 interface address.
107  V6(Ifv6Net),
108}
109
110impl core::fmt::Display for IfNet {
111  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112    match self {
113      Self::V4(addr) => write!(f, "{addr}"),
114      Self::V6(addr) => write!(f, "{addr}"),
115    }
116  }
117}
118
119impl From<Ifv4Net> for IfNet {
120  fn from(value: Ifv4Net) -> Self {
121    Self::V4(value)
122  }
123}
124
125impl From<Ifv6Net> for IfNet {
126  fn from(value: Ifv6Net) -> Self {
127    Self::V6(value)
128  }
129}
130
131impl IfNet {
132  /// Creates a new `IfNet` from an [`IpNet`].
133  #[inline]
134  pub const fn from_net(index: u32, addr: IpNet) -> Self {
135    match addr {
136      IpNet::V4(addr) => Self::V4(Ifv4Net::new(index, addr)),
137      IpNet::V6(addr) => Self::V6(Ifv6Net::new(index, addr)),
138    }
139  }
140
141  /// Creates a new IP interface address from an index, [`IpAddr`] and prefix length.
142  #[inline]
143  pub const fn with_prefix_len(
144    index: u32,
145    addr: IpAddr,
146    prefix_len: u8,
147  ) -> Result<Self, PrefixLenError> {
148    match addr {
149      IpAddr::V4(addr) => match Ifv4Net::with_prefix_len(index, addr, prefix_len) {
150        Ok(addr) => Ok(Self::V4(addr)),
151        Err(err) => Err(err),
152      },
153      IpAddr::V6(addr) => match Ifv6Net::with_prefix_len(index, addr, prefix_len) {
154        Ok(addr) => Ok(Self::V6(addr)),
155        Err(err) => Err(err),
156      },
157    }
158  }
159
160  /// Creates a new IP interface address from an index, [`IpAddr`] and prefix length.
161  /// If called from a const context it will verify prefix length at compile time.
162  /// Otherwise it will panic at runtime if prefix length is not less then or equal to 32.
163  #[inline]
164  pub const fn with_prefix_len_assert(index: u32, addr: IpAddr, prefix_len: u8) -> Self {
165    match addr {
166      IpAddr::V4(addr) => Self::V4(Ifv4Net::with_prefix_len_assert(index, addr, prefix_len)),
167      IpAddr::V6(addr) => Self::V6(Ifv6Net::with_prefix_len_assert(index, addr, prefix_len)),
168    }
169  }
170
171  /// Returns the index of the interface.
172  #[inline]
173  pub const fn index(&self) -> u32 {
174    match self {
175      Self::V4(addr) => addr.index(),
176      Self::V6(addr) => addr.index(),
177    }
178  }
179
180  /// Returns the name of the interface.
181  ///
182  /// This method will invoke the `if_indextoname` function to get the name of the interface internally.
183  pub fn name(&self) -> std::io::Result<smol_str::SmolStr> {
184    crate::idx_to_name::ifindex_to_name(self.index())
185  }
186
187  /// Returns the address of the interface.
188  #[inline]
189  pub const fn addr(&self) -> IpAddr {
190    match self {
191      Self::V4(addr) => IpAddr::V4(addr.addr()),
192      Self::V6(addr) => IpAddr::V6(addr.addr()),
193    }
194  }
195
196  /// Returns the net of the interface.
197  #[inline]
198  pub const fn net(&self) -> IpNet {
199    match self {
200      Self::V4(addr) => IpNet::V4(*addr.net()),
201      Self::V6(addr) => IpNet::V6(*addr.net()),
202    }
203  }
204
205  /// Returns the prefix length of the interface address.
206  #[inline]
207  pub const fn prefix_len(&self) -> u8 {
208    match self {
209      Self::V4(addr) => addr.addr.prefix_len(),
210      Self::V6(addr) => addr.addr.prefix_len(),
211    }
212  }
213
214  /// Returns the maximum prefix length of the interface address.
215  #[inline]
216  pub const fn max_prefix_len(&self) -> u8 {
217    match self {
218      Self::V4(addr) => addr.addr.max_prefix_len(),
219      Self::V6(addr) => addr.addr.max_prefix_len(),
220    }
221  }
222}
223
224#[cfg(test)]
225mod tests {
226  use super::*;
227
228  #[test]
229  fn test_ifv4_net() {
230    let addr = Ipv4Addr::new(192, 168, 1, 1);
231    let net = Ifv4Net::with_prefix_len_assert(1, addr, 24);
232    assert_eq!(net.index(), 1);
233    assert_eq!(net.addr(), addr);
234    assert_eq!(net.prefix_len(), 24);
235    assert_eq!(net.max_prefix_len(), 32);
236    assert!(net.name().is_ok());
237    net.hostmask();
238  }
239
240  #[test]
241  fn test_ifv6_net() {
242    let addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
243    let net = Ifv6Net::with_prefix_len_assert(1, addr, 64);
244    assert_eq!(net.index(), 1);
245    assert_eq!(net.addr(), addr);
246    assert_eq!(net.prefix_len(), 64);
247    assert_eq!(net.max_prefix_len(), 128);
248    assert!(net.name().is_ok());
249    net.hostmask();
250  }
251
252  #[test]
253  fn test_if_net() {
254    let ipnet = IpNet::V4(Ipv4Net::new_assert(Ipv4Addr::new(192, 168, 1, 1), 24));
255    let from_ipnet = IfNet::from_net(1, ipnet);
256
257    let addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
258    let net = IfNet::with_prefix_len(1, addr, 24).unwrap();
259    let net1 = IfNet::with_prefix_len_assert(1, addr, 24);
260    assert_eq!(net, net1);
261    assert_eq!(net, from_ipnet);
262    assert_eq!(net.index(), 1);
263    assert_eq!(net.addr(), addr);
264    assert_eq!(net.prefix_len(), 24);
265    assert_eq!(net.max_prefix_len(), 32);
266    assert!(net.name().is_ok());
267    assert_eq!(
268      net.net(),
269      IpNet::V4(Ipv4Net::new_assert(Ipv4Addr::new(192, 168, 1, 1), 24))
270    );
271
272    let ipnet = IpNet::V6(Ipv6Net::new_assert(
273      Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
274      64,
275    ));
276    let from_ipnet = IfNet::from_net(1, ipnet);
277
278    let addr = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
279    let net = IfNet::with_prefix_len(1, addr, 64).unwrap();
280    let net1 = IfNet::with_prefix_len_assert(1, addr, 64);
281    assert_eq!(net, net1);
282    assert_eq!(net, from_ipnet);
283    assert_eq!(net.index(), 1);
284    assert_eq!(net.addr(), addr);
285    assert_eq!(net.prefix_len(), 64);
286    assert_eq!(net.max_prefix_len(), 128);
287    assert!(net.name().is_ok());
288    assert_eq!(
289      net.net(),
290      IpNet::V6(Ipv6Net::new_assert(
291        Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
292        64
293      ))
294    );
295  }
296}