支持tap网卡,优化tun网卡配置
This commit is contained in:
@@ -0,0 +1,328 @@
|
||||
use winapi::shared::ifdef::NET_LUID;
|
||||
use winapi::shared::minwindef::*;
|
||||
|
||||
use winapi::um::fileapi::*;
|
||||
use winapi::um::setupapi::*;
|
||||
use winapi::um::winnt::*;
|
||||
|
||||
use scopeguard::{guard, ScopeGuard};
|
||||
use winreg::RegKey;
|
||||
|
||||
use std::io;
|
||||
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
|
||||
|
||||
use crate::{decode_utf16, encode_utf16, ffi};
|
||||
|
||||
/// tap-windows hardware ID
|
||||
const HARDWARE_ID: &str = "tap0901";
|
||||
|
||||
winapi::DEFINE_GUID! {
|
||||
GUID_NETWORK_ADAPTER,
|
||||
0x4d36e972, 0xe325, 0x11ce,
|
||||
0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18
|
||||
}
|
||||
|
||||
/// Create a new interface and returns its NET_LUID
|
||||
pub fn create_interface() -> io::Result<NET_LUID> {
|
||||
let devinfo = ffi::create_device_info_list(&GUID_NETWORK_ADAPTER)?;
|
||||
|
||||
let _guard = guard((), |_| {
|
||||
let _ = ffi::destroy_device_info_list(devinfo);
|
||||
});
|
||||
|
||||
let class_name = ffi::class_name_from_guid(&GUID_NETWORK_ADAPTER)?;
|
||||
|
||||
let devinfo_data = ffi::create_device_info(
|
||||
devinfo,
|
||||
&class_name,
|
||||
&GUID_NETWORK_ADAPTER,
|
||||
&encode_utf16(""),
|
||||
DICD_GENERATE_ID,
|
||||
)?;
|
||||
|
||||
ffi::set_selected_device(devinfo, &devinfo_data)?;
|
||||
ffi::set_device_registry_property(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
SPDRP_HARDWAREID,
|
||||
&encode_utf16(HARDWARE_ID),
|
||||
)?;
|
||||
|
||||
ffi::build_driver_info_list(devinfo, &devinfo_data, SPDIT_COMPATDRIVER)?;
|
||||
|
||||
let _guard = guard((), |_| {
|
||||
let _ = ffi::destroy_driver_info_list(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
SPDIT_COMPATDRIVER,
|
||||
);
|
||||
});
|
||||
|
||||
let mut driver_version = 0;
|
||||
let mut member_index = 0;
|
||||
|
||||
while let Some(drvinfo_data) = ffi::enum_driver_info(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
SPDIT_COMPATDRIVER,
|
||||
member_index,
|
||||
) {
|
||||
member_index += 1;
|
||||
|
||||
let drvinfo_data = match drvinfo_data {
|
||||
Ok(drvinfo_data) => drvinfo_data,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
if drvinfo_data.DriverVersion <= driver_version {
|
||||
continue;
|
||||
}
|
||||
|
||||
let drvinfo_detail = match ffi::get_driver_info_detail(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
&drvinfo_data,
|
||||
) {
|
||||
Ok(drvinfo_detail) => drvinfo_detail,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let is_compatible = drvinfo_detail
|
||||
.HardwareID
|
||||
.split(|b| *b == 0)
|
||||
.map(|id| decode_utf16(id))
|
||||
.any(|id| id.eq_ignore_ascii_case(HARDWARE_ID));
|
||||
|
||||
if !is_compatible {
|
||||
continue;
|
||||
}
|
||||
|
||||
match ffi::set_selected_driver(devinfo, &devinfo_data, &drvinfo_data) {
|
||||
Ok(_) => (),
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
driver_version = drvinfo_data.DriverVersion;
|
||||
}
|
||||
|
||||
if driver_version == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound, "No driver found"));
|
||||
}
|
||||
|
||||
let uninstaller = guard((), |_| {
|
||||
let _ = ffi::call_class_installer(devinfo, &devinfo_data, DIF_REMOVE);
|
||||
});
|
||||
|
||||
ffi::call_class_installer(devinfo, &devinfo_data, DIF_REGISTERDEVICE)?;
|
||||
|
||||
let _ = ffi::call_class_installer(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
DIF_REGISTER_COINSTALLERS,
|
||||
);
|
||||
let _ = ffi::call_class_installer(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
DIF_INSTALLINTERFACES,
|
||||
);
|
||||
|
||||
ffi::call_class_installer(devinfo, &devinfo_data, DIF_INSTALLDEVICE)?;
|
||||
|
||||
let key = ffi::open_dev_reg_key(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
DICS_FLAG_GLOBAL,
|
||||
0,
|
||||
DIREG_DRV,
|
||||
KEY_QUERY_VALUE | KEY_NOTIFY,
|
||||
)?;
|
||||
|
||||
let key = RegKey::predef(key);
|
||||
|
||||
while let Err(_) = key.get_value::<DWORD, &str>("*IfType") {
|
||||
ffi::notify_change_key_value(
|
||||
key.raw_handle(),
|
||||
TRUE,
|
||||
REG_NOTIFY_CHANGE_NAME,
|
||||
2000,
|
||||
)?;
|
||||
}
|
||||
|
||||
while let Err(_) = key.get_value::<DWORD, &str>("NetLuidIndex") {
|
||||
ffi::notify_change_key_value(
|
||||
key.raw_handle(),
|
||||
TRUE,
|
||||
REG_NOTIFY_CHANGE_NAME,
|
||||
2000,
|
||||
)?;
|
||||
}
|
||||
|
||||
let if_type: DWORD = key.get_value("*IfType")?;
|
||||
let luid_index: DWORD = key.get_value("NetLuidIndex")?;
|
||||
|
||||
// Defuse the uninstaller
|
||||
ScopeGuard::into_inner(uninstaller);
|
||||
|
||||
let mut luid = NET_LUID { Value: 0 };
|
||||
|
||||
luid.set_IfType(if_type as _);
|
||||
luid.set_NetLuidIndex(luid_index as _);
|
||||
|
||||
Ok(luid)
|
||||
}
|
||||
|
||||
/// Check if the given interface exists and is a valid tap-windows device
|
||||
pub fn check_interface(luid: &NET_LUID) -> io::Result<()> {
|
||||
let devinfo = ffi::get_class_devs(&GUID_NETWORK_ADAPTER, DIGCF_PRESENT)?;
|
||||
|
||||
let _guard = guard((), |_| {
|
||||
let _ = ffi::destroy_device_info_list(devinfo);
|
||||
});
|
||||
|
||||
let mut member_index = 0;
|
||||
|
||||
while let Some(devinfo_data) = ffi::enum_device_info(devinfo, member_index)
|
||||
{
|
||||
member_index += 1;
|
||||
|
||||
let devinfo_data = match devinfo_data {
|
||||
Ok(devinfo_data) => devinfo_data,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let hardware_id = match ffi::get_device_registry_property(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
SPDRP_HARDWAREID,
|
||||
) {
|
||||
Ok(hardware_id) => hardware_id,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
if !decode_utf16(&hardware_id).eq_ignore_ascii_case(HARDWARE_ID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let key = match ffi::open_dev_reg_key(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
DICS_FLAG_GLOBAL,
|
||||
0,
|
||||
DIREG_DRV,
|
||||
KEY_QUERY_VALUE | KEY_NOTIFY,
|
||||
) {
|
||||
Ok(key) => RegKey::predef(key),
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let if_type: DWORD = match key.get_value("*IfType") {
|
||||
Ok(if_type) => if_type,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let luid_index: DWORD = match key.get_value("NetLuidIndex") {
|
||||
Ok(luid_index) => luid_index,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let mut luid2 = NET_LUID { Value: 0 };
|
||||
|
||||
luid2.set_IfType(if_type as _);
|
||||
luid2.set_NetLuidIndex(luid_index as _);
|
||||
|
||||
if luid.Value != luid2.Value {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found it!
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "TAP Device not found"))
|
||||
}
|
||||
|
||||
/// Deletes an existing interface
|
||||
pub fn delete_interface(luid: &NET_LUID) -> io::Result<()> {
|
||||
let devinfo = ffi::get_class_devs(&GUID_NETWORK_ADAPTER, DIGCF_PRESENT)?;
|
||||
|
||||
let _guard = guard((), |_| {
|
||||
let _ = ffi::destroy_device_info_list(devinfo);
|
||||
});
|
||||
|
||||
let mut member_index = 0;
|
||||
|
||||
while let Some(devinfo_data) = ffi::enum_device_info(devinfo, member_index)
|
||||
{
|
||||
member_index += 1;
|
||||
|
||||
let devinfo_data = match devinfo_data {
|
||||
Ok(devinfo_data) => devinfo_data,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let hardware_id = match ffi::get_device_registry_property(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
SPDRP_HARDWAREID,
|
||||
) {
|
||||
Ok(hardware_id) => hardware_id,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
if !decode_utf16(&hardware_id).eq_ignore_ascii_case(HARDWARE_ID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let key = match ffi::open_dev_reg_key(
|
||||
devinfo,
|
||||
&devinfo_data,
|
||||
DICS_FLAG_GLOBAL,
|
||||
0,
|
||||
DIREG_DRV,
|
||||
KEY_QUERY_VALUE | KEY_NOTIFY,
|
||||
) {
|
||||
Ok(key) => RegKey::predef(key),
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let if_type: DWORD = match key.get_value("*IfType") {
|
||||
Ok(if_type) => if_type,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let luid_index: DWORD = match key.get_value("NetLuidIndex") {
|
||||
Ok(luid_index) => luid_index,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let mut luid2 = NET_LUID { Value: 0 };
|
||||
|
||||
luid2.set_IfType(if_type as _);
|
||||
luid2.set_NetLuidIndex(luid_index as _);
|
||||
|
||||
if luid.Value != luid2.Value {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found it!
|
||||
return ffi::call_class_installer(devinfo, &devinfo_data, DIF_REMOVE);
|
||||
}
|
||||
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "TAP Device not found"))
|
||||
}
|
||||
|
||||
/// Open an handle to an interface
|
||||
pub fn open_interface(luid: &NET_LUID) -> io::Result<HANDLE> {
|
||||
let guid = ffi::luid_to_guid(luid)
|
||||
.and_then(|guid| ffi::string_from_guid(&guid))?;
|
||||
|
||||
let path = format!(r"\\.\Global\{}.tap", &decode_utf16(&guid));
|
||||
|
||||
ffi::create_file(
|
||||
&encode_utf16(&path),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_SYSTEM,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
use std::{io, net, time};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use winapi::shared::ifdef::NET_LUID;
|
||||
use winapi::shared::minwindef::*;
|
||||
use winapi::um::winioctl::*;
|
||||
use winapi::um::winnt::HANDLE;
|
||||
|
||||
use crate::{decode_utf16, encode_utf16, ffi, IFace, netsh, route};
|
||||
|
||||
mod iface;
|
||||
|
||||
pub struct TapDevice {
|
||||
luid: NET_LUID,
|
||||
handle: HANDLE,
|
||||
|
||||
}
|
||||
unsafe impl Send for TapDevice{}
|
||||
unsafe impl Sync for TapDevice{}
|
||||
|
||||
impl TapDevice {
|
||||
/// Retieve the mac of the interface
|
||||
pub fn get_mac(&self) -> io::Result<[u8; 6]> {
|
||||
let mut mac = [0; 6];
|
||||
|
||||
ffi::device_io_control(
|
||||
self.handle,
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS),
|
||||
&(),
|
||||
&mut mac,
|
||||
)
|
||||
.map(|_| mac)
|
||||
}
|
||||
|
||||
/// Retrieve the version of the driver
|
||||
pub fn get_version(&self) -> io::Result<[u32; 3]> {
|
||||
let mut version = [0; 3];
|
||||
|
||||
ffi::device_io_control(
|
||||
self.handle,
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS),
|
||||
&(),
|
||||
&mut version,
|
||||
)
|
||||
.map(|_| version)
|
||||
}
|
||||
|
||||
/// Retieve the mtu of the interface
|
||||
pub fn get_mtu(&self) -> io::Result<u32> {
|
||||
let mut mtu = 0;
|
||||
|
||||
ffi::device_io_control(
|
||||
self.handle,
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS),
|
||||
&(),
|
||||
&mut mtu,
|
||||
)
|
||||
.map(|_| mtu)
|
||||
}
|
||||
|
||||
|
||||
/// Set the status of the interface, true for connected,
|
||||
/// false for disconnected.
|
||||
pub fn set_status(&self, status: bool) -> io::Result<()> {
|
||||
let status: u32 = if status { 1 } else { 0 };
|
||||
ffi::device_io_control(
|
||||
self.handle,
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS),
|
||||
&status,
|
||||
&mut (),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TapDevice {
|
||||
pub fn create() -> io::Result<Self> {
|
||||
let luid = iface::create_interface()?;
|
||||
// Even after retrieving the luid, we might need to wait
|
||||
let start = time::Instant::now();
|
||||
let handle = loop {
|
||||
// If we surpassed 2 seconds just return
|
||||
let now = time::Instant::now();
|
||||
if now - start > time::Duration::from_secs(3) {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::TimedOut,
|
||||
"Interface timed out",
|
||||
));
|
||||
}
|
||||
|
||||
match iface::open_interface(&luid) {
|
||||
Err(_) => {
|
||||
std::thread::yield_now();
|
||||
continue;
|
||||
}
|
||||
Ok(handle) => break handle,
|
||||
};
|
||||
};
|
||||
Ok(Self { luid, handle })
|
||||
}
|
||||
|
||||
pub fn open(name: &str) -> io::Result<Self> {
|
||||
let name = encode_utf16(name);
|
||||
|
||||
let luid = ffi::alias_to_luid(&name)?;
|
||||
iface::check_interface(&luid)?;
|
||||
|
||||
let handle = iface::open_interface(&luid)?;
|
||||
Ok(Self { luid, handle })
|
||||
}
|
||||
|
||||
pub fn delete(self) -> io::Result<()> {
|
||||
iface::delete_interface(&self.luid)
|
||||
}
|
||||
}
|
||||
|
||||
impl IFace for TapDevice {
|
||||
fn shutdown(&self) -> io::Result<()> {
|
||||
self.set_status(false)
|
||||
}
|
||||
|
||||
fn get_index(&self) -> io::Result<u32> {
|
||||
ffi::luid_to_index(&self.luid).map(|index| index as u32)
|
||||
}
|
||||
|
||||
fn get_name(&self) -> io::Result<String> {
|
||||
ffi::luid_to_alias(&self.luid).map(|name| decode_utf16(&name))
|
||||
}
|
||||
|
||||
fn set_name(&self, new_name: &str) -> io::Result<()> {
|
||||
let name = self.get_name()?;
|
||||
netsh::set_interface_name(&name, new_name)
|
||||
}
|
||||
|
||||
fn set_ip<IP>(&self, address: IP, mask: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
|
||||
let index = self.get_index()?;
|
||||
netsh::set_interface_ip(index, &address.into(), &mask.into())
|
||||
}
|
||||
|
||||
fn add_route<IP>(&self, dest: IP, netmask: IP, gateway: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
|
||||
let index = self.get_index()?;
|
||||
route::add_route(index, dest.into(), netmask.into(), gateway.into())
|
||||
}
|
||||
|
||||
fn delete_route<IP>(&self, dest: IP, netmask: IP, gateway: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
|
||||
let index = self.get_index()?;
|
||||
route::delete_route(index, dest.into(), netmask.into(), gateway.into())
|
||||
}
|
||||
|
||||
fn set_mtu(&self, mtu: u16) -> io::Result<()> {
|
||||
let index = self.get_index()?;
|
||||
netsh::set_interface_mtu(index, mtu)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl TapDevice {
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
ffi::read_file(self.handle, buf).map(|res| res as _)
|
||||
}
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
ffi::write_file(self.handle, buf).map(|res| res as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TapDevice {
|
||||
fn drop(&mut self) {
|
||||
let _ = ffi::close_handle(self.handle);
|
||||
let _ = iface::delete_interface(&self.luid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user