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