From fe10f6ed3700c3e9f23352979ec0a62fe6cd0794 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Thu, 21 May 2026 18:27:38 +0200 Subject: [PATCH] fix(gateway): ignore self-injected packet frames AF_PACKET sockets can report packets sent by the host as well as packets received from the LAN. The gateway writes remote-client frames onto the wired interface, so treating those outgoing packets as fresh LAN input can reflect self-injected traffic back to the relay. Read packet metadata with `recvfrom` and skip `PACKET_OUTGOING` frames before returning a LAN frame to the bridge loop. This keeps capture scoped to inbound LAN traffic and is a prerequisite for periodic CAM refresh frames. Test Plan: - cargo fmt --check - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check Refs: PLAN.md gateway AF_PACKET bridge --- crates/lanparty-gateway/src/packet.rs | 52 +++++++++++++++++++-------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/crates/lanparty-gateway/src/packet.rs b/crates/lanparty-gateway/src/packet.rs index f80d673..1955fcf 100644 --- a/crates/lanparty-gateway/src/packet.rs +++ b/crates/lanparty-gateway/src/packet.rs @@ -92,21 +92,33 @@ impl PacketSocket { } pub fn recv_frame(&self, buffer: &mut [u8]) -> io::Result { - let received = unsafe { - // SAFETY: buffer.as_mut_ptr() is valid for buffer.len() bytes for the duration of - // recv, and recv initializes at most that many bytes. - libc::recv( - self.fd.as_raw_fd(), - buffer.as_mut_ptr().cast::(), - buffer.len(), - 0, - ) - }; - if received < 0 { - return Err(io::Error::last_os_error()); + loop { + let mut address = unsafe { + // SAFETY: sockaddr_ll is a plain old data kernel ABI struct; zero is a valid + // base before recvfrom initializes the peer address. + std::mem::zeroed::() + }; + let mut address_len = std::mem::size_of::() as libc::socklen_t; + let received = unsafe { + // SAFETY: buffer.as_mut_ptr() is valid for buffer.len() bytes for the duration + // of recvfrom, and recvfrom initializes at most that many bytes. address points + // to a sockaddr_ll-sized output buffer and address_len carries that size. + libc::recvfrom( + self.fd.as_raw_fd(), + buffer.as_mut_ptr().cast::(), + buffer.len(), + 0, + (&mut address as *mut libc::sockaddr_ll).cast::(), + &mut address_len, + ) + }; + if received < 0 { + return Err(io::Error::last_os_error()); + } + if is_inbound_packet_type(address.sll_pkttype) { + return Ok(received as usize); + } } - - Ok(received as usize) } } @@ -142,6 +154,10 @@ pub fn interface_index(interface: &str) -> io::Result { Ok(index) } +fn is_inbound_packet_type(packet_type: u8) -> bool { + packet_type != libc::PACKET_OUTGOING +} + #[cfg(test)] mod tests { use super::*; @@ -164,4 +180,12 @@ mod tests { assert_ne!(error.kind(), io::ErrorKind::InvalidInput); } + + #[test] + fn classifies_inbound_packet_types() { + assert!(!is_inbound_packet_type(libc::PACKET_OUTGOING)); + assert!(is_inbound_packet_type(libc::PACKET_HOST)); + assert!(is_inbound_packet_type(libc::PACKET_BROADCAST)); + assert!(is_inbound_packet_type(libc::PACKET_MULTICAST)); + } }