• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Network interface name resolution.
2 //!
3 //! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
4 //! or "socan1" into device numbers.
5 
6 use std::{ffi::{CStr, CString}, fmt};
7 use crate::{errno::Errno, Error, NixPath, Result};
8 use libc::{c_uint, IF_NAMESIZE};
9 
10 #[cfg(not(solarish))]
11 /// type alias for InterfaceFlags
12 pub type IflagsType = libc::c_int;
13 #[cfg(solarish)]
14 /// type alias for InterfaceFlags
15 pub type IflagsType = libc::c_longlong;
16 
17 /// Resolve an interface into an interface number.
if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint>18 pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
19     let if_index = name
20         .with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?;
21 
22     if if_index == 0 {
23         Err(Error::last())
24     } else {
25         Ok(if_index)
26     }
27 }
28 
29 /// Resolve an interface number into an interface.
if_indextoname(index: c_uint) -> Result<CString>30 pub fn if_indextoname(index: c_uint) -> Result<CString> {
31     // We need to allocate this anyway, so doing it directly is faster.
32     let mut buf = vec![0u8; IF_NAMESIZE];
33 
34     let return_buf = unsafe {
35         libc::if_indextoname(index, buf.as_mut_ptr().cast())
36     };
37 
38     Errno::result(return_buf.cast())?;
39     Ok(CStr::from_bytes_until_nul(buf.as_slice()).unwrap().to_owned())
40 }
41 
42 libc_bitflags!(
43     /// Standard interface flags, used by `getifaddrs`
44     pub struct InterfaceFlags: IflagsType {
45 
46         /// Interface is running. (see
47         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
48         IFF_UP as IflagsType;
49         /// Valid broadcast address set. (see
50         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
51         IFF_BROADCAST as IflagsType;
52         /// Internal debugging flag. (see
53         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
54         #[cfg(not(target_os = "haiku"))]
55         IFF_DEBUG as IflagsType;
56         /// Interface is a loopback interface. (see
57         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
58         IFF_LOOPBACK as IflagsType;
59         /// Interface is a point-to-point link. (see
60         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
61         IFF_POINTOPOINT as IflagsType;
62         /// Avoid use of trailers. (see
63         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
64         #[cfg(any(
65                   linux_android,
66                   solarish,
67                   apple_targets,
68                   target_os = "fuchsia",
69                   target_os = "netbsd"))]
70         IFF_NOTRAILERS as IflagsType;
71         /// Interface manages own routes.
72         #[cfg(any(target_os = "dragonfly"))]
73         IFF_SMART as IflagsType;
74         /// Resources allocated. (see
75         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
76         #[cfg(any(
77                   linux_android,
78                   bsd,
79                   solarish,
80                   target_os = "fuchsia"))]
81         IFF_RUNNING as IflagsType;
82         /// No arp protocol, L2 destination address not set. (see
83         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
84         IFF_NOARP as IflagsType;
85         /// Interface is in promiscuous mode. (see
86         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
87         IFF_PROMISC as IflagsType;
88         /// Receive all multicast packets. (see
89         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
90         IFF_ALLMULTI as IflagsType;
91         /// Master of a load balancing bundle. (see
92         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
93         #[cfg(any(linux_android, target_os = "fuchsia"))]
94         IFF_MASTER;
95         /// transmission in progress, tx hardware queue is full
96         #[cfg(any(target_os = "freebsd", apple_targets, netbsdlike))]
97         IFF_OACTIVE;
98         /// Protocol code on board.
99         #[cfg(solarish)]
100         IFF_INTELLIGENT as IflagsType;
101         /// Slave of a load balancing bundle. (see
102         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
103         #[cfg(any(linux_android, target_os = "fuchsia"))]
104         IFF_SLAVE;
105         /// Can't hear own transmissions.
106         #[cfg(bsd)]
107         IFF_SIMPLEX;
108         /// Supports multicast. (see
109         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
110         IFF_MULTICAST as IflagsType;
111         /// Per link layer defined bit.
112         #[cfg(bsd)]
113         IFF_LINK0;
114         /// Multicast using broadcast.
115         #[cfg(solarish)]
116         IFF_MULTI_BCAST as IflagsType;
117         /// Is able to select media type via ifmap. (see
118         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
119         #[cfg(any(linux_android, target_os = "fuchsia"))]
120         IFF_PORTSEL;
121         /// Per link layer defined bit.
122         #[cfg(bsd)]
123         IFF_LINK1;
124         /// Non-unique address.
125         #[cfg(solarish)]
126         IFF_UNNUMBERED as IflagsType;
127         /// Auto media selection active. (see
128         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
129         #[cfg(any(linux_android, target_os = "fuchsia"))]
130         IFF_AUTOMEDIA;
131         /// Per link layer defined bit.
132         #[cfg(bsd)]
133         IFF_LINK2;
134         /// Use alternate physical connection.
135         #[cfg(any(freebsdlike, apple_targets))]
136         IFF_ALTPHYS;
137         /// DHCP controls interface.
138         #[cfg(solarish)]
139         IFF_DHCPRUNNING as IflagsType;
140         /// The addresses are lost when the interface goes down. (see
141         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
142         #[cfg(any(linux_android, target_os = "fuchsia"))]
143         IFF_DYNAMIC;
144         /// Do not advertise.
145         #[cfg(solarish)]
146         IFF_PRIVATE as IflagsType;
147         /// Driver signals L1 up. Volatile.
148         #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
149         IFF_LOWER_UP;
150         /// Interface is in polling mode.
151         #[cfg(any(target_os = "dragonfly"))]
152         IFF_POLLING_COMPAT;
153         /// Unconfigurable using ioctl(2).
154         #[cfg(any(target_os = "freebsd"))]
155         IFF_CANTCONFIG;
156         /// Do not transmit packets.
157         #[cfg(solarish)]
158         IFF_NOXMIT as IflagsType;
159         /// Driver signals dormant. Volatile.
160         #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
161         IFF_DORMANT;
162         /// User-requested promisc mode.
163         #[cfg(freebsdlike)]
164         IFF_PPROMISC;
165         /// Just on-link subnet.
166         #[cfg(solarish)]
167         IFF_NOLOCAL as IflagsType;
168         /// Echo sent packets. Volatile.
169         #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
170         IFF_ECHO;
171         /// User-requested monitor mode.
172         #[cfg(freebsdlike)]
173         IFF_MONITOR;
174         /// Address is deprecated.
175         #[cfg(solarish)]
176         IFF_DEPRECATED as IflagsType;
177         /// Static ARP.
178         #[cfg(freebsdlike)]
179         IFF_STATICARP;
180         /// Address from stateless addrconf.
181         #[cfg(solarish)]
182         IFF_ADDRCONF as IflagsType;
183         /// Interface is in polling mode.
184         #[cfg(any(target_os = "dragonfly"))]
185         IFF_NPOLLING;
186         /// Router on interface.
187         #[cfg(solarish)]
188         IFF_ROUTER as IflagsType;
189         /// Interface is in polling mode.
190         #[cfg(any(target_os = "dragonfly"))]
191         IFF_IDIRECT;
192         /// Interface is winding down
193         #[cfg(any(target_os = "freebsd"))]
194         IFF_DYING;
195         /// No NUD on interface.
196         #[cfg(solarish)]
197         IFF_NONUD as IflagsType;
198         /// Interface is being renamed
199         #[cfg(any(target_os = "freebsd"))]
200         IFF_RENAMING;
201         /// Anycast address.
202         #[cfg(solarish)]
203         IFF_ANYCAST as IflagsType;
204         /// Don't exchange routing info.
205         #[cfg(solarish)]
206         IFF_NORTEXCH as IflagsType;
207         /// Do not provide packet information
208         #[cfg(any(linux_android, target_os = "fuchsia"))]
209         IFF_NO_PI as IflagsType;
210         /// TUN device (no Ethernet headers)
211         #[cfg(any(linux_android, target_os = "fuchsia"))]
212         IFF_TUN as IflagsType;
213         /// TAP device
214         #[cfg(any(linux_android, target_os = "fuchsia"))]
215         IFF_TAP as IflagsType;
216         /// IPv4 interface.
217         #[cfg(solarish)]
218         IFF_IPV4 as IflagsType;
219         /// IPv6 interface.
220         #[cfg(solarish)]
221         IFF_IPV6 as IflagsType;
222         /// in.mpathd test address
223         #[cfg(solarish)]
224         IFF_NOFAILOVER as IflagsType;
225         /// Interface has failed
226         #[cfg(solarish)]
227         IFF_FAILED as IflagsType;
228         /// Interface is a hot-spare
229         #[cfg(solarish)]
230         IFF_STANDBY as IflagsType;
231         /// Functioning but not used
232         #[cfg(solarish)]
233         IFF_INACTIVE as IflagsType;
234         /// Interface is offline
235         #[cfg(solarish)]
236         IFF_OFFLINE as IflagsType;
237         /// Has CoS marking supported
238         #[cfg(solarish)]
239         IFF_COS_ENABLED as IflagsType;
240         /// Prefer as source addr
241         #[cfg(solarish)]
242         IFF_PREFERRED as IflagsType;
243         /// RFC3041
244         #[cfg(solarish)]
245         IFF_TEMPORARY as IflagsType;
246         /// MTU set
247         #[cfg(solarish)]
248         IFF_FIXEDMTU as IflagsType;
249         /// Cannot send/receive packets
250         #[cfg(solarish)]
251         IFF_VIRTUAL as IflagsType;
252         /// Local address in use
253         #[cfg(solarish)]
254         IFF_DUPLICATE as IflagsType;
255         /// IPMP IP interface
256         #[cfg(solarish)]
257         IFF_IPMP as IflagsType;
258     }
259 );
260 
261 impl fmt::Display for InterfaceFlags {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result262     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
263         bitflags::parser::to_writer(self, f)
264     }
265 }
266 
267 
268 #[cfg(any(
269     bsd,
270     target_os = "fuchsia",
271     target_os = "linux",
272     solarish,
273 ))]
274 mod if_nameindex {
275     use super::*;
276 
277     use std::ffi::CStr;
278     use std::fmt;
279     use std::marker::PhantomData;
280     use std::ptr::NonNull;
281 
282     /// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index
283     /// (1, 2, 3, etc) that identifies it in the OS's networking stack.
284     #[allow(missing_copy_implementations)]
285     #[repr(transparent)]
286     pub struct Interface(libc::if_nameindex);
287 
288     impl Interface {
289         /// Obtain the index of this interface.
index(&self) -> c_uint290         pub fn index(&self) -> c_uint {
291             self.0.if_index
292         }
293 
294         /// Obtain the name of this interface.
name(&self) -> &CStr295         pub fn name(&self) -> &CStr {
296             unsafe { CStr::from_ptr(self.0.if_name) }
297         }
298     }
299 
300     impl fmt::Debug for Interface {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result301         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
302             f.debug_struct("Interface")
303                 .field("index", &self.index())
304                 .field("name", &self.name())
305                 .finish()
306         }
307     }
308 
309     /// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
310     #[repr(transparent)]
311     pub struct Interfaces {
312         ptr: NonNull<libc::if_nameindex>,
313     }
314 
315     impl Interfaces {
316         /// Iterate over the interfaces in this list.
317         #[inline]
iter(&self) -> InterfacesIter<'_>318         pub fn iter(&self) -> InterfacesIter<'_> {
319             self.into_iter()
320         }
321 
322         /// Convert this to a slice of interfaces. Note that the underlying interfaces list is
323         /// null-terminated, so calling this calculates the length. If random access isn't needed,
324         /// [`Interfaces::iter()`] should be used instead.
to_slice(&self) -> &[Interface]325         pub fn to_slice(&self) -> &[Interface] {
326             let ifs = self.ptr.as_ptr().cast();
327             let len = self.iter().count();
328             unsafe { std::slice::from_raw_parts(ifs, len) }
329         }
330     }
331 
332     impl Drop for Interfaces {
drop(&mut self)333         fn drop(&mut self) {
334             unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
335         }
336     }
337 
338     impl fmt::Debug for Interfaces {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result339         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
340             self.to_slice().fmt(f)
341         }
342     }
343 
344     impl<'a> IntoIterator for &'a Interfaces {
345         type IntoIter = InterfacesIter<'a>;
346         type Item = &'a Interface;
347         #[inline]
into_iter(self) -> Self::IntoIter348         fn into_iter(self) -> Self::IntoIter {
349             InterfacesIter {
350                 ptr: self.ptr.as_ptr(),
351                 _marker: PhantomData,
352             }
353         }
354     }
355 
356     /// An iterator over the interfaces in an [`Interfaces`].
357     #[derive(Debug)]
358     pub struct InterfacesIter<'a> {
359         ptr: *const libc::if_nameindex,
360         _marker: PhantomData<&'a Interfaces>,
361     }
362 
363     impl<'a> Iterator for InterfacesIter<'a> {
364         type Item = &'a Interface;
365         #[inline]
next(&mut self) -> Option<Self::Item>366         fn next(&mut self) -> Option<Self::Item> {
367             unsafe {
368                 if (*self.ptr).if_index == 0 {
369                     None
370                 } else {
371                     let ret = &*(self.ptr as *const Interface);
372                     self.ptr = self.ptr.add(1);
373                     Some(ret)
374                 }
375             }
376         }
377     }
378 
379     /// Retrieve a list of the network interfaces available on the local system.
380     ///
381     /// ```
382     /// let interfaces = nix::net::if_::if_nameindex().unwrap();
383     /// for iface in &interfaces {
384     ///     println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
385     /// }
386     /// ```
if_nameindex() -> Result<Interfaces>387     pub fn if_nameindex() -> Result<Interfaces> {
388         unsafe {
389             let ifs = libc::if_nameindex();
390             let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
391             Ok(Interfaces { ptr })
392         }
393     }
394 }
395 #[cfg(any(
396     bsd,
397     target_os = "fuchsia",
398     target_os = "linux",
399     solarish,
400 ))]
401 pub use if_nameindex::*;
402