Skip to content

Commit

Permalink
Add a wrapper around if_nameindex
Browse files Browse the repository at this point in the history
  • Loading branch information
coolreader18 committed Jun 4, 2021
1 parent db2af19 commit 7094a2d
Showing 1 changed file with 145 additions and 1 deletion.
146 changes: 145 additions & 1 deletion src/net/if_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
//! or "socan1" into device numbers.

use crate::{Error, NixPath, Result};
use libc::c_uint;
use crate::{Result, Error, NixPath};

/// Resolve an interface into a interface number.
pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
Expand Down Expand Up @@ -267,3 +267,147 @@ libc_bitflags!(
IFF_IPMP;
}
);

#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
mod get_interfaces {
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 [`interfaces()`].
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() as *const Interface;
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 information about the network interfaces available on the local system.
///
/// ```no_run
/// # fn main() -> nix::Result<()> {
/// let interfaces = nix::net::if_::interfaces()?;
/// for iface in &interfaces {
/// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
/// }
/// # Ok(()) }
/// ```
pub fn interfaces() -> Result<Interfaces> {
unsafe {
let ifs = libc::if_nameindex();
let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
Ok(Interfaces { ptr })
}
}
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
pub use get_interfaces::*;

0 comments on commit 7094a2d

Please sign in to comment.