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
51#[cfg(target_vendor = "apple")]
52pub mod apple;
53
54use getifaddrs::Interface;
55#[cfg(unix)]
56use std::io::{IoSlice, IoSliceMut};
57use std::ops::Deref;
58#[cfg(unix)]
59use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
60
61#[allow(dead_code)]
62pub(crate) const ETHER_ADDR_LEN: u8 = 6;
63
64#[allow(dead_code)]
65pub(crate) fn get_if_addrs_by_name(if_name: String) -> std::io::Result<Vec<Interface>> {
66 let addrs = getifaddrs::getifaddrs()?;
67 let ifs = addrs.filter(|v| v.name == if_name).collect();
68 Ok(ifs)
69}
70
71/// A transparent wrapper around DeviceImpl, providing synchronous I/O operations.
72///
73/// # Examples
74///
75/// Basic read/write operation:
76///
77/// ```no_run
78/// use std::net::Ipv4Addr;
79/// use tun_rs::DeviceBuilder;
80///
81/// fn main() -> std::io::Result<()> {
82/// // Create a TUN device using the builder
83/// let mut tun = DeviceBuilder::new()
84/// .name("my-tun")
85/// .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
86/// .build_sync()?;
87///
88/// // Send a packet
89/// // Example IP packet (Replace with real IP message)
90/// let packet = b"[IP Packet: 10.0.0.1 -> 10.0.0.2] Hello, TUN!";
91/// tun.send(packet)?;
92/// println!("Sent {} bytes IP packet", packet.len());
93///
94/// // Receive a packet
95/// let mut buf = [0u8; 1500];
96/// let n = tun.recv(&mut buf)?;
97/// println!("Received {} bytes: {:?}", n, &buf[..n]);
98///
99/// Ok(())
100/// }
101/// ```
102#[repr(transparent)]
103pub struct SyncDevice(pub(crate) DeviceImpl);
104
105impl SyncDevice {
106 /// Creates a `SyncDevice` from a raw file descriptor.
107 ///
108 /// # Safety
109 /// - The file descriptor (`fd`) must be an owned file descriptor.
110 /// - It must be valid and open.
111 /// - The file descriptor must refer to a TUN/TAP device.
112 /// - After calling this function, the `SyncDevice` takes ownership of the fd and will close it when dropped.
113 ///
114 /// This function is only available on Unix platforms.
115 ///
116 /// # Example
117 ///
118 /// On iOS using PacketTunnelProvider:
119 ///
120 /// ```no_run
121 /// # #[cfg(unix)]
122 /// # {
123 /// use std::os::fd::RawFd;
124 /// use tun_rs::SyncDevice;
125 ///
126 /// // On iOS, obtain fd from PacketTunnelProvider.packetFlow
127 /// // let fd: RawFd = packet_flow.value(forKeyPath: "socket.fileDescriptor") as! Int32
128 /// let fd: RawFd = 10; // Example value - obtain from platform VPN APIs
129 ///
130 /// // SAFETY: fd must be a valid, open file descriptor to a TUN device
131 /// let dev = unsafe { SyncDevice::from_fd(fd)? };
132 ///
133 /// // Device now owns the file descriptor
134 /// let mut buf = [0u8; 1500];
135 /// let n = dev.recv(&mut buf)?;
136 /// println!("Received {} bytes", n);
137 /// # }
138 /// # Ok::<(), std::io::Error>(())
139 /// ```
140 ///
141 /// On Android using VpnService:
142 ///
143 /// ```no_run
144 /// # #[cfg(unix)]
145 /// # {
146 /// use tun_rs::SyncDevice;
147 ///
148 /// // On Android, obtain fd from VpnService.Builder.establish()
149 /// // ParcelFileDescriptor vpnInterface = builder.establish();
150 /// // int fd = vpnInterface.getFd();
151 /// let fd = 10; // Example value - obtain from VpnService
152 ///
153 /// // SAFETY: fd must be valid and open
154 /// let dev = unsafe { SyncDevice::from_fd(fd)? };
155 ///
156 /// let mut buf = [0u8; 1500];
157 /// loop {
158 /// let n = dev.recv(&mut buf)?;
159 /// // Process packet...
160 /// }
161 /// # }
162 /// # Ok::<(), std::io::Error>(())
163 /// ```
164 #[cfg(unix)]
165 pub unsafe fn from_fd(fd: RawFd) -> std::io::Result<Self> {
166 Ok(SyncDevice(DeviceImpl::from_fd(fd)?))
167 }
168 /// # Safety
169 /// The fd passed in must be a valid, open file descriptor.
170 /// Unlike [`from_fd`], this function does **not** take ownership of `fd`,
171 /// and therefore will not close it when dropped.
172 /// The caller is responsible for ensuring the lifetime and eventual closure of `fd`.
173 #[cfg(unix)]
174 pub(crate) unsafe fn borrow_raw(fd: RawFd) -> std::io::Result<Self> {
175 Ok(SyncDevice(DeviceImpl::borrow_raw(fd)?))
176 }
177 /// Receives data from the device into the provided buffer.
178 ///
179 /// Returns the number of bytes read, or an I/O error.
180 ///
181 /// # Example
182 /// ```no_run
183 /// use std::net::Ipv4Addr;
184 /// use tun_rs::DeviceBuilder;
185 /// let mut tun = DeviceBuilder::new()
186 /// .name("my-tun")
187 /// .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
188 /// .build_sync()
189 /// .unwrap();
190 /// let mut buf = [0u8; 1500];
191 /// tun.recv(&mut buf).unwrap();
192 /// ```
193 /// # Note
194 /// Blocking the current thread if no packet is available
195 #[inline]
196 pub fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
197 self.0.recv(buf)
198 }
199 /// Sends data from the provided buffer to the device.
200 ///
201 /// Returns the number of bytes written, or an I/O error.
202 ///
203 /// # Example
204 /// ```no_run
205 /// use std::net::Ipv4Addr;
206 /// use tun_rs::DeviceBuilder;
207 /// let mut tun = DeviceBuilder::new()
208 /// .name("my-tun")
209 /// .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
210 /// .build_sync()
211 /// .unwrap();
212 /// tun.send(b"hello").unwrap();
213 /// ```
214 #[inline]
215 pub fn send(&self, buf: &[u8]) -> std::io::Result<usize> {
216 self.0.send(buf)
217 }
218 /// Attempts to receive data from the device in a non-blocking fashion.
219 ///
220 /// Returns the number of bytes read or an error if the operation would block.
221 #[cfg(target_os = "windows")]
222 #[inline]
223 pub fn try_recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
224 self.0.try_recv(buf)
225 }
226 /// Attempts to send data to the device in a non-blocking fashion.
227 ///
228 /// Returns the number of bytes written or an error if the operation would block.
229 #[cfg(target_os = "windows")]
230 #[inline]
231 pub fn try_send(&self, buf: &[u8]) -> std::io::Result<usize> {
232 self.0.try_send(buf)
233 }
234 /// Shuts down the device on Windows.
235 ///
236 /// This may close the device or signal that no further operations will occur.
237 #[cfg(target_os = "windows")]
238 pub fn shutdown(&self) -> std::io::Result<()> {
239 self.0.shutdown()
240 }
241 #[cfg(all(unix, feature = "experimental"))]
242 pub fn shutdown(&self) -> std::io::Result<()> {
243 Err(std::io::Error::from(std::io::ErrorKind::Unsupported))
244 }
245 /// Reads data into the provided buffer, with support for interruption.
246 ///
247 /// This function attempts to read from the underlying file descriptor into `buf`,
248 /// and can be interrupted using the given [`InterruptEvent`]. If the `event` is triggered
249 /// while the read operation is blocked, the function will return early with
250 /// an error of kind [`std::io::ErrorKind::Interrupted`].
251 ///
252 /// # Arguments
253 ///
254 /// * `buf` - The buffer to store the read data.
255 /// * `event` - An [`InterruptEvent`] used to interrupt the blocking read.
256 ///
257 /// # Returns
258 ///
259 /// On success, returns the number of bytes read. On failure, returns an [`std::io::Error`].
260 ///
261 /// # Platform-specific Behavior
262 ///
263 /// On **Unix platforms**, it is recommended to use this together with `set_nonblocking(true)`.
264 /// Without setting non-blocking mode, concurrent reads may not respond properly to interrupt signals.
265 ///
266 /// # Feature
267 ///
268 /// This method is only available when the `interruptible` feature is enabled.
269 #[cfg(feature = "interruptible")]
270 pub fn recv_intr(&self, buf: &mut [u8], event: &InterruptEvent) -> std::io::Result<usize> {
271 self.0.read_interruptible(buf, event, None)
272 }
273
274 /// Like [`recv_intr`](Self::recv_intr), but with an optional timeout.
275 ///
276 /// This function reads data from the device into the provided buffer, but can be
277 /// interrupted by the given event or by the timeout expiring.
278 ///
279 /// # Arguments
280 ///
281 /// * `buf` - The buffer to store the read data
282 /// * `event` - The interrupt event that can cancel the operation
283 /// * `timeout` - Optional duration to wait before returning with a timeout error
284 ///
285 /// # Returns
286 ///
287 /// - `Ok(n)` - Successfully read `n` bytes
288 /// - `Err(e)` with `ErrorKind::Interrupted` - Operation was interrupted by the event
289 /// - `Err(e)` with `ErrorKind::TimedOut` - Timeout expired before data was available
290 ///
291 /// # Example
292 ///
293 /// ```no_run
294 /// # #[cfg(all(unix, feature = "interruptible"))]
295 /// # {
296 /// use std::time::Duration;
297 /// use tun_rs::{DeviceBuilder, InterruptEvent};
298 ///
299 /// let dev = DeviceBuilder::new()
300 /// .ipv4("10.0.0.1", 24, None)
301 /// .build_sync()?;
302 ///
303 /// let event = InterruptEvent::new()?;
304 /// let mut buf = vec![0u8; 1500];
305 ///
306 /// // Read with a 5-second timeout
307 /// match dev.recv_intr_timeout(&mut buf, &event, Some(Duration::from_secs(5))) {
308 /// Ok(n) => println!("Received {} bytes", n),
309 /// Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
310 /// println!("Timed out waiting for data");
311 /// }
312 /// Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
313 /// println!("Interrupted by event");
314 /// }
315 /// Err(e) => return Err(e),
316 /// }
317 /// # }
318 /// # Ok::<(), std::io::Error>(())
319 /// ```
320 ///
321 /// # Feature
322 ///
323 /// This method is only available when the `interruptible` feature is enabled.
324 #[cfg(feature = "interruptible")]
325 pub fn recv_intr_timeout(
326 &self,
327 buf: &mut [u8],
328 event: &InterruptEvent,
329 timeout: Option<std::time::Duration>,
330 ) -> std::io::Result<usize> {
331 self.0.read_interruptible(buf, event, timeout)
332 }
333 /// Like [`recv_intr`](Self::recv_intr), but reads into multiple buffers.
334 ///
335 /// This function behaves the same as [`recv_intr`](Self::recv_intr),
336 /// but uses `readv` to fill the provided set of non-contiguous buffers.
337 ///
338 /// # Feature
339 ///
340 /// This method is only available when the `interruptible` feature is enabled.
341 #[cfg(all(unix, feature = "interruptible"))]
342 pub fn recv_vectored_intr(
343 &self,
344 bufs: &mut [IoSliceMut<'_>],
345 event: &InterruptEvent,
346 ) -> std::io::Result<usize> {
347 self.0.readv_interruptible(bufs, event, None)
348 }
349
350 /// Like [`recv_vectored_intr`](Self::recv_vectored_intr), but with an optional timeout.
351 ///
352 /// This function reads data from the device into multiple buffers using vectored I/O,
353 /// but can be interrupted by the given event or by the timeout expiring.
354 ///
355 /// # Arguments
356 ///
357 /// * `bufs` - Multiple buffers to store the read data
358 /// * `event` - The interrupt event that can cancel the operation
359 /// * `timeout` - Optional duration to wait before returning with a timeout error
360 ///
361 /// # Returns
362 ///
363 /// - `Ok(n)` - Successfully read `n` bytes total across all buffers
364 /// - `Err(e)` with `ErrorKind::Interrupted` - Operation was interrupted by the event
365 /// - `Err(e)` with `ErrorKind::TimedOut` - Timeout expired before data was available
366 ///
367 /// # Example
368 ///
369 /// ```no_run
370 /// # #[cfg(all(unix, feature = "interruptible"))]
371 /// # {
372 /// use std::io::IoSliceMut;
373 /// use std::time::Duration;
374 /// use tun_rs::{DeviceBuilder, InterruptEvent};
375 ///
376 /// let dev = DeviceBuilder::new()
377 /// .ipv4("10.0.0.1", 24, None)
378 /// .build_sync()?;
379 ///
380 /// let event = InterruptEvent::new()?;
381 /// let mut header = [0u8; 20];
382 /// let mut payload = [0u8; 1480];
383 /// let mut bufs = [IoSliceMut::new(&mut header), IoSliceMut::new(&mut payload)];
384 ///
385 /// // Read with timeout into multiple buffers
386 /// match dev.recv_vectored_intr_timeout(&mut bufs, &event, Some(Duration::from_secs(5))) {
387 /// Ok(n) => println!("Received {} bytes", n),
388 /// Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
389 /// println!("Timed out");
390 /// }
391 /// Err(e) => return Err(e),
392 /// }
393 /// # }
394 /// # Ok::<(), std::io::Error>(())
395 /// ```
396 ///
397 /// # Feature
398 ///
399 /// This method is only available when the `interruptible` feature is enabled.
400 #[cfg(all(unix, feature = "interruptible"))]
401 pub fn recv_vectored_intr_timeout(
402 &self,
403 bufs: &mut [IoSliceMut<'_>],
404 event: &InterruptEvent,
405 timeout: Option<std::time::Duration>,
406 ) -> std::io::Result<usize> {
407 self.0.readv_interruptible(bufs, event, timeout)
408 }
409 #[cfg(feature = "interruptible")]
410 pub fn wait_readable_intr(&self, event: &InterruptEvent) -> std::io::Result<()> {
411 self.0.wait_readable_interruptible(event, None)
412 }
413
414 /// Like [`wait_readable_intr`](Self::wait_readable_intr), but with an optional timeout.
415 ///
416 /// This function waits until the device becomes readable, but can be interrupted
417 /// by the given event or by the timeout expiring.
418 ///
419 /// # Arguments
420 ///
421 /// * `event` - The interrupt event that can cancel the wait
422 /// * `timeout` - Optional duration to wait before returning with a timeout error
423 ///
424 /// # Returns
425 ///
426 /// - `Ok(())` - Device is now readable
427 /// - `Err(e)` with `ErrorKind::Interrupted` - Wait was interrupted by the event
428 /// - `Err(e)` with `ErrorKind::TimedOut` - Timeout expired
429 ///
430 /// # Example
431 ///
432 /// ```no_run
433 /// # #[cfg(all(unix, feature = "interruptible"))]
434 /// # {
435 /// use std::time::Duration;
436 /// use tun_rs::{DeviceBuilder, InterruptEvent};
437 ///
438 /// let dev = DeviceBuilder::new()
439 /// .ipv4("10.0.0.1", 24, None)
440 /// .build_sync()?;
441 ///
442 /// let event = InterruptEvent::new()?;
443 ///
444 /// // Wait for readability with timeout
445 /// match dev.wait_readable_intr_timeout(&event, Some(Duration::from_secs(10))) {
446 /// Ok(()) => {
447 /// println!("Device is readable");
448 /// // Now try to read...
449 /// }
450 /// Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
451 /// println!("Timed out waiting for data");
452 /// }
453 /// Err(e) => return Err(e),
454 /// }
455 /// # }
456 /// # Ok::<(), std::io::Error>(())
457 /// ```
458 ///
459 /// # Feature
460 ///
461 /// This method is only available when the `interruptible` feature is enabled.
462 #[cfg(feature = "interruptible")]
463 pub fn wait_readable_intr_timeout(
464 &self,
465 event: &InterruptEvent,
466 timeout: Option<std::time::Duration>,
467 ) -> std::io::Result<()> {
468 self.0.wait_readable_interruptible(event, timeout)
469 }
470 #[cfg(feature = "interruptible")]
471 pub fn send_intr(&self, buf: &[u8], event: &InterruptEvent) -> std::io::Result<usize> {
472 self.0.write_interruptible(buf, event)
473 }
474
475 /// Sends data to the device from multiple buffers using vectored I/O, with interruption support.
476 ///
477 /// Like [`send_intr`](Self::send_intr), but uses `writev` to send from multiple
478 /// non-contiguous buffers in a single operation.
479 ///
480 /// # Arguments
481 ///
482 /// * `bufs` - Multiple buffers containing the data to send
483 /// * `event` - The interrupt event that can cancel the operation
484 ///
485 /// # Returns
486 ///
487 /// - `Ok(n)` - Successfully sent `n` bytes total from all buffers
488 /// - `Err(e)` with `ErrorKind::Interrupted` - Operation was interrupted by the event
489 ///
490 /// # Example
491 ///
492 /// ```no_run
493 /// # #[cfg(all(unix, feature = "interruptible"))]
494 /// # {
495 /// use std::io::IoSlice;
496 /// use tun_rs::{DeviceBuilder, InterruptEvent};
497 ///
498 /// let dev = DeviceBuilder::new()
499 /// .ipv4("10.0.0.1", 24, None)
500 /// .build_sync()?;
501 ///
502 /// let event = InterruptEvent::new()?;
503 /// let header = [0x45, 0x00, 0x00, 0x14]; // IPv4 header
504 /// let payload = b"Hello, TUN!";
505 /// let bufs = [IoSlice::new(&header), IoSlice::new(payload)];
506 ///
507 /// match dev.send_vectored_intr(&bufs, &event) {
508 /// Ok(n) => println!("Sent {} bytes", n),
509 /// Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
510 /// println!("Send was interrupted");
511 /// }
512 /// Err(e) => return Err(e),
513 /// }
514 /// # }
515 /// # Ok::<(), std::io::Error>(())
516 /// ```
517 ///
518 /// # Feature
519 ///
520 /// This method is only available when the `interruptible` feature is enabled.
521 #[cfg(all(unix, feature = "interruptible"))]
522 pub fn send_vectored_intr(
523 &self,
524 bufs: &[IoSlice<'_>],
525 event: &InterruptEvent,
526 ) -> std::io::Result<usize> {
527 self.0.writev_interruptible(bufs, event)
528 }
529
530 /// Waits for the device to become writable, with interruption support.
531 ///
532 /// This function waits until the device is ready to accept data for sending,
533 /// but can be interrupted by the given event.
534 ///
535 /// # Arguments
536 ///
537 /// * `event` - The interrupt event that can cancel the wait
538 ///
539 /// # Returns
540 ///
541 /// - `Ok(())` - Device is now writable
542 /// - `Err(e)` with `ErrorKind::Interrupted` - Wait was interrupted by the event
543 ///
544 /// # Example
545 ///
546 /// ```no_run
547 /// # #[cfg(all(unix, feature = "interruptible"))]
548 /// # {
549 /// use tun_rs::{DeviceBuilder, InterruptEvent};
550 ///
551 /// let dev = DeviceBuilder::new()
552 /// .ipv4("10.0.0.1", 24, None)
553 /// .build_sync()?;
554 ///
555 /// let event = InterruptEvent::new()?;
556 ///
557 /// // Wait for device to be writable
558 /// match dev.wait_writable_intr(&event) {
559 /// Ok(()) => {
560 /// println!("Device is writable");
561 /// // Now send data...
562 /// }
563 /// Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
564 /// println!("Wait was interrupted");
565 /// }
566 /// Err(e) => return Err(e),
567 /// }
568 /// # }
569 /// # Ok::<(), std::io::Error>(())
570 /// ```
571 ///
572 /// # Feature
573 ///
574 /// This method is only available when the `interruptible` feature is enabled.
575 #[cfg(all(unix, feature = "interruptible"))]
576 #[inline]
577 pub fn wait_writable_intr(&self, event: &InterruptEvent) -> std::io::Result<()> {
578 self.0.wait_writable_interruptible(event)
579 }
580 /// Receives data from the device into multiple buffers using vectored I/O.
581 ///
582 /// **Note:** This method operates on a single packet only. It will only read data from one packet,
583 /// even if multiple buffers are provided.
584 ///
585 /// Returns the total number of bytes read from the packet, or an error.
586 ///
587 /// # Example
588 ///
589 /// ```no_run
590 /// # #[cfg(unix)]
591 /// # {
592 /// use std::io::IoSliceMut;
593 /// use tun_rs::DeviceBuilder;
594 ///
595 /// let dev = DeviceBuilder::new()
596 /// .ipv4("10.0.0.1", 24, None)
597 /// .build_sync()?;
598 ///
599 /// // Prepare multiple buffers for receiving data
600 /// let mut header = [0u8; 20];
601 /// let mut payload = [0u8; 1480];
602 /// let mut bufs = [IoSliceMut::new(&mut header), IoSliceMut::new(&mut payload)];
603 ///
604 /// // Read one packet into the buffers
605 /// let n = dev.recv_vectored(&mut bufs)?;
606 /// println!("Received {} bytes total", n);
607 /// # }
608 /// # Ok::<(), std::io::Error>(())
609 /// ```
610 #[cfg(unix)]
611 pub fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> std::io::Result<usize> {
612 self.0.recv_vectored(bufs)
613 }
614 /// Sends data to the device from multiple buffers using vectored I/O.
615 ///
616 /// **Note:** This method operates on a single packet only. It will only send the data contained in
617 /// the provided buffers as one packet.
618 ///
619 /// Returns the total number of bytes written for the packet, or an error.
620 ///
621 /// # Example
622 ///
623 /// ```no_run
624 /// # #[cfg(unix)]
625 /// # {
626 /// use std::io::IoSlice;
627 /// use tun_rs::DeviceBuilder;
628 ///
629 /// let dev = DeviceBuilder::new()
630 /// .ipv4("10.0.0.1", 24, None)
631 /// .build_sync()?;
632 ///
633 /// // Send a packet with header and payload in separate buffers
634 /// let header = [0x45, 0x00, 0x00, 0x14]; // IPv4 header
635 /// let payload = b"Hello, TUN!";
636 /// let bufs = [IoSlice::new(&header), IoSlice::new(payload)];
637 ///
638 /// let n = dev.send_vectored(&bufs)?;
639 /// println!("Sent {} bytes", n);
640 /// # }
641 /// # Ok::<(), std::io::Error>(())
642 /// ```
643 #[cfg(unix)]
644 pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> std::io::Result<usize> {
645 self.0.send_vectored(bufs)
646 }
647 /// Checks whether the device is currently operating in nonblocking mode.
648 ///
649 /// Returns `true` if nonblocking mode is enabled, `false` otherwise, or an error.
650 ///
651 /// # Example
652 ///
653 /// ```no_run
654 /// # #[cfg(unix)]
655 /// # {
656 /// use tun_rs::DeviceBuilder;
657 ///
658 /// let dev = DeviceBuilder::new()
659 /// .ipv4("10.0.0.1", 24, None)
660 /// .build_sync()?;
661 ///
662 /// // Check current nonblocking mode
663 /// if dev.is_nonblocking()? {
664 /// println!("Device is in nonblocking mode");
665 /// } else {
666 /// println!("Device is in blocking mode");
667 /// }
668 /// # }
669 /// # Ok::<(), std::io::Error>(())
670 /// ```
671 #[cfg(unix)]
672 pub fn is_nonblocking(&self) -> std::io::Result<bool> {
673 self.0.is_nonblocking()
674 }
675
676 /// Sets the nonblocking mode for the device.
677 ///
678 /// - `nonblocking`: Pass `true` to enable nonblocking mode, `false` to disable.
679 ///
680 /// Returns an empty result on success or an I/O error.
681 ///
682 /// # Example
683 ///
684 /// ```no_run
685 /// # #[cfg(unix)]
686 /// # {
687 /// use tun_rs::DeviceBuilder;
688 ///
689 /// let dev = DeviceBuilder::new()
690 /// .ipv4("10.0.0.1", 24, None)
691 /// .build_sync()?;
692 ///
693 /// // Enable nonblocking mode for non-blocking I/O
694 /// dev.set_nonblocking(true)?;
695 ///
696 /// // Now recv() will return WouldBlock if no data is available
697 /// let mut buf = [0u8; 1500];
698 /// match dev.recv(&mut buf) {
699 /// Ok(n) => println!("Received {} bytes", n),
700 /// Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
701 /// println!("No data available");
702 /// }
703 /// Err(e) => return Err(e),
704 /// }
705 /// # }
706 /// # Ok::<(), std::io::Error>(())
707 /// ```
708 #[cfg(unix)]
709 pub fn set_nonblocking(&self, nonblocking: bool) -> std::io::Result<()> {
710 self.0.set_nonblocking(nonblocking)
711 }
712
713 /// Creates a new queue for multi-queue TUN/TAP devices on Linux.
714 ///
715 /// # Prerequisites
716 /// - The `IFF_MULTI_QUEUE` flag must be enabled (via `.multi_queue(true)` in DeviceBuilder).
717 /// - The system must support network interface multi-queue functionality.
718 ///
719 /// # Description
720 /// When multi-queue is enabled, create a new queue by duplicating an existing one.
721 /// This allows parallel packet processing across multiple threads/CPU cores.
722 ///
723 /// # Example
724 ///
725 /// ```no_run
726 /// # #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
727 /// # {
728 /// use std::thread;
729 /// use tun_rs::DeviceBuilder;
730 ///
731 /// let dev = DeviceBuilder::new()
732 /// .ipv4("10.0.0.1", 24, None)
733 /// .with(|builder| {
734 /// builder.multi_queue(true) // Enable multi-queue support
735 /// })
736 /// .build_sync()?;
737 ///
738 /// // Clone the device to create a new queue
739 /// let dev_clone = dev.try_clone()?;
740 ///
741 /// // Use the cloned device in another thread for parallel processing
742 /// thread::spawn(move || {
743 /// let mut buf = [0u8; 1500];
744 /// loop {
745 /// if let Ok(n) = dev_clone.recv(&mut buf) {
746 /// println!("Thread 2 received {} bytes", n);
747 /// }
748 /// }
749 /// });
750 ///
751 /// // Process packets in the main thread
752 /// let mut buf = [0u8; 1500];
753 /// loop {
754 /// let n = dev.recv(&mut buf)?;
755 /// println!("Thread 1 received {} bytes", n);
756 /// }
757 /// # }
758 /// # Ok::<(), std::io::Error>(())
759 /// ```
760 #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
761 pub fn try_clone(&self) -> std::io::Result<SyncDevice> {
762 let device_impl = self.0.try_clone()?;
763 Ok(SyncDevice(device_impl))
764 }
765}
766#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
767impl SyncDevice {
768 #[cfg(feature = "interruptible")]
769 pub fn send_multiple_intr<B: ExpandBuffer>(
770 &self,
771 gro_table: &mut GROTable,
772 bufs: &mut [B],
773 offset: usize,
774 event: &InterruptEvent,
775 ) -> std::io::Result<usize> {
776 self.send_multiple0(gro_table, bufs, offset, |tun, buf| {
777 tun.write_interruptible(buf, event)
778 })
779 }
780 #[cfg(feature = "interruptible")]
781 pub fn recv_multiple_intr<B: AsRef<[u8]> + AsMut<[u8]>>(
782 &self,
783 original_buffer: &mut [u8],
784 bufs: &mut [B],
785 sizes: &mut [usize],
786 offset: usize,
787 event: &InterruptEvent,
788 ) -> std::io::Result<usize> {
789 self.recv_multiple0(original_buffer, bufs, sizes, offset, |tun, buf| {
790 tun.read_interruptible(buf, event, None)
791 })
792 }
793}
794
795impl Deref for SyncDevice {
796 type Target = DeviceImpl;
797 fn deref(&self) -> &Self::Target {
798 &self.0
799 }
800}
801
802#[cfg(unix)]
803impl FromRawFd for SyncDevice {
804 unsafe fn from_raw_fd(fd: RawFd) -> Self {
805 SyncDevice::from_fd(fd).expect(
806 "Failed to create device from file descriptor. \
807 The provided fd must be a valid, open file descriptor for a TUN/TAP device.",
808 )
809 }
810}
811#[cfg(unix)]
812impl AsRawFd for SyncDevice {
813 fn as_raw_fd(&self) -> RawFd {
814 self.0.as_raw_fd()
815 }
816}
817#[cfg(unix)]
818impl AsFd for SyncDevice {
819 fn as_fd(&self) -> BorrowedFd<'_> {
820 unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
821 }
822}
823#[cfg(unix)]
824impl IntoRawFd for SyncDevice {
825 fn into_raw_fd(self) -> RawFd {
826 self.0.into_raw_fd()
827 }
828}
829
830#[cfg(unix)]
831pub struct BorrowedSyncDevice<'dev> {
832 dev: SyncDevice,
833 _phantom: std::marker::PhantomData<&'dev SyncDevice>,
834}
835#[cfg(unix)]
836impl Deref for BorrowedSyncDevice<'_> {
837 type Target = SyncDevice;
838 fn deref(&self) -> &Self::Target {
839 &self.dev
840 }
841}
842#[cfg(unix)]
843impl BorrowedSyncDevice<'_> {
844 /// # Safety
845 /// The fd passed in must be a valid, open file descriptor.
846 /// Unlike [`SyncDevice::from_fd`], this function does **not** take ownership of `fd`,
847 /// and therefore will not close it when dropped.
848 /// The caller is responsible for ensuring the lifetime and eventual closure of `fd`.
849 pub unsafe fn borrow_raw(fd: RawFd) -> std::io::Result<Self> {
850 #[allow(unused_unsafe)]
851 unsafe {
852 Ok(Self {
853 dev: SyncDevice::borrow_raw(fd)?,
854 _phantom: std::marker::PhantomData,
855 })
856 }
857 }
858}