Skip to main content

tun_rs/platform/unix/
device.rs

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