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
This commit is contained in:
@@ -47,6 +47,7 @@ Platform-neutral remote client relay session:
|
|||||||
- relay QUIC connection with pinned relay certificate trust
|
- relay QUIC connection with pinned relay certificate trust
|
||||||
- client hello with room, virtual MAC, and datagram budget
|
- client hello with room, virtual MAC, and datagram budget
|
||||||
- welcome/reject handling with assigned peer id and effective TAP MTU
|
- 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
|
- Ethernet frame send/receive helpers over QUIC DATAGRAM
|
||||||
- client tunnel statistics for frame/datagram rx/tx and drops
|
- client tunnel statistics for frame/datagram rx/tx and drops
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use lanparty_ctrl::{
|
|||||||
CONTROL_LENGTH_PREFIX_LEN, ControlMessage, EndpointHello, MAX_CONTROL_MESSAGE_LEN, RELAY_ALPN,
|
CONTROL_LENGTH_PREFIX_LEN, ControlMessage, EndpointHello, MAX_CONTROL_MESSAGE_LEN, RELAY_ALPN,
|
||||||
RoomCode, ServerWelcome, decode_control_frame, encode_control_message,
|
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 lanparty_proto::{EthernetFrame, FrameType, MacAddr, decode_datagram, encode_datagram};
|
||||||
use quinn::{ClientConfig, Endpoint, crypto::rustls::QuicClientConfig};
|
use quinn::{ClientConfig, Endpoint, crypto::rustls::QuicClientConfig};
|
||||||
use rustls::pki_types::CertificateDer;
|
use rustls::pki_types::CertificateDer;
|
||||||
@@ -214,6 +214,7 @@ pub struct ClientSession {
|
|||||||
connection: quinn::Connection,
|
connection: quinn::Connection,
|
||||||
config: ClientSessionConfig,
|
config: ClientSessionConfig,
|
||||||
welcome: ServerWelcome,
|
welcome: ServerWelcome,
|
||||||
|
quic_max_datagram_size: u16,
|
||||||
stats: Arc<ClientTunnelStats>,
|
stats: Arc<ClientTunnelStats>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,6 +247,16 @@ impl ClientSession {
|
|||||||
&self.welcome
|
&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]
|
#[must_use]
|
||||||
pub fn relay_io(&self) -> ClientRelayIo {
|
pub fn relay_io(&self) -> ClientRelayIo {
|
||||||
ClientRelayIo::new(
|
ClientRelayIo::new(
|
||||||
@@ -415,9 +426,8 @@ pub async fn connect_client(config: ClientSessionConfig) -> Result<ClientSession
|
|||||||
let peer_datagram_size = connection
|
let peer_datagram_size = connection
|
||||||
.max_datagram_size()
|
.max_datagram_size()
|
||||||
.context("relay did not negotiate QUIC DATAGRAM support")?;
|
.context("relay did not negotiate QUIC DATAGRAM support")?;
|
||||||
let hello_datagram_size = usize::from(config.max_datagram_size())
|
let hello_datagram_size =
|
||||||
.min(peer_datagram_size)
|
negotiated_quic_datagram_size(config.max_datagram_size(), peer_datagram_size);
|
||||||
.min(usize::from(u16::MAX)) as u16;
|
|
||||||
let hello = EndpointHello::client(
|
let hello = EndpointHello::client(
|
||||||
config.room().clone(),
|
config.room().clone(),
|
||||||
config.virtual_mac(),
|
config.virtual_mac(),
|
||||||
@@ -432,6 +442,7 @@ pub async fn connect_client(config: ClientSessionConfig) -> Result<ClientSession
|
|||||||
connection,
|
connection,
|
||||||
config,
|
config,
|
||||||
welcome,
|
welcome,
|
||||||
|
quic_max_datagram_size: hello_datagram_size,
|
||||||
stats: Arc::default(),
|
stats: Arc::default(),
|
||||||
}),
|
}),
|
||||||
ControlMessage::Reject(reject) => bail!(
|
ControlMessage::Reject(reject) => bail!(
|
||||||
@@ -443,6 +454,11 @@ pub async fn connect_client(config: ClientSessionConfig) -> Result<ClientSession
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn negotiated_quic_datagram_size(configured: u16, peer: usize) -> u16 {
|
||||||
|
usize::from(configured).min(peer).min(usize::from(u16::MAX)) as u16
|
||||||
|
}
|
||||||
|
|
||||||
fn relay_client_config(relay_ca_cert_der: &[u8]) -> Result<ClientConfig> {
|
fn relay_client_config(relay_ca_cert_der: &[u8]) -> Result<ClientConfig> {
|
||||||
let mut roots = rustls::RootCertStore::empty();
|
let mut roots = rustls::RootCertStore::empty();
|
||||||
roots
|
roots
|
||||||
@@ -550,6 +566,16 @@ mod tests {
|
|||||||
assert!(identity.virtual_mac().is_valid_client_identity());
|
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]
|
#[test]
|
||||||
fn identity_store_creates_and_reuses_mac() {
|
fn identity_store_creates_and_reuses_mac() {
|
||||||
let path = unique_temp_identity_path();
|
let path = unique_temp_identity_path();
|
||||||
@@ -648,6 +674,12 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(client.welcome().room_id(), 7);
|
assert_eq!(client.welcome().room_id(), 7);
|
||||||
assert_eq!(client.welcome().peer_id(), 2);
|
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();
|
let relay_io = client.relay_io();
|
||||||
assert_eq!(relay_io.welcome().peer_id(), 2);
|
assert_eq!(relay_io.welcome().peer_id(), 2);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user