diff --git a/Cargo.toml b/Cargo.toml index 7249ec220..614a3f932 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ 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: # @@ -46,7 +46,7 @@ libc = { version = "0.2.151", default-features = false, features = ["extra_trait # 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: # @@ -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" diff --git a/src/backend/libc/net/mod.rs b/src/backend/libc/net/mod.rs index d7ab68d52..4f8e99a4d 100644 --- a/src/backend/libc/net/mod.rs +++ b/src/backend/libc/net/mod.rs @@ -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")))] diff --git a/src/backend/libc/net/netdevice.rs b/src/backend/libc/net/netdevice.rs new file mode 100644 index 000000000..2fbb55b81 --- /dev/null +++ b/src/backend/libc/net/netdevice.rs @@ -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 { + 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 { + 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) + } +} diff --git a/src/backend/linux_raw/net/mod.rs b/src/backend/linux_raw/net/mod.rs index f83c54621..0387d8b42 100644 --- a/src/backend/linux_raw/net/mod.rs +++ b/src/backend/linux_raw/net/mod.rs @@ -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; diff --git a/src/backend/linux_raw/net/netdevice.rs b/src/backend/linux_raw/net/netdevice.rs new file mode 100644 index 000000000..850b99f7a --- /dev/null +++ b/src/backend/linux_raw/net/netdevice.rs @@ -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 { + 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 { + 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) + } +} diff --git a/src/net/mod.rs b/src/net/mod.rs index 73ae2f089..7ec8bc698 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -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::{ diff --git a/src/net/netdevice.rs b/src/net/netdevice.rs new file mode 100644 index 000000000..960c8a04f --- /dev/null +++ b/src/net/netdevice.rs @@ -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 { + 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 { + 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::() + .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::() + .unwrap(); + assert_eq!(Ok("lo".to_owned()), index_to_name(fd, loopback_index)); + } +}