tun_rs/
builder.rs

1use std::io;
2use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3use std::str::FromStr;
4
5use crate::platform::{DeviceImpl, SyncDevice};
6
7/// Represents the OSI layer at which the TUN interface operates.
8///
9/// - **L2**: Data Link Layer (available on Windows, Linux, and FreeBSD; used for TAP interfaces).
10/// - **L3**: Network Layer (default for TUN interfaces).
11#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
12#[non_exhaustive]
13pub enum Layer {
14    /// Data Link Layer.
15    #[cfg(any(
16        target_os = "windows",
17        target_os = "linux",
18        target_os = "freebsd",
19        target_os = "macos",
20        target_os = "openbsd",
21        target_os = "netbsd",
22    ))]
23    L2,
24    /// Network Layer (default for TUN interfaces).
25    #[default]
26    L3,
27}
28
29/// Configuration for a TUN/TAP interface.
30///
31/// This structure stores settings such as the device name, operating layer,
32/// and platform-specific parameters (e.g., GUID, wintun file, ring capacity on Windows).
33#[derive(Clone, Default, Debug)]
34pub(crate) struct DeviceConfig {
35    /// The name of the device/interface.
36    pub(crate) dev_name: Option<String>,
37    /// The description of the device/interface.
38    #[cfg(windows)]
39    pub(crate) description: Option<String>,
40    /// Available with Layer::L2; creates a pair of feth devices, with peer_feth as the IO interface name.
41    #[cfg(target_os = "macos")]
42    pub(crate) peer_feth: Option<String>,
43    /// If true (default), the program will automatically add or remove routes on macOS or FreeBSD to provide consistent routing behavior across all platforms.
44    /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
45    /// Set this to be false to obtain the platform's default routing behavior.
46    #[cfg(any(
47        target_os = "macos",
48        target_os = "freebsd",
49        target_os = "openbsd",
50        target_os = "netbsd"
51    ))]
52    pub(crate) associate_route: Option<bool>,
53    /// If true (default), the existing device with the given name will be used if possible.
54    /// If false, an error will be returned if a device with the specified name already exists.
55    #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
56    pub(crate) reuse_dev: Option<bool>,
57    /// If true, the feth device will be kept after the program exits;
58    /// if false (default), the device will be destroyed automatically.
59    #[cfg(any(target_os = "macos", target_os = "windows"))]
60    pub(crate) persist: Option<bool>,
61    /// Specifies whether the interface operates at L2 or L3.
62    #[allow(dead_code)]
63    pub(crate) layer: Option<Layer>,
64    /// Device GUID on Windows.
65    #[cfg(windows)]
66    pub(crate) device_guid: Option<u128>,
67    #[cfg(windows)]
68    pub(crate) wintun_log: Option<bool>,
69    /// Path to the wintun file on Windows.
70    #[cfg(windows)]
71    pub(crate) wintun_file: Option<String>,
72    /// Capacity of the ring buffer on Windows.
73    #[cfg(windows)]
74    pub(crate) ring_capacity: Option<u32>,
75    /// Whether to call WintunDeleteDriver to remove the driver.
76    /// Default: false.
77    #[cfg(windows)]
78    pub(crate) delete_driver: Option<bool>,
79    #[cfg(windows)]
80    pub(crate) mac_address: Option<String>,
81    /// switch of Enable/Disable packet information for network driver
82    #[cfg(any(
83        target_os = "macos",
84        target_os = "linux",
85        target_os = "freebsd",
86        target_os = "netbsd"
87    ))]
88    pub(crate) packet_information: Option<bool>,
89    /// Enable/Disable TUN offloads.
90    /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
91    #[cfg(target_os = "linux")]
92    pub(crate) offload: Option<bool>,
93    /// Enable multi queue support
94    #[cfg(target_os = "linux")]
95    pub(crate) multi_queue: Option<bool>,
96}
97type IPV4 = (
98    io::Result<Ipv4Addr>,
99    io::Result<u8>,
100    Option<io::Result<Ipv4Addr>>,
101);
102/// A builder for configuring a TUN/TAP interface.
103///
104/// This builder allows you to set parameters such as device name, MTU,
105/// IPv4/IPv6 addresses, MAC address, and other platform-specific options.
106///
107/// # Examples
108///
109/// Creating a basic IPv4 TUN interface:
110///
111/// ````no_run
112/// use std::net::Ipv4Addr;
113/// use tun_rs::DeviceBuilder;
114///
115/// fn main() -> std::io::Result<()> {
116///     let tun = DeviceBuilder::new()
117///         .name("my-tun")
118///         .mtu(1500)
119///         .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
120///         .build_sync()?;
121///     Ok(())
122/// }
123/// ````
124///
125/// Creating an IPv6 TUN interface:
126///
127/// ````no_run
128/// use std::net::Ipv6Addr;
129/// use tun_rs::DeviceBuilder;
130///
131/// fn main() -> std::io::Result<()> {
132///     let tun = DeviceBuilder::new()
133///         .name("my-tun6")
134///         .mtu(1500)
135///         .ipv6(Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 1), 64)
136///         .build_sync()?;
137///     Ok(())
138/// }
139/// ````
140///
141/// Creating an L2 TAP interface (platform-dependent):
142///
143/// ````no_run
144/// #[cfg(any(
145///     target_os = "windows",
146///     all(target_os = "linux", not(target_env = "ohos")),
147///     target_os = "freebsd",
148///     target_os = "macos",
149///     target_os = "openbsd",
150///     target_os = "netbsd"
151/// ))]
152/// use tun_rs::{DeviceBuilder, Layer};
153///
154/// #[cfg(any(
155///     target_os = "windows",
156///     all(target_os = "linux", not(target_env = "ohos")),
157///     target_os = "freebsd",
158///     target_os = "macos",
159///     target_os = "openbsd",
160///     target_os = "netbsd"
161/// ))]
162/// fn main() -> std::io::Result<()> {
163///     let tap = DeviceBuilder::new()
164///         .name("my-tap")
165///         .layer(Layer::L2)
166///         .mac_addr([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])
167///         .mtu(1500)
168///         .build_sync()?;
169///     Ok(())
170/// }
171/// ````
172#[doc(hidden)]
173pub struct DeviceBuilderGuard<'a>(&'a mut DeviceBuilder);
174
175#[doc(hidden)]
176impl DeviceBuilderGuard<'_> {
177    /// Sets the device description (effective only on Windows L3 mode).
178    #[cfg(windows)]
179    pub fn description<S: Into<String>>(&mut self, description: S) -> &mut Self {
180        self.0.description = Some(description.into());
181        self
182    }
183
184    /// Sets the IPv4 MTU specifically for Windows.
185    #[cfg(windows)]
186    pub fn mtu_v4(&mut self, mtu: u16) -> &mut Self {
187        self.0.mtu = Some(mtu);
188        self
189    }
190    /// Sets the IPv6 MTU specifically for Windows.
191    #[cfg(windows)]
192    pub fn mtu_v6(&mut self, mtu: u16) -> &mut Self {
193        self.0.mtu_v6 = Some(mtu);
194        self
195    }
196    /// Sets the MAC address for the device (effective only in L2 mode).
197    #[cfg(any(
198        target_os = "windows",
199        target_os = "linux",
200        target_os = "freebsd",
201        target_os = "openbsd",
202        target_os = "macos",
203        target_os = "netbsd"
204    ))]
205    pub fn mac_addr(&mut self, mac_addr: [u8; 6]) -> &mut Self {
206        self.0.mac_addr = Some(mac_addr);
207        self
208    }
209
210    /// Sets the device GUID on Windows.
211    /// By default, GUID is chosen by the system at random.
212    #[cfg(windows)]
213    pub fn device_guid(&mut self, device_guid: u128) -> &mut Self {
214        self.0.device_guid = Some(device_guid);
215        self
216    }
217    /// Enables or disables Wintun logging.
218    ///
219    /// By default, logging is disabled.
220    #[cfg(windows)]
221    pub fn wintun_log(&mut self, wintun_log: bool) -> &mut Self {
222        self.0.wintun_log = Some(wintun_log);
223        self
224    }
225    /// Sets the `wintun.dll` file path on Windows.
226    #[cfg(windows)]
227    pub fn wintun_file(&mut self, wintun_file: String) -> &mut Self {
228        self.0.wintun_file = Some(wintun_file);
229        self
230    }
231    /// Sets the ring capacity on Windows.
232    /// This specifies the capacity of the packet ring buffer in bytes.
233    /// By default, the ring capacity is set to `0x20_0000` (2 MB).
234    #[cfg(windows)]
235    pub fn ring_capacity(&mut self, ring_capacity: u32) -> &mut Self {
236        self.0.ring_capacity = Some(ring_capacity);
237        self
238    }
239    /// Sets the routing metric on Windows.
240    #[cfg(windows)]
241    pub fn metric(&mut self, metric: u16) -> &mut Self {
242        self.0.metric = Some(metric);
243        self
244    }
245    /// Whether to call `WintunDeleteDriver` to remove the driver.
246    /// Default: false.
247    /// # Note
248    /// The clean-up work closely depends on whether the destructor can be normally executed
249    #[cfg(windows)]
250    pub fn delete_driver(&mut self, delete_driver: bool) -> &mut Self {
251        self.0.delete_driver = Some(delete_driver);
252        self
253    }
254    /// Sets the transmit queue length on Linux.
255    #[cfg(target_os = "linux")]
256    pub fn tx_queue_len(&mut self, tx_queue_len: u32) -> &mut Self {
257        self.0.tx_queue_len = Some(tx_queue_len);
258        self
259    }
260    /// Enables TUN offloads on Linux.
261    /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
262    #[cfg(target_os = "linux")]
263    pub fn offload(&mut self, offload: bool) -> &mut Self {
264        self.0.offload = Some(offload);
265        self
266    }
267    /// Enables multi-queue support on Linux.
268    #[cfg(target_os = "linux")]
269    pub fn multi_queue(&mut self, multi_queue: bool) -> &mut Self {
270        self.0.multi_queue = Some(multi_queue);
271        self
272    }
273    /// Enables or disables packet information for the network driver
274    /// on macOS, Linux.
275    ///
276    /// This option is disabled by default (`false`).
277    /// # Note
278    /// There is no native way to enable/disable packet information on macOS.
279    /// The elimination of the packet information on macOS according to this setting
280    /// is processed by this library.
281    #[cfg(any(
282        target_os = "macos",
283        target_os = "linux",
284        target_os = "freebsd",
285        target_os = "netbsd"
286    ))]
287    pub fn packet_information(&mut self, packet_information: bool) -> &mut Self {
288        self.0.packet_information = Some(packet_information);
289        self
290    }
291    /// Available on Layer::L2;
292    /// creates a pair of `feth` devices, with `peer_feth` as the IO interface name.
293    #[cfg(target_os = "macos")]
294    pub fn peer_feth<S: Into<String>>(&mut self, peer_feth: S) -> &mut Self {
295        self.0.peer_feth = Some(peer_feth.into());
296        self
297    }
298    /// If true (default), the program will automatically add or remove routes on macOS or FreeBSD to provide consistent routing behavior across all platforms.
299    /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
300    /// Set this to be false to obtain the platform's default routing behavior.
301    #[cfg(any(
302        target_os = "macos",
303        target_os = "freebsd",
304        target_os = "openbsd",
305        target_os = "netbsd"
306    ))]
307    pub fn associate_route(&mut self, associate_route: bool) -> &mut Self {
308        self.0.associate_route = Some(associate_route);
309        self
310    }
311    /// Only works in TAP mode.
312    /// If true (default), the existing device with the given name will be used if possible.
313    /// If false, an error will be returned if a device with the specified name already exists.
314    #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
315    pub fn reuse_dev(&mut self, reuse: bool) -> &mut Self {
316        self.0.reuse_dev = Some(reuse);
317        self
318    }
319    /// Only works in TAP mode.
320    /// If true, the `feth` device will be kept after the program exits;
321    /// if false (default), the device will be destroyed automatically.
322    #[cfg(any(target_os = "macos", target_os = "windows"))]
323    pub fn persist(&mut self, persist: bool) -> &mut Self {
324        self.0.persist = Some(persist);
325        self
326    }
327}
328/// This is a unified constructor of a device for various platforms. The specification of every API can be found by looking at
329/// the documentation of the concrete platform.
330#[derive(Default)]
331pub struct DeviceBuilder {
332    dev_name: Option<String>,
333    #[cfg(windows)]
334    description: Option<String>,
335    #[cfg(target_os = "macos")]
336    peer_feth: Option<String>,
337    #[cfg(any(
338        target_os = "macos",
339        target_os = "freebsd",
340        target_os = "openbsd",
341        target_os = "netbsd"
342    ))]
343    associate_route: Option<bool>,
344    #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
345    reuse_dev: Option<bool>,
346    #[cfg(any(target_os = "macos", target_os = "windows"))]
347    persist: Option<bool>,
348    enabled: Option<bool>,
349    mtu: Option<u16>,
350    #[cfg(windows)]
351    mtu_v6: Option<u16>,
352    ipv4: Option<IPV4>,
353    ipv6: Option<Vec<(io::Result<Ipv6Addr>, io::Result<u8>)>>,
354    layer: Option<Layer>,
355    #[cfg(any(
356        target_os = "windows",
357        target_os = "linux",
358        target_os = "freebsd",
359        target_os = "openbsd",
360        target_os = "macos",
361        target_os = "netbsd"
362    ))]
363    mac_addr: Option<[u8; 6]>,
364    #[cfg(windows)]
365    device_guid: Option<u128>,
366    #[cfg(windows)]
367    wintun_log: Option<bool>,
368    #[cfg(windows)]
369    wintun_file: Option<String>,
370    #[cfg(windows)]
371    ring_capacity: Option<u32>,
372    #[cfg(windows)]
373    metric: Option<u16>,
374    #[cfg(windows)]
375    delete_driver: Option<bool>,
376    /// switch of Enable/Disable packet information for network driver
377    #[cfg(any(
378        target_os = "macos",
379        target_os = "linux",
380        target_os = "freebsd",
381        target_os = "netbsd"
382    ))]
383    packet_information: Option<bool>,
384    #[cfg(target_os = "linux")]
385    tx_queue_len: Option<u32>,
386    /// Enable/Disable TUN offloads.
387    /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
388    #[cfg(target_os = "linux")]
389    offload: Option<bool>,
390    /// Enable multi queue support
391    #[cfg(target_os = "linux")]
392    multi_queue: Option<bool>,
393}
394
395impl DeviceBuilder {
396    /// Creates a new DeviceBuilder instance with default settings.
397    pub fn new() -> Self {
398        Self::default()
399    }
400    /// Sets the device name.
401    pub fn name<S: Into<String>>(mut self, dev_name: S) -> Self {
402        self.dev_name = Some(dev_name.into());
403        self
404    }
405    /// Sets the device description (effective only on Windows L3 mode).
406    #[cfg(windows)]
407    pub fn description<S: Into<String>>(mut self, description: S) -> Self {
408        self.description = Some(description.into());
409        self
410    }
411    /// Sets the device MTU (Maximum Transmission Unit).
412    pub fn mtu(mut self, mtu: u16) -> Self {
413        self.mtu = Some(mtu);
414        #[cfg(windows)]
415        {
416            // On Windows, also set the MTU for IPv6.
417            self.mtu_v6 = Some(mtu);
418        }
419        self
420    }
421    /// Sets the IPv4 MTU specifically for Windows.
422    #[cfg(windows)]
423    pub fn mtu_v4(mut self, mtu: u16) -> Self {
424        self.mtu = Some(mtu);
425        self
426    }
427    /// Sets the IPv6 MTU specifically for Windows.
428    #[cfg(windows)]
429    pub fn mtu_v6(mut self, mtu: u16) -> Self {
430        self.mtu_v6 = Some(mtu);
431        self
432    }
433    /// Sets the MAC address for the device (effective only in L2 mode).
434    #[cfg(any(
435        target_os = "windows",
436        target_os = "linux",
437        target_os = "freebsd",
438        target_os = "openbsd",
439        target_os = "macos",
440        target_os = "netbsd"
441    ))]
442    pub fn mac_addr(mut self, mac_addr: [u8; 6]) -> Self {
443        self.mac_addr = Some(mac_addr);
444        self
445    }
446    /// Configures the IPv4 address for the device.
447    ///
448    /// - `address`: The IPv4 address of the device.
449    /// - `mask`: The subnet mask or prefix length.
450    /// - `destination`: Optional destination address for point-to-point links.
451    /// # Example
452    /// ```
453    /// use std::net::Ipv4Addr;
454    /// use tun_rs::DeviceBuilder;
455    /// DeviceBuilder::new().ipv4(Ipv4Addr::new(10, 0, 0, 12), 24, None);
456    /// ```
457    pub fn ipv4<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
458        mut self,
459        address: IPv4,
460        mask: Netmask,
461        destination: Option<IPv4>,
462    ) -> Self {
463        self.ipv4 = Some((address.ipv4(), mask.prefix(), destination.map(|v| v.ipv4())));
464        self
465    }
466    /// Configures a single IPv6 address for the device.
467    ///
468    /// - `address`: The IPv6 address.
469    /// - `mask`: The subnet mask or prefix length.
470    /// # Example
471    /// ```
472    /// use tun_rs::DeviceBuilder;
473    /// DeviceBuilder::new().ipv6("CDCD:910A:2222:5498:8475:1111:3900:2021", 64);
474    /// ```
475    pub fn ipv6<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
476        mut self,
477        address: IPv6,
478        mask: Netmask,
479    ) -> Self {
480        if let Some(v) = &mut self.ipv6 {
481            v.push((address.ipv6(), mask.prefix()));
482        } else {
483            self.ipv6 = Some(vec![(address.ipv6(), mask.prefix())]);
484        }
485
486        self
487    }
488    /// Configures multiple IPv6 addresses in batch.
489    ///
490    /// Accepts a slice of (IPv6 address, netmask) tuples.
491    /// # Example
492    /// ```rust
493    /// use tun_rs::DeviceBuilder;
494    /// DeviceBuilder::new().ipv6_tuple(&[
495    ///     ("CDCD:910A:2222:5498:8475:1111:3900:2022", 64),
496    ///     ("CDCD:910A:2222:5498:8475:1111:3900:2023", 64),
497    /// ]);
498    /// ```
499    pub fn ipv6_tuple<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
500        mut self,
501        addrs: &[(IPv6, Netmask)],
502    ) -> Self {
503        if let Some(v) = &mut self.ipv6 {
504            for (address, mask) in addrs {
505                v.push((address.ipv6(), mask.prefix()));
506            }
507        } else {
508            self.ipv6 = Some(
509                addrs
510                    .iter()
511                    .map(|(ip, mask)| (ip.ipv6(), mask.prefix()))
512                    .collect(),
513            );
514        }
515        self
516    }
517    /// Sets the operating layer (L2 or L3) for the device.
518    ///
519    /// * L2 corresponds to TAP
520    /// * L3 corresponds to TUN
521    pub fn layer(mut self, layer: Layer) -> Self {
522        self.layer = Some(layer);
523        self
524    }
525    /// Sets the device GUID on Windows.
526    /// By default, GUID is chosen by the system at random.
527    #[cfg(windows)]
528    pub fn device_guid(mut self, device_guid: u128) -> Self {
529        self.device_guid = Some(device_guid);
530        self
531    }
532    /// Enables or disables Wintun logging.
533    ///
534    /// By default, logging is disabled.
535    #[cfg(windows)]
536    pub fn wintun_log(mut self, wintun_log: bool) -> Self {
537        self.wintun_log = Some(wintun_log);
538        self
539    }
540    /// Sets the `wintun.dll` file path on Windows.
541    #[cfg(windows)]
542    pub fn wintun_file(mut self, wintun_file: String) -> Self {
543        self.wintun_file = Some(wintun_file);
544        self
545    }
546    /// Sets the ring capacity on Windows.
547    /// This specifies the capacity of the packet ring buffer in bytes.
548    /// By default, the ring capacity is set to `0x20_0000` (2 MB).
549    #[cfg(windows)]
550    pub fn ring_capacity(mut self, ring_capacity: u32) -> Self {
551        self.ring_capacity = Some(ring_capacity);
552        self
553    }
554    /// Sets the routing metric on Windows.
555    #[cfg(windows)]
556    pub fn metric(mut self, metric: u16) -> Self {
557        self.metric = Some(metric);
558        self
559    }
560    /// Whether to call `WintunDeleteDriver` to remove the driver.
561    /// Default: false.
562    /// # Note
563    /// The clean-up work closely depends on whether the destructor can be normally executed
564    #[cfg(windows)]
565    pub fn delete_driver(mut self, delete_driver: bool) -> Self {
566        self.delete_driver = Some(delete_driver);
567        self
568    }
569    /// Sets the transmit queue length on Linux.
570    #[cfg(target_os = "linux")]
571    pub fn tx_queue_len(mut self, tx_queue_len: u32) -> Self {
572        self.tx_queue_len = Some(tx_queue_len);
573        self
574    }
575    /// Enables TUN offloads on Linux.
576    /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
577    #[cfg(target_os = "linux")]
578    pub fn offload(mut self, offload: bool) -> Self {
579        self.offload = Some(offload);
580        self
581    }
582    /// Enables multi-queue support on Linux.
583    #[cfg(target_os = "linux")]
584    pub fn multi_queue(mut self, multi_queue: bool) -> Self {
585        self.multi_queue = Some(multi_queue);
586        self
587    }
588    /// Enables or disables packet information for the network driver
589    /// on macOS, Linux.
590    ///
591    /// This option is disabled by default (`false`).
592    /// # Note
593    /// There is no native way to enable/disable packet information on macOS.
594    /// The elimination of the packet information on macOS according to this setting
595    /// is processed by this library.
596    #[cfg(any(
597        target_os = "macos",
598        target_os = "linux",
599        target_os = "freebsd",
600        target_os = "netbsd"
601    ))]
602    pub fn packet_information(mut self, packet_information: bool) -> Self {
603        self.packet_information = Some(packet_information);
604        self
605    }
606    /// Available on Layer::L2;
607    /// creates a pair of `feth` devices, with `peer_feth` as the IO interface name.
608    #[cfg(target_os = "macos")]
609    pub fn peer_feth<S: Into<String>>(mut self, peer_feth: S) -> Self {
610        self.peer_feth = Some(peer_feth.into());
611        self
612    }
613    /// If true (default), the program will automatically add or remove routes on macOS or FreeBSD to provide consistent routing behavior across all platforms.
614    /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
615    /// Set this to be false to obtain the platform's default routing behavior.
616    #[cfg(any(
617        target_os = "macos",
618        target_os = "freebsd",
619        target_os = "openbsd",
620        target_os = "netbsd"
621    ))]
622    pub fn associate_route(mut self, associate_route: bool) -> Self {
623        self.associate_route = Some(associate_route);
624        self
625    }
626    /// Only works in TAP mode.
627    /// If true (default), the existing device with the given name will be used if possible.
628    /// If false, an error will be returned if a device with the specified name already exists.
629    #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
630    pub fn reuse_dev(mut self, reuse: bool) -> Self {
631        self.reuse_dev = Some(reuse);
632        self
633    }
634    /// Only works in TAP mode.
635    /// If true, the `feth` device will be kept after the program exits;
636    /// if false (default), the device will be destroyed automatically.
637    #[cfg(any(target_os = "macos", target_os = "windows"))]
638    pub fn persist(mut self, persist: bool) -> Self {
639        self.persist = Some(persist);
640        self
641    }
642    /// Enables or disables the device.
643    /// Defaults to be enabled.
644    pub fn enable(mut self, enable: bool) -> Self {
645        self.enabled = Some(enable);
646        self
647    }
648    pub(crate) fn build_config(&mut self) -> DeviceConfig {
649        DeviceConfig {
650            dev_name: self.dev_name.take(),
651            #[cfg(windows)]
652            description: self.description.take(),
653            #[cfg(target_os = "macos")]
654            peer_feth: self.peer_feth.take(),
655            #[cfg(any(
656                target_os = "macos",
657                target_os = "freebsd",
658                target_os = "openbsd",
659                target_os = "netbsd"
660            ))]
661            associate_route: self.associate_route,
662            #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
663            reuse_dev: self.reuse_dev,
664            #[cfg(any(target_os = "macos", target_os = "windows"))]
665            persist: self.persist,
666            layer: self.layer.take(),
667            #[cfg(windows)]
668            device_guid: self.device_guid.take(),
669            #[cfg(windows)]
670            wintun_log: self.wintun_log.take(),
671            #[cfg(windows)]
672            wintun_file: self.wintun_file.take(),
673            #[cfg(windows)]
674            ring_capacity: self.ring_capacity.take(),
675            #[cfg(windows)]
676            delete_driver: self.delete_driver.take(),
677            #[cfg(windows)]
678            mac_address: self.mac_addr.map(|v| {
679                use std::fmt::Write;
680                v.iter()
681                    .fold(String::with_capacity(v.len() * 2), |mut s, b| {
682                        write!(&mut s, "{b:02X}").unwrap();
683                        s
684                    })
685            }),
686            #[cfg(any(
687                target_os = "macos",
688                target_os = "linux",
689                target_os = "freebsd",
690                target_os = "netbsd"
691            ))]
692            packet_information: self.packet_information.take(),
693            #[cfg(target_os = "linux")]
694            offload: self.offload.take(),
695            #[cfg(target_os = "linux")]
696            multi_queue: self.multi_queue.take(),
697        }
698    }
699    pub(crate) fn config(self, device: &DeviceImpl) -> io::Result<()> {
700        if let Some(mtu) = self.mtu {
701            device.set_mtu(mtu)?;
702        }
703        #[cfg(windows)]
704        if let Some(mtu) = self.mtu_v6 {
705            device.set_mtu_v6(mtu)?;
706        }
707        #[cfg(windows)]
708        if let Some(metric) = self.metric {
709            device.set_metric(metric)?;
710        }
711        #[cfg(target_os = "linux")]
712        if let Some(tx_queue_len) = self.tx_queue_len {
713            device.set_tx_queue_len(tx_queue_len)?;
714        }
715        #[cfg(any(
716            target_os = "linux",
717            target_os = "freebsd",
718            target_os = "macos",
719            target_os = "openbsd",
720            target_os = "netbsd"
721        ))]
722        if let Some(mac_addr) = self.mac_addr {
723            device.set_mac_address(mac_addr)?;
724        }
725
726        if let Some((address, prefix, destination)) = self.ipv4 {
727            let prefix = prefix?;
728            let address = address?;
729            let destination = destination.transpose()?;
730            device.set_network_address(address, prefix, destination)?;
731        }
732        if let Some(ipv6) = self.ipv6 {
733            for (address, prefix) in ipv6 {
734                let prefix = prefix?;
735                let address = address?;
736                device.add_address_v6(address, prefix)?;
737            }
738        }
739        device.enabled(self.enabled.unwrap_or(true))?;
740        Ok(())
741    }
742    /// Builds a synchronous device instance and applies all configuration parameters.
743    pub fn build_sync(mut self) -> io::Result<SyncDevice> {
744        let device = DeviceImpl::new(self.build_config())?;
745        self.config(&device)?;
746        Ok(SyncDevice(device))
747    }
748    /// Builds an asynchronous device instance.
749    ///
750    /// This method is available only when either async_io or async_tokio feature is enabled.
751    ///
752    /// # Note
753    /// Choose one of the two async runtimes; otherwise, a compile error will be incurred if both are enabled.
754    #[cfg(any(feature = "async_io", feature = "async_tokio"))]
755    pub fn build_async(self) -> io::Result<crate::AsyncDevice> {
756        let sync_device = self.build_sync()?;
757        let device = crate::AsyncDevice::new_dev(sync_device.0)?;
758        Ok(device)
759    }
760    /// To conveniently set the platform-specific parameters without breaking the calling chain.
761    /// # Ergonomic
762    ///
763    /// For example:
764    /// ````no_run
765    /// use tun_rs::DeviceBuilder;
766    /// let builder = DeviceBuilder::new().name("tun1");
767    /// #[cfg(target_os = "macos")]
768    /// let builder = builder.associate_route(false);
769    /// #[cfg(windows)]
770    /// let builder = builder.wintun_log(false);
771    /// let dev = builder.build_sync().unwrap();
772    /// ````
773    /// This is tedious and breaks the calling chain.
774    ///
775    /// With `with`, we can just set platform-specific parameters as follows without breaking the calling chain:
776    /// ````no_run
777    /// use tun_rs::DeviceBuilder;
778    /// let dev = DeviceBuilder::new().name("tun1").with(|opt|{
779    ///    #[cfg(windows)]
780    ///    opt.wintun_log(false);
781    ///    #[cfg(target_os = "macos")]
782    ///    opt.associate_route(false).packet_information(false);
783    /// }).build_sync().unwrap();
784    /// ````
785    pub fn with<F: FnMut(&mut DeviceBuilderGuard)>(mut self, mut f: F) -> Self {
786        let mut borrow = DeviceBuilderGuard(&mut self);
787        f(&mut borrow);
788        self
789    }
790}
791
792/// Trait for converting various types into an IPv4 address.
793pub trait ToIpv4Address {
794    /// Attempts to convert the implementing type into an `Ipv4Addr`.
795    /// Returns the IPv4 address on success or an error on failure.
796    fn ipv4(&self) -> io::Result<Ipv4Addr>;
797}
798impl ToIpv4Address for Ipv4Addr {
799    fn ipv4(&self) -> io::Result<Ipv4Addr> {
800        Ok(*self)
801    }
802}
803impl ToIpv4Address for IpAddr {
804    fn ipv4(&self) -> io::Result<Ipv4Addr> {
805        match self {
806            IpAddr::V4(ip) => Ok(*ip),
807            IpAddr::V6(_) => Err(io::Error::new(
808                io::ErrorKind::InvalidData,
809                "invalid address",
810            )),
811        }
812    }
813}
814impl ToIpv4Address for String {
815    fn ipv4(&self) -> io::Result<Ipv4Addr> {
816        self.as_str().ipv4()
817    }
818}
819impl ToIpv4Address for &str {
820    fn ipv4(&self) -> io::Result<Ipv4Addr> {
821        match Ipv4Addr::from_str(self) {
822            Ok(ip) => Ok(ip),
823            Err(_e) => Err(io::Error::new(
824                io::ErrorKind::InvalidData,
825                "invalid IPv4 str",
826            )),
827        }
828    }
829}
830
831/// Trait for converting various types into an IPv6 address.
832pub trait ToIpv6Address {
833    /// Attempts to convert the implementing type into an `Ipv6Addr`.
834    /// Returns the IPv6 address on success or an error on failure.
835    fn ipv6(&self) -> io::Result<Ipv6Addr>;
836}
837
838impl ToIpv6Address for Ipv6Addr {
839    fn ipv6(&self) -> io::Result<Ipv6Addr> {
840        Ok(*self)
841    }
842}
843impl ToIpv6Address for IpAddr {
844    fn ipv6(&self) -> io::Result<Ipv6Addr> {
845        match self {
846            IpAddr::V4(_) => Err(io::Error::new(
847                io::ErrorKind::InvalidData,
848                "invalid address",
849            )),
850            IpAddr::V6(ip) => Ok(*ip),
851        }
852    }
853}
854impl ToIpv6Address for String {
855    fn ipv6(&self) -> io::Result<Ipv6Addr> {
856        self.as_str().ipv6()
857    }
858}
859impl ToIpv6Address for &str {
860    fn ipv6(&self) -> io::Result<Ipv6Addr> {
861        match Ipv6Addr::from_str(self) {
862            Ok(ip) => Ok(ip),
863            Err(_e) => Err(io::Error::new(
864                io::ErrorKind::InvalidData,
865                "invalid IPv6 str",
866            )),
867        }
868    }
869}
870/// Trait for converting various types into an IPv4 netmask (prefix length).
871pub trait ToIpv4Netmask {
872    /// Returns the prefix length (i.e., the number of consecutive 1s in the netmask).
873    fn prefix(&self) -> io::Result<u8>;
874    /// Computes the IPv4 netmask based on the prefix length.
875    fn netmask(&self) -> io::Result<Ipv4Addr> {
876        let ip = u32::MAX
877            .checked_shl(32 - self.prefix()? as u32)
878            .unwrap_or(0);
879        Ok(Ipv4Addr::from(ip))
880    }
881}
882
883impl ToIpv4Netmask for u8 {
884    fn prefix(&self) -> io::Result<u8> {
885        if *self > 32 {
886            return Err(io::Error::new(
887                io::ErrorKind::InvalidData,
888                "invalid IP prefix length",
889            ));
890        }
891        Ok(*self)
892    }
893}
894
895impl ToIpv4Netmask for Ipv4Addr {
896    fn prefix(&self) -> io::Result<u8> {
897        let ip = u32::from_be_bytes(self.octets());
898        // Validate that the netmask is contiguous (all 1s followed by all 0s).
899        if ip.leading_ones() != ip.count_ones() {
900            return Err(io::Error::new(
901                io::ErrorKind::InvalidData,
902                "invalid netmask",
903            ));
904        }
905        Ok(ip.leading_ones() as u8)
906    }
907}
908impl ToIpv4Netmask for String {
909    fn prefix(&self) -> io::Result<u8> {
910        ToIpv4Netmask::prefix(&self.as_str())
911    }
912}
913impl ToIpv4Netmask for &str {
914    fn prefix(&self) -> io::Result<u8> {
915        match Ipv4Addr::from_str(self) {
916            Ok(ip) => ip.prefix(),
917            Err(_e) => Err(io::Error::new(
918                io::ErrorKind::InvalidData,
919                "invalid netmask str",
920            )),
921        }
922    }
923}
924/// Trait for converting various types into an IPv6 netmask (prefix length).
925pub trait ToIpv6Netmask {
926    /// Returns the prefix length.
927    fn prefix(&self) -> io::Result<u8>;
928    /// Computes the IPv6 netmask based on the prefix length.
929    fn netmask(&self) -> io::Result<Ipv6Addr> {
930        let ip = u128::MAX
931            .checked_shl(128 - self.prefix()? as u32)
932            .unwrap_or(0);
933        Ok(Ipv6Addr::from(ip))
934    }
935}
936
937impl ToIpv6Netmask for u8 {
938    fn prefix(&self) -> io::Result<u8> {
939        if *self > 128 {
940            return Err(io::Error::new(
941                io::ErrorKind::InvalidData,
942                "invalid IP prefix length",
943            ));
944        }
945        Ok(*self)
946    }
947}
948
949impl ToIpv6Netmask for Ipv6Addr {
950    fn prefix(&self) -> io::Result<u8> {
951        let ip = u128::from_be_bytes(self.octets());
952        if ip.leading_ones() != ip.count_ones() {
953            return Err(io::Error::new(
954                io::ErrorKind::InvalidData,
955                "invalid netmask",
956            ));
957        }
958        Ok(ip.leading_ones() as u8)
959    }
960}
961impl ToIpv6Netmask for String {
962    fn prefix(&self) -> io::Result<u8> {
963        ToIpv6Netmask::prefix(&self.as_str())
964    }
965}
966impl ToIpv6Netmask for &str {
967    fn prefix(&self) -> io::Result<u8> {
968        match Ipv6Addr::from_str(self) {
969            Ok(ip) => ip.prefix(),
970            Err(_e) => Err(io::Error::new(
971                io::ErrorKind::InvalidData,
972                "invalid netmask str",
973            )),
974        }
975    }
976}