rustix/net/
netdevice.rs

1//! Low-level Linux network device access
2//!
3//! The methods in this module take a socket's file descriptor to communicate
4//! with the kernel in their ioctl call:
5//!  - glibc uses an `AF_UNIX`, `AF_INET`, or `AF_INET6` socket. The address
6//!    family itself does not matter and glibc tries the next address family if
7//!    socket creation with one fails.
8//!  - Android (bionic) uses an `AF_INET` socket.
9//!  - Both create the socket with `SOCK_DGRAM|SOCK_CLOEXEC` type/flag.
10//!  - The [manual pages] specify that the ioctl calls “can be used on any
11//!    socket's file descriptor regardless of the family or type”.
12//!
13//! # References
14//!  - [Linux]
15//!
16//! [manual pages]: https://man7.org/linux/man-pages/man7/netdevice.7.html
17//! [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html
18
19use crate::fd::AsFd;
20use crate::io;
21#[cfg(feature = "alloc")]
22use alloc::{borrow::ToOwned, string::String};
23
24/// `ioctl(fd, SIOCGIFINDEX, ifreq)`—Returns the interface index for a given
25/// name.
26///
27/// See the [module-level documentation] for information about `fd` usage.
28///
29/// # References
30///  - [Linux]
31///
32/// [module-level documentation]: self
33/// [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html
34#[inline]
35#[doc(alias = "SIOCGIFINDEX")]
36pub fn name_to_index<Fd: AsFd>(fd: Fd, if_name: &str) -> io::Result<u32> {
37    crate::backend::net::netdevice::name_to_index(fd.as_fd(), if_name)
38}
39
40/// `ioctl(fd, SIOCGIFNAME, ifreq)`—Returns the interface name for a given
41/// index.
42///
43/// See the [module-level documentation] for information about `fd` usage.
44///
45/// See also [`index_to_name_inlined`] which does not require `alloc` feature.
46///
47/// # References
48///  - [Linux]
49///
50/// [module-level documentation]: self
51/// [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html
52#[inline]
53#[doc(alias = "SIOCGIFNAME")]
54#[cfg(feature = "alloc")]
55#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
56pub fn index_to_name<Fd: AsFd>(fd: Fd, index: u32) -> io::Result<String> {
57    let (len, ifrn_name) = crate::backend::net::netdevice::index_to_name(fd.as_fd(), index)?;
58
59    core::str::from_utf8(&ifrn_name[..len])
60        .map_err(|_| io::Errno::ILSEQ)
61        .map(ToOwned::to_owned)
62}
63
64/// `ioctl(fd, SIOCGIFNAME, ifreq)`—Returns the interface name for a given
65/// index.
66///
67/// See the [module-level documentation] for information about `fd` usage.
68///
69/// # References
70///  - [Linux]
71///
72/// [module-level documentation]: self
73/// [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html
74#[inline]
75#[doc(alias = "SIOCGIFNAME")]
76pub fn index_to_name_inlined<Fd: AsFd>(fd: Fd, index: u32) -> io::Result<InlinedName> {
77    let (len, ifrn_name) = crate::backend::net::netdevice::index_to_name(fd.as_fd(), index)?;
78
79    // Check if the name is valid UTF-8.
80    core::str::from_utf8(&ifrn_name[..len])
81        .map_err(|_| io::Errno::ILSEQ)
82        .map(|_| InlinedName {
83            len,
84            name: ifrn_name,
85        })
86}
87
88/// The inlined interface name.
89#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
90pub struct InlinedName {
91    len: usize,
92    name: [u8; 16],
93}
94
95impl InlinedName {
96    /// Returns the str representation of the inlined name.
97    pub fn as_str(&self) -> &str {
98        self.as_ref()
99    }
100
101    /// Returns the bytes representation of the inlined name.
102    pub fn as_bytes(&self) -> &[u8] {
103        self.as_ref()
104    }
105}
106
107impl AsRef<[u8]> for InlinedName {
108    fn as_ref(&self) -> &[u8] {
109        &self.name[..self.len]
110    }
111}
112
113impl AsRef<str> for InlinedName {
114    fn as_ref(&self) -> &str {
115        // SAFETY: `InlinedName` is constructed with valid UTF-8.
116        core::str::from_utf8(&self.name[..self.len]).unwrap()
117    }
118}
119
120impl core::borrow::Borrow<str> for InlinedName {
121    fn borrow(&self) -> &str {
122        self.as_ref()
123    }
124}
125
126impl core::fmt::Display for InlinedName {
127    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
128        self.as_str().fmt(f)
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::{index_to_name, index_to_name_inlined, name_to_index};
135    use crate::fd::AsFd;
136    use crate::net::{AddressFamily, SocketFlags, SocketType};
137
138    #[test]
139    fn test_name_to_index() {
140        let fd = crate::net::socket_with(
141            AddressFamily::INET,
142            SocketType::DGRAM,
143            SocketFlags::CLOEXEC,
144            None,
145        )
146        .unwrap();
147
148        let loopback_index = std::fs::read_to_string("/sys/class/net/lo/ifindex")
149            .unwrap()
150            .as_str()
151            .split_at(1)
152            .0
153            .parse::<u32>()
154            .unwrap();
155        assert_eq!(Ok(loopback_index), name_to_index(fd.as_fd(), "lo"));
156    }
157
158    #[test]
159    fn test_index_to_name_inlined() {
160        let fd = crate::net::socket_with(
161            AddressFamily::INET,
162            SocketType::DGRAM,
163            SocketFlags::CLOEXEC,
164            None,
165        )
166        .unwrap();
167
168        let loopback_index = std::fs::read_to_string("/sys/class/net/lo/ifindex")
169            .unwrap()
170            .as_str()
171            .split_at(1)
172            .0
173            .parse::<u32>()
174            .unwrap();
175        assert_eq!(
176            "lo",
177            index_to_name_inlined(fd.as_fd(), loopback_index)
178                .unwrap()
179                .as_str(),
180        );
181    }
182
183    #[test]
184    #[cfg(feature = "alloc")]
185    fn test_index_to_name() {
186        let fd = crate::net::socket_with(
187            AddressFamily::INET,
188            SocketType::DGRAM,
189            SocketFlags::CLOEXEC,
190            None,
191        )
192        .unwrap();
193
194        let loopback_index = std::fs::read_to_string("/sys/class/net/lo/ifindex")
195            .unwrap()
196            .as_str()
197            .split_at(1)
198            .0
199            .parse::<u32>()
200            .unwrap();
201        assert_eq!(
202            Ok("lo".to_owned()),
203            index_to_name(fd.as_fd(), loopback_index)
204        );
205    }
206}