|
3 | 3 | //! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
|
4 | 4 | //! or "socan1" into device numbers.
|
5 | 5 |
|
| 6 | +use crate::{Error, NixPath, Result}; |
6 | 7 | use libc::c_uint;
|
7 |
| -use crate::{Result, Error, NixPath}; |
8 | 8 |
|
9 | 9 | /// Resolve an interface into a interface number.
|
10 | 10 | pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
|
@@ -267,3 +267,145 @@ libc_bitflags!(
|
267 | 267 | IFF_IPMP;
|
268 | 268 | }
|
269 | 269 | );
|
| 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