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