Skip to main content

tun_rs/platform/unix/
device.rs

1use crate::platform::unix::{Fd, Tun};
2use crate::platform::DeviceImpl;
3use bytes::buf::UninitSlice;
4#[cfg(any(
5    all(target_os = "linux", not(target_env = "ohos")),
6    target_os = "macos",
7    target_os = "freebsd",
8    target_os = "openbsd",
9    target_os = "netbsd",
10))]
11use libc::{AF_INET, AF_INET6, SOCK_DGRAM};
12use std::io;
13use std::io::{IoSlice, IoSliceMut};
14use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
15
16impl FromRawFd for DeviceImpl {
17    /// # Safety
18    ///
19    /// The caller must ensure that `fd` is a valid, open file descriptor for a TUN/TAP device.
20    ///
21    /// # Panics
22    ///
23    /// This function will panic if the provided file descriptor is invalid or cannot be used
24    /// to create a TUN/TAP device. This is acceptable because providing an invalid fd violates
25    /// the safety contract of `FromRawFd`.
26    unsafe fn from_raw_fd(fd: RawFd) -> Self {
27        // If this panics, the caller violated the safety contract by providing an invalid fd
28        DeviceImpl::from_fd(fd).expect(
29            "Failed to create device from file descriptor. \
30                                         The provided fd must be a valid, open file descriptor \
31                                         for a TUN/TAP device.",
32        )
33    }
34}
35impl AsRawFd for DeviceImpl {
36    fn as_raw_fd(&self) -> RawFd {
37        self.tun.as_raw_fd()
38    }
39}
40impl AsFd for DeviceImpl {
41    fn as_fd(&self) -> BorrowedFd<'_> {
42        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
43    }
44}
45#[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
46impl std::os::unix::io::IntoRawFd for DeviceImpl {
47    fn into_raw_fd(self) -> RawFd {
48        self.tun.into_raw_fd()
49    }
50}
51impl DeviceImpl {
52    /// # Safety
53    /// The fd passed in must be an owned file descriptor; in particular, it must be open.
54    pub(crate) unsafe fn from_fd(fd: RawFd) -> io::Result<Self> {
55        let tun = Fd::new_unchecked(fd);
56        DeviceImpl::from_tun(Tun::new(tun))
57    }
58    /// # Safety
59    /// The fd passed in must be a valid, open file descriptor.
60    /// Unlike [`from_fd`], this function does **not** take ownership of `fd`,
61    /// and therefore will not close it when dropped.  
62    /// The caller is responsible for ensuring the lifetime and eventual closure of `fd`.
63    pub(crate) unsafe fn borrow_raw(fd: RawFd) -> io::Result<Self> {
64        let tun = Fd::new_unchecked_with_borrow(fd, true);
65        DeviceImpl::from_tun(Tun::new(tun))
66    }
67    pub(crate) fn is_nonblocking(&self) -> io::Result<bool> {
68        self.tun.is_nonblocking()
69    }
70    /// Moves this Device into or out of nonblocking mode.
71    pub(crate) fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
72        self.tun.set_nonblocking(nonblocking)
73    }
74
75    /// Recv a packet from tun device
76    #[inline]
77    pub(crate) fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
78        self.tun.recv(buf)
79    }
80    #[inline]
81    #[allow(dead_code)]
82    pub(crate) fn recv_uninit(&self, buf: &mut UninitSlice) -> io::Result<usize> {
83        self.tun.recv_uninit(buf)
84    }
85    #[inline]
86    pub(crate) fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
87        self.tun.recv_vectored(bufs)
88    }
89
90    /// Send a packet to tun device
91    #[inline]
92    pub(crate) fn send(&self, buf: &[u8]) -> io::Result<usize> {
93        self.tun.send(buf)
94    }
95    #[inline]
96    pub(crate) fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
97        self.tun.send_vectored(bufs)
98    }
99    #[cfg(feature = "interruptible")]
100    pub(crate) fn read_interruptible(
101        &self,
102        buf: &mut [u8],
103        event: &crate::InterruptEvent,
104        timeout: Option<std::time::Duration>,
105    ) -> io::Result<usize> {
106        self.tun.read_interruptible(buf, event, timeout)
107    }
108    #[cfg(feature = "interruptible")]
109    pub(crate) fn readv_interruptible(
110        &self,
111        bufs: &mut [IoSliceMut<'_>],
112        event: &crate::InterruptEvent,
113        timeout: Option<std::time::Duration>,
114    ) -> io::Result<usize> {
115        self.tun.readv_interruptible(bufs, event, timeout)
116    }
117    #[cfg(feature = "interruptible")]
118    #[inline]
119    pub(crate) fn wait_readable_interruptible(
120        &self,
121        event: &crate::InterruptEvent,
122        timeout: Option<std::time::Duration>,
123    ) -> io::Result<()> {
124        self.tun.wait_readable_interruptible(event, timeout)
125    }
126    #[cfg(feature = "interruptible")]
127    pub(crate) fn write_interruptible(
128        &self,
129        buf: &[u8],
130        event: &crate::InterruptEvent,
131    ) -> io::Result<usize> {
132        self.tun.write_interruptible(buf, event)
133    }
134    #[cfg(feature = "interruptible")]
135    #[inline]
136    pub(crate) fn writev_interruptible(
137        &self,
138        bufs: &[IoSlice<'_>],
139        event: &crate::InterruptEvent,
140    ) -> io::Result<usize> {
141        self.tun.writev_interruptible(bufs, event)
142    }
143    #[cfg(feature = "interruptible")]
144    #[inline]
145    pub(crate) fn wait_writable_interruptible(
146        &self,
147        event: &crate::InterruptEvent,
148    ) -> io::Result<()> {
149        self.tun.wait_writable_interruptible(event)
150    }
151}
152#[cfg(any(
153    all(target_os = "linux", not(target_env = "ohos")),
154    target_os = "macos",
155    target_os = "freebsd",
156    target_os = "openbsd",
157    target_os = "netbsd",
158))]
159impl DeviceImpl {
160    /// Retrieves the interface index for the network interface.
161    ///
162    /// This function converts the interface name (obtained via `self.name()`) into a
163    /// C-compatible string (CString) and then calls the libc function `if_nametoindex`
164    /// to retrieve the corresponding interface index.
165    pub fn if_index(&self) -> io::Result<u32> {
166        let _guard = self.op_lock.read().unwrap();
167        self.if_index_impl()
168    }
169    pub(crate) fn if_index_impl(&self) -> io::Result<u32> {
170        let if_name = std::ffi::CString::new(self.name_impl()?)?;
171        unsafe { Ok(libc::if_nametoindex(if_name.as_ptr())) }
172    }
173    /// Retrieves all IP addresses associated with the network interface.
174    ///
175    /// This function calls `getifaddrs` with the interface name,
176    /// then iterates over the returned list of interface addresses, extracting and collecting
177    /// the IP addresses into a vector.
178    pub fn addresses(&self) -> io::Result<Vec<std::net::IpAddr>> {
179        Ok(crate::platform::get_if_addrs_by_name(self.name_impl()?)?
180            .iter()
181            .filter_map(|v| v.address.ip_addr())
182            .collect())
183    }
184}
185#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos",))]
186impl DeviceImpl {
187    /// Returns whether the TUN device is set to ignore packet information (PI).
188    ///
189    /// When enabled, the device does not prepend the `struct tun_pi` header
190    /// to packets, which can simplify packet processing in some cases.
191    ///
192    /// # Returns
193    /// * `true` - The TUN device ignores packet information.
194    /// * `false` - The TUN device includes packet information.
195    /// # Note
196    /// Retrieve whether the packet is ignored for the TUN Device; The TAP device always returns `false`.
197    pub fn ignore_packet_info(&self) -> bool {
198        let _guard = self.op_lock.read().unwrap();
199        self.tun.ignore_packet_info()
200    }
201    /// Sets whether the TUN device should ignore packet information (PI).
202    ///
203    /// When `ignore_packet_info` is set to `true`, the TUN device does not
204    /// prepend the `struct tun_pi` header to packets. This can be useful
205    /// if the additional metadata is not needed.
206    ///
207    /// # Parameters
208    /// * `ign` - If `true`, the TUN device will ignore packet information.
209    ///   `  ` If `false`, it will include packet information.
210    /// # Note
211    /// This only works for a TUN device; The invocation will be ignored if the device is a TAP.
212    pub fn set_ignore_packet_info(&self, ign: bool) {
213        let _guard = self.op_lock.write().unwrap();
214        self.tun.set_ignore_packet_info(ign)
215    }
216}
217#[cfg(any(
218    all(target_os = "linux", not(target_env = "ohos")),
219    target_os = "freebsd",
220    target_os = "openbsd",
221    target_os = "netbsd",
222))]
223pub(crate) unsafe fn ctl() -> io::Result<Fd> {
224    Fd::new(libc::socket(AF_INET, SOCK_DGRAM | libc::SOCK_CLOEXEC, 0))
225}
226#[cfg(target_os = "macos")]
227pub(crate) unsafe fn ctl() -> io::Result<Fd> {
228    let fd = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))?;
229    _ = fd.set_cloexec();
230    Ok(fd)
231}
232#[cfg(any(
233    all(target_os = "linux", not(target_env = "ohos")),
234    target_os = "freebsd",
235    target_os = "openbsd",
236    target_os = "netbsd",
237))]
238pub(crate) unsafe fn ctl_v6() -> io::Result<Fd> {
239    Fd::new(libc::socket(AF_INET6, SOCK_DGRAM | libc::SOCK_CLOEXEC, 0))
240}
241#[cfg(target_os = "macos")]
242pub(crate) unsafe fn ctl_v6() -> io::Result<Fd> {
243    let fd = Fd::new(libc::socket(AF_INET6, SOCK_DGRAM, 0))?;
244    _ = fd.set_cloexec();
245    Ok(fd)
246}
247
248/// Helper function to safely copy a device name into a C buffer.
249/// This reduces code duplication across BSD platforms for setting interface names.
250#[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd",))]
251pub(crate) unsafe fn copy_device_name(name: &str, dest: *mut libc::c_char, max_len: usize) {
252    use std::ptr;
253    let copy_len = name.len().min(max_len - 1);
254    ptr::copy_nonoverlapping(name.as_ptr() as *const libc::c_char, dest, copy_len);
255}