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()
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    pub(crate) fn build_config(&mut self) -> DeviceConfig {
655        DeviceConfig {
656            dev_name: self.dev_name.take(),
657            #[cfg(windows)]
658            description: self.description.take(),
659            #[cfg(target_os = "macos")]
660            peer_feth: self.peer_feth.take(),
661            #[cfg(any(
662                target_os = "macos",
663                target_os = "freebsd",
664                target_os = "openbsd",
665                target_os = "netbsd"
666            ))]
667            associate_route: self.associate_route,
668            #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
669            reuse_dev: self.reuse_dev,
670            #[cfg(any(target_os = "macos", target_os = "windows"))]
671            persist: self.persist,
672            layer: self.layer.take(),
673            #[cfg(windows)]
674            device_guid: self.device_guid.take(),
675            #[cfg(windows)]
676            wintun_log: self.wintun_log.take(),
677            #[cfg(windows)]
678            wintun_file: self.wintun_file.take(),
679            #[cfg(windows)]
680            ring_capacity: self.ring_capacity.take(),
681            #[cfg(windows)]
682            delete_driver: self.delete_driver.take(),
683            #[cfg(windows)]
684            mac_address: self.mac_addr.map(|v| {
685                use std::fmt::Write;
686                v.iter()
687                    .fold(String::with_capacity(v.len() * 2), |mut s, b| {
688                        write!(&mut s, "{b:02X}").unwrap();
689                        s
690                    })
691            }),
692            #[cfg(any(
693                target_os = "macos",
694                target_os = "linux",
695                target_os = "freebsd",
696                target_os = "openbsd",
697                target_os = "netbsd"
698            ))]
699            packet_information: self.packet_information.take(),
700            #[cfg(target_os = "linux")]
701            offload: self.offload.take(),
702            #[cfg(target_os = "linux")]
703            multi_queue: self.multi_queue.take(),
704        }
705    }
706    pub(crate) fn config(self, device: &DeviceImpl) -> io::Result<()> {
707        if let Some(mtu) = self.mtu {
708            device.set_mtu(mtu)?;
709        }
710        #[cfg(windows)]
711        if let Some(mtu) = self.mtu_v6 {
712            device.set_mtu_v6(mtu)?;
713        }
714        #[cfg(windows)]
715        if let Some(metric) = self.metric {
716            device.set_metric(metric)?;
717        }
718        #[cfg(target_os = "linux")]
719        if let Some(tx_queue_len) = self.tx_queue_len {
720            device.set_tx_queue_len(tx_queue_len)?;
721        }
722        #[cfg(any(
723            target_os = "linux",
724            target_os = "freebsd",
725            target_os = "macos",
726            target_os = "openbsd",
727            target_os = "netbsd"
728        ))]
729        if let Some(mac_addr) = self.mac_addr {
730            device.set_mac_address(mac_addr)?;
731        }
732
733        if let Some((address, prefix, destination)) = self.ipv4 {
734            let prefix = prefix?;
735            let address = address?;
736            let destination = destination.transpose()?;
737            device.set_network_address(address, prefix, destination)?;
738        }
739        if let Some(ipv6) = self.ipv6 {
740            for (address, prefix) in ipv6 {
741                let prefix = prefix?;
742                let address = address?;
743                device.add_address_v6(address, prefix)?;
744            }
745        }
746        device.enabled(self.enabled.unwrap_or(true))?;
747        Ok(())
748    }
749    /// Builds a synchronous device instance and applies all configuration parameters.
750    pub fn build_sync(mut self) -> io::Result<SyncDevice> {
751        let device = DeviceImpl::new(self.build_config())?;
752        self.config(&device)?;
753        Ok(SyncDevice(device))
754    }
755    /// Builds an asynchronous device instance.
756    ///
757    /// This method is available only when either async_io or async_tokio feature is enabled.
758    ///
759    /// # Note
760    /// Choose one of the two async runtimes; otherwise, a compile error will be incurred if both are enabled.
761    #[cfg(any(feature = "async_io", feature = "async_tokio"))]
762    pub fn build_async(self) -> io::Result<crate::AsyncDevice> {
763        let sync_device = self.build_sync()?;
764        let device = crate::AsyncDevice::new_dev(sync_device.0)?;
765        Ok(device)
766    }
767    /// To conveniently set the platform-specific parameters without breaking the calling chain.
768    /// # Ergonomic
769    ///
770    /// For example:
771    /// ````no_run
772    /// use tun_rs::DeviceBuilder;
773    /// let builder = DeviceBuilder::new().name("tun1");
774    /// #[cfg(target_os = "macos")]
775    /// let builder = builder.associate_route(false);
776    /// #[cfg(windows)]
777    /// let builder = builder.wintun_log(false);
778    /// let dev = builder.build_sync().unwrap();
779    /// ````
780    /// This is tedious and breaks the calling chain.
781    ///
782    /// With `with`, we can just set platform-specific parameters as follows without breaking the calling chain:
783    /// ````no_run
784    /// use tun_rs::DeviceBuilder;
785    /// let dev = DeviceBuilder::new().name("tun1").with(|opt|{
786    ///    #[cfg(windows)]
787    ///    opt.wintun_log(false);
788    ///    #[cfg(target_os = "macos")]
789    ///    opt.associate_route(false).packet_information(false);
790    /// }).build_sync().unwrap();
791    /// ````
792    pub fn with<F: FnMut(&mut DeviceBuilderGuard)>(mut self, mut f: F) -> Self {
793        let mut borrow = DeviceBuilderGuard(&mut self);
794        f(&mut borrow);
795        self
796    }
797}
798
799/// Trait for converting various types into an IPv4 address.
800pub trait ToIpv4Address {
801    /// Attempts to convert the implementing type into an `Ipv4Addr`.
802    /// Returns the IPv4 address on success or an error on failure.
803    fn ipv4(&self) -> io::Result<Ipv4Addr>;
804}
805impl ToIpv4Address for Ipv4Addr {
806    fn ipv4(&self) -> io::Result<Ipv4Addr> {
807        Ok(*self)
808    }
809}
810impl ToIpv4Address for IpAddr {
811    fn ipv4(&self) -> io::Result<Ipv4Addr> {
812        match self {
813            IpAddr::V4(ip) => Ok(*ip),
814            IpAddr::V6(_) => Err(io::Error::new(
815                io::ErrorKind::InvalidData,
816                "invalid address",
817            )),
818        }
819    }
820}
821impl ToIpv4Address for String {
822    fn ipv4(&self) -> io::Result<Ipv4Addr> {
823        self.as_str().ipv4()
824    }
825}
826impl ToIpv4Address for &str {
827    fn ipv4(&self) -> io::Result<Ipv4Addr> {
828        match Ipv4Addr::from_str(self) {
829            Ok(ip) => Ok(ip),
830            Err(_e) => Err(io::Error::new(
831                io::ErrorKind::InvalidData,
832                "invalid IPv4 str",
833            )),
834        }
835    }
836}
837
838/// Trait for converting various types into an IPv6 address.
839pub trait ToIpv6Address {
840    /// Attempts to convert the implementing type into an `Ipv6Addr`.
841    /// Returns the IPv6 address on success or an error on failure.
842    fn ipv6(&self) -> io::Result<Ipv6Addr>;
843}
844
845impl ToIpv6Address for Ipv6Addr {
846    fn ipv6(&self) -> io::Result<Ipv6Addr> {
847        Ok(*self)
848    }
849}
850impl ToIpv6Address for IpAddr {
851    fn ipv6(&self) -> io::Result<Ipv6Addr> {
852        match self {
853            IpAddr::V4(_) => Err(io::Error::new(
854                io::ErrorKind::InvalidData,
855                "invalid address",
856            )),
857            IpAddr::V6(ip) => Ok(*ip),
858        }
859    }
860}
861impl ToIpv6Address for String {
862    fn ipv6(&self) -> io::Result<Ipv6Addr> {
863        self.as_str().ipv6()
864    }
865}
866impl ToIpv6Address for &str {
867    fn ipv6(&self) -> io::Result<Ipv6Addr> {
868        match Ipv6Addr::from_str(self) {
869            Ok(ip) => Ok(ip),
870            Err(_e) => Err(io::Error::new(
871                io::ErrorKind::InvalidData,
872                "invalid IPv6 str",
873            )),
874        }
875    }
876}
877/// Trait for converting various types into an IPv4 netmask (prefix length).
878pub trait ToIpv4Netmask {
879    /// Returns the prefix length (i.e., the number of consecutive 1s in the netmask).
880    fn prefix(&self) -> io::Result<u8>;
881    /// Computes the IPv4 netmask based on the prefix length.
882    fn netmask(&self) -> io::Result<Ipv4Addr> {
883        let ip = u32::MAX
884            .checked_shl(32 - self.prefix()? as u32)
885            .unwrap_or(0);
886        Ok(Ipv4Addr::from(ip))
887    }
888}
889
890impl ToIpv4Netmask for u8 {
891    fn prefix(&self) -> io::Result<u8> {
892        if *self > 32 {
893            return Err(io::Error::new(
894                io::ErrorKind::InvalidData,
895                "invalid IP prefix length",
896            ));
897        }
898        Ok(*self)
899    }
900}
901
902impl ToIpv4Netmask for Ipv4Addr {
903    fn prefix(&self) -> io::Result<u8> {
904        let ip = u32::from_be_bytes(self.octets());
905        // Validate that the netmask is contiguous (all 1s followed by all 0s).
906        if ip.leading_ones() != ip.count_ones() {
907            return Err(io::Error::new(
908                io::ErrorKind::InvalidData,
909                "invalid netmask",
910            ));
911        }
912        Ok(ip.leading_ones() as u8)
913    }
914}
915impl ToIpv4Netmask for String {
916    fn prefix(&self) -> io::Result<u8> {
917        ToIpv4Netmask::prefix(&self.as_str())
918    }
919}
920impl ToIpv4Netmask for &str {
921    fn prefix(&self) -> io::Result<u8> {
922        match Ipv4Addr::from_str(self) {
923            Ok(ip) => ip.prefix(),
924            Err(_e) => Err(io::Error::new(
925                io::ErrorKind::InvalidData,
926                "invalid netmask str",
927            )),
928        }
929    }
930}
931/// Trait for converting various types into an IPv6 netmask (prefix length).
932pub trait ToIpv6Netmask {
933    /// Returns the prefix length.
934    fn prefix(&self) -> io::Result<u8>;
935    /// Computes the IPv6 netmask based on the prefix length.
936    fn netmask(&self) -> io::Result<Ipv6Addr> {
937        let ip = u128::MAX
938            .checked_shl(128 - self.prefix()? as u32)
939            .unwrap_or(0);
940        Ok(Ipv6Addr::from(ip))
941    }
942}
943
944impl ToIpv6Netmask for u8 {
945    fn prefix(&self) -> io::Result<u8> {
946        if *self > 128 {
947            return Err(io::Error::new(
948                io::ErrorKind::InvalidData,
949                "invalid IP prefix length",
950            ));
951        }
952        Ok(*self)
953    }
954}
955
956impl ToIpv6Netmask for Ipv6Addr {
957    fn prefix(&self) -> io::Result<u8> {
958        let ip = u128::from_be_bytes(self.octets());
959        if ip.leading_ones() != ip.count_ones() {
960            return Err(io::Error::new(
961                io::ErrorKind::InvalidData,
962                "invalid netmask",
963            ));
964        }
965        Ok(ip.leading_ones() as u8)
966    }
967}
968impl ToIpv6Netmask for String {
969    fn prefix(&self) -> io::Result<u8> {
970        ToIpv6Netmask::prefix(&self.as_str())
971    }
972}
973impl ToIpv6Netmask for &str {
974    fn prefix(&self) -> io::Result<u8> {
975        match Ipv6Addr::from_str(self) {
976            Ok(ip) => ip.prefix(),
977            Err(_e) => Err(io::Error::new(
978                io::ErrorKind::InvalidData,
979                "invalid netmask str",
980            )),
981        }
982    }
983}