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