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