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> {
|
||||
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::<libc::c_void>(),
|
||||
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::<libc::sockaddr_ll>()
|
||||
};
|
||||
let mut address_len = std::mem::size_of::<libc::sockaddr_ll>() 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::<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)
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user