feat(client): refresh TAP IP diagnostics

The Windows client sampled TAP diagnostics once when opening the adapter. That
can miss the useful DHCP result because the TAP interface may not receive a LAN
address until after frame bridging starts.

Keep the stable TAP diagnostics fields from startup, but retain the interface
identity and re-read the TAP unicast IP whenever the periodic diagnostics tick
prints and reports a snapshot. Later status lines can now show the DHCP address
that arrives after the tunnel is already moving traffic.

Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check

Refs: PLAN.md
This commit is contained in:
2026-05-21 21:26:55 +02:00
parent 6adde91208
commit 3fa78fc935
2 changed files with 53 additions and 8 deletions
+51 -7
View File
@@ -1,6 +1,9 @@
#[cfg(windows)]
use std::net::IpAddr;
use std::{collections::BTreeMap, fs, net::SocketAddr, path::PathBuf};
use std::{
collections::BTreeMap,
fs,
net::{IpAddr, SocketAddr},
path::PathBuf,
};
#[cfg(windows)]
use std::{
sync::{Arc, mpsc},
@@ -139,6 +142,7 @@ async fn run_client(session: &ClientSession, relay_route_pin: &PinnedRelayRoute)
let OpenedTapAdapter {
tap,
tap_diagnostics,
tap_interface,
_route_guard,
} = open_tap_adapter(session)?;
let relay_route =
@@ -147,7 +151,11 @@ async fn run_client(session: &ClientSession, relay_route_pin: &PinnedRelayRoute)
print_verified_relay_route(&relay_route);
print_and_report_client_diagnostics(
session,
&client_diagnostics_snapshot(session, true, tap_diagnostics.clone()),
&client_diagnostics_snapshot(
session,
true,
current_tap_diagnostics(&tap_diagnostics, tap_interface),
),
)
.await;
println!(
@@ -186,7 +194,11 @@ async fn run_client(session: &ClientSession, relay_route_pin: &PinnedRelayRoute)
_ = diagnostics_check.tick() => {
print_and_report_client_diagnostics(
session,
&client_diagnostics_snapshot(session, true, tap_diagnostics.clone()),
&client_diagnostics_snapshot(
session,
true,
current_tap_diagnostics(&tap_diagnostics, tap_interface),
),
).await;
}
}
@@ -313,6 +325,7 @@ fn route_next_hop_label(next_hop: Option<std::net::IpAddr>) -> String {
struct OpenedTapAdapter {
tap: TapAdapter,
tap_diagnostics: TapDiagnostics,
tap_interface: NetworkInterfaceIdentity,
_route_guard: TapRouteProtectionGuard,
}
@@ -353,17 +366,17 @@ fn open_tap_adapter(session: &ClientSession) -> Result<OpenedTapAdapter> {
tap_interface.index(),
tap_interface.luid()
);
let tap_ip = tap_unicast_ip(tap_interface);
let tap_diagnostics = TapDiagnostics::new(
true,
Some(driver_mac),
Some(session.welcome().effective_tap_mtu()),
tap_ip,
None,
);
Ok(OpenedTapAdapter {
tap,
tap_diagnostics,
tap_interface,
_route_guard: route_guard,
})
}
@@ -398,6 +411,19 @@ fn client_diagnostics_snapshot(
)
}
#[cfg(windows)]
fn current_tap_diagnostics(
base: &TapDiagnostics,
identity: NetworkInterfaceIdentity,
) -> TapDiagnostics {
tap_diagnostics_with_ip(base, tap_unicast_ip(identity))
}
#[cfg_attr(not(windows), allow(dead_code))]
fn tap_diagnostics_with_ip(base: &TapDiagnostics, ip: Option<IpAddr>) -> TapDiagnostics {
TapDiagnostics::new(base.adapter_found(), base.mac(), base.mtu(), ip)
}
fn print_client_diagnostics(diagnostics: &ClientDiagnostics) {
println!("{}", format_client_diagnostics(diagnostics));
}
@@ -801,6 +827,24 @@ mod tests {
);
}
#[test]
fn refreshes_tap_diagnostics_ip_without_losing_static_fields() {
let base = TapDiagnostics::new(
true,
Some(mac(1)),
Some(1200),
Some("169.254.10.20".parse().unwrap()),
);
let refreshed =
tap_diagnostics_with_ip(&base, Some("10.73.42.51".parse::<IpAddr>().unwrap()));
assert!(refreshed.adapter_found());
assert_eq!(refreshed.mac(), Some(mac(1)));
assert_eq!(refreshed.mtu(), Some(1200));
assert_eq!(refreshed.ip().unwrap().to_string(), "10.73.42.51");
}
#[test]
fn formats_relay_lifecycle_events() {
let gateway = ControlMessage::PeerJoined(PeerInfo::new(1, Role::Gateway, None).unwrap());