Source code

Revision control

Copy as Markdown

Other Tools

//! 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
IFF_UP as IflagsType;
/// Valid broadcast address set. (see
IFF_BROADCAST as IflagsType;
/// Internal debugging flag. (see
#[cfg(not(target_os = "haiku"))]
IFF_DEBUG as IflagsType;
/// Interface is a loopback interface. (see
IFF_LOOPBACK as IflagsType;
/// Interface is a point-to-point link. (see
IFF_POINTOPOINT as IflagsType;
/// Avoid use of trailers. (see
#[cfg(any(
linux_android,
solarish,
apple_targets,
target_os = "fuchsia",
target_os = "netbsd"))]
IFF_NOTRAILERS as IflagsType;
/// Interface manages own routes.
#[cfg(any(target_os = "dragonfly"))]
IFF_SMART as IflagsType;
/// Resources allocated. (see
#[cfg(any(
linux_android,
bsd,
solarish,
target_os = "fuchsia"))]
IFF_RUNNING as IflagsType;
/// No arp protocol, L2 destination address not set. (see
IFF_NOARP as IflagsType;
/// Interface is in promiscuous mode. (see
IFF_PROMISC as IflagsType;
/// Receive all multicast packets. (see
IFF_ALLMULTI as IflagsType;
/// Master of a load balancing bundle. (see
#[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
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_SLAVE;
/// Can't hear own transmissions.
#[cfg(bsd)]
IFF_SIMPLEX;
/// Supports multicast. (see
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
#[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
#[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
#[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"))]
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"))]
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::*;