tun_rs/platform/
mod.rs

1#[cfg(unix)]
2pub(crate) mod unix;
3
4#[cfg(all(
5    unix,
6    not(any(
7        target_os = "windows",
8        target_os = "macos",
9        all(target_os = "linux", not(target_env = "ohos")),
10        target_os = "freebsd",
11        target_os = "openbsd",
12        target_os = "netbsd",
13    ))
14))]
15pub use self::unix::DeviceImpl;
16#[cfg(unix)]
17#[cfg(feature = "interruptible")]
18pub use unix::InterruptEvent;
19#[cfg(windows)]
20#[cfg(feature = "interruptible")]
21pub use windows::InterruptEvent;
22#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
23pub(crate) mod linux;
24#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
25pub use self::linux::*;
26
27#[cfg(target_os = "freebsd")]
28pub(crate) mod freebsd;
29#[cfg(target_os = "freebsd")]
30pub use self::freebsd::DeviceImpl;
31
32#[cfg(target_os = "macos")]
33pub(crate) mod macos;
34#[cfg(target_os = "macos")]
35pub use self::macos::DeviceImpl;
36#[cfg(target_os = "openbsd")]
37pub(crate) mod openbsd;
38#[cfg(target_os = "openbsd")]
39pub use self::openbsd::DeviceImpl;
40
41#[cfg(target_os = "netbsd")]
42pub(crate) mod netbsd;
43#[cfg(target_os = "netbsd")]
44pub use self::netbsd::DeviceImpl;
45
46#[cfg(target_os = "windows")]
47pub(crate) mod windows;
48#[cfg(target_os = "windows")]
49pub use self::windows::DeviceImpl;
50
51use getifaddrs::Interface;
52#[cfg(unix)]
53use std::io::{IoSlice, IoSliceMut};
54use std::ops::Deref;
55#[cfg(unix)]
56use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
57
58#[allow(dead_code)]
59pub(crate) const ETHER_ADDR_LEN: u8 = 6;
60
61#[allow(dead_code)]
62pub(crate) fn get_if_addrs_by_name(if_name: String) -> std::io::Result<Vec<Interface>> {
63    let addrs = getifaddrs::getifaddrs()?;
64    let ifs = addrs.filter(|v| v.name == if_name).collect();
65    Ok(ifs)
66}
67
68/// A transparent wrapper around DeviceImpl, providing synchronous I/O operations.
69///
70/// # Examples
71///
72/// Basic read/write operation:
73///
74/// ```no_run
75/// use std::net::Ipv4Addr;
76/// use tun_rs::DeviceBuilder;
77///
78/// fn main() -> std::io::Result<()> {
79///     // Create a TUN device using the builder
80///     let mut tun = DeviceBuilder::new()
81///         .name("my-tun")
82///         .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
83///         .build_sync()?;
84///
85///     // Send a packet
86///     // Example IP packet (Replace with real IP message)
87///     let packet = b"[IP Packet: 10.0.0.1 -> 10.0.0.2] Hello, TUN!";
88///     tun.send(packet)?;
89///     println!("Sent {} bytes IP packet", packet.len());
90///
91///     // Receive a packet
92///     let mut buf = [0u8; 1500];
93///     let n = tun.recv(&mut buf)?;
94///     println!("Received {} bytes: {:?}", n, &buf[..n]);
95///
96///     Ok(())
97/// }
98/// ```
99#[repr(transparent)]
100pub struct SyncDevice(pub(crate) DeviceImpl);
101
102impl SyncDevice {
103    /// Creates a new SyncDevice from a raw file descriptor.
104    ///
105    /// # Safety
106    /// - The file descriptor (`fd`) must be an owned file descriptor.
107    /// - It must be valid and open.
108    ///
109    /// This function is only available on Unix platforms.
110    #[cfg(unix)]
111    pub unsafe fn from_fd(fd: RawFd) -> std::io::Result<Self> {
112        Ok(SyncDevice(DeviceImpl::from_fd(fd)?))
113    }
114    /// # Safety
115    /// The fd passed in must be a valid, open file descriptor.
116    /// Unlike [`from_fd`], this function does **not** take ownership of `fd`,
117    /// and therefore will not close it when dropped.  
118    /// The caller is responsible for ensuring the lifetime and eventual closure of `fd`.
119    #[cfg(unix)]
120    pub(crate) unsafe fn borrow_raw(fd: RawFd) -> std::io::Result<Self> {
121        Ok(SyncDevice(DeviceImpl::borrow_raw(fd)?))
122    }
123    /// Receives data from the device into the provided buffer.
124    ///
125    /// Returns the number of bytes read, or an I/O error.
126    ///
127    /// # Example
128    /// ```no_run
129    /// use std::net::Ipv4Addr;
130    /// use tun_rs::DeviceBuilder;
131    /// let mut tun = DeviceBuilder::new()
132    ///     .name("my-tun")
133    ///     .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
134    ///     .build_sync()
135    ///     .unwrap();
136    /// let mut buf = [0u8; 1500];
137    /// tun.recv(&mut buf).unwrap();
138    /// ```
139    /// # Note
140    /// Blocking the current thread if no packet is available
141    pub fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
142        self.0.recv(buf)
143    }
144    /// Sends data from the provided buffer to the device.
145    ///
146    /// Returns the number of bytes written, or an I/O error.
147    ///
148    /// # Example
149    /// ```no_run
150    /// use std::net::Ipv4Addr;
151    /// use tun_rs::DeviceBuilder;
152    /// let mut tun = DeviceBuilder::new()
153    ///     .name("my-tun")
154    ///     .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
155    ///     .build_sync()
156    ///     .unwrap();
157    /// tun.send(b"hello").unwrap();
158    /// ```
159    pub fn send(&self, buf: &[u8]) -> std::io::Result<usize> {
160        self.0.send(buf)
161    }
162    /// Attempts to receive data from the device in a non-blocking fashion.
163    ///
164    /// Returns the number of bytes read or an error if the operation would block.
165    #[cfg(target_os = "windows")]
166    pub fn try_recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
167        self.0.try_recv(buf)
168    }
169    /// Attempts to send data to the device in a non-blocking fashion.
170    ///
171    /// Returns the number of bytes written or an error if the operation would block.
172    #[cfg(target_os = "windows")]
173    pub fn try_send(&self, buf: &[u8]) -> std::io::Result<usize> {
174        self.0.try_send(buf)
175    }
176    /// Shuts down the device on Windows.
177    ///
178    /// This may close the device or signal that no further operations will occur.
179    #[cfg(target_os = "windows")]
180    pub fn shutdown(&self) -> std::io::Result<()> {
181        self.0.shutdown()
182    }
183    #[cfg(all(unix, feature = "experimental"))]
184    pub fn shutdown(&self) -> std::io::Result<()> {
185        Err(std::io::Error::from(std::io::ErrorKind::Unsupported))
186    }
187    /// Reads data into the provided buffer, with support for interruption.
188    ///
189    /// This function attempts to read from the underlying file descriptor into `buf`,
190    /// and can be interrupted using the given [`InterruptEvent`]. If the `event` is triggered
191    /// while the read operation is blocked, the function will return early with
192    /// an error of kind [`std::io::ErrorKind::Interrupted`].
193    ///
194    /// # Arguments
195    ///
196    /// * `buf` - The buffer to store the read data.
197    /// * `event` - An [`InterruptEvent`] used to interrupt the blocking read.
198    ///
199    /// # Returns
200    ///
201    /// On success, returns the number of bytes read. On failure, returns an [`std::io::Error`].
202    ///
203    /// # Platform-specific Behavior
204    ///
205    /// On **Unix platforms**, it is recommended to use this together with `set_nonblocking(true)`.
206    /// Without setting non-blocking mode, concurrent reads may not respond properly to interrupt signals.
207    ///
208    /// # Feature
209    ///
210    /// This method is only available when the `interruptible` feature is enabled.
211    #[cfg(feature = "interruptible")]
212    pub fn recv_intr(&self, buf: &mut [u8], event: &InterruptEvent) -> std::io::Result<usize> {
213        self.0.read_interruptible(buf, event, None)
214    }
215    #[cfg(feature = "interruptible")]
216    pub fn recv_intr_timeout(
217        &self,
218        buf: &mut [u8],
219        event: &InterruptEvent,
220        timeout: Option<std::time::Duration>,
221    ) -> std::io::Result<usize> {
222        self.0.read_interruptible(buf, event, timeout)
223    }
224    /// Like [`recv_intr`](Self::recv_intr), but reads into multiple buffers.
225    ///
226    /// This function behaves the same as [`recv_intr`](Self::recv_intr),
227    /// but uses `readv` to fill the provided set of non-contiguous buffers.
228    ///
229    /// # Feature
230    ///
231    /// This method is only available when the `interruptible` feature is enabled.
232    #[cfg(all(unix, feature = "interruptible"))]
233    pub fn recv_vectored_intr(
234        &self,
235        bufs: &mut [IoSliceMut<'_>],
236        event: &InterruptEvent,
237    ) -> std::io::Result<usize> {
238        self.0.readv_interruptible(bufs, event, None)
239    }
240    #[cfg(all(unix, feature = "interruptible"))]
241    pub fn recv_vectored_intr_timeout(
242        &self,
243        bufs: &mut [IoSliceMut<'_>],
244        event: &InterruptEvent,
245        timeout: Option<std::time::Duration>,
246    ) -> std::io::Result<usize> {
247        self.0.readv_interruptible(bufs, event, timeout)
248    }
249    #[cfg(feature = "interruptible")]
250    pub fn wait_readable_intr(&self, event: &InterruptEvent) -> std::io::Result<()> {
251        self.0.wait_readable_interruptible(event, None)
252    }
253    #[cfg(feature = "interruptible")]
254    pub fn wait_readable_intr_timeout(
255        &self,
256        event: &InterruptEvent,
257        timeout: Option<std::time::Duration>,
258    ) -> std::io::Result<()> {
259        self.0.wait_readable_interruptible(event, timeout)
260    }
261    #[cfg(feature = "interruptible")]
262    pub fn send_intr(&self, buf: &[u8], event: &InterruptEvent) -> std::io::Result<usize> {
263        self.0.write_interruptible(buf, event)
264    }
265    #[cfg(all(unix, feature = "interruptible"))]
266    pub fn send_vectored_intr(
267        &self,
268        bufs: &[IoSlice<'_>],
269        event: &InterruptEvent,
270    ) -> std::io::Result<usize> {
271        self.0.writev_interruptible(bufs, event)
272    }
273    #[cfg(all(unix, feature = "interruptible"))]
274    #[inline]
275    pub fn wait_writable_intr(&self, event: &InterruptEvent) -> std::io::Result<()> {
276        self.0.wait_writable_interruptible(event)
277    }
278    /// Receives data from the device into multiple buffers using vectored I/O.
279    ///
280    /// **Note:** This method operates on a single packet only. It will only read data from one packet,
281    /// even if multiple buffers are provided.
282    ///
283    /// Returns the total number of bytes read from the packet, or an error.
284    #[cfg(unix)]
285    pub fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> std::io::Result<usize> {
286        self.0.recv_vectored(bufs)
287    }
288    /// Sends data to the device from multiple buffers using vectored I/O.
289    ///
290    /// **Note:** This method operates on a single packet only. It will only send the data contained in
291    /// the provided buffers as one packet.
292    ///
293    /// Returns the total number of bytes written for the packet, or an error.
294    #[cfg(unix)]
295    pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> std::io::Result<usize> {
296        self.0.send_vectored(bufs)
297    }
298    /// Checks whether the device is currently operating in nonblocking mode.
299    ///
300    /// Returns `true` if nonblocking mode is enabled, `false` otherwise, or an error.
301    #[cfg(unix)]
302    pub fn is_nonblocking(&self) -> std::io::Result<bool> {
303        self.0.is_nonblocking()
304    }
305
306    /// Sets the nonblocking mode for the device.
307    ///
308    /// - `nonblocking`: Pass `true` to enable nonblocking mode, `false` to disable.
309    ///
310    /// Returns an empty result on success or an I/O error.
311    #[cfg(unix)]
312    pub fn set_nonblocking(&self, nonblocking: bool) -> std::io::Result<()> {
313        self.0.set_nonblocking(nonblocking)
314    }
315
316    /// # Prerequisites
317    /// - The `IFF_MULTI_QUEUE` flag must be enabled.
318    /// - The system must support network interface multi-queue functionality.
319    ///
320    /// # Description
321    /// When multi-queue is enabled, create a new queue by duplicating an existing one.
322    #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
323    pub fn try_clone(&self) -> std::io::Result<SyncDevice> {
324        let device_impl = self.0.try_clone()?;
325        Ok(SyncDevice(device_impl))
326    }
327}
328#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
329impl SyncDevice {
330    #[cfg(feature = "interruptible")]
331    pub fn send_multiple_intr<B: ExpandBuffer>(
332        &self,
333        gro_table: &mut GROTable,
334        bufs: &mut [B],
335        offset: usize,
336        event: &InterruptEvent,
337    ) -> std::io::Result<usize> {
338        self.send_multiple0(gro_table, bufs, offset, |tun, buf| {
339            tun.write_interruptible(buf, event)
340        })
341    }
342    #[cfg(feature = "interruptible")]
343    pub fn recv_multiple_intr<B: AsRef<[u8]> + AsMut<[u8]>>(
344        &self,
345        original_buffer: &mut [u8],
346        bufs: &mut [B],
347        sizes: &mut [usize],
348        offset: usize,
349        event: &InterruptEvent,
350    ) -> std::io::Result<usize> {
351        self.recv_multiple0(original_buffer, bufs, sizes, offset, |tun, buf| {
352            tun.read_interruptible(buf, event, None)
353        })
354    }
355}
356
357impl Deref for SyncDevice {
358    type Target = DeviceImpl;
359    fn deref(&self) -> &Self::Target {
360        &self.0
361    }
362}
363
364#[cfg(unix)]
365impl FromRawFd for SyncDevice {
366    unsafe fn from_raw_fd(fd: RawFd) -> Self {
367        SyncDevice::from_fd(fd).unwrap()
368    }
369}
370#[cfg(unix)]
371impl AsRawFd for SyncDevice {
372    fn as_raw_fd(&self) -> RawFd {
373        self.0.as_raw_fd()
374    }
375}
376#[cfg(unix)]
377impl AsFd for SyncDevice {
378    fn as_fd(&self) -> BorrowedFd<'_> {
379        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
380    }
381}
382#[cfg(unix)]
383impl IntoRawFd for SyncDevice {
384    fn into_raw_fd(self) -> RawFd {
385        self.0.into_raw_fd()
386    }
387}
388
389#[cfg(unix)]
390pub struct BorrowedSyncDevice<'dev> {
391    dev: SyncDevice,
392    _phantom: std::marker::PhantomData<&'dev SyncDevice>,
393}
394#[cfg(unix)]
395impl Deref for BorrowedSyncDevice<'_> {
396    type Target = SyncDevice;
397    fn deref(&self) -> &Self::Target {
398        &self.dev
399    }
400}
401#[cfg(unix)]
402impl BorrowedSyncDevice<'_> {
403    /// # Safety
404    /// The fd passed in must be a valid, open file descriptor.
405    /// Unlike [`SyncDevice::from_fd`], this function does **not** take ownership of `fd`,
406    /// and therefore will not close it when dropped.  
407    /// The caller is responsible for ensuring the lifetime and eventual closure of `fd`.
408    pub unsafe fn borrow_raw(fd: RawFd) -> std::io::Result<Self> {
409        #[allow(unused_unsafe)]
410        unsafe {
411            Ok(Self {
412                dev: SyncDevice::borrow_raw(fd)?,
413                _phantom: std::marker::PhantomData,
414            })
415        }
416    }
417}