From e8d7cf7ff5888ed99580b7f10555da7e1c284f7b Mon Sep 17 00:00:00 2001 From: ddidderr Date: Thu, 21 May 2026 20:13:52 +0200 Subject: [PATCH] feat(client): retain QUIC datagram diagnostics Client connection setup already fails when QUIC DATAGRAM is unavailable and clamps the advertised datagram budget before sending hello. Keep that clamped value on ClientSession and expose it through QuicDiagnostics so the Windows client can report the negotiated budget without recomputing handshake details. The value remains owned by client-core because it is derived from the live quinn connection and the configured client budget during handshake. TAP and UI code can sample the snapshot later alongside tunnel counters. Test Plan: - cargo fmt --check - cargo test -p lanparty-client-core - cargo clippy -p lanparty-client-core --all-targets -- -D warnings - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check Refs: PLAN.md --- README.md | 1 + crates/lanparty-client-core/src/lib.rs | 40 +++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fc7dc82..e363c9f 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ Platform-neutral remote client relay session: - relay QUIC connection with pinned relay certificate trust - client hello with room, virtual MAC, and datagram budget - welcome/reject handling with assigned peer id and effective TAP MTU +- QUIC DATAGRAM support and negotiated datagram budget diagnostics - Ethernet frame send/receive helpers over QUIC DATAGRAM - client tunnel statistics for frame/datagram rx/tx and drops diff --git a/crates/lanparty-client-core/src/lib.rs b/crates/lanparty-client-core/src/lib.rs index 2d0c073..2d13e51 100644 --- a/crates/lanparty-client-core/src/lib.rs +++ b/crates/lanparty-client-core/src/lib.rs @@ -22,7 +22,7 @@ use lanparty_ctrl::{ CONTROL_LENGTH_PREFIX_LEN, ControlMessage, EndpointHello, MAX_CONTROL_MESSAGE_LEN, RELAY_ALPN, RoomCode, ServerWelcome, decode_control_frame, encode_control_message, }; -use lanparty_obs::TunnelStats; +use lanparty_obs::{QuicDiagnostics, TunnelStats}; use lanparty_proto::{EthernetFrame, FrameType, MacAddr, decode_datagram, encode_datagram}; use quinn::{ClientConfig, Endpoint, crypto::rustls::QuicClientConfig}; use rustls::pki_types::CertificateDer; @@ -214,6 +214,7 @@ pub struct ClientSession { connection: quinn::Connection, config: ClientSessionConfig, welcome: ServerWelcome, + quic_max_datagram_size: u16, stats: Arc, } @@ -246,6 +247,16 @@ impl ClientSession { &self.welcome } + #[must_use] + pub const fn quic_max_datagram_size(&self) -> u16 { + self.quic_max_datagram_size + } + + #[must_use] + pub const fn quic_diagnostics(&self) -> QuicDiagnostics { + QuicDiagnostics::new(true, Some(self.quic_max_datagram_size)) + } + #[must_use] pub fn relay_io(&self) -> ClientRelayIo { ClientRelayIo::new( @@ -415,9 +426,8 @@ pub async fn connect_client(config: ClientSessionConfig) -> Result Result bail!( @@ -443,6 +454,11 @@ pub async fn connect_client(config: ClientSessionConfig) -> Result u16 { + usize::from(configured).min(peer).min(usize::from(u16::MAX)) as u16 +} + fn relay_client_config(relay_ca_cert_der: &[u8]) -> Result { let mut roots = rustls::RootCertStore::empty(); roots @@ -550,6 +566,16 @@ mod tests { assert!(identity.virtual_mac().is_valid_client_identity()); } + #[test] + fn clamps_negotiated_quic_datagram_size() { + assert_eq!(negotiated_quic_datagram_size(1400, 1350), 1350); + assert_eq!(negotiated_quic_datagram_size(1300, 1400), 1300); + assert_eq!( + negotiated_quic_datagram_size(u16::MAX, usize::MAX), + u16::MAX + ); + } + #[test] fn identity_store_creates_and_reuses_mac() { let path = unique_temp_identity_path(); @@ -648,6 +674,12 @@ mod tests { ); assert_eq!(client.welcome().room_id(), 7); assert_eq!(client.welcome().peer_id(), 2); + assert!(client.quic_max_datagram_size() <= 1400); + assert!(client.quic_diagnostics().datagram_supported()); + assert_eq!( + client.quic_diagnostics().max_datagram_size(), + Some(client.quic_max_datagram_size()) + ); let relay_io = client.relay_io(); assert_eq!(relay_io.welcome().peer_id(), 2);