Fix BPF header length on OpenBSD.

The actual header length may be larger than the bpf_hdr struct due to aligning:
37ecb4d066/sys/net/bpf.c (L1649)
8f02f2a044/bsd/net/bpf.c (L3580)

Tests are only valid for 32 and 64 bit architectures. I did not bother
guarding them with additional cfg flags.
This commit is contained in:
Nathan K. Zhinn 2021-03-01 04:37:37 +00:00 committed by GitHub
parent 5388ef1f1d
commit 4ac265fb46
3 changed files with 39 additions and 6 deletions

View File

@ -1,8 +1,10 @@
use std::io;
use std::mem;
use std::os::unix::io::{AsRawFd, RawFd};
use libc;
use crate::wire::ETHERNET_HEADER_LEN;
use super::{ifreq, ifreq_for};
/// set interface
@ -14,9 +16,19 @@ const BIOCGBLEN: libc::c_ulong = 0x40044266;
/// set immediate/nonblocking read
#[cfg(any(target_os = "macos", target_os = "openbsd"))]
const BIOCIMMEDIATE: libc::c_ulong = 0x80044270;
// TODO: check if this is same for OSes other than macos
/// set bpf_hdr struct size
#[cfg(target_os = "macos")]
const SIZEOF_BPF_HDR: usize = 18;
/// set bpf_hdr struct size
#[cfg(target_os = "openbsd")]
const SIZEOF_BPF_HDR: usize = 24;
/// The actual header length may be larger than the bpf_hdr struct due to aligning
/// see https://github.com/openbsd/src/blob/37ecb4d066e5566411cc16b362d3960c93b1d0be/sys/net/bpf.c#L1649
/// and https://github.com/apple/darwin-xnu/blob/8f02f2a044b9bb1ad951987ef5bab20ec9486310/bsd/net/bpf.c#L3580
#[cfg(any(target_os = "macos", target_os = "openbsd"))]
const BPF_HDRLEN: usize = 18;
const BPF_HDRLEN: usize = (((SIZEOF_BPF_HDR + ETHERNET_HEADER_LEN) + mem::align_of::<u32>() - 1)
& !(mem::align_of::<u32>() - 1))
- ETHERNET_HEADER_LEN;
macro_rules! try_ioctl {
($fd:expr,$cmd:expr,$req:expr) => {
@ -145,3 +157,20 @@ impl Drop for BpfDevice {
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[cfg(target_os = "macos")]
fn test_aligned_bpf_hdr_len() {
assert_eq!(18, BPF_HDRLEN);
}
#[test]
#[cfg(target_os = "openbsd")]
fn test_aligned_bpf_hdr_len() {
assert_eq!(26, BPF_HDRLEN);
}
}

View File

@ -91,6 +91,9 @@ mod field {
pub const PAYLOAD: Rest = 14..;
}
/// The Ethernet header length
pub const HEADER_LEN: usize = field::PAYLOAD.start;
impl<T: AsRef<[u8]>> Frame<T> {
/// Imbue a raw octet buffer with Ethernet frame structure.
pub fn new_unchecked(buffer: T) -> Frame<T> {
@ -111,7 +114,7 @@ impl<T: AsRef<[u8]>> Frame<T> {
/// Returns `Err(Error::Truncated)` if the buffer is too short.
pub fn check_len(&self) -> Result<()> {
let len = self.buffer.as_ref().len();
if len < field::PAYLOAD.start {
if len < HEADER_LEN {
Err(Error::Truncated)
} else {
Ok(())
@ -125,13 +128,13 @@ impl<T: AsRef<[u8]>> Frame<T> {
/// Return the length of a frame header.
pub fn header_len() -> usize {
field::PAYLOAD.start
HEADER_LEN
}
/// Return the length of a buffer required to hold a packet with the payload
/// of a given length.
pub fn buffer_len(payload_len: usize) -> usize {
field::PAYLOAD.start + payload_len
HEADER_LEN + payload_len
}
/// Return the destination address field.
@ -262,7 +265,7 @@ impl Repr {
/// Return the length of a header that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
field::PAYLOAD.start
HEADER_LEN
}
/// Emit a high-level representation into an Ethernet II frame.

View File

@ -119,6 +119,7 @@ pub use self::pretty_print::PrettyPrinter;
pub use self::ethernet::{EtherType as EthernetProtocol,
Address as EthernetAddress,
Frame as EthernetFrame,
HEADER_LEN as ETHERNET_HEADER_LEN,
Repr as EthernetRepr};
#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))]