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
This commit is contained in:
@@ -92,21 +92,33 @@ impl PacketSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv_frame(&self, buffer: &mut [u8]) -> io::Result<usize> {
|
pub fn recv_frame(&self, buffer: &mut [u8]) -> io::Result<usize> {
|
||||||
let received = unsafe {
|
loop {
|
||||||
// SAFETY: buffer.as_mut_ptr() is valid for buffer.len() bytes for the duration of
|
let mut address = unsafe {
|
||||||
// recv, and recv initializes at most that many bytes.
|
// SAFETY: sockaddr_ll is a plain old data kernel ABI struct; zero is a valid
|
||||||
libc::recv(
|
// base before recvfrom initializes the peer address.
|
||||||
self.fd.as_raw_fd(),
|
std::mem::zeroed::<libc::sockaddr_ll>()
|
||||||
buffer.as_mut_ptr().cast::<libc::c_void>(),
|
};
|
||||||
buffer.len(),
|
let mut address_len = std::mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t;
|
||||||
0,
|
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
|
||||||
if received < 0 {
|
// to a sockaddr_ll-sized output buffer and address_len carries that size.
|
||||||
return Err(io::Error::last_os_error());
|
libc::recvfrom(
|
||||||
|
self.fd.as_raw_fd(),
|
||||||
|
buffer.as_mut_ptr().cast::<libc::c_void>(),
|
||||||
|
buffer.len(),
|
||||||
|
0,
|
||||||
|
(&mut address as *mut libc::sockaddr_ll).cast::<libc::sockaddr>(),
|
||||||
|
&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<u32> {
|
|||||||
Ok(index)
|
Ok(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_inbound_packet_type(packet_type: u8) -> bool {
|
||||||
|
packet_type != libc::PACKET_OUTGOING
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -164,4 +180,12 @@ mod tests {
|
|||||||
|
|
||||||
assert_ne!(error.kind(), io::ErrorKind::InvalidInput);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user