diff --git a/tun/src/lib.rs b/tun/src/lib.rs index 7e86059..99f74fc 100644 --- a/tun/src/lib.rs +++ b/tun/src/lib.rs @@ -6,4 +6,7 @@ mod imp; #[path = "unix/mod.rs"] pub(crate) mod imp; +mod options; + pub use imp::{TunInterface, TunQueue}; +pub use options::TunInterfaceOptions; diff --git a/tun/src/options.rs b/tun/src/options.rs new file mode 100644 index 0000000..7b7d478 --- /dev/null +++ b/tun/src/options.rs @@ -0,0 +1,38 @@ +use fehler::throws; +use std::io::Error; + +use super::TunInterface; + +#[derive(Default)] +pub struct TunInterfaceOptions { + /// (Windows + Linux) Name the tun interface. + pub(crate) name: Option, + /// (Linux) Don't include packet information. + pub(crate) no_pi: Option<()>, + /// (Linux) Avoid opening an existing persistant device. + pub(crate) tun_excl: Option<()>, +} + +impl TunInterfaceOptions { + pub fn new() -> Self { + Self::default() + } + + pub fn name(mut self, name: &str) -> Self { + self.name = Some(name.to_owned()); + self + } + + pub fn no_pi(mut self, enable: bool) { + self.no_pi = enable.then_some(()); + } + + pub fn tun_excl(mut self, enable: bool) { + self.tun_excl = enable.then_some(()); + } + + #[throws] + pub fn open(self) -> TunInterface { + TunInterface::new_with_options(self)? + } +} diff --git a/tun/src/unix/apple/mod.rs b/tun/src/unix/apple/mod.rs index a88078d..4ed002c 100644 --- a/tun/src/unix/apple/mod.rs +++ b/tun/src/unix/apple/mod.rs @@ -15,7 +15,7 @@ mod sys; pub use super::queue::TunQueue; -use super::{ifname_to_string, string_to_ifname}; +use super::{ifname_to_string, string_to_ifname, TunInterfaceOptions}; use kern_control::SysControlSocket; #[derive(Debug)] @@ -26,6 +26,11 @@ pub struct TunInterface { impl TunInterface { #[throws] pub fn new() -> TunInterface { + Self::new_with_options(TunInterfaceOptions::new())? + } + + #[throws] + pub fn new_with_options(_: TunInterfaceOptions) -> TunInterface { TunInterface::connect(0)? } diff --git a/tun/src/unix/linux/mod.rs b/tun/src/unix/linux/mod.rs index 242e7c9..af2693d 100644 --- a/tun/src/unix/linux/mod.rs +++ b/tun/src/unix/linux/mod.rs @@ -12,7 +12,7 @@ use log::info; use libc::in6_ifreq; -use super::{ifname_to_string, string_to_ifname}; +use super::{ifname_to_string, string_to_ifname, TunInterfaceOptions}; mod sys; @@ -24,16 +24,33 @@ pub struct TunInterface { impl TunInterface { #[throws] pub fn new() -> TunInterface { + Self::new_with_options(TunInterfaceOptions::new())? + } + + #[throws] + pub(crate) fn new_with_options(options: TunInterfaceOptions) -> TunInterface { let file = OpenOptions::new() .read(true) .write(true) .open("/dev/net/tun")?; + let mut flags = libc::IFF_TUN as i16; + + if options.no_pi.is_some() { + flags |= libc::IFF_NO_PI as i16; + } + if options.tun_excl.is_some() { + flags |= libc::IFF_TUN_EXCL as i16; + } + + let name = options + .name + .map(|name| string_to_ifname(&name)) + .unwrap_or([0; libc::IFNAMSIZ]); + let iff = libc::ifreq { - ifr_name: [0; libc::IFNAMSIZ], - ifr_ifru: libc::__c_anonymous_ifr_ifru { - ifru_flags: (libc::IFF_TUN | libc::IFF_TUN_EXCL | libc::IFF_NO_PI) as i16, - }, + ifr_name: name, + ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: flags }, }; unsafe { sys::tun_set_iff(file.as_raw_fd(), &iff)? }; diff --git a/tun/src/unix/mod.rs b/tun/src/unix/mod.rs index 8f194b7..e4960da 100644 --- a/tun/src/unix/mod.rs +++ b/tun/src/unix/mod.rs @@ -3,6 +3,8 @@ use std::{ os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}, }; +use super::TunInterfaceOptions; + mod queue; #[cfg(target_vendor = "apple")] @@ -71,7 +73,7 @@ mod test { use super::*; use std::io::Write; - + use std::net::Ipv4Addr; #[throws] diff --git a/tun/src/windows/mod.rs b/tun/src/windows/mod.rs index 6c0a19c..d38e7dc 100644 --- a/tun/src/windows/mod.rs +++ b/tun/src/windows/mod.rs @@ -5,6 +5,8 @@ use widestring::{u16cstr, U16CString}; use windows::Win32::Foundation::GetLastError; mod queue; +use super::TunInterfaceOptions; + pub use queue::TunQueue; pub struct TunInterface { @@ -15,7 +17,14 @@ pub struct TunInterface { impl TunInterface { #[throws] pub fn new() -> TunInterface { - let name = U16CString::from(u16cstr!("Burrow")); + Self::new_with_options(TunInterfaceOptions::new())? + } + + #[throws] + pub(crate) fn new_with_options(options: TunInterfaceOptions) -> TunInterface { + let name_owned = options.name.unwrap_or("Burrow".to_owned()); + let name = U16CString::from_str(&name_owned).unwrap(); + let handle = unsafe { sys::WINTUN.WintunCreateAdapter(name.as_ptr(), name.as_ptr(), ptr::null()) }; if handle.is_null() { @@ -23,7 +32,7 @@ impl TunInterface { } TunInterface { handle, - name: String::from("Burrow"), + name: name_owned, } }