Skip to main content

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}