Skip to content

Commit cb5290f

Browse files
committed
Add a wrapper around if_nameindex
1 parent db2af19 commit cb5290f

File tree

1 file changed

+145
-1
lines changed

1 file changed

+145
-1
lines changed

src/net/if_.rs

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
44
//! or "socan1" into device numbers.
55
6+
use crate::{Error, NixPath, Result};
67
use libc::c_uint;
7-
use crate::{Result, Error, NixPath};
88

99
/// Resolve an interface into a interface number.
1010
pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
@@ -267,3 +267,147 @@ libc_bitflags!(
267267
IFF_IPMP;
268268
}
269269
);
270+
271+
#[cfg(any(
272+
target_os = "dragonfly",
273+
target_os = "freebsd",
274+
target_os = "fuchsia",
275+
target_os = "ios",
276+
target_os = "linux",
277+
target_os = "macos",
278+
target_os = "netbsd",
279+
target_os = "openbsd",
280+
))]
281+
mod if_nameindex {
282+
use super::*;
283+
284+
use std::ffi::CStr;
285+
use std::fmt;
286+
use std::marker::PhantomData;
287+
use std::ptr::NonNull;
288+
289+
/// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index
290+
/// (1, 2, 3, etc) that identifies it in the OS's networking stack.
291+
#[allow(missing_copy_implementations)]
292+
#[repr(transparent)]
293+
pub struct Interface(libc::if_nameindex);
294+
295+
impl Interface {
296+
/// Obtain the index of this interface.
297+
pub fn index(&self) -> c_uint {
298+
self.0.if_index
299+
}
300+
301+
/// Obtain the name of this interface.
302+
pub fn name(&self) -> &CStr {
303+
unsafe { CStr::from_ptr(self.0.if_name) }
304+
}
305+
}
306+
307+
impl fmt::Debug for Interface {
308+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
309+
f.debug_struct("Interface")
310+
.field("index", &self.index())
311+
.field("name", &self.name())
312+
.finish()
313+
}
314+
}
315+
316+
/// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
317+
pub struct Interfaces {
318+
ptr: NonNull<libc::if_nameindex>,
319+
}
320+
321+
impl Interfaces {
322+
/// Iterate over the interfaces in this list.
323+
#[inline]
324+
pub fn iter(&self) -> InterfacesIter<'_> {
325+
self.into_iter()
326+
}
327+
328+
/// Convert this to a slice of interfaces. Note that the underlying interfaces list is
329+
/// null-terminated, so calling this calculates the length. If random access isn't needed,
330+
/// [`Interfaces::iter()`] should be used instead.
331+
pub fn to_slice(&self) -> &[Interface] {
332+
let ifs = self.ptr.as_ptr() as *const Interface;
333+
let len = self.iter().count();
334+
unsafe { std::slice::from_raw_parts(ifs, len) }
335+
}
336+
}
337+
338+
impl Drop for Interfaces {
339+
fn drop(&mut self) {
340+
unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
341+
}
342+
}
343+
344+
impl fmt::Debug for Interfaces {
345+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
346+
self.to_slice().fmt(f)
347+
}
348+
}
349+
350+
impl<'a> IntoIterator for &'a Interfaces {
351+
type IntoIter = InterfacesIter<'a>;
352+
type Item = &'a Interface;
353+
#[inline]
354+
fn into_iter(self) -> Self::IntoIter {
355+
InterfacesIter {
356+
ptr: self.ptr.as_ptr(),
357+
_marker: PhantomData,
358+
}
359+
}
360+
}
361+
362+
/// An iterator over the interfaces in an [`Interfaces`].
363+
#[derive(Debug)]
364+
pub struct InterfacesIter<'a> {
365+
ptr: *const libc::if_nameindex,
366+
_marker: PhantomData<&'a Interfaces>,
367+
}
368+
369+
impl<'a> Iterator for InterfacesIter<'a> {
370+
type Item = &'a Interface;
371+
#[inline]
372+
fn next(&mut self) -> Option<Self::Item> {
373+
unsafe {
374+
if (*self.ptr).if_index == 0 {
375+
None
376+
} else {
377+
let ret = &*(self.ptr as *const Interface);
378+
self.ptr = self.ptr.add(1);
379+
Some(ret)
380+
}
381+
}
382+
}
383+
}
384+
385+
/// Retrieve a list of the network interfaces available on the local system.
386+
///
387+
/// ```no_run
388+
/// # fn main() -> nix::Result<()> {
389+
/// let interfaces = nix::net::if_::if_nameindex()?;
390+
/// for iface in &interfaces {
391+
/// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
392+
/// }
393+
/// # Ok(()) }
394+
/// ```
395+
pub fn if_nameindex() -> Result<Interfaces> {
396+
unsafe {
397+
let ifs = libc::if_nameindex();
398+
let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
399+
Ok(Interfaces { ptr })
400+
}
401+
}
402+
}
403+
#[cfg(any(
404+
target_os = "dragonfly",
405+
target_os = "freebsd",
406+
target_os = "fuchsia",
407+
target_os = "ios",
408+
target_os = "linux",
409+
target_os = "macos",
410+
target_os = "netbsd",
411+
target_os = "openbsd",
412+
))]
413+
pub use if_nameindex::*;

0 commit comments

Comments
 (0)