Skip to content

Commit 4bdde2e

Browse files
committed
Add name_to_index() & index_to_name()
1 parent 5ff2b62 commit 4bdde2e

File tree

6 files changed

+213
-0
lines changed

6 files changed

+213
-0
lines changed

src/backend/libc/net/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub(crate) mod ext;
88
target_os = "wasi"
99
)))]
1010
pub(crate) mod msghdr;
11+
#[cfg(target_os = "linux")]
12+
pub(crate) mod netdevice;
1113
pub(crate) mod read_sockaddr;
1214
pub(crate) mod send_recv;
1315
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]

src/backend/libc/net/netdevice.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#![allow(unsafe_code)]
2+
3+
use crate::backend::io::syscalls::ioctl;
4+
use crate::io;
5+
use crate::net::netdevice::open_socket;
6+
use libc::{c_char, ifreq, IFNAMSIZ, SIOCGIFINDEX, SIOCGIFNAME};
7+
use std::ffi::{CStr, CString};
8+
use std::mem::MaybeUninit;
9+
use std::os::fd::AsFd;
10+
use std::ptr::{addr_of, addr_of_mut};
11+
12+
#[cfg(target_os = "linux")]
13+
pub(crate) fn index_to_name(if_name: String) -> io::Result<u32> {
14+
if if_name.len() >= IFNAMSIZ as usize {
15+
return Err(io::Errno::NODEV);
16+
}
17+
let mut ifrn_name = [0; IFNAMSIZ as usize];
18+
let c_string_name = CString::new(if_name).map_err(|_cstr_err| io::Errno::INVAL)?;
19+
// Convert from CString to c_char array.
20+
c_string_name
21+
.as_bytes_with_nul()
22+
.iter()
23+
.map(|byte| *byte as c_char)
24+
.enumerate()
25+
.for_each(|(i, c_char_byte)| ifrn_name[i] = c_char_byte);
26+
27+
let mut uninit_ifreq = MaybeUninit::<ifreq>::uninit();
28+
let uninit_ifreq_ptr = uninit_ifreq.as_mut_ptr();
29+
unsafe {
30+
addr_of_mut!((*uninit_ifreq_ptr).ifr_name).write(ifrn_name);
31+
}
32+
33+
let fd = open_socket()?;
34+
unsafe { ioctl(fd.as_fd(), SIOCGIFINDEX as _, uninit_ifreq_ptr as _) }?;
35+
let index = unsafe { *addr_of!((*uninit_ifreq_ptr).ifr_ifru.ifru_ifindex) };
36+
Ok(index as u32)
37+
}
38+
39+
#[cfg(target_os = "linux")]
40+
pub(crate) fn name_to_index(index: u32) -> io::Result<String> {
41+
let mut uninit_ifreq = MaybeUninit::<ifreq>::uninit();
42+
let uninit_ifreq_ptr = uninit_ifreq.as_mut_ptr();
43+
unsafe {
44+
addr_of_mut!((*uninit_ifreq_ptr).ifr_ifru.ifru_ifindex).write(index as _);
45+
}
46+
47+
let fd = open_socket()?;
48+
unsafe { ioctl(fd.as_fd(), SIOCGIFNAME as _, uninit_ifreq_ptr as _) }?;
49+
50+
let ifr_name = unsafe { *addr_of!((*uninit_ifreq_ptr).ifr_name) };
51+
let mut result_name = [0; IFNAMSIZ as usize];
52+
// Convert from c_char array to u8 array.
53+
ifr_name
54+
.iter()
55+
.map(|v| *v as u8)
56+
.enumerate()
57+
.for_each(|(i, u8_byte)| result_name[i] = u8_byte);
58+
59+
let name = CStr::from_bytes_until_nul(&result_name).map_err(|_cstr_err| io::Errno::INVAL)?;
60+
61+
Ok(name.to_string_lossy().to_string())
62+
}

src/backend/linux_raw/net/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
pub(crate) mod addr;
22
pub(crate) mod msghdr;
3+
#[cfg(target_os = "linux")]
4+
pub(crate) mod netdevice;
35
pub(crate) mod read_sockaddr;
46
pub(crate) mod send_recv;
57
pub(crate) mod sockopt;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#![allow(unsafe_code)]
2+
3+
use crate::backend::io::syscalls::ioctl;
4+
use crate::io;
5+
use crate::net::netdevice::open_socket;
6+
use linux_raw_sys::ctypes::c_char;
7+
use linux_raw_sys::ioctl::{SIOCGIFINDEX, SIOCGIFNAME};
8+
use linux_raw_sys::net::{ifreq, IFNAMSIZ};
9+
use std::ffi::{CStr, CString};
10+
use std::mem::MaybeUninit;
11+
use std::os::fd::AsFd;
12+
use std::ptr::{addr_of, addr_of_mut};
13+
14+
#[cfg(target_os = "linux")]
15+
pub(crate) fn index_to_name(if_name: String) -> io::Result<u32> {
16+
if if_name.len() >= IFNAMSIZ as usize {
17+
return Err(io::Errno::NODEV);
18+
}
19+
let mut ifrn_name = [0; IFNAMSIZ as usize];
20+
let c_string_name = CString::new(if_name).map_err(|_cstr_err| io::Errno::INVAL)?;
21+
// Convert from CString to c_char array.
22+
c_string_name
23+
.as_bytes_with_nul()
24+
.iter()
25+
.map(|byte| *byte as c_char)
26+
.enumerate()
27+
.for_each(|(i, c_char_byte)| ifrn_name[i] = c_char_byte);
28+
29+
let mut uninit_ifreq = MaybeUninit::<ifreq>::uninit();
30+
let uninit_ifreq_ptr = uninit_ifreq.as_mut_ptr();
31+
unsafe {
32+
addr_of_mut!((*uninit_ifreq_ptr).ifr_ifrn.ifrn_name).write(ifrn_name);
33+
}
34+
35+
let fd = open_socket()?;
36+
unsafe { ioctl(fd.as_fd(), SIOCGIFINDEX, uninit_ifreq_ptr as _) }?;
37+
let index = unsafe { *addr_of!((*uninit_ifreq_ptr).ifr_ifru.ifru_ivalue) };
38+
Ok(index as u32)
39+
}
40+
41+
#[cfg(target_os = "linux")]
42+
pub(crate) fn name_to_index(index: u32) -> io::Result<String> {
43+
let mut uninit_ifreq = MaybeUninit::<ifreq>::uninit();
44+
let uninit_ifreq_ptr = uninit_ifreq.as_mut_ptr();
45+
unsafe {
46+
addr_of_mut!((*uninit_ifreq_ptr).ifr_ifru.ifru_ivalue).write(index as _);
47+
}
48+
49+
let fd = open_socket()?;
50+
unsafe { ioctl(fd.as_fd(), SIOCGIFNAME, uninit_ifreq_ptr as _) }?;
51+
52+
let ifrn_name = unsafe { *addr_of!((*uninit_ifreq_ptr).ifr_ifrn.ifrn_name) };
53+
let mut result_name = [0; IFNAMSIZ as usize];
54+
// Convert from c_char array to u8 array.
55+
ifrn_name
56+
.iter()
57+
.map(|v| *v as u8)
58+
.enumerate()
59+
.for_each(|(i, u8_byte)| result_name[i] = u8_byte);
60+
61+
let name = CStr::from_bytes_until_nul(&result_name).map_err(|_cstr_err| io::Errno::INVAL)?;
62+
Ok(name.to_string_lossy().to_string())
63+
}

src/net/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ mod types;
1616
#[cfg(windows)]
1717
mod wsa;
1818

19+
#[cfg(target_os = "linux")]
20+
pub mod netdevice;
1921
pub mod sockopt;
2022

2123
pub use crate::maybe_polyfill::net::{

src/net/netdevice.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//! Low-level Linux network device access
2+
//!
3+
//! # References
4+
//! - [Linux]
5+
//!
6+
//! [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html
7+
8+
use crate::io;
9+
use crate::io::Errno;
10+
use crate::net::{socket, AddressFamily, SocketType};
11+
use std::os::fd::OwnedFd;
12+
13+
/// Creates a socket used to communicate with the kernel in the ioctl calls.
14+
#[cfg(target_os = "linux")]
15+
pub(crate) fn open_socket() -> io::Result<OwnedFd> {
16+
if let Ok(fd) = socket(AddressFamily::UNIX, SocketType::DGRAM, None) {
17+
Ok(fd)
18+
} else if let Ok(fd) = socket(AddressFamily::INET, SocketType::DGRAM, None) {
19+
Ok(fd)
20+
} else if let Ok(fd) = socket(AddressFamily::INET6, SocketType::DGRAM, None) {
21+
Ok(fd)
22+
} else {
23+
Err(Errno::NOENT)
24+
}
25+
}
26+
27+
/// `ioctl(fd, SIOCGIFNAME, ifreq)`—Returns the interface name for a given index.
28+
///
29+
/// # References
30+
/// - [Linux]
31+
///
32+
/// [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html
33+
#[inline]
34+
#[doc(alias = "SIOCGIFNAME")]
35+
#[cfg(target_os = "linux")]
36+
pub fn name_to_index(index: u32) -> io::Result<String> {
37+
crate::backend::net::netdevice::name_to_index(index)
38+
}
39+
40+
/// `ioctl(fd, SIOCGIFINDEX, ifreq)`—Returns the interface index for a given name.
41+
///
42+
/// # References
43+
/// - [Linux]
44+
///
45+
/// [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html
46+
#[inline]
47+
#[doc(alias = "SIOCGIFINDEX")]
48+
#[cfg(target_os = "linux")]
49+
pub fn index_to_name(if_name: String) -> io::Result<u32> {
50+
crate::backend::net::netdevice::index_to_name(if_name)
51+
}
52+
53+
#[cfg(test)]
54+
mod tests {
55+
use crate::backend::net::netdevice::{index_to_name, name_to_index};
56+
57+
#[test]
58+
#[cfg(target_os = "linux")]
59+
fn test_index_to_name() {
60+
let loopback_index = std::fs::read_to_string("/sys/class/net/lo/ifindex")
61+
.unwrap()
62+
.as_str()
63+
.split_at(1)
64+
.0
65+
.parse::<u32>()
66+
.unwrap();
67+
assert_eq!(Ok(loopback_index), index_to_name("lo".to_owned()));
68+
}
69+
70+
#[test]
71+
#[cfg(target_os = "linux")]
72+
fn test_name_to_index() {
73+
let loopback_index = std::fs::read_to_string("/sys/class/net/lo/ifindex")
74+
.unwrap()
75+
.as_str()
76+
.split_at(1)
77+
.0
78+
.parse::<u32>()
79+
.unwrap();
80+
assert_eq!(Ok("lo".to_owned()), name_to_index(loopback_index));
81+
}
82+
}

0 commit comments

Comments
 (0)