Skip to content

Commit

Permalink
Add name_to_index() & index_to_name() (#972)
Browse files Browse the repository at this point in the history
* Add `name_to_index()` & `index_to_name()`

* Update to libc 0.2.152.
  • Loading branch information
arctic-alpaca authored Jan 11, 2024
1 parent 9095854 commit e460984
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 3 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ once_cell = { version = "1.5.2", optional = true }
[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies]
linux-raw-sys = { version = "0.4.11", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] }
libc_errno = { package = "errno", version = "0.3.8", default-features = false, optional = true }
libc = { version = "0.2.151", default-features = false, features = ["extra_traits"], optional = true }
libc = { version = "0.2.152", default-features = false, features = ["extra_traits"], optional = true }

# Dependencies for platforms where only libc is supported:
#
# On all other Unix-family platforms, and under Miri, we always use the libc
# backend, so enable its dependencies unconditionally.
[target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies]
libc_errno = { package = "errno", version = "0.3.8", default-features = false }
libc = { version = "0.2.150", default-features = false, features = ["extra_traits"] }
libc = { version = "0.2.152", default-features = false, features = ["extra_traits"] }

# Additional dependencies for Linux with the libc backend:
#
Expand Down Expand Up @@ -74,7 +74,7 @@ default-features = false

[dev-dependencies]
tempfile = "3.5.0"
libc = "0.2.150"
libc = "0.2.152"
libc_errno = { package = "errno", version = "0.3.8", default-features = false }
serial_test = "2.0.0"
memoffset = "0.9.0"
Expand Down
2 changes: 2 additions & 0 deletions src/backend/libc/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub(crate) mod ext;
target_os = "wasi"
)))]
pub(crate) mod msghdr;
#[cfg(linux_kernel)]
pub(crate) mod netdevice;
pub(crate) mod read_sockaddr;
pub(crate) mod send_recv;
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
Expand Down
52 changes: 52 additions & 0 deletions src/backend/libc/net/netdevice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#![allow(unsafe_code)]

#[cfg(feature = "alloc")]
use crate::alloc::string::String;
use crate::backend::io::syscalls::ioctl;
use crate::fd::AsFd;
use crate::io;
#[cfg(feature = "alloc")]
use libc::SIOCGIFNAME;
use libc::{__c_anonymous_ifr_ifru, c_char, ifreq, IFNAMSIZ, SIOCGIFINDEX};

pub(crate) fn name_to_index(fd: impl AsFd, if_name: &str) -> io::Result<u32> {
let if_name_bytes = if_name.as_bytes();
if if_name_bytes.len() >= IFNAMSIZ as usize {
return Err(io::Errno::NODEV);
}

let mut ifreq = ifreq {
ifr_name: [0; 16],
ifr_ifru: __c_anonymous_ifr_ifru { ifru_ifindex: 0 },
};

let mut if_name_c_char_iter = if_name_bytes.iter().map(|byte| *byte as c_char);
ifreq.ifr_name[..if_name_bytes.len()].fill_with(|| if_name_c_char_iter.next().unwrap());

unsafe { ioctl(fd.as_fd(), SIOCGIFINDEX as _, &mut ifreq as *mut ifreq as _) }?;
let index = unsafe { ifreq.ifr_ifru.ifru_ifindex };
Ok(index as u32)
}

#[cfg(feature = "alloc")]
pub(crate) fn index_to_name(fd: impl AsFd, index: u32) -> io::Result<String> {
let mut ifreq = ifreq {
ifr_name: [0; 16],
ifr_ifru: __c_anonymous_ifr_ifru {
ifru_ifindex: index as _,
},
};

unsafe { ioctl(fd.as_fd(), SIOCGIFNAME as _, &mut ifreq as *mut ifreq as _) }?;

if let Some(nul_byte) = ifreq.ifr_name.iter().position(|char| *char == 0) {
let name: String = ifreq.ifr_name[..nul_byte]
.iter()
.map(|v| *v as u8 as char)
.collect();

Ok(name)
} else {
Err(io::Errno::INVAL)
}
}
2 changes: 2 additions & 0 deletions src/backend/linux_raw/net/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub(crate) mod addr;
pub(crate) mod msghdr;
#[cfg(linux_kernel)]
pub(crate) mod netdevice;
pub(crate) mod read_sockaddr;
pub(crate) mod send_recv;
pub(crate) mod sockopt;
Expand Down
54 changes: 54 additions & 0 deletions src/backend/linux_raw/net/netdevice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#![allow(unsafe_code)]

#[cfg(feature = "alloc")]
use crate::alloc::string::String;
use crate::backend::io::syscalls::ioctl;
use crate::fd::AsFd;
use crate::io;
use linux_raw_sys::ioctl::SIOCGIFINDEX;
#[cfg(feature = "alloc")]
use linux_raw_sys::ioctl::SIOCGIFNAME;
use linux_raw_sys::net::{ifreq, ifreq__bindgen_ty_1, ifreq__bindgen_ty_2, IFNAMSIZ};

pub(crate) fn name_to_index(fd: impl AsFd, if_name: &str) -> io::Result<u32> {
let if_name_bytes = if_name.as_bytes();
if if_name_bytes.len() >= IFNAMSIZ as usize {
return Err(io::Errno::NODEV);
}

let mut ifreq = ifreq {
ifr_ifrn: ifreq__bindgen_ty_1 { ifrn_name: [0; 16] },
ifr_ifru: ifreq__bindgen_ty_2 { ifru_ivalue: 0 },
};
unsafe { ifreq.ifr_ifrn.ifrn_name[..if_name_bytes.len()].copy_from_slice(if_name_bytes) };

unsafe { ioctl(fd.as_fd(), SIOCGIFINDEX, &mut ifreq as *mut ifreq as _) }?;
let index = unsafe { ifreq.ifr_ifru.ifru_ivalue };
Ok(index as u32)
}

#[cfg(feature = "alloc")]
pub(crate) fn index_to_name(fd: impl AsFd, index: u32) -> io::Result<String> {
let mut ifreq = ifreq {
ifr_ifrn: ifreq__bindgen_ty_1 { ifrn_name: [0; 16] },
ifr_ifru: ifreq__bindgen_ty_2 {
ifru_ivalue: index as _,
},
};

unsafe { ioctl(fd.as_fd(), SIOCGIFNAME, &mut ifreq as *mut ifreq as _) }?;

if let Some(nul_byte) = unsafe { ifreq.ifr_ifrn.ifrn_name }
.iter()
.position(|char| *char == 0)
{
let name = unsafe { ifreq.ifr_ifrn.ifrn_name }[..nul_byte]
.iter()
.map(|v| *v as char)
.collect();

Ok(name)
} else {
Err(io::Errno::INVAL)
}
}
2 changes: 2 additions & 0 deletions src/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ mod types;
#[cfg(windows)]
mod wsa;

#[cfg(linux_kernel)]
pub mod netdevice;
pub mod sockopt;

pub use crate::maybe_polyfill::net::{
Expand Down
99 changes: 99 additions & 0 deletions src/net/netdevice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//! Low-level Linux network device access
//!
//! The methods in this module take a socket's file descriptor to communicate with
//! the kernel in their ioctl call:
//! - glibc uses an `AF_UNIX`, `AF_INET`, or `AF_INET6` socket.
//! The address family itself does not matter and glibc tries the next address family if socket creation with one fails.
//! - Android (bionic) uses an `AF_INET` socket.
//! - Both create the socket with `SOCK_DGRAM|SOCK_CLOEXEC` type/flag.
//! - The [man-pages] specify, that the ioctl calls "can be used on any socket's file descriptor regardless of the
//! family or type".
//!
//! # References
//! - [Linux]
//!
//! [man-pages]: https://man7.org/linux/man-pages/man7/netdevice.7.html
//! [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html

#[cfg(feature = "alloc")]
use crate::alloc::string::String;
use crate::fd::AsFd;
use crate::io;

/// `ioctl(fd, SIOCGIFINDEX, ifreq)`—Returns the interface index for a given name.
///
/// See the [module-level documentation] for information about `fd` usage.
///
/// # References
/// - [Linux]
///
/// [module-level documentation]: self
/// [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html
#[inline]
#[doc(alias = "SIOCGIFINDEX")]
pub fn name_to_index(fd: impl AsFd, if_name: &str) -> io::Result<u32> {
crate::backend::net::netdevice::name_to_index(fd, if_name)
}

/// `ioctl(fd, SIOCGIFNAME, ifreq)`—Returns the interface name for a given index.
///
/// See the [module-level documentation] for information about `fd` usage.
///
/// # References
/// - [Linux]
///
/// [module-level documentation]: self
/// [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html
#[inline]
#[doc(alias = "SIOCGIFNAME")]
#[cfg(feature = "alloc")]
pub fn index_to_name(fd: impl AsFd, index: u32) -> io::Result<String> {
crate::backend::net::netdevice::index_to_name(fd, index)
}

#[cfg(test)]
mod tests {
use crate::backend::net::netdevice::{index_to_name, name_to_index};
use crate::net::{AddressFamily, SocketFlags, SocketType};

#[test]
fn test_name_to_index() {
let fd = crate::net::socket_with(
AddressFamily::INET,
SocketType::DGRAM,
SocketFlags::CLOEXEC,
None,
)
.unwrap();

let loopback_index = std::fs::read_to_string("/sys/class/net/lo/ifindex")
.unwrap()
.as_str()
.split_at(1)
.0
.parse::<u32>()
.unwrap();
assert_eq!(Ok(loopback_index), name_to_index(fd, "lo"));
}

#[test]
#[cfg(feature = "alloc")]
fn test_index_to_name() {
let fd = crate::net::socket_with(
AddressFamily::INET,
SocketType::DGRAM,
SocketFlags::CLOEXEC,
None,
)
.unwrap();

let loopback_index = std::fs::read_to_string("/sys/class/net/lo/ifindex")
.unwrap()
.as_str()
.split_at(1)
.0
.parse::<u32>()
.unwrap();
assert_eq!(Ok("lo".to_owned()), index_to_name(fd, loopback_index));
}
}

0 comments on commit e460984

Please sign in to comment.