diff --git a/README.md b/README.md index dcf22df..ae1a6bc 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,8 @@ adapter may need to be disabled/enabled or reinstalled before it reloads the configured `NetworkAddress`. It prints and reports client diagnostics snapshots with relay reachability, route-pinning, QUIC datagram budget, TAP status/IP, frame/datagram counters, -and drops. +and drops. The periodic diagnostics refresh the TAP unicast IP so DHCP results +that arrive after bridging starts become visible in later status lines. Relay lifecycle events are logged as they arrive, including gateway joins and peer leaves. The client remembers peer identities from join and catch-up events so later leave logs can identify a disconnected LAN gateway or client MAC when diff --git a/crates/lanparty-client-win/src/main.rs b/crates/lanparty-client-win/src/main.rs index 09c8e70..3505990 100644 --- a/crates/lanparty-client-win/src/main.rs +++ b/crates/lanparty-client-win/src/main.rs @@ -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) -> 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 { 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) -> 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::().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());