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 std::{ffi::{CStr, CString}, fmt};
7use crate::{errno::Errno, Error, NixPath, Result};
8use libc::{c_uint, IF_NAMESIZE};
9
10#[cfg(not(solarish))]
11/// type alias for InterfaceFlags
12pub type IflagsType = libc::c_int;
13#[cfg(solarish)]
14/// type alias for InterfaceFlags
15pub type IflagsType = libc::c_longlong;
16
17/// Resolve an interface into an interface number.
18pub 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.
30pub 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
42libc_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
261impl fmt::Display for InterfaceFlags {
262    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))]
274mod 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.
290        pub fn index(&self) -> c_uint {
291            self.0.if_index
292        }
293
294        /// Obtain the name of this interface.
295        pub fn name(&self) -> &CStr {
296            unsafe { CStr::from_ptr(self.0.if_name) }
297        }
298    }
299
300    impl fmt::Debug for Interface {
301        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]
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.
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 {
333        fn drop(&mut self) {
334            unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
335        }
336    }
337
338    impl fmt::Debug for Interfaces {
339        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]
348        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]
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    /// ```
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))]
401pub use if_nameindex::*;