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