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);