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 on Windows.
472    #[cfg(windows)]
473    pub fn metric(&mut self, metric: u16) -> &mut Self {
474        self.0.metric = Some(metric);
475        self
476    }
477    /// Whether to call `WintunDeleteDriver` to remove the driver.
478    /// Default: false.
479    /// # Note
480    /// The clean-up work closely depends on whether the destructor can be normally executed
481    #[cfg(windows)]
482    pub fn delete_driver(&mut self, delete_driver: bool) -> &mut Self {
483        self.0.delete_driver = Some(delete_driver);
484        self
485    }
486    /// Sets the transmit queue length on Linux.
487    #[cfg(target_os = "linux")]
488    pub fn tx_queue_len(&mut self, tx_queue_len: u32) -> &mut Self {
489        self.0.tx_queue_len = Some(tx_queue_len);
490        self
491    }
492    /// Enables TUN offloads on Linux.
493    /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
494    #[cfg(target_os = "linux")]
495    pub fn offload(&mut self, offload: bool) -> &mut Self {
496        self.0.offload = Some(offload);
497        self
498    }
499    /// Enables multi-queue support on Linux.
500    #[cfg(target_os = "linux")]
501    pub fn multi_queue(&mut self, multi_queue: bool) -> &mut Self {
502        self.0.multi_queue = Some(multi_queue);
503        self
504    }
505    /// Enables or disables packet information for the network driver(TUN)
506    /// on macOS, Linux, freebsd, openbsd, netbsd.
507    ///
508    /// This option is disabled by default (`false`).
509    /// # Note
510    /// There is no native way to enable/disable packet information on macOS.
511    /// The elimination of the packet information on macOS according to this setting
512    /// is processed by this library.
513    /// The set value `v` can be retrieved by `ignore_packet_info`, the returned value is `!v`.
514    #[cfg(any(
515        target_os = "macos",
516        target_os = "linux",
517        target_os = "freebsd",
518        target_os = "openbsd",
519        target_os = "netbsd"
520    ))]
521    pub fn packet_information(&mut self, packet_information: bool) -> &mut Self {
522        self.0.packet_information = Some(packet_information);
523        self
524    }
525    /// Available on Layer::L2;
526    /// creates a pair of `feth` devices, with `peer_feth` as the IO interface name.
527    #[cfg(target_os = "macos")]
528    pub fn peer_feth<S: Into<String>>(&mut self, peer_feth: S) -> &mut Self {
529        self.0.peer_feth = Some(peer_feth.into());
530        self
531    }
532    /// If true (default), the program will automatically add or remove routes on macOS or FreeBSD to provide consistent routing behavior across all platforms.
533    /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
534    /// Set this to be false to obtain the platform's default routing behavior.
535    #[cfg(any(
536        target_os = "macos",
537        target_os = "freebsd",
538        target_os = "openbsd",
539        target_os = "netbsd"
540    ))]
541    pub fn associate_route(&mut self, associate_route: bool) -> &mut Self {
542        self.0.associate_route = Some(associate_route);
543        self
544    }
545    /// Only works in TAP mode.
546    /// If true (default), the existing device with the given name will be used if possible.
547    /// If false, an error will be returned if a device with the specified name already exists.
548    #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
549    pub fn reuse_dev(&mut self, reuse: bool) -> &mut Self {
550        self.0.reuse_dev = Some(reuse);
551        self
552    }
553    /// Only works in TAP mode.
554    /// If true, the `feth` device will be kept after the program exits;
555    /// if false (default), the device will be destroyed automatically.
556    #[cfg(any(target_os = "macos", target_os = "windows"))]
557    pub fn persist(&mut self, persist: bool) -> &mut Self {
558        self.0.persist = Some(persist);
559        self
560    }
561}
562/// This is a unified constructor of a device for various platforms. The specification of every API can be found by looking at
563/// the documentation of the concrete platform.
564#[derive(Default)]
565pub struct DeviceBuilder {
566    dev_name: Option<String>,
567    #[cfg(windows)]
568    description: Option<String>,
569    #[cfg(target_os = "macos")]
570    peer_feth: Option<String>,
571    #[cfg(any(
572        target_os = "macos",
573        target_os = "freebsd",
574        target_os = "openbsd",
575        target_os = "netbsd"
576    ))]
577    associate_route: Option<bool>,
578    #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
579    reuse_dev: Option<bool>,
580    #[cfg(any(target_os = "macos", target_os = "windows"))]
581    persist: Option<bool>,
582    enabled: Option<bool>,
583    mtu: Option<u16>,
584    #[cfg(windows)]
585    mtu_v6: Option<u16>,
586    ipv4: Option<IPV4>,
587    ipv6: Option<Vec<(io::Result<Ipv6Addr>, io::Result<u8>)>>,
588    layer: Option<Layer>,
589    #[cfg(any(
590        target_os = "windows",
591        target_os = "linux",
592        target_os = "freebsd",
593        target_os = "openbsd",
594        target_os = "macos",
595        target_os = "netbsd"
596    ))]
597    mac_addr: Option<[u8; 6]>,
598    #[cfg(windows)]
599    device_guid: Option<u128>,
600    #[cfg(windows)]
601    wintun_log: Option<bool>,
602    #[cfg(windows)]
603    wintun_file: Option<String>,
604    #[cfg(windows)]
605    ring_capacity: Option<u32>,
606    #[cfg(windows)]
607    metric: Option<u16>,
608    #[cfg(windows)]
609    delete_driver: Option<bool>,
610    /// switch of Enable/Disable packet information for network driver
611    #[cfg(any(
612        target_os = "macos",
613        target_os = "linux",
614        target_os = "freebsd",
615        target_os = "openbsd",
616        target_os = "netbsd"
617    ))]
618    packet_information: Option<bool>,
619    #[cfg(target_os = "linux")]
620    tx_queue_len: Option<u32>,
621    /// Enable/Disable TUN offloads.
622    /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
623    #[cfg(target_os = "linux")]
624    offload: Option<bool>,
625    /// Enable multi queue support
626    #[cfg(target_os = "linux")]
627    multi_queue: Option<bool>,
628}
629
630impl DeviceBuilder {
631    /// Creates a new DeviceBuilder instance with default settings.
632    pub fn new() -> Self {
633        Self::default().enable(true)
634    }
635    /// Sets the device name.
636    pub fn name<S: Into<String>>(mut self, dev_name: S) -> Self {
637        self.dev_name = Some(dev_name.into());
638        self
639    }
640    /// Sets the device description (effective only on Windows L3 mode).
641    #[cfg(windows)]
642    pub fn description<S: Into<String>>(mut self, description: S) -> Self {
643        self.description = Some(description.into());
644        self
645    }
646    /// Sets the device MTU (Maximum Transmission Unit).
647    pub fn mtu(mut self, mtu: u16) -> Self {
648        self.mtu = Some(mtu);
649        #[cfg(windows)]
650        {
651            // On Windows, also set the MTU for IPv6.
652            self.mtu_v6 = Some(mtu);
653        }
654        self
655    }
656    /// Sets the IPv4 MTU specifically for Windows.
657    #[cfg(windows)]
658    pub fn mtu_v4(mut self, mtu: u16) -> Self {
659        self.mtu = Some(mtu);
660        self
661    }
662    /// Sets the IPv6 MTU specifically for Windows.
663    #[cfg(windows)]
664    pub fn mtu_v6(mut self, mtu: u16) -> Self {
665        self.mtu_v6 = Some(mtu);
666        self
667    }
668    /// Sets the MAC address for the device (effective only in L2 mode).
669    #[cfg(any(
670        target_os = "windows",
671        target_os = "linux",
672        target_os = "freebsd",
673        target_os = "openbsd",
674        target_os = "macos",
675        target_os = "netbsd"
676    ))]
677    pub fn mac_addr(mut self, mac_addr: [u8; 6]) -> Self {
678        self.mac_addr = Some(mac_addr);
679        self
680    }
681    /// Configures the IPv4 address for the device.
682    ///
683    /// - `address`: The IPv4 address of the device.
684    /// - `mask`: The subnet mask or prefix length.
685    /// - `destination`: Optional destination address for point-to-point links.
686    /// # Example
687    /// ```
688    /// use std::net::Ipv4Addr;
689    /// use tun_rs::DeviceBuilder;
690    /// DeviceBuilder::new().ipv4(Ipv4Addr::new(10, 0, 0, 12), 24, None);
691    /// ```
692    pub fn ipv4<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
693        mut self,
694        address: IPv4,
695        mask: Netmask,
696        destination: Option<IPv4>,
697    ) -> Self {
698        self.ipv4 = Some((address.ipv4(), mask.prefix(), destination.map(|v| v.ipv4())));
699        self
700    }
701    /// Configures a single IPv6 address for the device.
702    ///
703    /// - `address`: The IPv6 address.
704    /// - `mask`: The subnet mask or prefix length.
705    /// # Example
706    /// ```
707    /// use tun_rs::DeviceBuilder;
708    /// DeviceBuilder::new().ipv6("CDCD:910A:2222:5498:8475:1111:3900:2021", 64);
709    /// ```
710    pub fn ipv6<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
711        mut self,
712        address: IPv6,
713        mask: Netmask,
714    ) -> Self {
715        if let Some(v) = &mut self.ipv6 {
716            v.push((address.ipv6(), mask.prefix()));
717        } else {
718            self.ipv6 = Some(vec![(address.ipv6(), mask.prefix())]);
719        }
720
721        self
722    }
723    /// Configures multiple IPv6 addresses in batch.
724    ///
725    /// Accepts a slice of (IPv6 address, netmask) tuples.
726    /// # Example
727    /// ```rust
728    /// use tun_rs::DeviceBuilder;
729    /// DeviceBuilder::new().ipv6_tuple(&[
730    ///     ("CDCD:910A:2222:5498:8475:1111:3900:2022", 64),
731    ///     ("CDCD:910A:2222:5498:8475:1111:3900:2023", 64),
732    /// ]);
733    /// ```
734    pub fn ipv6_tuple<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
735        mut self,
736        addrs: &[(IPv6, Netmask)],
737    ) -> Self {
738        if let Some(v) = &mut self.ipv6 {
739            for (address, mask) in addrs {
740                v.push((address.ipv6(), mask.prefix()));
741            }
742        } else {
743            self.ipv6 = Some(
744                addrs
745                    .iter()
746                    .map(|(ip, mask)| (ip.ipv6(), mask.prefix()))
747                    .collect(),
748            );
749        }
750        self
751    }
752    /// Sets the operating layer (L2 or L3) for the device.
753    ///
754    /// * L2 corresponds to TAP
755    /// * L3 corresponds to TUN
756    pub fn layer(mut self, layer: Layer) -> Self {
757        self.layer = Some(layer);
758        self
759    }
760    /// Sets the device GUID on Windows.
761    /// By default, GUID is chosen by the system at random.
762    #[cfg(windows)]
763    pub fn device_guid(mut self, device_guid: u128) -> Self {
764        self.device_guid = Some(device_guid);
765        self
766    }
767    /// Enables or disables Wintun logging.
768    ///
769    /// By default, logging is disabled.
770    #[cfg(windows)]
771    pub fn wintun_log(mut self, wintun_log: bool) -> Self {
772        self.wintun_log = Some(wintun_log);
773        self
774    }
775    /// Sets the `wintun.dll` file path on Windows.
776    #[cfg(windows)]
777    pub fn wintun_file(mut self, wintun_file: String) -> Self {
778        self.wintun_file = Some(wintun_file);
779        self
780    }
781    /// Sets the ring capacity on Windows.
782    /// This specifies the capacity of the packet ring buffer in bytes.
783    /// By default, the ring capacity is set to `0x20_0000` (2 MB).
784    #[cfg(windows)]
785    pub fn ring_capacity(mut self, ring_capacity: u32) -> Self {
786        self.ring_capacity = Some(ring_capacity);
787        self
788    }
789    /// Sets the routing metric on Windows.
790    #[cfg(windows)]
791    pub fn metric(mut self, metric: u16) -> Self {
792        self.metric = Some(metric);
793        self
794    }
795    /// Whether to call `WintunDeleteDriver` to remove the driver.
796    /// Default: false.
797    /// # Note
798    /// The clean-up work closely depends on whether the destructor can be normally executed
799    #[cfg(windows)]
800    pub fn delete_driver(mut self, delete_driver: bool) -> Self {
801        self.delete_driver = Some(delete_driver);
802        self
803    }
804    /// Sets the transmit queue length on Linux.
805    #[cfg(target_os = "linux")]
806    pub fn tx_queue_len(mut self, tx_queue_len: u32) -> Self {
807        self.tx_queue_len = Some(tx_queue_len);
808        self
809    }
810    /// Enables TUN offloads on Linux.
811    /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
812    #[cfg(target_os = "linux")]
813    pub fn offload(mut self, offload: bool) -> Self {
814        self.offload = Some(offload);
815        self
816    }
817    /// Enables multi-queue support on Linux.
818    #[cfg(target_os = "linux")]
819    pub fn multi_queue(mut self, multi_queue: bool) -> Self {
820        self.multi_queue = Some(multi_queue);
821        self
822    }
823    /// Enables or disables packet information for the network driver(TUN)
824    /// on macOS, Linux, freebsd, openbsd, netbsd.
825    ///
826    /// This option is disabled by default (`false`).
827    /// # Note
828    /// There is no native way to enable/disable packet information on macOS.
829    /// The elimination of the packet information on macOS according to this setting
830    /// is processed by this library.
831    /// The set value `v` can be retrieved by `ignore_packet_info`, the returned value is `!v`.
832    #[cfg(any(
833        target_os = "macos",
834        target_os = "linux",
835        target_os = "freebsd",
836        target_os = "openbsd",
837        target_os = "netbsd"
838    ))]
839    pub fn packet_information(mut self, packet_information: bool) -> Self {
840        self.packet_information = Some(packet_information);
841        self
842    }
843    /// Available on Layer::L2;
844    /// creates a pair of `feth` devices, with `peer_feth` as the IO interface name.
845    #[cfg(target_os = "macos")]
846    pub fn peer_feth<S: Into<String>>(mut self, peer_feth: S) -> Self {
847        self.peer_feth = Some(peer_feth.into());
848        self
849    }
850    /// If true (default), the program will automatically add or remove routes on macOS or FreeBSD to provide consistent routing behavior across all platforms.
851    /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
852    /// Set this to be false to obtain the platform's default routing behavior.
853    #[cfg(any(
854        target_os = "macos",
855        target_os = "freebsd",
856        target_os = "openbsd",
857        target_os = "netbsd"
858    ))]
859    pub fn associate_route(mut self, associate_route: bool) -> Self {
860        self.associate_route = Some(associate_route);
861        self
862    }
863    /// Only works in TAP mode.
864    /// If true (default), the existing device with the given name will be used if possible.
865    /// If false, an error will be returned if a device with the specified name already exists.
866    #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
867    pub fn reuse_dev(mut self, reuse: bool) -> Self {
868        self.reuse_dev = Some(reuse);
869        self
870    }
871    /// Only works in TAP mode.
872    /// If true, the `feth` device will be kept after the program exits;
873    /// if false (default), the device will be destroyed automatically.
874    #[cfg(any(target_os = "macos", target_os = "windows"))]
875    pub fn persist(mut self, persist: bool) -> Self {
876        self.persist = Some(persist);
877        self
878    }
879    /// Enables or disables the device.
880    /// Defaults to be enabled.
881    pub fn enable(mut self, enable: bool) -> Self {
882        self.enabled = Some(enable);
883        self
884    }
885
886    /// Keeps the device enable state unchanged.
887    ///
888    /// This method does not explicitly enable or disable the device.
889    /// The existing system state is preserved.
890    pub fn inherit_enable_state(mut self) -> Self {
891        self.enabled = None;
892        self
893    }
894    pub(crate) fn build_config(&mut self) -> DeviceConfig {
895        DeviceConfig {
896            dev_name: self.dev_name.take(),
897            #[cfg(windows)]
898            description: self.description.take(),
899            #[cfg(target_os = "macos")]
900            peer_feth: self.peer_feth.take(),
901            #[cfg(any(
902                target_os = "macos",
903                target_os = "freebsd",
904                target_os = "openbsd",
905                target_os = "netbsd"
906            ))]
907            associate_route: self.associate_route,
908            #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
909            reuse_dev: self.reuse_dev,
910            #[cfg(any(target_os = "macos", target_os = "windows"))]
911            persist: self.persist,
912            layer: self.layer.take(),
913            #[cfg(windows)]
914            device_guid: self.device_guid.take(),
915            #[cfg(windows)]
916            wintun_log: self.wintun_log.take(),
917            #[cfg(windows)]
918            wintun_file: self.wintun_file.take(),
919            #[cfg(windows)]
920            ring_capacity: self.ring_capacity.take(),
921            #[cfg(windows)]
922            delete_driver: self.delete_driver.take(),
923            #[cfg(windows)]
924            mac_address: self.mac_addr.map(|v| {
925                use std::fmt::Write;
926                v.iter()
927                    .fold(String::with_capacity(v.len() * 2), |mut s, b| {
928                        write!(&mut s, "{b:02X}").unwrap();
929                        s
930                    })
931            }),
932            #[cfg(any(
933                target_os = "macos",
934                target_os = "linux",
935                target_os = "freebsd",
936                target_os = "openbsd",
937                target_os = "netbsd"
938            ))]
939            packet_information: self.packet_information.take(),
940            #[cfg(target_os = "linux")]
941            offload: self.offload.take(),
942            #[cfg(target_os = "linux")]
943            multi_queue: self.multi_queue.take(),
944        }
945    }
946    pub(crate) fn config(self, device: &DeviceImpl) -> io::Result<()> {
947        if let Some(mtu) = self.mtu {
948            device.set_mtu(mtu)?;
949        }
950        #[cfg(windows)]
951        if let Some(mtu) = self.mtu_v6 {
952            device.set_mtu_v6(mtu)?;
953        }
954        #[cfg(windows)]
955        if let Some(metric) = self.metric {
956            device.set_metric(metric)?;
957        }
958        #[cfg(target_os = "linux")]
959        if let Some(tx_queue_len) = self.tx_queue_len {
960            device.set_tx_queue_len(tx_queue_len)?;
961        }
962        #[cfg(any(
963            target_os = "linux",
964            target_os = "freebsd",
965            target_os = "macos",
966            target_os = "openbsd",
967            target_os = "netbsd"
968        ))]
969        if let Some(mac_addr) = self.mac_addr {
970            device.set_mac_address(mac_addr)?;
971        }
972
973        if let Some((address, prefix, destination)) = self.ipv4 {
974            let prefix = prefix?;
975            let address = address?;
976            let destination = destination.transpose()?;
977            device.set_network_address(address, prefix, destination)?;
978        }
979        if let Some(ipv6) = self.ipv6 {
980            for (address, prefix) in ipv6 {
981                let prefix = prefix?;
982                let address = address?;
983                device.add_address_v6(address, prefix)?;
984            }
985        }
986        if let Some(enabled) = self.enabled {
987            device.enabled(enabled)?;
988        }
989        Ok(())
990    }
991    /// Builds a synchronous device instance and applies all configuration parameters.
992    pub fn build_sync(mut self) -> io::Result<SyncDevice> {
993        let device = DeviceImpl::new(self.build_config())?;
994        self.config(&device)?;
995        Ok(SyncDevice(device))
996    }
997    /// Builds an asynchronous device instance.
998    ///
999    /// This method is available only when either async_io or async_tokio feature is enabled.
1000    ///
1001    /// # Note
1002    /// Choose one of the two async runtimes; otherwise, a compile error will be incurred if both are enabled.
1003    #[cfg(any(feature = "async_io", feature = "async_tokio"))]
1004    pub fn build_async(self) -> io::Result<crate::AsyncDevice> {
1005        let sync_device = self.build_sync()?;
1006        let device = crate::AsyncDevice::new_dev(sync_device.0)?;
1007        Ok(device)
1008    }
1009    /// To conveniently set the platform-specific parameters without breaking the calling chain.
1010    /// # Ergonomic
1011    ///
1012    /// For example:
1013    /// ````no_run
1014    /// use tun_rs::DeviceBuilder;
1015    /// let builder = DeviceBuilder::new().name("tun1");
1016    /// #[cfg(target_os = "macos")]
1017    /// let builder = builder.associate_route(false);
1018    /// #[cfg(windows)]
1019    /// let builder = builder.wintun_log(false);
1020    /// let dev = builder.build_sync().unwrap();
1021    /// ````
1022    /// This is tedious and breaks the calling chain.
1023    ///
1024    /// With `with`, we can just set platform-specific parameters as follows without breaking the calling chain:
1025    /// ````no_run
1026    /// use tun_rs::DeviceBuilder;
1027    /// let dev = DeviceBuilder::new().name("tun1").with(|opt|{
1028    ///    #[cfg(windows)]
1029    ///    opt.wintun_log(false);
1030    ///    #[cfg(target_os = "macos")]
1031    ///    opt.associate_route(false).packet_information(false);
1032    /// }).build_sync().unwrap();
1033    /// ````
1034    pub fn with<F: FnMut(&mut DeviceBuilderGuard)>(mut self, mut f: F) -> Self {
1035        let mut borrow = DeviceBuilderGuard(&mut self);
1036        f(&mut borrow);
1037        self
1038    }
1039}
1040
1041/// Trait for converting various types into an IPv4 address.
1042pub trait ToIpv4Address {
1043    /// Attempts to convert the implementing type into an `Ipv4Addr`.
1044    /// Returns the IPv4 address on success or an error on failure.
1045    fn ipv4(&self) -> io::Result<Ipv4Addr>;
1046}
1047impl ToIpv4Address for Ipv4Addr {
1048    fn ipv4(&self) -> io::Result<Ipv4Addr> {
1049        Ok(*self)
1050    }
1051}
1052impl ToIpv4Address for IpAddr {
1053    fn ipv4(&self) -> io::Result<Ipv4Addr> {
1054        match self {
1055            IpAddr::V4(ip) => Ok(*ip),
1056            IpAddr::V6(_) => Err(io::Error::new(
1057                io::ErrorKind::InvalidData,
1058                "invalid address",
1059            )),
1060        }
1061    }
1062}
1063impl ToIpv4Address for String {
1064    fn ipv4(&self) -> io::Result<Ipv4Addr> {
1065        self.as_str().ipv4()
1066    }
1067}
1068impl ToIpv4Address for &str {
1069    fn ipv4(&self) -> io::Result<Ipv4Addr> {
1070        match Ipv4Addr::from_str(self) {
1071            Ok(ip) => Ok(ip),
1072            Err(_e) => Err(io::Error::new(
1073                io::ErrorKind::InvalidData,
1074                "invalid IPv4 str",
1075            )),
1076        }
1077    }
1078}
1079
1080/// Trait for converting various types into an IPv6 address.
1081pub trait ToIpv6Address {
1082    /// Attempts to convert the implementing type into an `Ipv6Addr`.
1083    /// Returns the IPv6 address on success or an error on failure.
1084    fn ipv6(&self) -> io::Result<Ipv6Addr>;
1085}
1086
1087impl ToIpv6Address for Ipv6Addr {
1088    fn ipv6(&self) -> io::Result<Ipv6Addr> {
1089        Ok(*self)
1090    }
1091}
1092impl ToIpv6Address for IpAddr {
1093    fn ipv6(&self) -> io::Result<Ipv6Addr> {
1094        match self {
1095            IpAddr::V4(_) => Err(io::Error::new(
1096                io::ErrorKind::InvalidData,
1097                "invalid address",
1098            )),
1099            IpAddr::V6(ip) => Ok(*ip),
1100        }
1101    }
1102}
1103impl ToIpv6Address for String {
1104    fn ipv6(&self) -> io::Result<Ipv6Addr> {
1105        self.as_str().ipv6()
1106    }
1107}
1108impl ToIpv6Address for &str {
1109    fn ipv6(&self) -> io::Result<Ipv6Addr> {
1110        match Ipv6Addr::from_str(self) {
1111            Ok(ip) => Ok(ip),
1112            Err(_e) => Err(io::Error::new(
1113                io::ErrorKind::InvalidData,
1114                "invalid IPv6 str",
1115            )),
1116        }
1117    }
1118}
1119/// Trait for converting various types into an IPv4 netmask (prefix length).
1120pub trait ToIpv4Netmask {
1121    /// Returns the prefix length (i.e., the number of consecutive 1s in the netmask).
1122    fn prefix(&self) -> io::Result<u8>;
1123    /// Computes the IPv4 netmask based on the prefix length.
1124    fn netmask(&self) -> io::Result<Ipv4Addr> {
1125        let ip = u32::MAX
1126            .checked_shl(32 - self.prefix()? as u32)
1127            .unwrap_or(0);
1128        Ok(Ipv4Addr::from(ip))
1129    }
1130}
1131
1132impl ToIpv4Netmask for u8 {
1133    fn prefix(&self) -> io::Result<u8> {
1134        if *self > 32 {
1135            return Err(io::Error::new(
1136                io::ErrorKind::InvalidData,
1137                "invalid IP prefix length",
1138            ));
1139        }
1140        Ok(*self)
1141    }
1142}
1143
1144impl ToIpv4Netmask for Ipv4Addr {
1145    fn prefix(&self) -> io::Result<u8> {
1146        let ip = u32::from_be_bytes(self.octets());
1147        // Validate that the netmask is contiguous (all 1s followed by all 0s).
1148        if ip.leading_ones() != ip.count_ones() {
1149            return Err(io::Error::new(
1150                io::ErrorKind::InvalidData,
1151                "invalid netmask",
1152            ));
1153        }
1154        Ok(ip.leading_ones() as u8)
1155    }
1156}
1157impl ToIpv4Netmask for String {
1158    fn prefix(&self) -> io::Result<u8> {
1159        ToIpv4Netmask::prefix(&self.as_str())
1160    }
1161}
1162impl ToIpv4Netmask for &str {
1163    fn prefix(&self) -> io::Result<u8> {
1164        match Ipv4Addr::from_str(self) {
1165            Ok(ip) => ip.prefix(),
1166            Err(_e) => Err(io::Error::new(
1167                io::ErrorKind::InvalidData,
1168                "invalid netmask str",
1169            )),
1170        }
1171    }
1172}
1173/// Trait for converting various types into an IPv6 netmask (prefix length).
1174pub trait ToIpv6Netmask {
1175    /// Returns the prefix length.
1176    fn prefix(&self) -> io::Result<u8>;
1177    /// Computes the IPv6 netmask based on the prefix length.
1178    fn netmask(&self) -> io::Result<Ipv6Addr> {
1179        let ip = u128::MAX
1180            .checked_shl(128 - self.prefix()? as u32)
1181            .unwrap_or(0);
1182        Ok(Ipv6Addr::from(ip))
1183    }
1184}
1185
1186impl ToIpv6Netmask for u8 {
1187    fn prefix(&self) -> io::Result<u8> {
1188        if *self > 128 {
1189            return Err(io::Error::new(
1190                io::ErrorKind::InvalidData,
1191                "invalid IP prefix length",
1192            ));
1193        }
1194        Ok(*self)
1195    }
1196}
1197
1198impl ToIpv6Netmask for Ipv6Addr {
1199    fn prefix(&self) -> io::Result<u8> {
1200        let ip = u128::from_be_bytes(self.octets());
1201        if ip.leading_ones() != ip.count_ones() {
1202            return Err(io::Error::new(
1203                io::ErrorKind::InvalidData,
1204                "invalid netmask",
1205            ));
1206        }
1207        Ok(ip.leading_ones() as u8)
1208    }
1209}
1210impl ToIpv6Netmask for String {
1211    fn prefix(&self) -> io::Result<u8> {
1212        ToIpv6Netmask::prefix(&self.as_str())
1213    }
1214}
1215impl ToIpv6Netmask for &str {
1216    fn prefix(&self) -> io::Result<u8> {
1217        match Ipv6Addr::from_str(self) {
1218            Ok(ip) => ip.prefix(),
1219            Err(_e) => Err(io::Error::new(
1220                io::ErrorKind::InvalidData,
1221                "invalid netmask str",
1222            )),
1223        }
1224    }
1225}