405 lines
14 KiB
Rust
405 lines
14 KiB
Rust
//! Network interface name resolution.
|
|
//!
|
|
//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
|
|
//! or "socan1" into device numbers.
|
|
|
|
use std::{ffi::{CStr, CString}, fmt};
|
|
use crate::{errno::Errno, Error, NixPath, Result};
|
|
use libc::{c_uint, IF_NAMESIZE};
|
|
|
|
#[cfg(not(solarish))]
|
|
/// type alias for InterfaceFlags
|
|
pub type IflagsType = libc::c_int;
|
|
#[cfg(solarish)]
|
|
/// type alias for InterfaceFlags
|
|
pub type IflagsType = libc::c_longlong;
|
|
|
|
/// Resolve an interface into an interface number.
|
|
pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
|
|
let if_index = name
|
|
.with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?;
|
|
|
|
if if_index == 0 {
|
|
Err(Error::last())
|
|
} else {
|
|
Ok(if_index)
|
|
}
|
|
}
|
|
|
|
/// Resolve an interface number into an interface.
|
|
pub fn if_indextoname(index: c_uint) -> Result<CString> {
|
|
// We need to allocate this anyway, so doing it directly is faster.
|
|
let mut buf = vec![0u8; IF_NAMESIZE];
|
|
|
|
let return_buf = unsafe {
|
|
libc::if_indextoname(index, buf.as_mut_ptr().cast())
|
|
};
|
|
|
|
Errno::result(return_buf.cast())?;
|
|
Ok(CStr::from_bytes_until_nul(buf.as_slice()).unwrap().to_owned())
|
|
}
|
|
|
|
libc_bitflags!(
|
|
/// Standard interface flags, used by `getifaddrs`
|
|
pub struct InterfaceFlags: IflagsType {
|
|
|
|
/// Interface is running. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
IFF_UP as IflagsType;
|
|
/// Valid broadcast address set. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
IFF_BROADCAST as IflagsType;
|
|
/// Internal debugging flag. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
#[cfg(not(any(target_os = "haiku", target_os = "cygwin")))]
|
|
IFF_DEBUG as IflagsType;
|
|
/// Interface is a loopback interface. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
IFF_LOOPBACK as IflagsType;
|
|
/// Interface is a point-to-point link. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
IFF_POINTOPOINT as IflagsType;
|
|
/// Avoid use of trailers. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
#[cfg(any(
|
|
linux_android,
|
|
solarish,
|
|
apple_targets,
|
|
target_os = "fuchsia",
|
|
target_os = "netbsd",
|
|
target_os = "cygwin"))]
|
|
IFF_NOTRAILERS as IflagsType;
|
|
/// Interface manages own routes.
|
|
#[cfg(any(target_os = "dragonfly"))]
|
|
IFF_SMART as IflagsType;
|
|
/// Resources allocated. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
#[cfg(any(
|
|
linux_android,
|
|
bsd,
|
|
solarish,
|
|
target_os = "fuchsia",
|
|
target_os = "cygwin"))]
|
|
IFF_RUNNING as IflagsType;
|
|
/// No arp protocol, L2 destination address not set. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
IFF_NOARP as IflagsType;
|
|
/// Interface is in promiscuous mode. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
IFF_PROMISC as IflagsType;
|
|
/// Receive all multicast packets. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
#[cfg(not(target_os = "cygwin"))]
|
|
IFF_ALLMULTI as IflagsType;
|
|
/// Master of a load balancing bundle. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
#[cfg(any(linux_android, target_os = "fuchsia"))]
|
|
IFF_MASTER;
|
|
/// transmission in progress, tx hardware queue is full
|
|
#[cfg(any(target_os = "freebsd", apple_targets, netbsdlike))]
|
|
IFF_OACTIVE;
|
|
/// Protocol code on board.
|
|
#[cfg(solarish)]
|
|
IFF_INTELLIGENT as IflagsType;
|
|
/// Slave of a load balancing bundle. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
#[cfg(any(linux_android, target_os = "fuchsia"))]
|
|
IFF_SLAVE;
|
|
/// Can't hear own transmissions.
|
|
#[cfg(bsd)]
|
|
IFF_SIMPLEX;
|
|
/// Supports multicast. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
IFF_MULTICAST as IflagsType;
|
|
/// Per link layer defined bit.
|
|
#[cfg(bsd)]
|
|
IFF_LINK0;
|
|
/// Multicast using broadcast.
|
|
#[cfg(solarish)]
|
|
IFF_MULTI_BCAST as IflagsType;
|
|
/// Is able to select media type via ifmap. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
#[cfg(any(linux_android, target_os = "fuchsia"))]
|
|
IFF_PORTSEL;
|
|
/// Per link layer defined bit.
|
|
#[cfg(bsd)]
|
|
IFF_LINK1;
|
|
/// Non-unique address.
|
|
#[cfg(solarish)]
|
|
IFF_UNNUMBERED as IflagsType;
|
|
/// Auto media selection active. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
#[cfg(any(linux_android, target_os = "fuchsia"))]
|
|
IFF_AUTOMEDIA;
|
|
/// Per link layer defined bit.
|
|
#[cfg(bsd)]
|
|
IFF_LINK2;
|
|
/// Use alternate physical connection.
|
|
#[cfg(any(freebsdlike, apple_targets))]
|
|
IFF_ALTPHYS;
|
|
/// DHCP controls interface.
|
|
#[cfg(solarish)]
|
|
IFF_DHCPRUNNING as IflagsType;
|
|
/// The addresses are lost when the interface goes down. (see
|
|
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
|
#[cfg(any(linux_android, target_os = "fuchsia"))]
|
|
IFF_DYNAMIC;
|
|
/// Do not advertise.
|
|
#[cfg(solarish)]
|
|
IFF_PRIVATE as IflagsType;
|
|
/// Driver signals L1 up. Volatile.
|
|
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
|
|
IFF_LOWER_UP;
|
|
/// Interface is in polling mode.
|
|
#[cfg(any(target_os = "dragonfly"))]
|
|
IFF_POLLING_COMPAT;
|
|
/// Unconfigurable using ioctl(2).
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
IFF_CANTCONFIG;
|
|
/// Do not transmit packets.
|
|
#[cfg(solarish)]
|
|
IFF_NOXMIT as IflagsType;
|
|
/// Driver signals dormant. Volatile.
|
|
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
|
|
IFF_DORMANT;
|
|
/// User-requested promisc mode.
|
|
#[cfg(freebsdlike)]
|
|
IFF_PPROMISC;
|
|
/// Just on-link subnet.
|
|
#[cfg(solarish)]
|
|
IFF_NOLOCAL as IflagsType;
|
|
/// Echo sent packets. Volatile.
|
|
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
|
|
IFF_ECHO;
|
|
/// User-requested monitor mode.
|
|
#[cfg(freebsdlike)]
|
|
IFF_MONITOR;
|
|
/// Address is deprecated.
|
|
#[cfg(solarish)]
|
|
IFF_DEPRECATED as IflagsType;
|
|
/// Static ARP.
|
|
#[cfg(freebsdlike)]
|
|
IFF_STATICARP;
|
|
/// Address from stateless addrconf.
|
|
#[cfg(solarish)]
|
|
IFF_ADDRCONF as IflagsType;
|
|
/// Interface is in polling mode.
|
|
#[cfg(any(target_os = "dragonfly"))]
|
|
IFF_NPOLLING;
|
|
/// Router on interface.
|
|
#[cfg(solarish)]
|
|
IFF_ROUTER as IflagsType;
|
|
/// Interface is in polling mode.
|
|
#[cfg(any(target_os = "dragonfly"))]
|
|
IFF_IDIRECT;
|
|
/// Interface is winding down
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
IFF_DYING;
|
|
/// No NUD on interface.
|
|
#[cfg(solarish)]
|
|
IFF_NONUD as IflagsType;
|
|
/// Interface is being renamed
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
IFF_RENAMING;
|
|
/// Anycast address.
|
|
#[cfg(solarish)]
|
|
IFF_ANYCAST as IflagsType;
|
|
/// Don't exchange routing info.
|
|
#[cfg(solarish)]
|
|
IFF_NORTEXCH as IflagsType;
|
|
/// Do not provide packet information
|
|
#[cfg(any(linux_android, target_os = "fuchsia"))]
|
|
IFF_NO_PI as IflagsType;
|
|
/// TUN device (no Ethernet headers)
|
|
#[cfg(any(linux_android, target_os = "fuchsia"))]
|
|
IFF_TUN as IflagsType;
|
|
/// TAP device
|
|
#[cfg(any(linux_android, target_os = "fuchsia"))]
|
|
IFF_TAP as IflagsType;
|
|
/// IPv4 interface.
|
|
#[cfg(solarish)]
|
|
IFF_IPV4 as IflagsType;
|
|
/// IPv6 interface.
|
|
#[cfg(solarish)]
|
|
IFF_IPV6 as IflagsType;
|
|
/// in.mpathd test address
|
|
#[cfg(solarish)]
|
|
IFF_NOFAILOVER as IflagsType;
|
|
/// Interface has failed
|
|
#[cfg(solarish)]
|
|
IFF_FAILED as IflagsType;
|
|
/// Interface is a hot-spare
|
|
#[cfg(solarish)]
|
|
IFF_STANDBY as IflagsType;
|
|
/// Functioning but not used
|
|
#[cfg(solarish)]
|
|
IFF_INACTIVE as IflagsType;
|
|
/// Interface is offline
|
|
#[cfg(solarish)]
|
|
IFF_OFFLINE as IflagsType;
|
|
/// Has CoS marking supported
|
|
#[cfg(solarish)]
|
|
IFF_COS_ENABLED as IflagsType;
|
|
/// Prefer as source addr
|
|
#[cfg(solarish)]
|
|
IFF_PREFERRED as IflagsType;
|
|
/// RFC3041
|
|
#[cfg(solarish)]
|
|
IFF_TEMPORARY as IflagsType;
|
|
/// MTU set
|
|
#[cfg(solarish)]
|
|
IFF_FIXEDMTU as IflagsType;
|
|
/// Cannot send/receive packets
|
|
#[cfg(solarish)]
|
|
IFF_VIRTUAL as IflagsType;
|
|
/// Local address in use
|
|
#[cfg(solarish)]
|
|
IFF_DUPLICATE as IflagsType;
|
|
/// IPMP IP interface
|
|
#[cfg(solarish)]
|
|
IFF_IPMP as IflagsType;
|
|
}
|
|
);
|
|
|
|
impl fmt::Display for InterfaceFlags {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
bitflags::parser::to_writer(self, f)
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(any(
|
|
bsd,
|
|
target_os = "fuchsia",
|
|
target_os = "linux",
|
|
solarish,
|
|
))]
|
|
mod if_nameindex {
|
|
use super::*;
|
|
|
|
use std::ffi::CStr;
|
|
use std::fmt;
|
|
use std::marker::PhantomData;
|
|
use std::ptr::NonNull;
|
|
|
|
/// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index
|
|
/// (1, 2, 3, etc) that identifies it in the OS's networking stack.
|
|
#[allow(missing_copy_implementations)]
|
|
#[repr(transparent)]
|
|
pub struct Interface(libc::if_nameindex);
|
|
|
|
impl Interface {
|
|
/// Obtain the index of this interface.
|
|
pub fn index(&self) -> c_uint {
|
|
self.0.if_index
|
|
}
|
|
|
|
/// Obtain the name of this interface.
|
|
pub fn name(&self) -> &CStr {
|
|
unsafe { CStr::from_ptr(self.0.if_name) }
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Interface {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
f.debug_struct("Interface")
|
|
.field("index", &self.index())
|
|
.field("name", &self.name())
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
/// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
|
|
#[repr(transparent)]
|
|
pub struct Interfaces {
|
|
ptr: NonNull<libc::if_nameindex>,
|
|
}
|
|
|
|
impl Interfaces {
|
|
/// Iterate over the interfaces in this list.
|
|
#[inline]
|
|
pub fn iter(&self) -> InterfacesIter<'_> {
|
|
self.into_iter()
|
|
}
|
|
|
|
/// Convert this to a slice of interfaces. Note that the underlying interfaces list is
|
|
/// null-terminated, so calling this calculates the length. If random access isn't needed,
|
|
/// [`Interfaces::iter()`] should be used instead.
|
|
pub fn to_slice(&self) -> &[Interface] {
|
|
let ifs = self.ptr.as_ptr().cast();
|
|
let len = self.iter().count();
|
|
unsafe { std::slice::from_raw_parts(ifs, len) }
|
|
}
|
|
}
|
|
|
|
impl Drop for Interfaces {
|
|
fn drop(&mut self) {
|
|
unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Interfaces {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
self.to_slice().fmt(f)
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a Interfaces {
|
|
type IntoIter = InterfacesIter<'a>;
|
|
type Item = &'a Interface;
|
|
#[inline]
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
InterfacesIter {
|
|
ptr: self.ptr.as_ptr(),
|
|
_marker: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An iterator over the interfaces in an [`Interfaces`].
|
|
#[derive(Debug)]
|
|
pub struct InterfacesIter<'a> {
|
|
ptr: *const libc::if_nameindex,
|
|
_marker: PhantomData<&'a Interfaces>,
|
|
}
|
|
|
|
impl<'a> Iterator for InterfacesIter<'a> {
|
|
type Item = &'a Interface;
|
|
#[inline]
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
unsafe {
|
|
if (*self.ptr).if_index == 0 {
|
|
None
|
|
} else {
|
|
let ret = &*(self.ptr as *const Interface);
|
|
self.ptr = self.ptr.add(1);
|
|
Some(ret)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Retrieve a list of the network interfaces available on the local system.
|
|
///
|
|
/// ```
|
|
/// let interfaces = nix::net::if_::if_nameindex().unwrap();
|
|
/// for iface in &interfaces {
|
|
/// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
|
|
/// }
|
|
/// ```
|
|
pub fn if_nameindex() -> Result<Interfaces> {
|
|
unsafe {
|
|
let ifs = libc::if_nameindex();
|
|
let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
|
|
Ok(Interfaces { ptr })
|
|
}
|
|
}
|
|
}
|
|
#[cfg(any(
|
|
bsd,
|
|
target_os = "fuchsia",
|
|
target_os = "linux",
|
|
solarish,
|
|
))]
|
|
pub use if_nameindex::*;
|