Skip to content

Commit d246e76

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

File tree

6 files changed

+204
-0
lines changed

6 files changed

+204
-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: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#![allow(unsafe_code)]
2+
3+
#[cfg(feature = "alloc")]
4+
use crate::alloc::string::String;
5+
use crate::backend::io::syscalls::ioctl;
6+
use crate::fd::AsFd;
7+
use crate::io;
8+
use crate::net::netdevice::open_socket;
9+
#[cfg(feature = "alloc")]
10+
use libc::SIOCGIFNAME;
11+
use libc::{__c_anonymous_ifr_ifru, c_char, ifreq, IFNAMSIZ, SIOCGIFINDEX};
12+
13+
#[cfg(target_os = "linux")]
14+
pub(crate) fn name_to_index(if_name: &str) -> io::Result<u32> {
15+
let if_name_bytes = if_name.as_bytes();
16+
if if_name_bytes.len() >= IFNAMSIZ as usize {
17+
return Err(io::Errno::NODEV);
18+
}
19+
20+
let mut ifreq = ifreq {
21+
ifr_name: [0; 16],
22+
ifr_ifru: __c_anonymous_ifr_ifru { ifru_ifindex: 0 },
23+
};
24+
25+
let mut if_name_c_char_iter = if_name_bytes.iter().map(|byte| *byte as c_char);
26+
ifreq.ifr_name[..if_name_bytes.len()].fill_with(|| if_name_c_char_iter.next().unwrap());
27+
28+
let fd = open_socket()?;
29+
unsafe { ioctl(fd.as_fd(), SIOCGIFINDEX as _, &mut ifreq as *mut ifreq as _) }?;
30+
let index = unsafe { ifreq.ifr_ifru.ifru_ifindex };
31+
Ok(index as u32)
32+
}
33+
34+
#[cfg(all(target_os = "linux", feature = "alloc"))]
35+
pub(crate) fn index_to_name(index: u32) -> io::Result<String> {
36+
let mut ifreq = ifreq {
37+
ifr_name: [0; 16],
38+
ifr_ifru: __c_anonymous_ifr_ifru {
39+
ifru_ifindex: index as _,
40+
},
41+
};
42+
43+
let fd = open_socket()?;
44+
unsafe { ioctl(fd.as_fd(), SIOCGIFNAME as _, &mut ifreq as *mut ifreq as _) }?;
45+
46+
if let Some(nul_byte) = ifreq.ifr_name.iter().position(|char| *char == 0) {
47+
let name: String = ifreq.ifr_name[..nul_byte]
48+
.iter()
49+
.map(|v| *v as u8 as char)
50+
.collect();
51+
52+
Ok(name)
53+
} else {
54+
Err(io::Errno::INVAL)
55+
}
56+
}

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: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#![allow(unsafe_code)]
2+
3+
#[cfg(feature = "alloc")]
4+
use crate::alloc::string::String;
5+
use crate::backend::io::syscalls::ioctl;
6+
use crate::fd::AsFd;
7+
use crate::io;
8+
use crate::net::netdevice::open_socket;
9+
use linux_raw_sys::ioctl::SIOCGIFINDEX;
10+
#[cfg(feature = "alloc")]
11+
use linux_raw_sys::ioctl::SIOCGIFNAME;
12+
use linux_raw_sys::net::{ifreq, ifreq__bindgen_ty_1, ifreq__bindgen_ty_2, IFNAMSIZ};
13+
14+
#[cfg(target_os = "linux")]
15+
pub(crate) fn name_to_index(if_name: &str) -> io::Result<u32> {
16+
let if_name_bytes = if_name.as_bytes();
17+
if if_name_bytes.len() >= IFNAMSIZ as usize {
18+
return Err(io::Errno::NODEV);
19+
}
20+
21+
let mut ifreq = ifreq {
22+
ifr_ifrn: ifreq__bindgen_ty_1 { ifrn_name: [0; 16] },
23+
ifr_ifru: ifreq__bindgen_ty_2 { ifru_ivalue: 0 },
24+
};
25+
unsafe { ifreq.ifr_ifrn.ifrn_name[..if_name_bytes.len()].copy_from_slice(if_name_bytes) };
26+
27+
let fd = open_socket()?;
28+
unsafe { ioctl(fd.as_fd(), SIOCGIFINDEX, &mut ifreq as *mut ifreq as _) }?;
29+
let index = unsafe { ifreq.ifr_ifru.ifru_ivalue };
30+
Ok(index as u32)
31+
}
32+
33+
#[cfg(all(target_os = "linux", feature = "alloc"))]
34+
pub(crate) fn index_to_name(index: u32) -> io::Result<String> {
35+
let mut ifreq = ifreq {
36+
ifr_ifrn: ifreq__bindgen_ty_1 { ifrn_name: [0; 16] },
37+
ifr_ifru: ifreq__bindgen_ty_2 {
38+
ifru_ivalue: index as _,
39+
},
40+
};
41+
42+
let fd = open_socket()?;
43+
unsafe { ioctl(fd.as_fd(), SIOCGIFNAME, &mut ifreq as *mut ifreq as _) }?;
44+
45+
if let Some(nul_byte) = unsafe { ifreq.ifr_ifrn.ifrn_name }
46+
.iter()
47+
.position(|char| *char == 0)
48+
{
49+
let name = unsafe { ifreq.ifr_ifrn.ifrn_name }[..nul_byte]
50+
.iter()
51+
.map(|v| *v as char)
52+
.collect();
53+
54+
Ok(name)
55+
} else {
56+
Err(io::Errno::INVAL)
57+
}
58+
}

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

0 commit comments

Comments
 (0)