Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure BPF read is 4-byte aligned #655

Merged
merged 3 commits into from
May 30, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 18 additions & 14 deletions pnet_datalink/src/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ use pnet_sys;
use std::collections::VecDeque;
use std::ffi::CString;
use std::io;
use std::mem;
use std::mem::{self, align_of};
use std::ptr;
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,20 @@ 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 offsetting the write buffer, then overwriting the null header
// 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.
let align = align_of::<bpf::bpf_hdr>();
buffer_offset = (ETHERNET_HEADER_SIZE - NULL_HEADER_SIZE).next_multiple_of(align);
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 +242,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 +370,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 +429,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