tun_rs/builder.rs
1/*!
2# Device Builder Module
3
4This module provides the [`DeviceBuilder`] struct for configuring and creating TUN/TAP interfaces.
5
6## Overview
7
8The builder pattern allows you to specify all configuration parameters before creating a device.
9Configuration includes:
10- Device name
11- IP addresses (IPv4 and IPv6, single or multiple)
12- MTU (Maximum Transmission Unit)
13- Operating layer (L2/TAP or L3/TUN)
14- MAC address (for L2/TAP mode)
15- Platform-specific options (offload, multi-queue, packet information, etc.)
16
17## Basic Usage
18
19```no_run
20use tun_rs::DeviceBuilder;
21
22// Create a TUN (L3) device
23let dev = DeviceBuilder::new()
24 .name("tun0")
25 .ipv4("10.0.0.1", 24, None)
26 .mtu(1400)
27 .build_sync()?;
28# Ok::<(), std::io::Error>(())
29```
30
31## Platform-Specific Configuration
32
33The builder provides platform-specific methods accessible via the `with()` method:
34
35### Linux Specific
36
37```no_run
38# #[cfg(target_os = "linux")]
39# {
40use tun_rs::DeviceBuilder;
41
42let dev = DeviceBuilder::new()
43 .ipv4("10.0.0.1", 24, None)
44 .with(|builder| {
45 builder
46 .offload(true) // Enable TSO/GSO offload
47 .multi_queue(true) // Enable multi-queue support
48 .tx_queue_len(1000) // Set transmit queue length
49 })
50 .build_sync()?;
51# Ok::<(), std::io::Error>(())
52# }
53```
54
55### Windows Specific
56
57```no_run
58# #[cfg(target_os = "windows")]
59# {
60use tun_rs::DeviceBuilder;
61
62let dev = DeviceBuilder::new()
63 .ipv4("10.0.0.1", 24, None)
64 .with(|builder| {
65 builder
66 .ring_capacity(0x40_0000) // 4MB ring buffer
67 .wintun_log(true) // Enable Wintun logging
68 .description("My VPN") // Set device description
69 })
70 .build_sync()?;
71# Ok::<(), std::io::Error>(())
72# }
73```
74
75### macOS Specific
76
77```no_run
78# #[cfg(target_os = "macos")]
79# {
80use tun_rs::DeviceBuilder;
81
82let dev = DeviceBuilder::new()
83 .ipv4("10.0.0.1", 24, None)
84 .with(|builder| {
85 builder
86 .associate_route(true) // Auto-configure routes
87 .packet_information(false) // Disable packet headers
88 })
89 .build_sync()?;
90# Ok::<(), std::io::Error>(())
91# }
92```
93
94## Layer Selection
95
96Choose between Layer 2 (TAP) and Layer 3 (TUN):
97
98```no_run
99# #[cfg(any(target_os = "linux", target_os = "windows", target_os = "freebsd"))]
100# {
101use tun_rs::{DeviceBuilder, Layer};
102
103// TAP interface (Layer 2)
104let tap = DeviceBuilder::new()
105 .name("tap0")
106 .layer(Layer::L2)
107 .mac_addr([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])
108 .build_sync()?;
109
110// TUN interface (Layer 3, default)
111let tun = DeviceBuilder::new()
112 .name("tun0")
113 .layer(Layer::L3)
114 .ipv4("10.0.0.1", 24, None)
115 .build_sync()?;
116# Ok::<(), std::io::Error>(())
117# }
118```
119
120## Multiple IP Addresses
121
122You can configure multiple IPv6 addresses during device creation:
123
124```no_run
125use tun_rs::DeviceBuilder;
126
127let dev = DeviceBuilder::new()
128 .ipv4("10.0.0.1", 24, None)
129 .ipv6("fd00::1", 64)
130 .ipv6("fd00::2", 64)
131 .build_sync()?;
132# Ok::<(), std::io::Error>(())
133```
134
135Additional addresses can be added after creation using [`crate::SyncDevice::add_address_v4`]
136and [`crate::SyncDevice::add_address_v6`] methods.
137
138## Configuration Precedence
139
140- Most settings have sensible defaults
141- Unspecified values use platform defaults
142- Some settings are mandatory (e.g., at least one IP address for routing)
143- Platform-specific settings are ignored on other platforms
144
145## Error Handling
146
147The `build_sync()` and `build_async()` methods return `io::Result<Device>`.
148Common errors include:
149- Permission denied (need root/administrator privileges)
150- Device name already exists
151- Invalid IP address or netmask
152- Platform-specific driver not found (e.g., Wintun on Windows)
153*/
154
155use std::io;
156use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
157use std::str::FromStr;
158
159use crate::platform::{DeviceImpl, SyncDevice};
160
161/// Represents the OSI layer at which the TUN/TAP interface operates.
162///
163/// This enum determines whether the interface works at the Data Link Layer (L2/TAP)
164/// or the Network Layer (L3/TUN).
165///
166/// # Layer 2 (TAP) - Data Link Layer
167///
168/// TAP interfaces operate at the Ethernet frame level (Layer 2), handling complete
169/// Ethernet frames including MAC addresses. This is useful for:
170/// - Bridging networks
171/// - Emulating full Ethernet connectivity
172/// - Applications requiring MAC-level control
173/// - Creating virtual switches
174///
175/// TAP mode requires setting a MAC address and can work with protocols like ARP.
176///
177/// **Platform availability**: Windows, Linux, FreeBSD, macOS, OpenBSD, NetBSD
178///
179/// # Layer 3 (TUN) - Network Layer
180///
181/// TUN interfaces operate at the IP packet level (Layer 3), handling IP packets
182/// directly without Ethernet framing. This is the default and most common mode for:
183/// - VPN implementations
184/// - IP tunneling
185/// - Point-to-point connections
186/// - Routing between networks
187///
188/// TUN mode is simpler and more efficient than TAP when Ethernet-level features
189/// are not needed.
190///
191/// **Platform availability**: All platforms
192///
193/// # Examples
194///
195/// Creating a TAP (L2) interface:
196///
197/// ```no_run
198/// # #[cfg(any(target_os = "linux", target_os = "windows", target_os = "freebsd"))]
199/// # {
200/// use tun_rs::{DeviceBuilder, Layer};
201///
202/// let tap = DeviceBuilder::new()
203/// .name("tap0")
204/// .layer(Layer::L2)
205/// .mac_addr([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])
206/// .build_sync()?;
207/// # Ok::<(), std::io::Error>(())
208/// # }
209/// ```
210///
211/// Creating a TUN (L3) interface (default):
212///
213/// ```no_run
214/// use tun_rs::{DeviceBuilder, Layer};
215///
216/// // L3 is the default, explicit specification is optional
217/// let tun = DeviceBuilder::new()
218/// .name("tun0")
219/// .layer(Layer::L3)
220/// .ipv4("10.0.0.1", 24, None)
221/// .build_sync()?;
222/// # Ok::<(), std::io::Error>(())
223/// ```
224///
225/// # Platform-Specific Notes
226///
227/// - On macOS, TAP mode uses a pair of `feth` (fake ethernet) interfaces
228/// - On Windows, TAP mode requires the tap-windows driver
229/// - On Linux, both modes use the kernel TUN/TAP driver
230/// - Android and iOS only support TUN (L3) mode
231#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
232#[non_exhaustive]
233pub enum Layer {
234 /// Data Link Layer (Ethernet frames with MAC addresses).
235 ///
236 /// TAP mode operates at Layer 2, handling complete Ethernet frames.
237 /// Requires a MAC address to be configured.
238 ///
239 /// Available on: Windows, Linux, FreeBSD, macOS, OpenBSD, NetBSD
240 #[cfg(any(
241 target_os = "windows",
242 target_os = "linux",
243 target_os = "freebsd",
244 target_os = "macos",
245 target_os = "openbsd",
246 target_os = "netbsd",
247 ))]
248 L2,
249
250 /// Network Layer (IP packets).
251 ///
252 /// TUN mode operates at Layer 3, handling IP packets directly.
253 /// This is the default and most common mode for VPN and tunneling applications.
254 ///
255 /// Available on: All platforms
256 #[default]
257 L3,
258}
259
260/// Configuration for a TUN/TAP interface.
261///
262/// This structure stores settings such as the device name, operating layer,
263/// and platform-specific parameters (e.g., GUID, wintun file, ring capacity on Windows).
264#[derive(Clone, Default, Debug)]
265pub(crate) struct DeviceConfig {
266 /// The name of the device/interface.
267 pub(crate) dev_name: Option<String>,
268 /// The description of the device/interface.
269 #[cfg(windows)]
270 pub(crate) description: Option<String>,
271 /// Available with Layer::L2; creates a pair of feth devices, with peer_feth as the IO interface name.
272 #[cfg(target_os = "macos")]
273 pub(crate) peer_feth: Option<String>,
274 /// If true (default), the program will automatically add or remove routes on macOS or FreeBSD to provide consistent routing behavior across all platforms.
275 /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
276 /// Set this to be false to obtain the platform's default routing behavior.
277 #[cfg(any(
278 target_os = "macos",
279 target_os = "freebsd",
280 target_os = "openbsd",
281 target_os = "netbsd"
282 ))]
283 pub(crate) associate_route: Option<bool>,
284 /// If true (default), the existing device with the given name will be used if possible.
285 /// If false, an error will be returned if a device with the specified name already exists.
286 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
287 pub(crate) reuse_dev: Option<bool>,
288 /// If true, the feth device will be kept after the program exits;
289 /// if false (default), the device will be destroyed automatically.
290 #[cfg(any(target_os = "macos", target_os = "windows"))]
291 pub(crate) persist: Option<bool>,
292 /// Specifies whether the interface operates at L2 or L3.
293 #[allow(dead_code)]
294 pub(crate) layer: Option<Layer>,
295 /// Device GUID on Windows.
296 #[cfg(windows)]
297 pub(crate) device_guid: Option<u128>,
298 #[cfg(windows)]
299 pub(crate) wintun_log: Option<bool>,
300 /// Path to the wintun file on Windows.
301 #[cfg(windows)]
302 pub(crate) wintun_file: Option<String>,
303 /// Capacity of the ring buffer on Windows.
304 #[cfg(windows)]
305 pub(crate) ring_capacity: Option<u32>,
306 /// Whether to call WintunDeleteDriver to remove the driver.
307 /// Default: false.
308 #[cfg(windows)]
309 pub(crate) delete_driver: Option<bool>,
310 #[cfg(windows)]
311 pub(crate) mac_address: Option<String>,
312 /// switch of Enable/Disable packet information for network driver
313 #[cfg(any(
314 target_os = "macos",
315 target_os = "linux",
316 target_os = "freebsd",
317 target_os = "openbsd",
318 target_os = "netbsd"
319 ))]
320 pub(crate) packet_information: Option<bool>,
321 /// Enable/Disable TUN offloads.
322 /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
323 #[cfg(target_os = "linux")]
324 pub(crate) offload: Option<bool>,
325 /// Enable multi queue support
326 #[cfg(target_os = "linux")]
327 pub(crate) multi_queue: Option<bool>,
328}
329type IPV4 = (
330 io::Result<Ipv4Addr>,
331 io::Result<u8>,
332 Option<io::Result<Ipv4Addr>>,
333);
334/// A builder for configuring a TUN/TAP interface.
335///
336/// This builder allows you to set parameters such as device name, MTU,
337/// IPv4/IPv6 addresses, MAC address, and other platform-specific options.
338///
339/// # Examples
340///
341/// Creating a basic IPv4 TUN interface:
342///
343/// ````no_run
344/// use std::net::Ipv4Addr;
345/// use tun_rs::DeviceBuilder;
346///
347/// fn main() -> std::io::Result<()> {
348/// let tun = DeviceBuilder::new()
349/// .name("my-tun")
350/// .mtu(1500)
351/// .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
352/// .build_sync()?;
353/// Ok(())
354/// }
355/// ````
356///
357/// Creating an IPv6 TUN interface:
358///
359/// ````no_run
360/// use std::net::Ipv6Addr;
361/// use tun_rs::DeviceBuilder;
362///
363/// fn main() -> std::io::Result<()> {
364/// let tun = DeviceBuilder::new()
365/// .name("my-tun6")
366/// .mtu(1500)
367/// .ipv6(Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 1), 64)
368/// .build_sync()?;
369/// Ok(())
370/// }
371/// ````
372///
373/// Creating an L2 TAP interface (platform-dependent):
374///
375/// ````no_run
376/// #[cfg(any(
377/// target_os = "windows",
378/// all(target_os = "linux", not(target_env = "ohos")),
379/// target_os = "freebsd",
380/// target_os = "macos",
381/// target_os = "openbsd",
382/// target_os = "netbsd"
383/// ))]
384/// use tun_rs::{DeviceBuilder, Layer};
385///
386/// #[cfg(any(
387/// target_os = "windows",
388/// all(target_os = "linux", not(target_env = "ohos")),
389/// target_os = "freebsd",
390/// target_os = "macos",
391/// target_os = "openbsd",
392/// target_os = "netbsd"
393/// ))]
394/// fn main() -> std::io::Result<()> {
395/// let tap = DeviceBuilder::new()
396/// .name("my-tap")
397/// .layer(Layer::L2)
398/// .mac_addr([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])
399/// .mtu(1500)
400/// .build_sync()?;
401/// Ok(())
402/// }
403/// ````
404#[doc(hidden)]
405pub struct DeviceBuilderGuard<'a>(&'a mut DeviceBuilder);
406
407#[doc(hidden)]
408impl DeviceBuilderGuard<'_> {
409 /// Sets the device description (effective only on Windows L3 mode).
410 #[cfg(windows)]
411 pub fn description<S: Into<String>>(&mut self, description: S) -> &mut Self {
412 self.0.description = Some(description.into());
413 self
414 }
415
416 /// Sets the IPv4 MTU specifically for Windows.
417 #[cfg(windows)]
418 pub fn mtu_v4(&mut self, mtu: u16) -> &mut Self {
419 self.0.mtu = Some(mtu);
420 self
421 }
422 /// Sets the IPv6 MTU specifically for Windows.
423 #[cfg(windows)]
424 pub fn mtu_v6(&mut self, mtu: u16) -> &mut Self {
425 self.0.mtu_v6 = Some(mtu);
426 self
427 }
428 /// Sets the MAC address for the device (effective only in L2 mode).
429 #[cfg(any(
430 target_os = "windows",
431 target_os = "linux",
432 target_os = "freebsd",
433 target_os = "openbsd",
434 target_os = "macos",
435 target_os = "netbsd"
436 ))]
437 pub fn mac_addr(&mut self, mac_addr: [u8; 6]) -> &mut Self {
438 self.0.mac_addr = Some(mac_addr);
439 self
440 }
441
442 /// Sets the device GUID on Windows.
443 /// By default, GUID is chosen by the system at random.
444 #[cfg(windows)]
445 pub fn device_guid(&mut self, device_guid: u128) -> &mut Self {
446 self.0.device_guid = Some(device_guid);
447 self
448 }
449 /// Enables or disables Wintun logging.
450 ///
451 /// By default, logging is disabled.
452 #[cfg(windows)]
453 pub fn wintun_log(&mut self, wintun_log: bool) -> &mut Self {
454 self.0.wintun_log = Some(wintun_log);
455 self
456 }
457 /// Sets the `wintun.dll` file path on Windows.
458 #[cfg(windows)]
459 pub fn wintun_file(&mut self, wintun_file: String) -> &mut Self {
460 self.0.wintun_file = Some(wintun_file);
461 self
462 }
463 /// Sets the ring capacity on Windows.
464 /// This specifies the capacity of the packet ring buffer in bytes.
465 /// By default, the ring capacity is set to `0x20_0000` (2 MB).
466 #[cfg(windows)]
467 pub fn ring_capacity(&mut self, ring_capacity: u32) -> &mut Self {
468 self.0.ring_capacity = Some(ring_capacity);
469 self
470 }
471 /// Sets the routing metric (routing cost) for the interface on Windows.
472 ///
473 /// The metric determines the priority of this interface when multiple routes exist
474 /// to the same destination. Lower metric values have higher priority.
475 ///
476 /// # Arguments
477 ///
478 /// * `metric` - The metric value (lower values = higher priority)
479 ///
480 /// # Example
481 ///
482 /// ```no_run
483 /// # #[cfg(target_os = "windows")]
484 /// # {
485 /// use tun_rs::DeviceBuilder;
486 ///
487 /// let dev = DeviceBuilder::new()
488 /// .ipv4("10.0.0.1", 24, None)
489 /// .with(|builder| {
490 /// builder.metric(10) // Set lower metric for higher priority
491 /// })
492 /// .build_sync()?;
493 /// # }
494 /// # Ok::<(), std::io::Error>(())
495 /// ```
496 ///
497 /// # Platform
498 ///
499 /// Windows only.
500 #[cfg(windows)]
501 pub fn metric(&mut self, metric: u16) -> &mut Self {
502 self.0.metric = Some(metric);
503 self
504 }
505 /// Whether to call `WintunDeleteDriver` to remove the driver.
506 /// Default: false.
507 /// # Note
508 /// The clean-up work closely depends on whether the destructor can be normally executed
509 #[cfg(windows)]
510 pub fn delete_driver(&mut self, delete_driver: bool) -> &mut Self {
511 self.0.delete_driver = Some(delete_driver);
512 self
513 }
514 /// Sets the transmit queue length for the network interface on Linux.
515 ///
516 /// The transmit queue length controls how many packets can be queued for
517 /// transmission by the network stack. A larger queue can help with bursty
518 /// traffic but may increase latency.
519 ///
520 /// # Arguments
521 ///
522 /// * `tx_queue_len` - The queue length in packets (typical values: 100-10000)
523 ///
524 /// # Example
525 ///
526 /// ```no_run
527 /// # #[cfg(target_os = "linux")]
528 /// # {
529 /// use tun_rs::DeviceBuilder;
530 ///
531 /// let dev = DeviceBuilder::new()
532 /// .ipv4("10.0.0.1", 24, None)
533 /// .with(|builder| {
534 /// builder.tx_queue_len(1000) // Set queue length to 1000 packets
535 /// })
536 /// .build_sync()?;
537 /// # }
538 /// # Ok::<(), std::io::Error>(())
539 /// ```
540 ///
541 /// # Platform
542 ///
543 /// Linux only.
544 #[cfg(target_os = "linux")]
545 pub fn tx_queue_len(&mut self, tx_queue_len: u32) -> &mut Self {
546 self.0.tx_queue_len = Some(tx_queue_len);
547 self
548 }
549 /// Enables Generic Segmentation Offload (GSO) and Generic Receive Offload (GRO) on Linux.
550 ///
551 /// When enabled, the TUN device can handle larger packets, allowing the kernel to perform
552 /// segmentation and coalescing for improved network throughput. This is particularly beneficial
553 /// for high-bandwidth TCP and UDP traffic.
554 ///
555 /// After enabling offload, you should use [`recv_multiple`](crate::SyncDevice::recv_multiple)
556 /// and [`send_multiple`](crate::SyncDevice::send_multiple) for optimal performance.
557 ///
558 /// # Arguments
559 ///
560 /// * `offload` - `true` to enable offload, `false` to disable (default: false)
561 ///
562 /// # Example
563 ///
564 /// ```no_run
565 /// # #[cfg(target_os = "linux")]
566 /// # {
567 /// use tun_rs::{DeviceBuilder, GROTable, VIRTIO_NET_HDR_LEN, IDEAL_BATCH_SIZE};
568 ///
569 /// let dev = DeviceBuilder::new()
570 /// .ipv4("10.0.0.1", 24, None)
571 /// .with(|builder| {
572 /// builder.offload(true) // Enable TSO/GSO/GRO
573 /// })
574 /// .build_sync()?;
575 ///
576 /// // Use with recv_multiple for best performance
577 /// let mut gro_table = GROTable::default();
578 /// let mut original_buffer = vec![0u8; VIRTIO_NET_HDR_LEN + 65535];
579 /// let mut bufs = vec![vec![0u8; 1500]; IDEAL_BATCH_SIZE];
580 /// let mut sizes = vec![0; IDEAL_BATCH_SIZE];
581 ///
582 /// let num = dev.recv_multiple(&mut original_buffer, &mut bufs, &mut sizes, 0)?;
583 /// println!("Received {} packets", num);
584 /// # }
585 /// # Ok::<(), std::io::Error>(())
586 /// ```
587 ///
588 /// # Performance
589 ///
590 /// Enabling offload can provide 2-10x throughput improvement for TCP traffic.
591 ///
592 /// # Platform
593 ///
594 /// Linux only. Requires kernel support for IFF_VNET_HDR (Linux 2.6.32+).
595 #[cfg(target_os = "linux")]
596 pub fn offload(&mut self, offload: bool) -> &mut Self {
597 self.0.offload = Some(offload);
598 self
599 }
600 /// Enables multi-queue support for parallel packet processing on Linux.
601 ///
602 /// When enabled, the TUN device can be cloned to create multiple queues, allowing
603 /// packet processing to be distributed across multiple CPU cores for improved performance.
604 /// Each queue can be used independently in separate threads.
605 ///
606 /// # Arguments
607 ///
608 /// * `multi_queue` - `true` to enable multi-queue support (default: false)
609 ///
610 /// # Example
611 ///
612 /// ```no_run
613 /// # #[cfg(target_os = "linux")]
614 /// # {
615 /// use std::thread;
616 /// use tun_rs::DeviceBuilder;
617 ///
618 /// let dev = DeviceBuilder::new()
619 /// .ipv4("10.0.0.1", 24, None)
620 /// .with(|builder| {
621 /// builder.multi_queue(true) // Enable multi-queue
622 /// })
623 /// .build_sync()?;
624 ///
625 /// // Clone the device for use in another thread
626 /// let dev_clone = dev.try_clone()?;
627 ///
628 /// thread::spawn(move || {
629 /// let mut buf = [0u8; 1500];
630 /// loop {
631 /// if let Ok(n) = dev_clone.recv(&mut buf) {
632 /// println!("Thread 2: {} bytes", n);
633 /// }
634 /// }
635 /// });
636 ///
637 /// // Process in main thread
638 /// let mut buf = [0u8; 1500];
639 /// loop {
640 /// let n = dev.recv(&mut buf)?;
641 /// println!("Thread 1: {} bytes", n);
642 /// }
643 /// # }
644 /// # Ok::<(), std::io::Error>(())
645 /// ```
646 ///
647 /// # Performance
648 ///
649 /// Multi-queue allows parallel packet processing across CPU cores, improving throughput
650 /// for multi-core systems.
651 ///
652 /// # Platform
653 ///
654 /// Linux only. Requires kernel support for IFF_MULTI_QUEUE.
655 #[cfg(target_os = "linux")]
656 pub fn multi_queue(&mut self, multi_queue: bool) -> &mut Self {
657 self.0.multi_queue = Some(multi_queue);
658 self
659 }
660 /// Enables or disables packet information for the network driver(TUN)
661 /// on macOS, Linux, freebsd, openbsd, netbsd.
662 ///
663 /// This option is disabled by default (`false`).
664 /// # Note
665 /// There is no native way to enable/disable packet information on macOS.
666 /// The elimination of the packet information on macOS according to this setting
667 /// is processed by this library.
668 /// The set value `v` can be retrieved by `ignore_packet_info`, the returned value is `!v`.
669 #[cfg(any(
670 target_os = "macos",
671 target_os = "linux",
672 target_os = "freebsd",
673 target_os = "openbsd",
674 target_os = "netbsd"
675 ))]
676 pub fn packet_information(&mut self, packet_information: bool) -> &mut Self {
677 self.0.packet_information = Some(packet_information);
678 self
679 }
680 /// Creates a pair of `feth` devices for TAP mode on macOS.
681 ///
682 /// On macOS, TAP mode (Layer 2) is implemented using a pair of fake Ethernet (`feth`)
683 /// devices. One device is used for I/O operations, and the other (specified by `peer_feth`)
684 /// is the peer interface. Both devices must be configured and brought up for proper operation.
685 ///
686 /// # Arguments
687 ///
688 /// * `peer_feth` - The name of the peer interface (e.g., "feth1")
689 ///
690 /// # Example
691 ///
692 /// ```no_run
693 /// # #[cfg(target_os = "macos")]
694 /// # {
695 /// use tun_rs::{DeviceBuilder, Layer};
696 ///
697 /// // Create a TAP interface with a peer device
698 /// let dev = DeviceBuilder::new()
699 /// .name("feth0")
700 /// .layer(Layer::L2)
701 /// .with(|builder| {
702 /// builder.peer_feth("feth1") // Specify the peer interface name
703 /// })
704 /// .build_sync()?;
705 /// # }
706 /// # Ok::<(), std::io::Error>(())
707 /// ```
708 ///
709 /// # Platform
710 ///
711 /// macOS only, Layer 2 (TAP) mode only.
712 #[cfg(target_os = "macos")]
713 pub fn peer_feth<S: Into<String>>(&mut self, peer_feth: S) -> &mut Self {
714 self.0.peer_feth = Some(peer_feth.into());
715 self
716 }
717 /// Controls automatic route management on BSD and macOS platforms.
718 ///
719 /// When enabled (the default), the library automatically adds or removes routes
720 /// to provide consistent routing behavior across all platforms. When disabled,
721 /// the system's native routing behavior is used.
722 ///
723 /// # Arguments
724 ///
725 /// * `associate_route` - `true` to enable automatic route management (default),
726 /// `false` to use native system routing
727 ///
728 /// # Example
729 ///
730 /// ```no_run
731 /// # #[cfg(any(target_os = "macos", target_os = "freebsd"))]
732 /// # {
733 /// use tun_rs::DeviceBuilder;
734 ///
735 /// // Use native system routing without automatic route management
736 /// let dev = DeviceBuilder::new()
737 /// .ipv4("10.0.0.1", 24, None)
738 /// .with(|builder| {
739 /// builder.associate_route(false) // Disable automatic route management
740 /// })
741 /// .build_sync()?;
742 /// # }
743 /// # Ok::<(), std::io::Error>(())
744 /// ```
745 ///
746 /// # Platform
747 ///
748 /// macOS, FreeBSD, OpenBSD, NetBSD only.
749 #[cfg(any(
750 target_os = "macos",
751 target_os = "freebsd",
752 target_os = "openbsd",
753 target_os = "netbsd"
754 ))]
755 pub fn associate_route(&mut self, associate_route: bool) -> &mut Self {
756 self.0.associate_route = Some(associate_route);
757 self
758 }
759 /// Controls whether to reuse an existing device with the same name.
760 ///
761 /// In TAP mode, if a device with the specified name already exists:
762 /// - When `true` (default): The existing device will be reused
763 /// - When `false`: An error will be returned
764 ///
765 /// # Arguments
766 ///
767 /// * `reuse` - `true` to reuse existing devices (default), `false` to error on conflicts
768 ///
769 /// # Example
770 ///
771 /// ```no_run
772 /// # #[cfg(any(target_os = "macos", target_os = "windows"))]
773 /// # {
774 /// use tun_rs::{DeviceBuilder, Layer};
775 ///
776 /// // Don't reuse existing device - fail if it already exists
777 /// let dev = DeviceBuilder::new()
778 /// .name("tap0")
779 /// .layer(Layer::L2)
780 /// .with(|builder| {
781 /// builder.reuse_dev(false) // Error if tap0 already exists
782 /// })
783 /// .build_sync()?;
784 /// # }
785 /// # Ok::<(), std::io::Error>(())
786 /// ```
787 ///
788 /// # Platform
789 ///
790 /// macOS, Windows, NetBSD only. TAP mode (Layer 2) only.
791 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
792 pub fn reuse_dev(&mut self, reuse: bool) -> &mut Self {
793 self.0.reuse_dev = Some(reuse);
794 self
795 }
796 /// Controls whether the TAP device persists after the program exits.
797 ///
798 /// In TAP mode:
799 /// - When `true`: The device remains after the program terminates
800 /// - When `false` (default): The device is automatically destroyed on exit
801 ///
802 /// # Arguments
803 ///
804 /// * `persist` - `true` to keep the device after exit, `false` to auto-destroy (default)
805 ///
806 /// # Example
807 ///
808 /// ```no_run
809 /// # #[cfg(any(target_os = "macos", target_os = "windows"))]
810 /// # {
811 /// use tun_rs::{DeviceBuilder, Layer};
812 ///
813 /// // Create a persistent TAP device that survives program exit
814 /// let dev = DeviceBuilder::new()
815 /// .name("tap0")
816 /// .layer(Layer::L2)
817 /// .with(|builder| {
818 /// builder.persist(true) // Keep device after program exits
819 /// })
820 /// .build_sync()?;
821 /// # }
822 /// # Ok::<(), std::io::Error>(())
823 /// ```
824 ///
825 /// # Platform
826 ///
827 /// macOS, Windows only. TAP mode (Layer 2) only.
828 #[cfg(any(target_os = "macos", target_os = "windows"))]
829 pub fn persist(&mut self, persist: bool) -> &mut Self {
830 self.0.persist = Some(persist);
831 self
832 }
833}
834/// This is a unified constructor of a device for various platforms. The specification of every API can be found by looking at
835/// the documentation of the concrete platform.
836#[derive(Default)]
837pub struct DeviceBuilder {
838 dev_name: Option<String>,
839 #[cfg(windows)]
840 description: Option<String>,
841 #[cfg(target_os = "macos")]
842 peer_feth: Option<String>,
843 #[cfg(any(
844 target_os = "macos",
845 target_os = "freebsd",
846 target_os = "openbsd",
847 target_os = "netbsd"
848 ))]
849 associate_route: Option<bool>,
850 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
851 reuse_dev: Option<bool>,
852 #[cfg(any(target_os = "macos", target_os = "windows"))]
853 persist: Option<bool>,
854 enabled: Option<bool>,
855 mtu: Option<u16>,
856 #[cfg(windows)]
857 mtu_v6: Option<u16>,
858 ipv4: Option<IPV4>,
859 ipv6: Option<Vec<(io::Result<Ipv6Addr>, io::Result<u8>)>>,
860 layer: Option<Layer>,
861 #[cfg(any(
862 target_os = "windows",
863 target_os = "linux",
864 target_os = "freebsd",
865 target_os = "openbsd",
866 target_os = "macos",
867 target_os = "netbsd"
868 ))]
869 mac_addr: Option<[u8; 6]>,
870 #[cfg(windows)]
871 device_guid: Option<u128>,
872 #[cfg(windows)]
873 wintun_log: Option<bool>,
874 #[cfg(windows)]
875 wintun_file: Option<String>,
876 #[cfg(windows)]
877 ring_capacity: Option<u32>,
878 #[cfg(windows)]
879 metric: Option<u16>,
880 #[cfg(windows)]
881 delete_driver: Option<bool>,
882 /// switch of Enable/Disable packet information for network driver
883 #[cfg(any(
884 target_os = "macos",
885 target_os = "linux",
886 target_os = "freebsd",
887 target_os = "openbsd",
888 target_os = "netbsd"
889 ))]
890 packet_information: Option<bool>,
891 #[cfg(target_os = "linux")]
892 tx_queue_len: Option<u32>,
893 /// Enable/Disable TUN offloads.
894 /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
895 #[cfg(target_os = "linux")]
896 offload: Option<bool>,
897 /// Enable multi queue support
898 #[cfg(target_os = "linux")]
899 multi_queue: Option<bool>,
900}
901
902impl DeviceBuilder {
903 /// Creates a new DeviceBuilder instance with default settings.
904 pub fn new() -> Self {
905 Self::default().enable(true)
906 }
907 /// Sets the device name.
908 pub fn name<S: Into<String>>(mut self, dev_name: S) -> Self {
909 self.dev_name = Some(dev_name.into());
910 self
911 }
912 /// Sets the device description (effective only on Windows L3 mode).
913 #[cfg(windows)]
914 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
915 self.description = Some(description.into());
916 self
917 }
918 /// Sets the device MTU (Maximum Transmission Unit).
919 pub fn mtu(mut self, mtu: u16) -> Self {
920 self.mtu = Some(mtu);
921 #[cfg(windows)]
922 {
923 // On Windows, also set the MTU for IPv6.
924 self.mtu_v6 = Some(mtu);
925 }
926 self
927 }
928 /// Sets the IPv4 MTU specifically for Windows.
929 #[cfg(windows)]
930 pub fn mtu_v4(mut self, mtu: u16) -> Self {
931 self.mtu = Some(mtu);
932 self
933 }
934 /// Sets the IPv6 MTU specifically for Windows.
935 #[cfg(windows)]
936 pub fn mtu_v6(mut self, mtu: u16) -> Self {
937 self.mtu_v6 = Some(mtu);
938 self
939 }
940 /// Sets the MAC address for the device (effective only in L2 mode).
941 #[cfg(any(
942 target_os = "windows",
943 target_os = "linux",
944 target_os = "freebsd",
945 target_os = "openbsd",
946 target_os = "macos",
947 target_os = "netbsd"
948 ))]
949 pub fn mac_addr(mut self, mac_addr: [u8; 6]) -> Self {
950 self.mac_addr = Some(mac_addr);
951 self
952 }
953 /// Configures the IPv4 address for the device.
954 ///
955 /// - `address`: The IPv4 address of the device.
956 /// - `mask`: The subnet mask or prefix length.
957 /// - `destination`: Optional destination address for point-to-point links.
958 /// # Example
959 /// ```
960 /// use std::net::Ipv4Addr;
961 /// use tun_rs::DeviceBuilder;
962 /// DeviceBuilder::new().ipv4(Ipv4Addr::new(10, 0, 0, 12), 24, None);
963 /// ```
964 pub fn ipv4<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
965 mut self,
966 address: IPv4,
967 mask: Netmask,
968 destination: Option<IPv4>,
969 ) -> Self {
970 self.ipv4 = Some((address.ipv4(), mask.prefix(), destination.map(|v| v.ipv4())));
971 self
972 }
973 /// Configures a single IPv6 address for the device.
974 ///
975 /// - `address`: The IPv6 address.
976 /// - `mask`: The subnet mask or prefix length.
977 /// # Example
978 /// ```
979 /// use tun_rs::DeviceBuilder;
980 /// DeviceBuilder::new().ipv6("CDCD:910A:2222:5498:8475:1111:3900:2021", 64);
981 /// ```
982 pub fn ipv6<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
983 mut self,
984 address: IPv6,
985 mask: Netmask,
986 ) -> Self {
987 if let Some(v) = &mut self.ipv6 {
988 v.push((address.ipv6(), mask.prefix()));
989 } else {
990 self.ipv6 = Some(vec![(address.ipv6(), mask.prefix())]);
991 }
992
993 self
994 }
995 /// Configures multiple IPv6 addresses in batch.
996 ///
997 /// Accepts a slice of (IPv6 address, netmask) tuples.
998 /// # Example
999 /// ```rust
1000 /// use tun_rs::DeviceBuilder;
1001 /// DeviceBuilder::new().ipv6_tuple(&[
1002 /// ("CDCD:910A:2222:5498:8475:1111:3900:2022", 64),
1003 /// ("CDCD:910A:2222:5498:8475:1111:3900:2023", 64),
1004 /// ]);
1005 /// ```
1006 pub fn ipv6_tuple<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
1007 mut self,
1008 addrs: &[(IPv6, Netmask)],
1009 ) -> Self {
1010 if let Some(v) = &mut self.ipv6 {
1011 for (address, mask) in addrs {
1012 v.push((address.ipv6(), mask.prefix()));
1013 }
1014 } else {
1015 self.ipv6 = Some(
1016 addrs
1017 .iter()
1018 .map(|(ip, mask)| (ip.ipv6(), mask.prefix()))
1019 .collect(),
1020 );
1021 }
1022 self
1023 }
1024 /// Sets the operating layer (L2 or L3) for the device.
1025 ///
1026 /// * L2 corresponds to TAP
1027 /// * L3 corresponds to TUN
1028 pub fn layer(mut self, layer: Layer) -> Self {
1029 self.layer = Some(layer);
1030 self
1031 }
1032 /// Sets the device GUID on Windows.
1033 /// By default, GUID is chosen by the system at random.
1034 #[cfg(windows)]
1035 pub fn device_guid(mut self, device_guid: u128) -> Self {
1036 self.device_guid = Some(device_guid);
1037 self
1038 }
1039 /// Enables or disables Wintun logging.
1040 ///
1041 /// By default, logging is disabled.
1042 #[cfg(windows)]
1043 pub fn wintun_log(mut self, wintun_log: bool) -> Self {
1044 self.wintun_log = Some(wintun_log);
1045 self
1046 }
1047 /// Sets the `wintun.dll` file path on Windows.
1048 #[cfg(windows)]
1049 pub fn wintun_file(mut self, wintun_file: String) -> Self {
1050 self.wintun_file = Some(wintun_file);
1051 self
1052 }
1053 /// Sets the ring capacity on Windows.
1054 /// This specifies the capacity of the packet ring buffer in bytes.
1055 /// By default, the ring capacity is set to `0x20_0000` (2 MB).
1056 #[cfg(windows)]
1057 pub fn ring_capacity(mut self, ring_capacity: u32) -> Self {
1058 self.ring_capacity = Some(ring_capacity);
1059 self
1060 }
1061 /// Sets the routing metric (routing cost) for the interface on Windows.
1062 ///
1063 /// The metric determines the priority of this interface when multiple routes exist
1064 /// to the same destination. Lower metric values have higher priority.
1065 ///
1066 /// # Arguments
1067 ///
1068 /// * `metric` - The metric value (lower values = higher priority)
1069 ///
1070 /// # Example
1071 ///
1072 /// ```no_run
1073 /// # #[cfg(target_os = "windows")]
1074 /// # {
1075 /// use tun_rs::DeviceBuilder;
1076 ///
1077 /// let dev = DeviceBuilder::new()
1078 /// .ipv4("10.0.0.1", 24, None)
1079 /// .metric(10) // Set lower metric for higher priority
1080 /// .build_sync()?;
1081 /// # }
1082 /// # Ok::<(), std::io::Error>(())
1083 /// ```
1084 ///
1085 /// # Platform
1086 ///
1087 /// Windows only.
1088 #[cfg(windows)]
1089 pub fn metric(mut self, metric: u16) -> Self {
1090 self.metric = Some(metric);
1091 self
1092 }
1093 /// Whether to call `WintunDeleteDriver` to remove the driver.
1094 /// Default: false.
1095 /// # Note
1096 /// The clean-up work closely depends on whether the destructor can be normally executed
1097 #[cfg(windows)]
1098 pub fn delete_driver(mut self, delete_driver: bool) -> Self {
1099 self.delete_driver = Some(delete_driver);
1100 self
1101 }
1102 /// Sets the transmit queue length on Linux.
1103 #[cfg(target_os = "linux")]
1104 pub fn tx_queue_len(mut self, tx_queue_len: u32) -> Self {
1105 self.tx_queue_len = Some(tx_queue_len);
1106 self
1107 }
1108 /// Enables TUN offloads on Linux.
1109 /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
1110 #[cfg(target_os = "linux")]
1111 pub fn offload(mut self, offload: bool) -> Self {
1112 self.offload = Some(offload);
1113 self
1114 }
1115 /// Enables multi-queue support on Linux.
1116 #[cfg(target_os = "linux")]
1117 pub fn multi_queue(mut self, multi_queue: bool) -> Self {
1118 self.multi_queue = Some(multi_queue);
1119 self
1120 }
1121 /// Enables or disables packet information for the network driver(TUN)
1122 /// on macOS, Linux, freebsd, openbsd, netbsd.
1123 ///
1124 /// This option is disabled by default (`false`).
1125 /// # Note
1126 /// There is no native way to enable/disable packet information on macOS.
1127 /// The elimination of the packet information on macOS according to this setting
1128 /// is processed by this library.
1129 /// The set value `v` can be retrieved by `ignore_packet_info`, the returned value is `!v`.
1130 #[cfg(any(
1131 target_os = "macos",
1132 target_os = "linux",
1133 target_os = "freebsd",
1134 target_os = "openbsd",
1135 target_os = "netbsd"
1136 ))]
1137 pub fn packet_information(mut self, packet_information: bool) -> Self {
1138 self.packet_information = Some(packet_information);
1139 self
1140 }
1141 /// Available on Layer::L2;
1142 /// creates a pair of `feth` devices, with `peer_feth` as the IO interface name.
1143 #[cfg(target_os = "macos")]
1144 pub fn peer_feth<S: Into<String>>(mut self, peer_feth: S) -> Self {
1145 self.peer_feth = Some(peer_feth.into());
1146 self
1147 }
1148 /// If true (default), the program will automatically add or remove routes on macOS or FreeBSD to provide consistent routing behavior across all platforms.
1149 /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
1150 /// Set this to be false to obtain the platform's default routing behavior.
1151 #[cfg(any(
1152 target_os = "macos",
1153 target_os = "freebsd",
1154 target_os = "openbsd",
1155 target_os = "netbsd"
1156 ))]
1157 pub fn associate_route(mut self, associate_route: bool) -> Self {
1158 self.associate_route = Some(associate_route);
1159 self
1160 }
1161 /// Only works in TAP mode.
1162 /// If true (default), the existing device with the given name will be used if possible.
1163 /// If false, an error will be returned if a device with the specified name already exists.
1164 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
1165 pub fn reuse_dev(mut self, reuse: bool) -> Self {
1166 self.reuse_dev = Some(reuse);
1167 self
1168 }
1169 /// Only works in TAP mode.
1170 /// If true, the `feth` device will be kept after the program exits;
1171 /// if false (default), the device will be destroyed automatically.
1172 #[cfg(any(target_os = "macos", target_os = "windows"))]
1173 pub fn persist(mut self, persist: bool) -> Self {
1174 self.persist = Some(persist);
1175 self
1176 }
1177 /// Enables or disables the network interface upon creation.
1178 ///
1179 /// By default, newly created TUN/TAP devices are enabled (brought up).
1180 /// Use this method to control whether the device should be automatically enabled
1181 /// or left in a disabled state.
1182 ///
1183 /// # Arguments
1184 ///
1185 /// * `enable` - `true` to enable the device (default), `false` to leave it disabled
1186 ///
1187 /// # Example
1188 ///
1189 /// ```no_run
1190 /// use tun_rs::DeviceBuilder;
1191 ///
1192 /// // Create a device but leave it disabled initially
1193 /// let dev = DeviceBuilder::new()
1194 /// .ipv4("10.0.0.1", 24, None)
1195 /// .enable(false) // Don't enable the device yet
1196 /// .build_sync()?;
1197 ///
1198 /// // Later, enable it manually using platform-specific methods
1199 /// // dev.enabled(true)?;
1200 /// # Ok::<(), std::io::Error>(())
1201 /// ```
1202 ///
1203 /// # See Also
1204 ///
1205 /// - [`inherit_enable_state`](Self::inherit_enable_state) - Preserve existing device state
1206 pub fn enable(mut self, enable: bool) -> Self {
1207 self.enabled = Some(enable);
1208 self
1209 }
1210
1211 /// Preserves the existing enable state of the network interface.
1212 ///
1213 /// When reusing an existing device, this method prevents the builder from
1214 /// explicitly setting the device's enable/disable state. The device will
1215 /// retain whatever state it currently has in the system.
1216 ///
1217 /// This is particularly useful when:
1218 /// - Reconnecting to an existing TUN/TAP device
1219 /// - You want to preserve the current system configuration
1220 /// - Avoiding unnecessary state changes that might disrupt existing connections
1221 ///
1222 /// # Example
1223 ///
1224 /// ```no_run
1225 /// # #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
1226 /// # {
1227 /// use tun_rs::DeviceBuilder;
1228 ///
1229 /// // Reuse an existing device and keep its current enabled state
1230 /// let dev = DeviceBuilder::new()
1231 /// .name("tun0")
1232 /// .ipv4("10.0.0.1", 24, None)
1233 /// .with(|builder| {
1234 /// builder.reuse_dev(true)
1235 /// })
1236 /// .inherit_enable_state() // Don't change the existing enable state
1237 /// .build_sync()?;
1238 /// # }
1239 /// # Ok::<(), std::io::Error>(())
1240 /// ```
1241 ///
1242 /// # See Also
1243 ///
1244 /// - [`enable`](Self::enable) - Explicitly enable or disable the device
1245 pub fn inherit_enable_state(mut self) -> Self {
1246 self.enabled = None;
1247 self
1248 }
1249 pub(crate) fn build_config(&mut self) -> DeviceConfig {
1250 DeviceConfig {
1251 dev_name: self.dev_name.take(),
1252 #[cfg(windows)]
1253 description: self.description.take(),
1254 #[cfg(target_os = "macos")]
1255 peer_feth: self.peer_feth.take(),
1256 #[cfg(any(
1257 target_os = "macos",
1258 target_os = "freebsd",
1259 target_os = "openbsd",
1260 target_os = "netbsd"
1261 ))]
1262 associate_route: self.associate_route,
1263 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
1264 reuse_dev: self.reuse_dev,
1265 #[cfg(any(target_os = "macos", target_os = "windows"))]
1266 persist: self.persist,
1267 layer: self.layer.take(),
1268 #[cfg(windows)]
1269 device_guid: self.device_guid.take(),
1270 #[cfg(windows)]
1271 wintun_log: self.wintun_log.take(),
1272 #[cfg(windows)]
1273 wintun_file: self.wintun_file.take(),
1274 #[cfg(windows)]
1275 ring_capacity: self.ring_capacity.take(),
1276 #[cfg(windows)]
1277 delete_driver: self.delete_driver.take(),
1278 #[cfg(windows)]
1279 mac_address: self.mac_addr.map(|v| {
1280 use std::fmt::Write;
1281 v.iter()
1282 .fold(String::with_capacity(v.len() * 2), |mut s, b| {
1283 write!(&mut s, "{b:02X}").unwrap();
1284 s
1285 })
1286 }),
1287 #[cfg(any(
1288 target_os = "macos",
1289 target_os = "linux",
1290 target_os = "freebsd",
1291 target_os = "openbsd",
1292 target_os = "netbsd"
1293 ))]
1294 packet_information: self.packet_information.take(),
1295 #[cfg(target_os = "linux")]
1296 offload: self.offload.take(),
1297 #[cfg(target_os = "linux")]
1298 multi_queue: self.multi_queue.take(),
1299 }
1300 }
1301 pub(crate) fn config(self, device: &DeviceImpl) -> io::Result<()> {
1302 if let Some(mtu) = self.mtu {
1303 device.set_mtu(mtu)?;
1304 }
1305 #[cfg(windows)]
1306 if let Some(mtu) = self.mtu_v6 {
1307 device.set_mtu_v6(mtu)?;
1308 }
1309 #[cfg(windows)]
1310 if let Some(metric) = self.metric {
1311 device.set_metric(metric)?;
1312 }
1313 #[cfg(target_os = "linux")]
1314 if let Some(tx_queue_len) = self.tx_queue_len {
1315 device.set_tx_queue_len(tx_queue_len)?;
1316 }
1317 #[cfg(any(
1318 target_os = "linux",
1319 target_os = "freebsd",
1320 target_os = "macos",
1321 target_os = "openbsd",
1322 target_os = "netbsd"
1323 ))]
1324 if let Some(mac_addr) = self.mac_addr {
1325 device.set_mac_address(mac_addr)?;
1326 }
1327
1328 if let Some((address, prefix, destination)) = self.ipv4 {
1329 let prefix = prefix?;
1330 let address = address?;
1331 let destination = destination.transpose()?;
1332 device.set_network_address(address, prefix, destination)?;
1333 }
1334 if let Some(ipv6) = self.ipv6 {
1335 for (address, prefix) in ipv6 {
1336 let prefix = prefix?;
1337 let address = address?;
1338 device.add_address_v6(address, prefix)?;
1339 }
1340 }
1341 if let Some(enabled) = self.enabled {
1342 device.enabled(enabled)?;
1343 }
1344 Ok(())
1345 }
1346 /// Builds a synchronous device instance and applies all configuration parameters.
1347 pub fn build_sync(mut self) -> io::Result<SyncDevice> {
1348 let device = DeviceImpl::new(self.build_config())?;
1349 self.config(&device)?;
1350 Ok(SyncDevice(device))
1351 }
1352 /// Builds an asynchronous device instance.
1353 ///
1354 /// This method is available only when either async_io or async_tokio feature is enabled.
1355 ///
1356 /// # Note
1357 /// Choose one of the two async runtimes; otherwise, a compile error will be incurred if both are enabled.
1358 #[cfg(any(feature = "async_io", feature = "async_tokio"))]
1359 pub fn build_async(self) -> io::Result<crate::AsyncDevice> {
1360 let sync_device = self.build_sync()?;
1361 let device = crate::AsyncDevice::new_dev(sync_device.0)?;
1362 Ok(device)
1363 }
1364 /// To conveniently set the platform-specific parameters without breaking the calling chain.
1365 /// # Ergonomic
1366 ///
1367 /// For example:
1368 /// ````no_run
1369 /// use tun_rs::DeviceBuilder;
1370 /// let builder = DeviceBuilder::new().name("tun1");
1371 /// #[cfg(target_os = "macos")]
1372 /// let builder = builder.associate_route(false);
1373 /// #[cfg(windows)]
1374 /// let builder = builder.wintun_log(false);
1375 /// let dev = builder.build_sync().unwrap();
1376 /// ````
1377 /// This is tedious and breaks the calling chain.
1378 ///
1379 /// With `with`, we can just set platform-specific parameters as follows without breaking the calling chain:
1380 /// ````no_run
1381 /// use tun_rs::DeviceBuilder;
1382 /// let dev = DeviceBuilder::new().name("tun1").with(|opt|{
1383 /// #[cfg(windows)]
1384 /// opt.wintun_log(false);
1385 /// #[cfg(target_os = "macos")]
1386 /// opt.associate_route(false).packet_information(false);
1387 /// }).build_sync().unwrap();
1388 /// ````
1389 pub fn with<F: FnMut(&mut DeviceBuilderGuard)>(mut self, mut f: F) -> Self {
1390 let mut borrow = DeviceBuilderGuard(&mut self);
1391 f(&mut borrow);
1392 self
1393 }
1394}
1395
1396/// Trait for converting various types into an IPv4 address.
1397pub trait ToIpv4Address {
1398 /// Attempts to convert the implementing type into an `Ipv4Addr`.
1399 /// Returns the IPv4 address on success or an error on failure.
1400 fn ipv4(&self) -> io::Result<Ipv4Addr>;
1401}
1402impl ToIpv4Address for Ipv4Addr {
1403 fn ipv4(&self) -> io::Result<Ipv4Addr> {
1404 Ok(*self)
1405 }
1406}
1407impl ToIpv4Address for IpAddr {
1408 fn ipv4(&self) -> io::Result<Ipv4Addr> {
1409 match self {
1410 IpAddr::V4(ip) => Ok(*ip),
1411 IpAddr::V6(_) => Err(io::Error::new(
1412 io::ErrorKind::InvalidData,
1413 "invalid address",
1414 )),
1415 }
1416 }
1417}
1418impl ToIpv4Address for String {
1419 fn ipv4(&self) -> io::Result<Ipv4Addr> {
1420 self.as_str().ipv4()
1421 }
1422}
1423impl ToIpv4Address for &str {
1424 fn ipv4(&self) -> io::Result<Ipv4Addr> {
1425 match Ipv4Addr::from_str(self) {
1426 Ok(ip) => Ok(ip),
1427 Err(_e) => Err(io::Error::new(
1428 io::ErrorKind::InvalidData,
1429 "invalid IPv4 str",
1430 )),
1431 }
1432 }
1433}
1434
1435/// Trait for converting various types into an IPv6 address.
1436pub trait ToIpv6Address {
1437 /// Attempts to convert the implementing type into an `Ipv6Addr`.
1438 /// Returns the IPv6 address on success or an error on failure.
1439 fn ipv6(&self) -> io::Result<Ipv6Addr>;
1440}
1441
1442impl ToIpv6Address for Ipv6Addr {
1443 fn ipv6(&self) -> io::Result<Ipv6Addr> {
1444 Ok(*self)
1445 }
1446}
1447impl ToIpv6Address for IpAddr {
1448 fn ipv6(&self) -> io::Result<Ipv6Addr> {
1449 match self {
1450 IpAddr::V4(_) => Err(io::Error::new(
1451 io::ErrorKind::InvalidData,
1452 "invalid address",
1453 )),
1454 IpAddr::V6(ip) => Ok(*ip),
1455 }
1456 }
1457}
1458impl ToIpv6Address for String {
1459 fn ipv6(&self) -> io::Result<Ipv6Addr> {
1460 self.as_str().ipv6()
1461 }
1462}
1463impl ToIpv6Address for &str {
1464 fn ipv6(&self) -> io::Result<Ipv6Addr> {
1465 match Ipv6Addr::from_str(self) {
1466 Ok(ip) => Ok(ip),
1467 Err(_e) => Err(io::Error::new(
1468 io::ErrorKind::InvalidData,
1469 "invalid IPv6 str",
1470 )),
1471 }
1472 }
1473}
1474/// Trait for converting various types into an IPv4 netmask (prefix length).
1475pub trait ToIpv4Netmask {
1476 /// Returns the prefix length (i.e., the number of consecutive 1s in the netmask).
1477 fn prefix(&self) -> io::Result<u8>;
1478 /// Computes the IPv4 netmask based on the prefix length.
1479 fn netmask(&self) -> io::Result<Ipv4Addr> {
1480 let ip = u32::MAX
1481 .checked_shl(32 - self.prefix()? as u32)
1482 .unwrap_or(0);
1483 Ok(Ipv4Addr::from(ip))
1484 }
1485}
1486
1487impl ToIpv4Netmask for u8 {
1488 fn prefix(&self) -> io::Result<u8> {
1489 if *self > 32 {
1490 return Err(io::Error::new(
1491 io::ErrorKind::InvalidData,
1492 "invalid IP prefix length",
1493 ));
1494 }
1495 Ok(*self)
1496 }
1497}
1498
1499impl ToIpv4Netmask for Ipv4Addr {
1500 fn prefix(&self) -> io::Result<u8> {
1501 let ip = u32::from_be_bytes(self.octets());
1502 // Validate that the netmask is contiguous (all 1s followed by all 0s).
1503 if ip.leading_ones() != ip.count_ones() {
1504 return Err(io::Error::new(
1505 io::ErrorKind::InvalidData,
1506 "invalid netmask",
1507 ));
1508 }
1509 Ok(ip.leading_ones() as u8)
1510 }
1511}
1512impl ToIpv4Netmask for String {
1513 fn prefix(&self) -> io::Result<u8> {
1514 ToIpv4Netmask::prefix(&self.as_str())
1515 }
1516}
1517impl ToIpv4Netmask for &str {
1518 fn prefix(&self) -> io::Result<u8> {
1519 match Ipv4Addr::from_str(self) {
1520 Ok(ip) => ip.prefix(),
1521 Err(_e) => Err(io::Error::new(
1522 io::ErrorKind::InvalidData,
1523 "invalid netmask str",
1524 )),
1525 }
1526 }
1527}
1528/// Trait for converting various types into an IPv6 netmask (prefix length).
1529pub trait ToIpv6Netmask {
1530 /// Returns the prefix length.
1531 fn prefix(&self) -> io::Result<u8>;
1532 /// Computes the IPv6 netmask based on the prefix length.
1533 fn netmask(&self) -> io::Result<Ipv6Addr> {
1534 let ip = u128::MAX
1535 .checked_shl(128 - self.prefix()? as u32)
1536 .unwrap_or(0);
1537 Ok(Ipv6Addr::from(ip))
1538 }
1539}
1540
1541impl ToIpv6Netmask for u8 {
1542 fn prefix(&self) -> io::Result<u8> {
1543 if *self > 128 {
1544 return Err(io::Error::new(
1545 io::ErrorKind::InvalidData,
1546 "invalid IP prefix length",
1547 ));
1548 }
1549 Ok(*self)
1550 }
1551}
1552
1553impl ToIpv6Netmask for Ipv6Addr {
1554 fn prefix(&self) -> io::Result<u8> {
1555 let ip = u128::from_be_bytes(self.octets());
1556 if ip.leading_ones() != ip.count_ones() {
1557 return Err(io::Error::new(
1558 io::ErrorKind::InvalidData,
1559 "invalid netmask",
1560 ));
1561 }
1562 Ok(ip.leading_ones() as u8)
1563 }
1564}
1565impl ToIpv6Netmask for String {
1566 fn prefix(&self) -> io::Result<u8> {
1567 ToIpv6Netmask::prefix(&self.as_str())
1568 }
1569}
1570impl ToIpv6Netmask for &str {
1571 fn prefix(&self) -> io::Result<u8> {
1572 match Ipv6Addr::from_str(self) {
1573 Ok(ip) => ip.prefix(),
1574 Err(_e) => Err(io::Error::new(
1575 io::ErrorKind::InvalidData,
1576 "invalid netmask str",
1577 )),
1578 }
1579 }
1580}