mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-10-30 16:05:31 +00:00
fix(windows): handle missing registry values during TUN name allocation (#2777)
* fix(windows): reuse TUN names with incomplete registry records A leftover Connection key must not force a new adapter identity after EasyTier has reserved the original name. Skip that identity before reading registry values and let rust-tun reopen or recreate the adapter. Keep conflict checks for other GUIDs: Wintun can otherwise rename an unrelated adapter that already uses the candidate name. Share the GUID calculation with VirtualNic so allocation and creation stay consistent. Verified on Windows 11 with concurrent processes, normal and forced exit, missing Name/PnpInstanceID values, and a different-GUID collision. The original reservation tests, Linux workspace Clippy, formatting, and Windows core checks pass. Co-authored-by: Daniel <2116528714@qq.com> --------- Co-authored-by: KKRainbow <5665404+KKRainbow@users.noreply.github.com>
This commit is contained in:
co-authored by
Daniel
KKRainbow
parent
c66076b8f3
commit
18f8e41fa1
@@ -24,7 +24,7 @@ use windows::{
|
||||
};
|
||||
|
||||
mod tun_name;
|
||||
pub(crate) use tun_name::reserve_tun_name;
|
||||
pub(crate) use tun_name::{reserve_tun_name, tun_device_guid};
|
||||
|
||||
pub fn disable_connection_reset<S: AsRawSocket>(socket: &S) -> io::Result<()> {
|
||||
let handle = SOCKET(socket.as_raw_socket() as usize);
|
||||
|
||||
@@ -52,24 +52,49 @@ fn reserve_name(name: &str) -> anyhow::Result<Option<OwnedHandle>> {
|
||||
Ok((!existed).then_some(handle))
|
||||
}
|
||||
|
||||
fn interface_exists(name: &str) -> anyhow::Result<bool> {
|
||||
pub(crate) fn tun_device_guid(name: &str) -> uuid::Uuid {
|
||||
let identity = format!("https://easytier.rs/wintun/{}", name.to_lowercase());
|
||||
uuid::Uuid::new_v5(&uuid::Uuid::NAMESPACE_URL, identity.as_bytes())
|
||||
}
|
||||
|
||||
fn interface_name_conflicts(name: &str) -> anyhow::Result<bool> {
|
||||
// IP interface tables can retain removed adapters after process exit.
|
||||
// Check the device tree: disabled devices still occupy their names, but
|
||||
// nonpresent devices left by a terminated process do not.
|
||||
let network = RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey(
|
||||
r"SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}",
|
||||
)?;
|
||||
let expected_guid = format!("{{{}}}", tun_device_guid(name));
|
||||
for guid in network.enum_keys() {
|
||||
let connection = match network.open_subkey(format!(r"{}\Connection", guid?)) {
|
||||
let guid = guid?;
|
||||
// The caller holds the name reservation, so another EasyTier instance
|
||||
// cannot use this identity. Let rust-tun reopen or recreate it even if
|
||||
// Windows retained an incomplete Connection key.
|
||||
if guid.eq_ignore_ascii_case(&expected_guid) {
|
||||
continue;
|
||||
}
|
||||
let connection = match network.open_subkey(format!(r"{}\Connection", guid)) {
|
||||
Ok(key) => key,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => continue,
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
let existing_name: String = connection.get_value("Name")?;
|
||||
let existing_name: String = match connection.get_value("Name") {
|
||||
Ok(name) => name,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => continue,
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
if !existing_name.eq_ignore_ascii_case(name) {
|
||||
continue;
|
||||
}
|
||||
let instance_id: String = connection.get_value("PnpInstanceID")?;
|
||||
let instance_id: String = match connection.get_value("PnpInstanceID") {
|
||||
Ok(id) => id,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
// The name exists, but device presence cannot be verified.
|
||||
// Keep it occupied rather than risk a name collision.
|
||||
return Ok(true);
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
let mut dev_inst = 0;
|
||||
let result = unsafe {
|
||||
CM_Locate_DevNodeW(
|
||||
@@ -99,7 +124,7 @@ pub(crate) fn reserve_tun_name(requested: &str) -> anyhow::Result<(String, Owned
|
||||
let Some(handle) = reserve_name(&name)? else {
|
||||
continue;
|
||||
};
|
||||
if !interface_exists(&name)? {
|
||||
if !interface_name_conflicts(&name)? {
|
||||
return Ok((name, handle));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,10 +581,7 @@ impl VirtualNic {
|
||||
config.platform_config(|config| {
|
||||
// Wintun uses the GUID for Windows network profile identity.
|
||||
// Include the name so explicitly shared NICs use the same GUID.
|
||||
let identity = format!("https://easytier.rs/wintun/{}", dev_name.to_lowercase());
|
||||
config.device_guid(
|
||||
uuid::Uuid::new_v5(&uuid::Uuid::NAMESPACE_URL, identity.as_bytes()).as_u128(),
|
||||
);
|
||||
config.device_guid(crate::arch::windows::tun_device_guid(&dev_name).as_u128());
|
||||
config.skip_config(true);
|
||||
config.ring_cap(Some(std::cmp::min(
|
||||
config.min_ring_cap() * 32,
|
||||
|
||||
Reference in New Issue
Block a user