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