nix/net/
if_.rs

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