feat(client): persist TAP MAC identity
The Windows client already generates and announces a stable locally administered MAC, but it only rejected the TAP adapter when the driver reported a different address. Persist the tunnel MAC to tap-windows6's NetworkAddress registry value before opening the adapter so the driver can load the intended current address. The TAP crate now keeps the driver registry key name from discovery, formats the NetworkAddress value as the 12-digit hex string expected by NDIS, and rejects invalid multicast, broadcast, or globally administered MACs before writing. Runtime validation stays in place. tap-windows6 reads NetworkAddress during adapter initialization, so an adapter that Windows already initialized with an old value may still need disable/enable or reinstall on the real Windows test machine before the GET_MAC ioctl reports the new identity. Test Plan: - cargo fmt --check - cargo test -p lanparty-client-tap - cargo test -p lanparty-client-win - cargo clippy -p lanparty-client-tap --all-targets -- -D warnings - cargo check -p lanparty-client-tap --target x86_64-pc-windows-gnu - cargo check -p lanparty-client-tap --target x86_64-pc-windows-msvc - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check Refs: PLAN.md
This commit is contained in:
@@ -18,15 +18,15 @@ use windows_sys::Win32::{
|
||||
System::{
|
||||
IO::DeviceIoControl,
|
||||
Registry::{
|
||||
HKEY, HKEY_LOCAL_MACHINE, KEY_READ, REG_SZ, RegCloseKey, RegEnumKeyExW, RegOpenKeyExW,
|
||||
RegQueryValueExW,
|
||||
HKEY, HKEY_LOCAL_MACHINE, KEY_READ, KEY_SET_VALUE, REG_SZ, RegCloseKey, RegEnumKeyExW,
|
||||
RegOpenKeyExW, RegQueryValueExW, RegSetValueExW,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
TAP_ADAPTER_KEY, TapAdapterInfo, is_tap_component_id, tap_ioctl_get_mac, tap_ioctl_get_mtu,
|
||||
tap_ioctl_set_media_status, validate_tap_ethernet_frame,
|
||||
tap_ioctl_set_media_status, tap_network_address_value, validate_tap_ethernet_frame,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -206,15 +206,28 @@ pub fn open_first_adapter() -> Result<TapAdapter> {
|
||||
TapAdapter::open(info)
|
||||
}
|
||||
|
||||
pub fn configure_adapter_mac(info: &TapAdapterInfo, mac: MacAddr) -> Result<()> {
|
||||
let driver_key_name = info
|
||||
.driver_key_name()
|
||||
.context("TAP adapter was not discovered from the Windows registry")?;
|
||||
let registry_path = format!("{TAP_ADAPTER_KEY}\\{driver_key_name}");
|
||||
let key = RegKey::open_with_access(HKEY_LOCAL_MACHINE, ®istry_path, KEY_SET_VALUE)
|
||||
.with_context(|| format!("failed to open TAP adapter registry key {registry_path}"))?;
|
||||
key.set_string("NetworkAddress", &tap_network_address_value(mac)?)
|
||||
.context("failed to configure TAP adapter NetworkAddress")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn available_adapters() -> Result<Vec<TapAdapterInfo>> {
|
||||
let adapters_key = RegKey::open(HKEY_LOCAL_MACHINE, TAP_ADAPTER_KEY)
|
||||
.context("failed to open TAP adapter registry key")?;
|
||||
let mut adapters = Vec::new();
|
||||
|
||||
for subkey in adapters_key.subkey_names()? {
|
||||
for subkey_name in adapters_key.subkey_names()? {
|
||||
let subkey = adapters_key
|
||||
.open_subkey(&subkey)
|
||||
.with_context(|| format!("failed to open TAP adapter registry subkey {subkey}"))?;
|
||||
.open_subkey(&subkey_name)
|
||||
.with_context(|| format!("failed to open TAP adapter registry subkey {subkey_name}"))?;
|
||||
let Some(component_id) = subkey.query_string("ComponentId")? else {
|
||||
continue;
|
||||
};
|
||||
@@ -225,7 +238,11 @@ pub fn available_adapters() -> Result<Vec<TapAdapterInfo>> {
|
||||
continue;
|
||||
};
|
||||
|
||||
adapters.push(TapAdapterInfo::new(instance_id, component_id)?);
|
||||
adapters.push(TapAdapterInfo::from_registry(
|
||||
subkey_name,
|
||||
instance_id,
|
||||
component_id,
|
||||
)?);
|
||||
}
|
||||
|
||||
Ok(adapters)
|
||||
@@ -272,11 +289,15 @@ struct RegKey(HKEY);
|
||||
|
||||
impl RegKey {
|
||||
fn open(root: HKEY, path: &str) -> io::Result<Self> {
|
||||
Self::open_with_access(root, path, KEY_READ)
|
||||
}
|
||||
|
||||
fn open_with_access(root: HKEY, path: &str, access: u32) -> io::Result<Self> {
|
||||
let path = wide_null(path);
|
||||
let mut key = null_mut();
|
||||
let status = unsafe {
|
||||
// SAFETY: path is NUL-terminated and phkresult points to valid storage.
|
||||
RegOpenKeyExW(root, path.as_ptr(), 0, KEY_READ, &mut key)
|
||||
RegOpenKeyExW(root, path.as_ptr(), 0, access, &mut key)
|
||||
};
|
||||
windows_status(status)?;
|
||||
|
||||
@@ -375,6 +396,24 @@ impl RegKey {
|
||||
|
||||
Ok(Some(String::from_utf16_lossy(&buffer)))
|
||||
}
|
||||
|
||||
fn set_string(&self, name: &str, value: &str) -> io::Result<()> {
|
||||
let name = wide_null(name);
|
||||
let value = wide_null(value);
|
||||
let status = unsafe {
|
||||
// SAFETY: name and value are NUL-terminated UTF-16 buffers. REG_SZ data length is
|
||||
// measured in bytes and includes the trailing NUL.
|
||||
RegSetValueExW(
|
||||
self.0,
|
||||
name.as_ptr(),
|
||||
0,
|
||||
REG_SZ,
|
||||
value.as_ptr().cast::<u8>(),
|
||||
(value.len() * std::mem::size_of::<u16>()) as u32,
|
||||
)
|
||||
};
|
||||
windows_status(status)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RegKey {
|
||||
|
||||
Reference in New Issue
Block a user