tun_rs/builder.rs
1/*!
2# Device Builder Module
3
4This module provides the [`DeviceBuilder`] struct for configuring and creating TUN/TAP interfaces.
5
6## Overview
7
8The builder pattern allows you to specify all configuration parameters before creating a device.
9Configuration includes:
10- Device name
11- IP addresses (IPv4 and IPv6, single or multiple)
12- MTU (Maximum Transmission Unit)
13- Operating layer (L2/TAP or L3/TUN)
14- MAC address (for L2/TAP mode)
15- Platform-specific options (offload, multi-queue, packet information, etc.)
16
17## Basic Usage
18
19```no_run
20use tun_rs::DeviceBuilder;
21
22// Create a TUN (L3) device
23let dev = DeviceBuilder::new()
24 .name("tun0")
25 .ipv4("10.0.0.1", 24, None)
26 .mtu(1400)
27 .build_sync()?;
28# Ok::<(), std::io::Error>(())
29```
30
31## Platform-Specific Configuration
32
33The builder provides platform-specific methods accessible via the `with()` method:
34
35### Linux Specific
36
37```no_run
38# #[cfg(target_os = "linux")]
39# {
40use tun_rs::DeviceBuilder;
41
42let dev = DeviceBuilder::new()
43 .ipv4("10.0.0.1", 24, None)
44 .with(|builder| {
45 builder
46 .offload(true) // Enable TSO/GSO offload
47 .multi_queue(true) // Enable multi-queue support
48 .tx_queue_len(1000) // Set transmit queue length
49 })
50 .build_sync()?;
51# Ok::<(), std::io::Error>(())
52# }
53```
54
55### Windows Specific
56
57```no_run
58# #[cfg(target_os = "windows")]
59# {
60use tun_rs::DeviceBuilder;
61
62let dev = DeviceBuilder::new()
63 .ipv4("10.0.0.1", 24, None)
64 .with(|builder| {
65 builder
66 .ring_capacity(0x40_0000) // 4MB ring buffer
67 .wintun_log(true) // Enable Wintun logging
68 .description("My VPN") // Set device description
69 })
70 .build_sync()?;
71# Ok::<(), std::io::Error>(())
72# }
73```
74
75### macOS Specific
76
77```no_run
78# #[cfg(target_os = "macos")]
79# {
80use tun_rs::DeviceBuilder;
81
82let dev = DeviceBuilder::new()
83 .ipv4("10.0.0.1", 24, None)
84 .with(|builder| {
85 builder
86 .associate_route(true) // Auto-configure routes
87 .packet_information(false) // Disable packet headers
88 })
89 .build_sync()?;
90# Ok::<(), std::io::Error>(())
91# }
92```
93
94## Layer Selection
95
96Choose between Layer 2 (TAP) and Layer 3 (TUN):
97
98```no_run
99# #[cfg(any(target_os = "linux", target_os = "windows", target_os = "freebsd"))]
100# {
101use tun_rs::{DeviceBuilder, Layer};
102
103// TAP interface (Layer 2)
104let tap = DeviceBuilder::new()
105 .name("tap0")
106 .layer(Layer::L2)
107 .mac_addr([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])
108 .build_sync()?;
109
110// TUN interface (Layer 3, default)
111let tun = DeviceBuilder::new()
112 .name("tun0")
113 .layer(Layer::L3)
114 .ipv4("10.0.0.1", 24, None)
115 .build_sync()?;
116# Ok::<(), std::io::Error>(())
117# }
118```
119
120## Multiple IP Addresses
121
122You can configure multiple IPv6 addresses during device creation:
123
124```no_run
125use tun_rs::DeviceBuilder;
126
127let dev = DeviceBuilder::new()
128 .ipv4("10.0.0.1", 24, None)
129 .ipv6("fd00::1", 64)
130 .ipv6("fd00::2", 64)
131 .build_sync()?;
132# Ok::<(), std::io::Error>(())
133```
134
135Additional addresses can be added after creation using [`crate::SyncDevice::add_address_v4`]
136and [`crate::SyncDevice::add_address_v6`] methods.
137
138## Configuration Precedence
139
140- Most settings have sensible defaults
141- Unspecified values use platform defaults
142- Some settings are mandatory (e.g., at least one IP address for routing)
143- Platform-specific settings are ignored on other platforms
144
145## Error Handling
146
147The `build_sync()` and `build_async()` methods return `io::Result<Device>`.
148Common errors include:
149- Permission denied (need root/administrator privileges)
150- Device name already exists
151- Invalid IP address or netmask
152- Platform-specific driver not found (e.g., Wintun on Windows)
153*/
154
155use std::io;
156use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
157use std::str::FromStr;
158
159use crate::platform::{DeviceImpl, SyncDevice};
160
161/// Represents the OSI layer at which the TUN/TAP interface operates.
162///
163/// This enum determines whether the interface works at the Data Link Layer (L2/TAP)
164/// or the Network Layer (L3/TUN).
165///
166/// # Layer 2 (TAP) - Data Link Layer
167///
168/// TAP interfaces operate at the Ethernet frame level (Layer 2), handling complete
169/// Ethernet frames including MAC addresses. This is useful for:
170/// - Bridging networks
171/// - Emulating full Ethernet connectivity
172/// - Applications requiring MAC-level control
173/// - Creating virtual switches
174///
175/// TAP mode requires setting a MAC address and can work with protocols like ARP.
176///
177/// **Platform availability**: Windows, Linux, FreeBSD, macOS, OpenBSD, NetBSD
178///
179/// # Layer 3 (TUN) - Network Layer
180///
181/// TUN interfaces operate at the IP packet level (Layer 3), handling IP packets
182/// directly without Ethernet framing. This is the default and most common mode for:
183/// - VPN implementations
184/// - IP tunneling
185/// - Point-to-point connections
186/// - Routing between networks
187///
188/// TUN mode is simpler and more efficient than TAP when Ethernet-level features
189/// are not needed.
190///
191/// **Platform availability**: All platforms
192///
193/// # Examples
194///
195/// Creating a TAP (L2) interface:
196///
197/// ```no_run
198/// # #[cfg(any(target_os = "linux", target_os = "windows", target_os = "freebsd"))]
199/// # {
200/// use tun_rs::{DeviceBuilder, Layer};
201///
202/// let tap = DeviceBuilder::new()
203/// .name("tap0")
204/// .layer(Layer::L2)
205/// .mac_addr([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])
206/// .build_sync()?;
207/// # Ok::<(), std::io::Error>(())
208/// # }
209/// ```
210///
211/// Creating a TUN (L3) interface (default):
212///
213/// ```no_run
214/// use tun_rs::{DeviceBuilder, Layer};
215///
216/// // L3 is the default, explicit specification is optional
217/// let tun = DeviceBuilder::new()
218/// .name("tun0")
219/// .layer(Layer::L3)
220/// .ipv4("10.0.0.1", 24, None)
221/// .build_sync()?;
222/// # Ok::<(), std::io::Error>(())
223/// ```
224///
225/// # Platform-Specific Notes
226///
227/// - On macOS, TAP mode uses a pair of `feth` (fake ethernet) interfaces
228/// - On Windows, TAP mode requires the tap-windows driver
229/// - On Linux, both modes use the kernel TUN/TAP driver
230/// - Android and iOS only support TUN (L3) mode
231#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
232#[non_exhaustive]
233pub enum Layer {
234 /// Data Link Layer (Ethernet frames with MAC addresses).
235 ///
236 /// TAP mode operates at Layer 2, handling complete Ethernet frames.
237 /// Requires a MAC address to be configured.
238 ///
239 /// Available on: Windows, Linux, FreeBSD, macOS, OpenBSD, NetBSD
240 #[cfg(any(
241 target_os = "windows",
242 target_os = "linux",
243 target_os = "freebsd",
244 target_os = "macos",
245 target_os = "openbsd",
246 target_os = "netbsd",
247 ))]
248 L2,
249
250 /// Network Layer (IP packets).
251 ///
252 /// TUN mode operates at Layer 3, handling IP packets directly.
253 /// This is the default and most common mode for VPN and tunneling applications.
254 ///
255 /// Available on: All platforms
256 #[default]
257 L3,
258}
259
260/// Configuration for a TUN/TAP interface.
261///
262/// This structure stores settings such as the device name, operating layer,
263/// and platform-specific parameters (e.g., GUID, wintun file, ring capacity on Windows).
264#[derive(Clone, Default, Debug)]
265pub(crate) struct DeviceConfig {
266 /// The name of the device/interface.
267 pub(crate) dev_name: Option<String>,
268 /// The description of the device/interface.
269 #[cfg(windows)]
270 pub(crate) description: Option<String>,
271 /// Available with Layer::L2; creates a pair of feth devices, with peer_feth as the IO interface name.
272 #[cfg(target_os = "macos")]
273 pub(crate) peer_feth: Option<String>,
274 /// If true (default), the program will automatically add or remove routes on macOS or FreeBSD to provide consistent routing behavior across all platforms.
275 /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
276 /// Set this to be false to obtain the platform's default routing behavior.
277 #[cfg(any(
278 target_os = "macos",
279 target_os = "freebsd",
280 target_os = "openbsd",
281 target_os = "netbsd"
282 ))]
283 pub(crate) associate_route: Option<bool>,
284 /// If true (default), the existing device with the given name will be used if possible.
285 /// If false, an error will be returned if a device with the specified name already exists.
286 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
287 pub(crate) reuse_dev: Option<bool>,
288 /// If true, the feth device will be kept after the program exits;
289 /// if false (default), the device will be destroyed automatically.
290 #[cfg(any(target_os = "macos", target_os = "windows"))]
291 pub(crate) persist: Option<bool>,
292 /// Specifies whether the interface operates at L2 or L3.
293 #[allow(dead_code)]
294 pub(crate) layer: Option<Layer>,
295 /// Device GUID on Windows.
296 #[cfg(windows)]
297 pub(crate) device_guid: Option<u128>,
298 #[cfg(windows)]
299 pub(crate) wintun_log: Option<bool>,
300 /// Path to the wintun file on Windows.
301 #[cfg(windows)]
302 pub(crate) wintun_file: Option<String>,
303 /// Capacity of the ring buffer on Windows.
304 #[cfg(windows)]
305 pub(crate) ring_capacity: Option<u32>,
306 /// Whether to call WintunDeleteDriver to remove the driver.
307 /// Default: false.
308 #[cfg(windows)]
309 pub(crate) delete_driver: Option<bool>,
310 #[cfg(windows)]
311 pub(crate) mac_address: Option<String>,
312 /// switch of Enable/Disable packet information for network driver
313 #[cfg(any(
314 target_os = "macos",
315 target_os = "linux",
316 target_os = "freebsd",
317 target_os = "openbsd",
318 target_os = "netbsd"
319 ))]
320 pub(crate) packet_information: Option<bool>,
321 /// Enable/Disable TUN offloads.
322 /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
323 #[cfg(target_os = "linux")]
324 pub(crate) offload: Option<bool>,
325 /// Enable multi queue support
326 #[cfg(target_os = "linux")]
327 pub(crate) multi_queue: Option<bool>,
328}
329type IPV4 = (
330 io::Result<Ipv4Addr>,
331 io::Result<u8>,
332 Option<io::Result<Ipv4Addr>>,
333);
334/// A builder for configuring a TUN/TAP interface.
335///
336/// This builder allows you to set parameters such as device name, MTU,
337/// IPv4/IPv6 addresses, MAC address, and other platform-specific options.
338///
339/// # Examples
340///
341/// Creating a basic IPv4 TUN interface:
342///
343/// ````no_run
344/// use std::net::Ipv4Addr;
345/// use tun_rs::DeviceBuilder;
346///
347/// fn main() -> std::io::Result<()> {
348/// let tun = DeviceBuilder::new()
349/// .name("my-tun")
350/// .mtu(1500)
351/// .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
352/// .build_sync()?;
353/// Ok(())
354/// }
355/// ````
356///
357/// Creating an IPv6 TUN interface:
358///
359/// ````no_run
360/// use std::net::Ipv6Addr;
361/// use tun_rs::DeviceBuilder;
362///
363/// fn main() -> std::io::Result<()> {
364/// let tun = DeviceBuilder::new()
365/// .name("my-tun6")
366/// .mtu(1500)
367/// .ipv6(Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 1), 64)
368/// .build_sync()?;
369/// Ok(())
370/// }
371/// ````
372///
373/// Creating an L2 TAP interface (platform-dependent):
374///
375/// ````no_run
376/// #[cfg(any(
377/// target_os = "windows",
378/// all(target_os = "linux", not(target_env = "ohos")),
379/// target_os = "freebsd",
380/// target_os = "macos",
381/// target_os = "openbsd",
382/// target_os = "netbsd"
383/// ))]
384/// use tun_rs::{DeviceBuilder, Layer};
385///
386/// #[cfg(any(
387/// target_os = "windows",
388/// all(target_os = "linux", not(target_env = "ohos")),
389/// target_os = "freebsd",
390/// target_os = "macos",
391/// target_os = "openbsd",
392/// target_os = "netbsd"
393/// ))]
394/// fn main() -> std::io::Result<()> {
395/// let tap = DeviceBuilder::new()
396/// .name("my-tap")
397/// .layer(Layer::L2)
398/// .mac_addr([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])
399/// .mtu(1500)
400/// .build_sync()?;
401/// Ok(())
402/// }
403/// ````
404#[doc(hidden)]
405pub struct DeviceBuilderGuard<'a>(&'a mut DeviceBuilder);
406
407#[doc(hidden)]
408impl DeviceBuilderGuard<'_> {
409 /// Sets the device description (effective only on Windows L3 mode).
410 #[cfg(windows)]
411 pub fn description<S: Into<String>>(&mut self, description: S) -> &mut Self {
412 self.0.description = Some(description.into());
413 self
414 }
415
416 /// Sets the IPv4 MTU specifically for Windows.
417 #[cfg(windows)]
418 pub fn mtu_v4(&mut self, mtu: u16) -> &mut Self {
419 self.0.mtu = Some(mtu);
420 self
421 }
422 /// Sets the IPv6 MTU specifically for Windows.
423 #[cfg(windows)]
424 pub fn mtu_v6(&mut self, mtu: u16) -> &mut Self {
425 self.0.mtu_v6 = Some(mtu);
426 self
427 }
428 /// Sets the MAC address for the device (effective only in L2 mode).
429 #[cfg(any(
430 target_os = "windows",
431 target_os = "linux",
432 target_os = "freebsd",
433 target_os = "openbsd",
434 target_os = "macos",
435 target_os = "netbsd"
436 ))]
437 pub fn mac_addr(&mut self, mac_addr: [u8; 6]) -> &mut Self {
438 self.0.mac_addr = Some(mac_addr);
439 self
440 }
441
442 /// Sets the device GUID on Windows.
443 /// By default, GUID is chosen by the system at random.
444 #[cfg(windows)]
445 pub fn device_guid(&mut self, device_guid: u128) -> &mut Self {
446 self.0.device_guid = Some(device_guid);
447 self
448 }
449 /// Enables or disables Wintun logging.
450 ///
451 /// By default, logging is disabled.
452 #[cfg(windows)]
453 pub fn wintun_log(&mut self, wintun_log: bool) -> &mut Self {
454 self.0.wintun_log = Some(wintun_log);
455 self
456 }
457 /// Sets the `wintun.dll` file path on Windows.
458 #[cfg(windows)]
459 pub fn wintun_file(&mut self, wintun_file: String) -> &mut Self {
460 self.0.wintun_file = Some(wintun_file);
461 self
462 }
463 /// Sets the ring capacity on Windows.
464 /// This specifies the capacity of the packet ring buffer in bytes.
465 /// By default, the ring capacity is set to `0x20_0000` (2 MB).
466 #[cfg(windows)]
467 pub fn ring_capacity(&mut self, ring_capacity: u32) -> &mut Self {
468 self.0.ring_capacity = Some(ring_capacity);
469 self
470 }
471 /// Sets the routing metric on Windows.
472 #[cfg(windows)]
473 pub fn metric(&mut self, metric: u16) -> &mut Self {
474 self.0.metric = Some(metric);
475 self
476 }
477 /// Whether to call `WintunDeleteDriver` to remove the driver.
478 /// Default: false.
479 /// # Note
480 /// The clean-up work closely depends on whether the destructor can be normally executed
481 #[cfg(windows)]
482 pub fn delete_driver(&mut self, delete_driver: bool) -> &mut Self {
483 self.0.delete_driver = Some(delete_driver);
484 self
485 }
486 /// Sets the transmit queue length on Linux.
487 #[cfg(target_os = "linux")]
488 pub fn tx_queue_len(&mut self, tx_queue_len: u32) -> &mut Self {
489 self.0.tx_queue_len = Some(tx_queue_len);
490 self
491 }
492 /// Enables TUN offloads on Linux.
493 /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
494 #[cfg(target_os = "linux")]
495 pub fn offload(&mut self, offload: bool) -> &mut Self {
496 self.0.offload = Some(offload);
497 self
498 }
499 /// Enables multi-queue support on Linux.
500 #[cfg(target_os = "linux")]
501 pub fn multi_queue(&mut self, multi_queue: bool) -> &mut Self {
502 self.0.multi_queue = Some(multi_queue);
503 self
504 }
505 /// Enables or disables packet information for the network driver(TUN)
506 /// on macOS, Linux, freebsd, openbsd, netbsd.
507 ///
508 /// This option is disabled by default (`false`).
509 /// # Note
510 /// There is no native way to enable/disable packet information on macOS.
511 /// The elimination of the packet information on macOS according to this setting
512 /// is processed by this library.
513 /// The set value `v` can be retrieved by `ignore_packet_info`, the returned value is `!v`.
514 #[cfg(any(
515 target_os = "macos",
516 target_os = "linux",
517 target_os = "freebsd",
518 target_os = "openbsd",
519 target_os = "netbsd"
520 ))]
521 pub fn packet_information(&mut self, packet_information: bool) -> &mut Self {
522 self.0.packet_information = Some(packet_information);
523 self
524 }
525 /// Available on Layer::L2;
526 /// creates a pair of `feth` devices, with `peer_feth` as the IO interface name.
527 #[cfg(target_os = "macos")]
528 pub fn peer_feth<S: Into<String>>(&mut self, peer_feth: S) -> &mut Self {
529 self.0.peer_feth = Some(peer_feth.into());
530 self
531 }
532 /// If true (default), the program will automatically add or remove routes on macOS or FreeBSD to provide consistent routing behavior across all platforms.
533 /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
534 /// Set this to be false to obtain the platform's default routing behavior.
535 #[cfg(any(
536 target_os = "macos",
537 target_os = "freebsd",
538 target_os = "openbsd",
539 target_os = "netbsd"
540 ))]
541 pub fn associate_route(&mut self, associate_route: bool) -> &mut Self {
542 self.0.associate_route = Some(associate_route);
543 self
544 }
545 /// Only works in TAP mode.
546 /// If true (default), the existing device with the given name will be used if possible.
547 /// If false, an error will be returned if a device with the specified name already exists.
548 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
549 pub fn reuse_dev(&mut self, reuse: bool) -> &mut Self {
550 self.0.reuse_dev = Some(reuse);
551 self
552 }
553 /// Only works in TAP mode.
554 /// If true, the `feth` device will be kept after the program exits;
555 /// if false (default), the device will be destroyed automatically.
556 #[cfg(any(target_os = "macos", target_os = "windows"))]
557 pub fn persist(&mut self, persist: bool) -> &mut Self {
558 self.0.persist = Some(persist);
559 self
560 }
561}
562/// This is a unified constructor of a device for various platforms. The specification of every API can be found by looking at
563/// the documentation of the concrete platform.
564#[derive(Default)]
565pub struct DeviceBuilder {
566 dev_name: Option<String>,
567 #[cfg(windows)]
568 description: Option<String>,
569 #[cfg(target_os = "macos")]
570 peer_feth: Option<String>,
571 #[cfg(any(
572 target_os = "macos",
573 target_os = "freebsd",
574 target_os = "openbsd",
575 target_os = "netbsd"
576 ))]
577 associate_route: Option<bool>,
578 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
579 reuse_dev: Option<bool>,
580 #[cfg(any(target_os = "macos", target_os = "windows"))]
581 persist: Option<bool>,
582 enabled: Option<bool>,
583 mtu: Option<u16>,
584 #[cfg(windows)]
585 mtu_v6: Option<u16>,
586 ipv4: Option<IPV4>,
587 ipv6: Option<Vec<(io::Result<Ipv6Addr>, io::Result<u8>)>>,
588 layer: Option<Layer>,
589 #[cfg(any(
590 target_os = "windows",
591 target_os = "linux",
592 target_os = "freebsd",
593 target_os = "openbsd",
594 target_os = "macos",
595 target_os = "netbsd"
596 ))]
597 mac_addr: Option<[u8; 6]>,
598 #[cfg(windows)]
599 device_guid: Option<u128>,
600 #[cfg(windows)]
601 wintun_log: Option<bool>,
602 #[cfg(windows)]
603 wintun_file: Option<String>,
604 #[cfg(windows)]
605 ring_capacity: Option<u32>,
606 #[cfg(windows)]
607 metric: Option<u16>,
608 #[cfg(windows)]
609 delete_driver: Option<bool>,
610 /// switch of Enable/Disable packet information for network driver
611 #[cfg(any(
612 target_os = "macos",
613 target_os = "linux",
614 target_os = "freebsd",
615 target_os = "openbsd",
616 target_os = "netbsd"
617 ))]
618 packet_information: Option<bool>,
619 #[cfg(target_os = "linux")]
620 tx_queue_len: Option<u32>,
621 /// Enable/Disable TUN offloads.
622 /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
623 #[cfg(target_os = "linux")]
624 offload: Option<bool>,
625 /// Enable multi queue support
626 #[cfg(target_os = "linux")]
627 multi_queue: Option<bool>,
628}
629
630impl DeviceBuilder {
631 /// Creates a new DeviceBuilder instance with default settings.
632 pub fn new() -> Self {
633 Self::default().enable(true)
634 }
635 /// Sets the device name.
636 pub fn name<S: Into<String>>(mut self, dev_name: S) -> Self {
637 self.dev_name = Some(dev_name.into());
638 self
639 }
640 /// Sets the device description (effective only on Windows L3 mode).
641 #[cfg(windows)]
642 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
643 self.description = Some(description.into());
644 self
645 }
646 /// Sets the device MTU (Maximum Transmission Unit).
647 pub fn mtu(mut self, mtu: u16) -> Self {
648 self.mtu = Some(mtu);
649 #[cfg(windows)]
650 {
651 // On Windows, also set the MTU for IPv6.
652 self.mtu_v6 = Some(mtu);
653 }
654 self
655 }
656 /// Sets the IPv4 MTU specifically for Windows.
657 #[cfg(windows)]
658 pub fn mtu_v4(mut self, mtu: u16) -> Self {
659 self.mtu = Some(mtu);
660 self
661 }
662 /// Sets the IPv6 MTU specifically for Windows.
663 #[cfg(windows)]
664 pub fn mtu_v6(mut self, mtu: u16) -> Self {
665 self.mtu_v6 = Some(mtu);
666 self
667 }
668 /// Sets the MAC address for the device (effective only in L2 mode).
669 #[cfg(any(
670 target_os = "windows",
671 target_os = "linux",
672 target_os = "freebsd",
673 target_os = "openbsd",
674 target_os = "macos",
675 target_os = "netbsd"
676 ))]
677 pub fn mac_addr(mut self, mac_addr: [u8; 6]) -> Self {
678 self.mac_addr = Some(mac_addr);
679 self
680 }
681 /// Configures the IPv4 address for the device.
682 ///
683 /// - `address`: The IPv4 address of the device.
684 /// - `mask`: The subnet mask or prefix length.
685 /// - `destination`: Optional destination address for point-to-point links.
686 /// # Example
687 /// ```
688 /// use std::net::Ipv4Addr;
689 /// use tun_rs::DeviceBuilder;
690 /// DeviceBuilder::new().ipv4(Ipv4Addr::new(10, 0, 0, 12), 24, None);
691 /// ```
692 pub fn ipv4<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
693 mut self,
694 address: IPv4,
695 mask: Netmask,
696 destination: Option<IPv4>,
697 ) -> Self {
698 self.ipv4 = Some((address.ipv4(), mask.prefix(), destination.map(|v| v.ipv4())));
699 self
700 }
701 /// Configures a single IPv6 address for the device.
702 ///
703 /// - `address`: The IPv6 address.
704 /// - `mask`: The subnet mask or prefix length.
705 /// # Example
706 /// ```
707 /// use tun_rs::DeviceBuilder;
708 /// DeviceBuilder::new().ipv6("CDCD:910A:2222:5498:8475:1111:3900:2021", 64);
709 /// ```
710 pub fn ipv6<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
711 mut self,
712 address: IPv6,
713 mask: Netmask,
714 ) -> Self {
715 if let Some(v) = &mut self.ipv6 {
716 v.push((address.ipv6(), mask.prefix()));
717 } else {
718 self.ipv6 = Some(vec![(address.ipv6(), mask.prefix())]);
719 }
720
721 self
722 }
723 /// Configures multiple IPv6 addresses in batch.
724 ///
725 /// Accepts a slice of (IPv6 address, netmask) tuples.
726 /// # Example
727 /// ```rust
728 /// use tun_rs::DeviceBuilder;
729 /// DeviceBuilder::new().ipv6_tuple(&[
730 /// ("CDCD:910A:2222:5498:8475:1111:3900:2022", 64),
731 /// ("CDCD:910A:2222:5498:8475:1111:3900:2023", 64),
732 /// ]);
733 /// ```
734 pub fn ipv6_tuple<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
735 mut self,
736 addrs: &[(IPv6, Netmask)],
737 ) -> Self {
738 if let Some(v) = &mut self.ipv6 {
739 for (address, mask) in addrs {
740 v.push((address.ipv6(), mask.prefix()));
741 }
742 } else {
743 self.ipv6 = Some(
744 addrs
745 .iter()
746 .map(|(ip, mask)| (ip.ipv6(), mask.prefix()))
747 .collect(),
748 );
749 }
750 self
751 }
752 /// Sets the operating layer (L2 or L3) for the device.
753 ///
754 /// * L2 corresponds to TAP
755 /// * L3 corresponds to TUN
756 pub fn layer(mut self, layer: Layer) -> Self {
757 self.layer = Some(layer);
758 self
759 }
760 /// Sets the device GUID on Windows.
761 /// By default, GUID is chosen by the system at random.
762 #[cfg(windows)]
763 pub fn device_guid(mut self, device_guid: u128) -> Self {
764 self.device_guid = Some(device_guid);
765 self
766 }
767 /// Enables or disables Wintun logging.
768 ///
769 /// By default, logging is disabled.
770 #[cfg(windows)]
771 pub fn wintun_log(mut self, wintun_log: bool) -> Self {
772 self.wintun_log = Some(wintun_log);
773 self
774 }
775 /// Sets the `wintun.dll` file path on Windows.
776 #[cfg(windows)]
777 pub fn wintun_file(mut self, wintun_file: String) -> Self {
778 self.wintun_file = Some(wintun_file);
779 self
780 }
781 /// Sets the ring capacity on Windows.
782 /// This specifies the capacity of the packet ring buffer in bytes.
783 /// By default, the ring capacity is set to `0x20_0000` (2 MB).
784 #[cfg(windows)]
785 pub fn ring_capacity(mut self, ring_capacity: u32) -> Self {
786 self.ring_capacity = Some(ring_capacity);
787 self
788 }
789 /// Sets the routing metric on Windows.
790 #[cfg(windows)]
791 pub fn metric(mut self, metric: u16) -> Self {
792 self.metric = Some(metric);
793 self
794 }
795 /// Whether to call `WintunDeleteDriver` to remove the driver.
796 /// Default: false.
797 /// # Note
798 /// The clean-up work closely depends on whether the destructor can be normally executed
799 #[cfg(windows)]
800 pub fn delete_driver(mut self, delete_driver: bool) -> Self {
801 self.delete_driver = Some(delete_driver);
802 self
803 }
804 /// Sets the transmit queue length on Linux.
805 #[cfg(target_os = "linux")]
806 pub fn tx_queue_len(mut self, tx_queue_len: u32) -> Self {
807 self.tx_queue_len = Some(tx_queue_len);
808 self
809 }
810 /// Enables TUN offloads on Linux.
811 /// After enabling, use `recv_multiple`/`send_multiple` for data transmission.
812 #[cfg(target_os = "linux")]
813 pub fn offload(mut self, offload: bool) -> Self {
814 self.offload = Some(offload);
815 self
816 }
817 /// Enables multi-queue support on Linux.
818 #[cfg(target_os = "linux")]
819 pub fn multi_queue(mut self, multi_queue: bool) -> Self {
820 self.multi_queue = Some(multi_queue);
821 self
822 }
823 /// Enables or disables packet information for the network driver(TUN)
824 /// on macOS, Linux, freebsd, openbsd, netbsd.
825 ///
826 /// This option is disabled by default (`false`).
827 /// # Note
828 /// There is no native way to enable/disable packet information on macOS.
829 /// The elimination of the packet information on macOS according to this setting
830 /// is processed by this library.
831 /// The set value `v` can be retrieved by `ignore_packet_info`, the returned value is `!v`.
832 #[cfg(any(
833 target_os = "macos",
834 target_os = "linux",
835 target_os = "freebsd",
836 target_os = "openbsd",
837 target_os = "netbsd"
838 ))]
839 pub fn packet_information(mut self, packet_information: bool) -> Self {
840 self.packet_information = Some(packet_information);
841 self
842 }
843 /// Available on Layer::L2;
844 /// creates a pair of `feth` devices, with `peer_feth` as the IO interface name.
845 #[cfg(target_os = "macos")]
846 pub fn peer_feth<S: Into<String>>(mut self, peer_feth: S) -> Self {
847 self.peer_feth = Some(peer_feth.into());
848 self
849 }
850 /// If true (default), the program will automatically add or remove routes on macOS or FreeBSD to provide consistent routing behavior across all platforms.
851 /// If false, the program will not modify or manage routes in any way, allowing the system to handle all routing natively.
852 /// Set this to be false to obtain the platform's default routing behavior.
853 #[cfg(any(
854 target_os = "macos",
855 target_os = "freebsd",
856 target_os = "openbsd",
857 target_os = "netbsd"
858 ))]
859 pub fn associate_route(mut self, associate_route: bool) -> Self {
860 self.associate_route = Some(associate_route);
861 self
862 }
863 /// Only works in TAP mode.
864 /// If true (default), the existing device with the given name will be used if possible.
865 /// If false, an error will be returned if a device with the specified name already exists.
866 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
867 pub fn reuse_dev(mut self, reuse: bool) -> Self {
868 self.reuse_dev = Some(reuse);
869 self
870 }
871 /// Only works in TAP mode.
872 /// If true, the `feth` device will be kept after the program exits;
873 /// if false (default), the device will be destroyed automatically.
874 #[cfg(any(target_os = "macos", target_os = "windows"))]
875 pub fn persist(mut self, persist: bool) -> Self {
876 self.persist = Some(persist);
877 self
878 }
879 /// Enables or disables the device.
880 /// Defaults to be enabled.
881 pub fn enable(mut self, enable: bool) -> Self {
882 self.enabled = Some(enable);
883 self
884 }
885
886 /// Keeps the device enable state unchanged.
887 ///
888 /// This method does not explicitly enable or disable the device.
889 /// The existing system state is preserved.
890 pub fn inherit_enable_state(mut self) -> Self {
891 self.enabled = None;
892 self
893 }
894 pub(crate) fn build_config(&mut self) -> DeviceConfig {
895 DeviceConfig {
896 dev_name: self.dev_name.take(),
897 #[cfg(windows)]
898 description: self.description.take(),
899 #[cfg(target_os = "macos")]
900 peer_feth: self.peer_feth.take(),
901 #[cfg(any(
902 target_os = "macos",
903 target_os = "freebsd",
904 target_os = "openbsd",
905 target_os = "netbsd"
906 ))]
907 associate_route: self.associate_route,
908 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
909 reuse_dev: self.reuse_dev,
910 #[cfg(any(target_os = "macos", target_os = "windows"))]
911 persist: self.persist,
912 layer: self.layer.take(),
913 #[cfg(windows)]
914 device_guid: self.device_guid.take(),
915 #[cfg(windows)]
916 wintun_log: self.wintun_log.take(),
917 #[cfg(windows)]
918 wintun_file: self.wintun_file.take(),
919 #[cfg(windows)]
920 ring_capacity: self.ring_capacity.take(),
921 #[cfg(windows)]
922 delete_driver: self.delete_driver.take(),
923 #[cfg(windows)]
924 mac_address: self.mac_addr.map(|v| {
925 use std::fmt::Write;
926 v.iter()
927 .fold(String::with_capacity(v.len() * 2), |mut s, b| {
928 write!(&mut s, "{b:02X}").unwrap();
929 s
930 })
931 }),
932 #[cfg(any(
933 target_os = "macos",
934 target_os = "linux",
935 target_os = "freebsd",
936 target_os = "openbsd",
937 target_os = "netbsd"
938 ))]
939 packet_information: self.packet_information.take(),
940 #[cfg(target_os = "linux")]
941 offload: self.offload.take(),
942 #[cfg(target_os = "linux")]
943 multi_queue: self.multi_queue.take(),
944 }
945 }
946 pub(crate) fn config(self, device: &DeviceImpl) -> io::Result<()> {
947 if let Some(mtu) = self.mtu {
948 device.set_mtu(mtu)?;
949 }
950 #[cfg(windows)]
951 if let Some(mtu) = self.mtu_v6 {
952 device.set_mtu_v6(mtu)?;
953 }
954 #[cfg(windows)]
955 if let Some(metric) = self.metric {
956 device.set_metric(metric)?;
957 }
958 #[cfg(target_os = "linux")]
959 if let Some(tx_queue_len) = self.tx_queue_len {
960 device.set_tx_queue_len(tx_queue_len)?;
961 }
962 #[cfg(any(
963 target_os = "linux",
964 target_os = "freebsd",
965 target_os = "macos",
966 target_os = "openbsd",
967 target_os = "netbsd"
968 ))]
969 if let Some(mac_addr) = self.mac_addr {
970 device.set_mac_address(mac_addr)?;
971 }
972
973 if let Some((address, prefix, destination)) = self.ipv4 {
974 let prefix = prefix?;
975 let address = address?;
976 let destination = destination.transpose()?;
977 device.set_network_address(address, prefix, destination)?;
978 }
979 if let Some(ipv6) = self.ipv6 {
980 for (address, prefix) in ipv6 {
981 let prefix = prefix?;
982 let address = address?;
983 device.add_address_v6(address, prefix)?;
984 }
985 }
986 if let Some(enabled) = self.enabled {
987 device.enabled(enabled)?;
988 }
989 Ok(())
990 }
991 /// Builds a synchronous device instance and applies all configuration parameters.
992 pub fn build_sync(mut self) -> io::Result<SyncDevice> {
993 let device = DeviceImpl::new(self.build_config())?;
994 self.config(&device)?;
995 Ok(SyncDevice(device))
996 }
997 /// Builds an asynchronous device instance.
998 ///
999 /// This method is available only when either async_io or async_tokio feature is enabled.
1000 ///
1001 /// # Note
1002 /// Choose one of the two async runtimes; otherwise, a compile error will be incurred if both are enabled.
1003 #[cfg(any(feature = "async_io", feature = "async_tokio"))]
1004 pub fn build_async(self) -> io::Result<crate::AsyncDevice> {
1005 let sync_device = self.build_sync()?;
1006 let device = crate::AsyncDevice::new_dev(sync_device.0)?;
1007 Ok(device)
1008 }
1009 /// To conveniently set the platform-specific parameters without breaking the calling chain.
1010 /// # Ergonomic
1011 ///
1012 /// For example:
1013 /// ````no_run
1014 /// use tun_rs::DeviceBuilder;
1015 /// let builder = DeviceBuilder::new().name("tun1");
1016 /// #[cfg(target_os = "macos")]
1017 /// let builder = builder.associate_route(false);
1018 /// #[cfg(windows)]
1019 /// let builder = builder.wintun_log(false);
1020 /// let dev = builder.build_sync().unwrap();
1021 /// ````
1022 /// This is tedious and breaks the calling chain.
1023 ///
1024 /// With `with`, we can just set platform-specific parameters as follows without breaking the calling chain:
1025 /// ````no_run
1026 /// use tun_rs::DeviceBuilder;
1027 /// let dev = DeviceBuilder::new().name("tun1").with(|opt|{
1028 /// #[cfg(windows)]
1029 /// opt.wintun_log(false);
1030 /// #[cfg(target_os = "macos")]
1031 /// opt.associate_route(false).packet_information(false);
1032 /// }).build_sync().unwrap();
1033 /// ````
1034 pub fn with<F: FnMut(&mut DeviceBuilderGuard)>(mut self, mut f: F) -> Self {
1035 let mut borrow = DeviceBuilderGuard(&mut self);
1036 f(&mut borrow);
1037 self
1038 }
1039}
1040
1041/// Trait for converting various types into an IPv4 address.
1042pub trait ToIpv4Address {
1043 /// Attempts to convert the implementing type into an `Ipv4Addr`.
1044 /// Returns the IPv4 address on success or an error on failure.
1045 fn ipv4(&self) -> io::Result<Ipv4Addr>;
1046}
1047impl ToIpv4Address for Ipv4Addr {
1048 fn ipv4(&self) -> io::Result<Ipv4Addr> {
1049 Ok(*self)
1050 }
1051}
1052impl ToIpv4Address for IpAddr {
1053 fn ipv4(&self) -> io::Result<Ipv4Addr> {
1054 match self {
1055 IpAddr::V4(ip) => Ok(*ip),
1056 IpAddr::V6(_) => Err(io::Error::new(
1057 io::ErrorKind::InvalidData,
1058 "invalid address",
1059 )),
1060 }
1061 }
1062}
1063impl ToIpv4Address for String {
1064 fn ipv4(&self) -> io::Result<Ipv4Addr> {
1065 self.as_str().ipv4()
1066 }
1067}
1068impl ToIpv4Address for &str {
1069 fn ipv4(&self) -> io::Result<Ipv4Addr> {
1070 match Ipv4Addr::from_str(self) {
1071 Ok(ip) => Ok(ip),
1072 Err(_e) => Err(io::Error::new(
1073 io::ErrorKind::InvalidData,
1074 "invalid IPv4 str",
1075 )),
1076 }
1077 }
1078}
1079
1080/// Trait for converting various types into an IPv6 address.
1081pub trait ToIpv6Address {
1082 /// Attempts to convert the implementing type into an `Ipv6Addr`.
1083 /// Returns the IPv6 address on success or an error on failure.
1084 fn ipv6(&self) -> io::Result<Ipv6Addr>;
1085}
1086
1087impl ToIpv6Address for Ipv6Addr {
1088 fn ipv6(&self) -> io::Result<Ipv6Addr> {
1089 Ok(*self)
1090 }
1091}
1092impl ToIpv6Address for IpAddr {
1093 fn ipv6(&self) -> io::Result<Ipv6Addr> {
1094 match self {
1095 IpAddr::V4(_) => Err(io::Error::new(
1096 io::ErrorKind::InvalidData,
1097 "invalid address",
1098 )),
1099 IpAddr::V6(ip) => Ok(*ip),
1100 }
1101 }
1102}
1103impl ToIpv6Address for String {
1104 fn ipv6(&self) -> io::Result<Ipv6Addr> {
1105 self.as_str().ipv6()
1106 }
1107}
1108impl ToIpv6Address for &str {
1109 fn ipv6(&self) -> io::Result<Ipv6Addr> {
1110 match Ipv6Addr::from_str(self) {
1111 Ok(ip) => Ok(ip),
1112 Err(_e) => Err(io::Error::new(
1113 io::ErrorKind::InvalidData,
1114 "invalid IPv6 str",
1115 )),
1116 }
1117 }
1118}
1119/// Trait for converting various types into an IPv4 netmask (prefix length).
1120pub trait ToIpv4Netmask {
1121 /// Returns the prefix length (i.e., the number of consecutive 1s in the netmask).
1122 fn prefix(&self) -> io::Result<u8>;
1123 /// Computes the IPv4 netmask based on the prefix length.
1124 fn netmask(&self) -> io::Result<Ipv4Addr> {
1125 let ip = u32::MAX
1126 .checked_shl(32 - self.prefix()? as u32)
1127 .unwrap_or(0);
1128 Ok(Ipv4Addr::from(ip))
1129 }
1130}
1131
1132impl ToIpv4Netmask for u8 {
1133 fn prefix(&self) -> io::Result<u8> {
1134 if *self > 32 {
1135 return Err(io::Error::new(
1136 io::ErrorKind::InvalidData,
1137 "invalid IP prefix length",
1138 ));
1139 }
1140 Ok(*self)
1141 }
1142}
1143
1144impl ToIpv4Netmask for Ipv4Addr {
1145 fn prefix(&self) -> io::Result<u8> {
1146 let ip = u32::from_be_bytes(self.octets());
1147 // Validate that the netmask is contiguous (all 1s followed by all 0s).
1148 if ip.leading_ones() != ip.count_ones() {
1149 return Err(io::Error::new(
1150 io::ErrorKind::InvalidData,
1151 "invalid netmask",
1152 ));
1153 }
1154 Ok(ip.leading_ones() as u8)
1155 }
1156}
1157impl ToIpv4Netmask for String {
1158 fn prefix(&self) -> io::Result<u8> {
1159 ToIpv4Netmask::prefix(&self.as_str())
1160 }
1161}
1162impl ToIpv4Netmask for &str {
1163 fn prefix(&self) -> io::Result<u8> {
1164 match Ipv4Addr::from_str(self) {
1165 Ok(ip) => ip.prefix(),
1166 Err(_e) => Err(io::Error::new(
1167 io::ErrorKind::InvalidData,
1168 "invalid netmask str",
1169 )),
1170 }
1171 }
1172}
1173/// Trait for converting various types into an IPv6 netmask (prefix length).
1174pub trait ToIpv6Netmask {
1175 /// Returns the prefix length.
1176 fn prefix(&self) -> io::Result<u8>;
1177 /// Computes the IPv6 netmask based on the prefix length.
1178 fn netmask(&self) -> io::Result<Ipv6Addr> {
1179 let ip = u128::MAX
1180 .checked_shl(128 - self.prefix()? as u32)
1181 .unwrap_or(0);
1182 Ok(Ipv6Addr::from(ip))
1183 }
1184}
1185
1186impl ToIpv6Netmask for u8 {
1187 fn prefix(&self) -> io::Result<u8> {
1188 if *self > 128 {
1189 return Err(io::Error::new(
1190 io::ErrorKind::InvalidData,
1191 "invalid IP prefix length",
1192 ));
1193 }
1194 Ok(*self)
1195 }
1196}
1197
1198impl ToIpv6Netmask for Ipv6Addr {
1199 fn prefix(&self) -> io::Result<u8> {
1200 let ip = u128::from_be_bytes(self.octets());
1201 if ip.leading_ones() != ip.count_ones() {
1202 return Err(io::Error::new(
1203 io::ErrorKind::InvalidData,
1204 "invalid netmask",
1205 ));
1206 }
1207 Ok(ip.leading_ones() as u8)
1208 }
1209}
1210impl ToIpv6Netmask for String {
1211 fn prefix(&self) -> io::Result<u8> {
1212 ToIpv6Netmask::prefix(&self.as_str())
1213 }
1214}
1215impl ToIpv6Netmask for &str {
1216 fn prefix(&self) -> io::Result<u8> {
1217 match Ipv6Addr::from_str(self) {
1218 Ok(ip) => ip.prefix(),
1219 Err(_e) => Err(io::Error::new(
1220 io::ErrorKind::InvalidData,
1221 "invalid netmask str",
1222 )),
1223 }
1224 }
1225}