diff --git a/crates/lanparty-client-tap/src/lib.rs b/crates/lanparty-client-tap/src/lib.rs index 7317e6f..a617dc9 100644 --- a/crates/lanparty-client-tap/src/lib.rs +++ b/crates/lanparty-client-tap/src/lib.rs @@ -4,13 +4,15 @@ //! how to find and open an installed TAP-Windows6 Ethernet adapter; the Windows //! client binary owns when to connect it to QUIC and how to protect routes. -use anyhow::{Result, bail}; +use anyhow::{Context, Result, bail}; +use lanparty_proto::{EthernetFrame, MAX_STANDARD_ETHERNET_FRAME_LEN}; pub const TAP_COMPONENT_ID: &str = "tap0901"; pub const TAP_ADAPTER_KEY: &str = r"SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}"; pub const TAP_DEVICE_PREFIX: &str = r"\\.\Global\"; pub const TAP_DEVICE_SUFFIX: &str = ".tap"; +pub const TAP_FRAME_BUFFER_LEN: usize = MAX_STANDARD_ETHERNET_FRAME_LEN; const FILE_DEVICE_UNKNOWN: u32 = 0x0000_0022; const METHOD_BUFFERED: u32 = 0; const FILE_ANY_ACCESS: u32 = 0; @@ -70,6 +72,19 @@ pub fn tap_device_path(instance_id: &str) -> String { format!("{TAP_DEVICE_PREFIX}{instance_id}{TAP_DEVICE_SUFFIX}") } +pub fn validate_tap_ethernet_frame(frame: &[u8]) -> Result<()> { + let frame = EthernetFrame::parse(frame).context("TAP Ethernet frame is malformed")?; + if frame.is_jumbo() { + bail!( + "TAP Ethernet frame length {} exceeds maximum {}", + frame.len(), + MAX_STANDARD_ETHERNET_FRAME_LEN + ); + } + + Ok(()) +} + #[must_use] pub const fn tap_control_code(request: u32) -> u32 { (FILE_DEVICE_UNKNOWN << 16) | (FILE_ANY_ACCESS << 14) | (request << 2) | METHOD_BUFFERED @@ -129,6 +144,17 @@ mod tests { assert_eq!(tap_ioctl_set_media_status(), 0x0022_0018); } + #[test] + fn validates_tap_ethernet_frames() { + let mut valid = vec![0; 14]; + valid[12..14].copy_from_slice(&0x0800_u16.to_be_bytes()); + + assert!(validate_tap_ethernet_frame(&valid).is_ok()); + assert!(validate_tap_ethernet_frame(&valid[..13]).is_err()); + valid.resize(TAP_FRAME_BUFFER_LEN + 1, 0); + assert!(validate_tap_ethernet_frame(&valid).is_err()); + } + #[test] fn validates_adapter_info() { let info = diff --git a/crates/lanparty-client-tap/src/windows.rs b/crates/lanparty-client-tap/src/windows.rs index 345da43..cf1952c 100644 --- a/crates/lanparty-client-tap/src/windows.rs +++ b/crates/lanparty-client-tap/src/windows.rs @@ -4,7 +4,7 @@ use std::{ ptr::{null, null_mut}, }; -use anyhow::{Context, Result}; +use anyhow::{Context, Result, bail}; use lanparty_proto::MacAddr; use windows_sys::Win32::{ Foundation::{ @@ -26,7 +26,7 @@ use windows_sys::Win32::{ use crate::{ TAP_ADAPTER_KEY, TapAdapterInfo, is_tap_component_id, tap_ioctl_get_mac, tap_ioctl_get_mtu, - tap_ioctl_set_media_status, + tap_ioctl_set_media_status, validate_tap_ethernet_frame, }; #[derive(Debug)] @@ -128,6 +128,13 @@ impl TapAdapter { Ok(bytes_read as usize) } + pub fn read_ethernet_frame(&self, buffer: &mut [u8]) -> Result { + let len = self.read_frame(buffer)?; + validate_tap_ethernet_frame(&buffer[..len])?; + + Ok(len) + } + pub fn write_frame(&self, frame: &[u8]) -> Result { let mut bytes_written = 0_u32; let ok = unsafe { @@ -148,6 +155,16 @@ impl TapAdapter { Ok(bytes_written as usize) } + pub fn write_ethernet_frame(&self, frame: &[u8]) -> Result<()> { + validate_tap_ethernet_frame(frame)?; + let written = self.write_frame(frame)?; + if written != frame.len() { + bail!("partial TAP frame write: {written}/{}", frame.len()); + } + + Ok(()) + } + fn device_io_control( &self, code: u32,