feat(proto): validate negotiated datagram budgets
PLAN.md keeps MVP traffic to one Ethernet frame per QUIC datagram with no fragmentation. The relay already negotiates a datagram budget, but the client and gateway send paths still relied on Quinn to reject oversized encoded Ethernet datagrams. Add a shared protocol validation helper for encoded datagram length versus the negotiated QUIC budget. Thread the negotiated budget into client and gateway send boundaries, reject oversized datagrams before send, and count those valid but unsent frames as local drops. Document the budget check in the workspace decomposition and gateway behavior. Test Plan: - cargo fmt --check - cargo test -p lanparty-proto -p lanparty-client-core -p lanparty-gateway - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check Refs: PLAN.md
This commit is contained in:
@@ -33,7 +33,7 @@ use lanparty_obs::TunnelStats;
|
||||
use lanparty_obs::{FrameAction, FrameDirection, FrameLog};
|
||||
use lanparty_proto::{
|
||||
EthernetFrame, FrameType, MAX_STANDARD_ETHERNET_FRAME_LEN, MacAddr, decode_datagram,
|
||||
encode_datagram,
|
||||
encode_datagram, validate_datagram_budget,
|
||||
};
|
||||
use quinn::{ClientConfig, Endpoint, crypto::rustls::QuicClientConfig};
|
||||
use rustls::pki_types::CertificateDer;
|
||||
@@ -196,6 +196,7 @@ pub struct GatewayConnection {
|
||||
connection: quinn::Connection,
|
||||
config: GatewayConfig,
|
||||
welcome: ServerWelcome,
|
||||
quic_max_datagram_size: u16,
|
||||
stats: Arc<GatewayTunnelStats>,
|
||||
}
|
||||
|
||||
@@ -228,8 +229,19 @@ impl GatewayConnection {
|
||||
&self.welcome
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn quic_max_datagram_size(&self) -> u16 {
|
||||
self.quic_max_datagram_size
|
||||
}
|
||||
|
||||
pub fn send_ethernet(&self, frame: &[u8]) -> Result<()> {
|
||||
send_gateway_ethernet(&self.connection, &self.welcome, &self.stats, frame)
|
||||
send_gateway_ethernet(
|
||||
&self.connection,
|
||||
&self.welcome,
|
||||
self.quic_max_datagram_size,
|
||||
&self.stats,
|
||||
frame,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn recv_ethernet(&self) -> Result<ReceivedEthernetFrame> {
|
||||
@@ -263,6 +275,7 @@ impl GatewayConnection {
|
||||
endpoint,
|
||||
connection,
|
||||
welcome,
|
||||
quic_max_datagram_size,
|
||||
stats,
|
||||
..
|
||||
} = self;
|
||||
@@ -292,7 +305,13 @@ impl GatewayConnection {
|
||||
}
|
||||
lan_frame = read_lan_ethernet(&packet_socket) => {
|
||||
let lan_frame = lan_frame?;
|
||||
send_gateway_ethernet(&connection, &welcome, &stats, &lan_frame)?;
|
||||
send_gateway_ethernet(
|
||||
&connection,
|
||||
&welcome,
|
||||
quic_max_datagram_size,
|
||||
&stats,
|
||||
&lan_frame,
|
||||
)?;
|
||||
println!(
|
||||
"{}",
|
||||
gateway_frame_log_line(
|
||||
@@ -363,6 +382,7 @@ impl GatewayConnection {
|
||||
fn send_gateway_ethernet(
|
||||
connection: &quinn::Connection,
|
||||
welcome: &ServerWelcome,
|
||||
quic_max_datagram_size: u16,
|
||||
stats: &GatewayTunnelStats,
|
||||
frame: &[u8],
|
||||
) -> Result<()> {
|
||||
@@ -381,6 +401,12 @@ fn send_gateway_ethernet(
|
||||
frame,
|
||||
)
|
||||
.context("failed to encode gateway Ethernet datagram")?;
|
||||
if let Err(error) =
|
||||
validate_datagram_budget(datagram.len(), usize::from(quic_max_datagram_size))
|
||||
{
|
||||
stats.record_dropped_frame();
|
||||
return Err(error).context("gateway Ethernet datagram exceeds negotiated QUIC budget");
|
||||
}
|
||||
|
||||
connection
|
||||
.send_datagram(Bytes::from(datagram))
|
||||
@@ -747,6 +773,7 @@ pub async fn connect_gateway(config: GatewayConfig) -> Result<GatewayConnection>
|
||||
connection,
|
||||
config,
|
||||
welcome,
|
||||
quic_max_datagram_size: hello_datagram_size,
|
||||
stats: Arc::default(),
|
||||
}),
|
||||
ControlMessage::Reject(reject) => bail!(
|
||||
@@ -968,6 +995,7 @@ mod tests {
|
||||
assert_eq!(gateway.config().interface(), "eth0");
|
||||
assert_eq!(gateway.welcome().room_id(), 7);
|
||||
assert_eq!(gateway.welcome().peer_id(), 1);
|
||||
assert!(gateway.quic_max_datagram_size() <= 1400);
|
||||
|
||||
let event = tokio::time::timeout(Duration::from_secs(5), gateway.recv_control_event())
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user