Skip to content

Commit 99fc58e

Browse files
Merge #1445
1445: Add a wrapper around if_nameindex r=asomers a=coolreader18 Co-authored-by: Noah <[email protected]>
2 parents db2af19 + 19182b9 commit 99fc58e

File tree

2 files changed

+145
-1
lines changed

2 files changed

+145
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
55

66
## [Unreleased] - ReleaseDate
77
### Added
8+
- Added `if_nameindex` (#[1445](https://github.com/nix-rust/nix/pull/1445))
9+
810
### Changed
911
### Fixed
1012
### Removed

src/net/if_.rs

Lines changed: 143 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,145 @@ 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+
/// ```
388+
/// let interfaces = nix::net::if_::if_nameindex().unwrap();
389+
/// for iface in &interfaces {
390+
/// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
391+
/// }
392+
/// ```
393+
pub fn if_nameindex() -> Result<Interfaces> {
394+
unsafe {
395+
let ifs = libc::if_nameindex();
396+
let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
397+
Ok(Interfaces { ptr })
398+
}
399+
}
400+
}
401+
#[cfg(any(
402+
target_os = "dragonfly",
403+
target_os = "freebsd",
404+
target_os = "fuchsia",
405+
target_os = "ios",
406+
target_os = "linux",
407+
target_os = "macos",
408+
target_os = "netbsd",
409+
target_os = "openbsd",
410+
))]
411+
pub use if_nameindex::*;

0 commit comments

Comments
 (0)