Skip to main content

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