Skip to content

Commit

Permalink
Ensure BPF read buffer is 4-byte aligned
Browse files Browse the repository at this point in the history
  • Loading branch information
frankplow committed Oct 21, 2023
1 parent 87f362d commit 386afc4
Showing 1 changed file with 18 additions and 13 deletions.
31 changes: 18 additions & 13 deletions pnet_datalink/src/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use std::sync::Arc;
use std::time::Duration;

static ETHERNET_HEADER_SIZE: usize = 14;
static NULL_HEADER_SIZE: usize = 4;

/// The BPF-specific configuration.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
Expand Down Expand Up @@ -184,14 +185,21 @@ pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Resu
}

let mut loopback = false;
let mut buffer_offset = 0;
let mut allocated_read_buffer_size = config.read_buffer_size;
// The loopback device does weird things
// FIXME This should really just be another L2 packet header type
if dlt == bpf::DLT_NULL {
loopback = true;
// So we can guaranatee that we can have a header before the packet.
// Loopback packets arrive without the header.
allocated_read_buffer_size += ETHERNET_HEADER_SIZE;
// The loopback device provides a smaller (4-byte) header than ethernet (14-byte).
// We deal with this by offseting the write buffer, then overwriting the null header

Check warning on line 195 in pnet_datalink/src/bpf.rs

View workflow job for this annotation

GitHub Actions / spell-check

"offseting" should be "offsetting".
// with a zeroed ethernet header. This is complicated by the fact that the buffer
// offset must be a multiple of four for pointer alignment, and that the write itself
// must be 4096 bytes.
//
// & !x is a bit-hack to round down to nearest multiple of x + 1.
buffer_offset = (ETHERNET_HEADER_SIZE - NULL_HEADER_SIZE + 4 - 1) & !(4 - 1);
allocated_read_buffer_size += buffer_offset;

// Allow packets to be read back after they are written
if let Err(e) = set_feedback(fd) {
Expand Down Expand Up @@ -235,6 +243,7 @@ pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Resu
fd: fd.clone(),
fd_set: unsafe { mem::zeroed() },
read_buffer: vec![0; allocated_read_buffer_size],
buffer_offset,
loopback: loopback,
timeout: config
.read_timeout
Expand Down Expand Up @@ -362,22 +371,18 @@ struct DataLinkReceiverImpl {
fd: Arc<pnet_sys::FileDesc>,
fd_set: libc::fd_set,
read_buffer: Vec<u8>,
buffer_offset: usize,
loopback: bool,
timeout: Option<libc::timespec>,
packets: VecDeque<(usize, usize)>,
}

impl DataLinkReceiver for DataLinkReceiverImpl {
fn next(&mut self) -> io::Result<&[u8]> {
// Loopback packets arrive with a 4 byte header instead of normal ethernet header.
// Discard that header and replace with zeroed out ethernet header.
let (header_size, buffer_offset) = if self.loopback {
(4, ETHERNET_HEADER_SIZE)
} else {
(0, 0)
};
let header_size = if self.loopback { NULL_HEADER_SIZE } else { 0 };

if self.packets.is_empty() {
let buffer = &mut self.read_buffer[buffer_offset..];
let buffer = &mut self.read_buffer[self.buffer_offset..];
let ret = unsafe {
libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set);
libc::pselect(
Expand Down Expand Up @@ -425,9 +430,9 @@ impl DataLinkReceiver for DataLinkReceiverImpl {
}
}
let (start, mut len) = self.packets.pop_front().unwrap();
len += buffer_offset;
len += self.buffer_offset;
// Zero out part that will become fake ethernet header if on loopback.
for i in (&mut self.read_buffer[start..start + buffer_offset]).iter_mut() {
for i in (&mut self.read_buffer[start..start + self.buffer_offset]).iter_mut() {
*i = 0;
}
Ok(&self.read_buffer[start..start + len])
Expand Down

0 comments on commit 386afc4

Please sign in to comment.