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)
214 }
215 /// Like [`recv_intr`](Self::recv_intr), but reads into multiple buffers.
216 ///
217 /// This function behaves the same as [`recv_intr`](Self::recv_intr),
218 /// but uses `readv` to fill the provided set of non-contiguous buffers.
219 ///
220 /// # Feature
221 ///
222 /// This method is only available when the `interruptible` feature is enabled.
223 #[cfg(all(unix, feature = "interruptible"))]
224 pub fn recv_vectored_intr(
225 &self,
226 bufs: &mut [IoSliceMut<'_>],
227 event: &InterruptEvent,
228 ) -> std::io::Result<usize> {
229 self.0.readv_interruptible(bufs, event)
230 }
231 #[cfg(feature = "interruptible")]
232 pub fn wait_readable_intr(&self, event: &InterruptEvent) -> std::io::Result<()> {
233 self.0.wait_readable_interruptible(event)
234 }
235 #[cfg(feature = "interruptible")]
236 pub fn send_intr(&self, buf: &[u8], event: &InterruptEvent) -> std::io::Result<usize> {
237 self.0.write_interruptible(buf, event)
238 }
239 #[cfg(all(unix, feature = "interruptible"))]
240 pub fn send_vectored_intr(
241 &self,
242 bufs: &[IoSlice<'_>],
243 event: &InterruptEvent,
244 ) -> std::io::Result<usize> {
245 self.0.writev_interruptible(bufs, event)
246 }
247 #[cfg(all(unix, feature = "interruptible"))]
248 #[inline]
249 pub fn wait_writable_intr(&self, event: &InterruptEvent) -> std::io::Result<()> {
250 self.0.wait_writable_interruptible(event)
251 }
252 /// Receives data from the device into multiple buffers using vectored I/O.
253 ///
254 /// **Note:** This method operates on a single packet only. It will only read data from one packet,
255 /// even if multiple buffers are provided.
256 ///
257 /// Returns the total number of bytes read from the packet, or an error.
258 #[cfg(unix)]
259 pub fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> std::io::Result<usize> {
260 self.0.recv_vectored(bufs)
261 }
262 /// Sends data to the device from multiple buffers using vectored I/O.
263 ///
264 /// **Note:** This method operates on a single packet only. It will only send the data contained in
265 /// the provided buffers as one packet.
266 ///
267 /// Returns the total number of bytes written for the packet, or an error.
268 #[cfg(unix)]
269 pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> std::io::Result<usize> {
270 self.0.send_vectored(bufs)
271 }
272 /// Checks whether the device is currently operating in nonblocking mode.
273 ///
274 /// Returns `true` if nonblocking mode is enabled, `false` otherwise, or an error.
275 #[cfg(unix)]
276 pub fn is_nonblocking(&self) -> std::io::Result<bool> {
277 self.0.is_nonblocking()
278 }
279
280 /// Sets the nonblocking mode for the device.
281 ///
282 /// - `nonblocking`: Pass `true` to enable nonblocking mode, `false` to disable.
283 ///
284 /// Returns an empty result on success or an I/O error.
285 #[cfg(unix)]
286 pub fn set_nonblocking(&self, nonblocking: bool) -> std::io::Result<()> {
287 self.0.set_nonblocking(nonblocking)
288 }
289
290 /// # Prerequisites
291 /// - The `IFF_MULTI_QUEUE` flag must be enabled.
292 /// - The system must support network interface multi-queue functionality.
293 ///
294 /// # Description
295 /// When multi-queue is enabled, create a new queue by duplicating an existing one.
296 #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
297 pub fn try_clone(&self) -> std::io::Result<SyncDevice> {
298 let device_impl = self.0.try_clone()?;
299 Ok(SyncDevice(device_impl))
300 }
301}
302#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
303impl SyncDevice {
304 #[cfg(feature = "interruptible")]
305 pub fn send_multiple_intr<B: ExpandBuffer>(
306 &self,
307 gro_table: &mut GROTable,
308 bufs: &mut [B],
309 offset: usize,
310 event: &InterruptEvent,
311 ) -> std::io::Result<usize> {
312 self.send_multiple0(gro_table, bufs, offset, |tun, buf| {
313 tun.write_interruptible(buf, event)
314 })
315 }
316 #[cfg(feature = "interruptible")]
317 pub fn recv_multiple_intr<B: AsRef<[u8]> + AsMut<[u8]>>(
318 &self,
319 original_buffer: &mut [u8],
320 bufs: &mut [B],
321 sizes: &mut [usize],
322 offset: usize,
323 event: &InterruptEvent,
324 ) -> std::io::Result<usize> {
325 self.recv_multiple0(original_buffer, bufs, sizes, offset, |tun, buf| {
326 tun.read_interruptible(buf, event)
327 })
328 }
329}
330
331impl Deref for SyncDevice {
332 type Target = DeviceImpl;
333 fn deref(&self) -> &Self::Target {
334 &self.0
335 }
336}
337
338#[cfg(unix)]
339impl FromRawFd for SyncDevice {
340 unsafe fn from_raw_fd(fd: RawFd) -> Self {
341 SyncDevice::from_fd(fd).unwrap()
342 }
343}
344#[cfg(unix)]
345impl AsRawFd for SyncDevice {
346 fn as_raw_fd(&self) -> RawFd {
347 self.0.as_raw_fd()
348 }
349}
350#[cfg(unix)]
351impl AsFd for SyncDevice {
352 fn as_fd(&self) -> BorrowedFd<'_> {
353 unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
354 }
355}
356#[cfg(unix)]
357impl IntoRawFd for SyncDevice {
358 fn into_raw_fd(self) -> RawFd {
359 self.0.into_raw_fd()
360 }
361}
362
363#[cfg(unix)]
364pub struct BorrowedSyncDevice<'dev> {
365 dev: SyncDevice,
366 _phantom: std::marker::PhantomData<&'dev SyncDevice>,
367}
368#[cfg(unix)]
369impl Deref for BorrowedSyncDevice<'_> {
370 type Target = SyncDevice;
371 fn deref(&self) -> &Self::Target {
372 &self.dev
373 }
374}
375#[cfg(unix)]
376impl BorrowedSyncDevice<'_> {
377 /// # Safety
378 /// The fd passed in must be a valid, open file descriptor.
379 /// Unlike [`from_fd`], this function does **not** take ownership of `fd`,
380 /// and therefore will not close it when dropped.
381 /// The caller is responsible for ensuring the lifetime and eventual closure of `fd`.
382 pub unsafe fn borrow_raw(fd: RawFd) -> std::io::Result<Self> {
383 #[allow(unused_unsafe)]
384 unsafe {
385 Ok(Self {
386 dev: SyncDevice::borrow_raw(fd)?,
387 _phantom: std::marker::PhantomData,
388 })
389 }
390 }
391}