From b611038d5b41a45ca70553550dbdef9aa1fd117c Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Sun, 9 Jul 2023 16:45:47 -0400 Subject: [PATCH] Use procfs crate for kernel version parsing This allows the logic to be shared between aya and the integration tests without exposing additional public API surface. --- aya-obj/src/obj.rs | 70 +++++-------- aya/Cargo.toml | 26 +++-- aya/src/maps/mod.rs | 30 +++--- aya/src/programs/cgroup_device.rs | 7 +- aya/src/programs/cgroup_skb.rs | 7 +- aya/src/programs/cgroup_sock.rs | 7 +- aya/src/programs/cgroup_sock_addr.rs | 7 +- aya/src/programs/cgroup_sockopt.rs | 7 +- aya/src/programs/cgroup_sysctl.rs | 7 +- aya/src/programs/mod.rs | 18 ++-- aya/src/programs/probe.rs | 6 +- aya/src/programs/xdp.rs | 13 +-- aya/src/sys/bpf.rs | 13 ++- aya/src/sys/mod.rs | 143 ++++---------------------- test/integration-test/Cargo.toml | 2 +- test/integration-test/tests/common.rs | 23 ----- test/integration-test/tests/load.rs | 8 +- test/integration-test/tests/smoke.rs | 14 +-- 18 files changed, 137 insertions(+), 271 deletions(-) delete mode 100644 test/integration-test/tests/common.rs diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index e9093e62..e5e2f3ab 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -109,7 +109,7 @@ pub struct Object { /// Program license pub license: CString, /// Kernel version - pub kernel_version: KernelVersion, + pub kernel_version: Option, /// Program BTF pub btf: Option, /// Program BTF.ext @@ -135,7 +135,7 @@ pub struct Program { /// The license pub license: CString, /// The kernel version - pub kernel_version: KernelVersion, + pub kernel_version: Option, /// The section containing the program pub section: ProgramSection, /// The section index of the program @@ -579,7 +579,7 @@ impl Object { let kernel_version = if let Some(section) = obj.section_by_name("version") { parse_version(Section::try_from(§ion)?.data, endianness)? } else { - KernelVersion::Any + None }; let mut bpf_obj = Object::new(endianness, license, kernel_version); @@ -631,7 +631,7 @@ impl Object { Ok(bpf_obj) } - fn new(endianness: Endianness, license: CString, kernel_version: KernelVersion) -> Object { + fn new(endianness: Endianness, license: CString, kernel_version: Option) -> Object { Object { endianness, license, @@ -1256,7 +1256,7 @@ fn parse_license(data: &[u8]) -> Result { .to_owned()) } -fn parse_version(data: &[u8], endianness: object::Endianness) -> Result { +fn parse_version(data: &[u8], endianness: object::Endianness) -> Result, ParseError> { let data = match data.len() { 4 => data.try_into().unwrap(), _ => { @@ -1271,9 +1271,10 @@ fn parse_version(data: &[u8], endianness: object::Endianness) -> Result u32::from_le_bytes(data), }; - Ok(match v { - KERNEL_VERSION_ANY => KernelVersion::Any, - v => KernelVersion::Version(v), + Ok(if v == KERNEL_VERSION_ANY { + None + } else { + Some(v) }) } @@ -1301,24 +1302,6 @@ fn get_map_field(btf: &Btf, type_id: u32) -> Result { Ok(arr.len) } -/// The parsed kernel version -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum KernelVersion { - /// Specified version - Version(u32), - /// Any version - Any, -} - -impl From for u32 { - fn from(version: KernelVersion) -> u32 { - match version { - KernelVersion::Any => KERNEL_VERSION_ANY, - KernelVersion::Version(v) => v, - } - } -} - // Parsed '.bss' '.data' and '.rodata' sections. These sections are arrays of // bytes and are relocated based on their section index. fn parse_data_map_section(section: &Section) -> Result { @@ -1592,23 +1575,20 @@ mod tests { Err(ParseError::InvalidKernelVersion { .. }) )); - assert_eq!( - parse_version(&0xFFFF_FFFEu32.to_le_bytes(), Endianness::Little) - .expect("failed to parse magic version"), - KernelVersion::Any - ); + assert!(matches!( + parse_version(&0xFFFF_FFFEu32.to_le_bytes(), Endianness::Little), + Ok(None) + )); - assert_eq!( - parse_version(&0xFFFF_FFFEu32.to_be_bytes(), Endianness::Big) - .expect("failed to parse magic version"), - KernelVersion::Any - ); + assert!(matches!( + parse_version(&0xFFFF_FFFEu32.to_be_bytes(), Endianness::Big), + Ok(None) + )); - assert_eq!( - parse_version(&1234u32.to_le_bytes(), Endianness::Little) - .expect("failed to parse magic version"), - KernelVersion::Version(1234) - ); + assert!(matches!( + parse_version(&1234u32.to_le_bytes(), Endianness::Little), + Ok(Some(1234)) + )); } #[test] @@ -1699,11 +1679,7 @@ mod tests { } fn fake_obj() -> Object { - Object::new( - Endianness::Little, - CString::new("GPL").unwrap(), - KernelVersion::Any, - ) + Object::new(Endianness::Little, CString::new("GPL").unwrap(), None) } #[test] @@ -1753,7 +1729,7 @@ mod tests { obj.parse_program(&fake_section(BpfSectionKind::Program,"kprobe/foo", bytes_of(&fake_ins()))), Ok((Program { license, - kernel_version: KernelVersion::Any, + kernel_version: None, section: ProgramSection::KProbe { .. }, .. }, Function { name, diff --git a/aya/Cargo.toml b/aya/Cargo.toml index f12ebf27..2abcfc47 100644 --- a/aya/Cargo.toml +++ b/aya/Cargo.toml @@ -11,21 +11,31 @@ documentation = "https://docs.rs/aya" edition = "2021" [dependencies] -libc = { version = "0.2.105" } +async-io = { version = "1.3", optional = true } aya-obj = { path = "../aya-obj", version = "0.1.0", features = ["std"] } -thiserror = "1" -object = { version = "0.31", default-features = false, features = ["std", "read_core", "elf"] } bitflags = "2.2.1" bytes = "1" lazy_static = "1" -parking_lot = { version = "0.12.0", features = ["send_guard"] } -tokio = { version = "1.24.0", features = ["macros", "rt", "rt-multi-thread", "net"], optional = true } -async-io = { version = "1.3", optional = true } +libc = { version = "0.2.105" } log = "0.4" +object = { version = "0.31", default-features = false, features = [ + "std", + "read_core", + "elf", +] } +parking_lot = { version = "0.12.0", features = ["send_guard"] } +thiserror = "1" +tokio = { version = "1.24.0", features = [ + "macros", + "rt", + "rt-multi-thread", + "net", +], optional = true } +procfs = { version = "0.15.1", default-features = false } [dev-dependencies] -matches = "0.1.8" futures = { version = "0.3.12", default-features = false, features = ["std"] } +matches = "0.1.8" [features] default = [] @@ -35,4 +45,4 @@ async_std = ["async-io", "async"] [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs","-D", "warnings"] +rustdoc-args = ["--cfg", "docsrs", "-D", "warnings"] diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index f2b7a6a5..3961c212 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -49,6 +49,7 @@ use std::{ use libc::{getrlimit, rlimit, RLIMIT_MEMLOCK, RLIM_INFINITY}; use log::warn; +use procfs::KernelVersion; use thiserror::Error; use crate::{ @@ -56,7 +57,7 @@ use crate::{ pin::PinError, sys::{ bpf_create_map, bpf_get_object, bpf_map_get_info_by_fd, bpf_map_get_next_key, - bpf_pin_object, kernel_version, + bpf_pin_object, }, util::nr_cpus, PinningType, Pod, @@ -489,18 +490,23 @@ impl MapData { let c_name = CString::new(name).map_err(|_| MapError::InvalidName { name: name.into() })?; - let fd = bpf_create_map(&c_name, &self.obj, self.btf_fd).map_err(|(code, io_error)| { - let k_ver = kernel_version().unwrap(); - if k_ver < (5, 11, 0) { - maybe_warn_rlimit(); - } + #[cfg(not(test))] + let kernel_version = KernelVersion::current().unwrap(); + #[cfg(test)] + let kernel_version = KernelVersion::new(0xff, 0xff, 0xff); + let fd = bpf_create_map(&c_name, &self.obj, self.btf_fd, kernel_version).map_err( + |(code, io_error)| { + if kernel_version < KernelVersion::new(5, 11, 0) { + maybe_warn_rlimit(); + } - MapError::CreateError { - name: name.into(), - code, - io_error, - } - })? as RawFd; + MapError::CreateError { + name: name.into(), + code, + io_error, + } + }, + )? as RawFd; self.fd = Some(fd); diff --git a/aya/src/programs/cgroup_device.rs b/aya/src/programs/cgroup_device.rs index ad211b83..b1a8e39d 100644 --- a/aya/src/programs/cgroup_device.rs +++ b/aya/src/programs/cgroup_device.rs @@ -1,4 +1,6 @@ //! Cgroup device programs. + +use procfs::KernelVersion; use std::os::fd::{AsRawFd, RawFd}; use crate::{ @@ -6,7 +8,7 @@ use crate::{ programs::{ define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, }, - sys::{bpf_link_create, bpf_prog_attach, kernel_version}, + sys::{bpf_link_create, bpf_prog_attach}, }; /// A program used to watch or prevent device interaction from a cgroup. @@ -62,8 +64,7 @@ impl CgroupDevice { let prog_fd = self.data.fd_or_err()?; let cgroup_fd = cgroup.as_raw_fd(); - let k_ver = kernel_version().unwrap(); - if k_ver >= (5, 7, 0) { + if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { let link_fd = bpf_link_create(prog_fd, cgroup_fd, BPF_CGROUP_DEVICE, None, 0).map_err( |(_, io_error)| ProgramError::SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/cgroup_skb.rs b/aya/src/programs/cgroup_skb.rs index ed92fd2c..f275441f 100644 --- a/aya/src/programs/cgroup_skb.rs +++ b/aya/src/programs/cgroup_skb.rs @@ -1,4 +1,6 @@ //! Cgroup skb programs. + +use procfs::KernelVersion; use std::{ hash::Hash, os::fd::{AsRawFd, RawFd}, @@ -13,7 +15,7 @@ use crate::{ programs::{ define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, }, - sys::{bpf_link_create, bpf_prog_attach, kernel_version}, + sys::{bpf_link_create, bpf_prog_attach}, }; /// A program used to inspect or filter network activity for a given cgroup. @@ -96,8 +98,7 @@ impl CgroupSkb { CgroupSkbAttachType::Ingress => BPF_CGROUP_INET_INGRESS, CgroupSkbAttachType::Egress => BPF_CGROUP_INET_EGRESS, }; - let k_ver = kernel_version().unwrap(); - if k_ver >= (5, 7, 0) { + if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { let link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, None, 0).map_err( |(_, io_error)| ProgramError::SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/cgroup_sock.rs b/aya/src/programs/cgroup_sock.rs index a1c32179..bddea214 100644 --- a/aya/src/programs/cgroup_sock.rs +++ b/aya/src/programs/cgroup_sock.rs @@ -1,6 +1,8 @@ //! Cgroup socket programs. + pub use aya_obj::programs::CgroupSockAttachType; +use procfs::KernelVersion; use std::{ hash::Hash, os::fd::{AsRawFd, RawFd}, @@ -12,7 +14,7 @@ use crate::{ programs::{ define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, }, - sys::{bpf_link_create, bpf_prog_attach, kernel_version}, + sys::{bpf_link_create, bpf_prog_attach}, }; /// A program that is called on socket creation, bind and release. @@ -71,8 +73,7 @@ impl CgroupSock { let prog_fd = self.data.fd_or_err()?; let cgroup_fd = cgroup.as_raw_fd(); let attach_type = self.data.expected_attach_type.unwrap(); - let k_ver = kernel_version().unwrap(); - if k_ver >= (5, 7, 0) { + if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { let link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, None, 0).map_err( |(_, io_error)| ProgramError::SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/cgroup_sock_addr.rs b/aya/src/programs/cgroup_sock_addr.rs index de9a48a2..555a7c95 100644 --- a/aya/src/programs/cgroup_sock_addr.rs +++ b/aya/src/programs/cgroup_sock_addr.rs @@ -1,6 +1,8 @@ //! Cgroup socket address programs. + pub use aya_obj::programs::CgroupSockAddrAttachType; +use procfs::KernelVersion; use std::{ hash::Hash, os::fd::{AsRawFd, RawFd}, @@ -12,7 +14,7 @@ use crate::{ programs::{ define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, }, - sys::{bpf_link_create, bpf_prog_attach, kernel_version}, + sys::{bpf_link_create, bpf_prog_attach}, }; /// A program that can be used to inspect or modify socket addresses (`struct sockaddr`). @@ -72,8 +74,7 @@ impl CgroupSockAddr { let prog_fd = self.data.fd_or_err()?; let cgroup_fd = cgroup.as_raw_fd(); let attach_type = self.data.expected_attach_type.unwrap(); - let k_ver = kernel_version().unwrap(); - if k_ver >= (5, 7, 0) { + if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { let link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, None, 0).map_err( |(_, io_error)| ProgramError::SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/cgroup_sockopt.rs b/aya/src/programs/cgroup_sockopt.rs index 021ba38a..9f855cb0 100644 --- a/aya/src/programs/cgroup_sockopt.rs +++ b/aya/src/programs/cgroup_sockopt.rs @@ -1,6 +1,8 @@ //! Cgroup socket option programs. + pub use aya_obj::programs::CgroupSockoptAttachType; +use procfs::KernelVersion; use std::{ hash::Hash, os::fd::{AsRawFd, RawFd}, @@ -12,7 +14,7 @@ use crate::{ programs::{ define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, }, - sys::{bpf_link_create, bpf_prog_attach, kernel_version}, + sys::{bpf_link_create, bpf_prog_attach}, }; /// A program that can be used to get or set options on sockets. @@ -69,8 +71,7 @@ impl CgroupSockopt { let prog_fd = self.data.fd_or_err()?; let cgroup_fd = cgroup.as_raw_fd(); let attach_type = self.data.expected_attach_type.unwrap(); - let k_ver = kernel_version().unwrap(); - if k_ver >= (5, 7, 0) { + if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { let link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, None, 0).map_err( |(_, io_error)| ProgramError::SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/cgroup_sysctl.rs b/aya/src/programs/cgroup_sysctl.rs index f2bfd56b..c3ce1011 100644 --- a/aya/src/programs/cgroup_sysctl.rs +++ b/aya/src/programs/cgroup_sysctl.rs @@ -1,4 +1,6 @@ //! Cgroup sysctl programs. + +use procfs::KernelVersion; use std::{ hash::Hash, os::fd::{AsRawFd, RawFd}, @@ -9,7 +11,7 @@ use crate::{ programs::{ define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, }, - sys::{bpf_link_create, bpf_prog_attach, kernel_version}, + sys::{bpf_link_create, bpf_prog_attach}, }; /// A program used to watch for sysctl changes. @@ -64,8 +66,7 @@ impl CgroupSysctl { let prog_fd = self.data.fd_or_err()?; let cgroup_fd = cgroup.as_raw_fd(); - let k_ver = kernel_version().unwrap(); - if k_ver >= (5, 7, 0) { + if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { let link_fd = bpf_link_create(prog_fd, cgroup_fd, BPF_CGROUP_SYSCTL, None, 0).map_err( |(_, io_error)| ProgramError::SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index b47687b2..5aa6b156 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -65,6 +65,7 @@ mod utils; pub mod xdp; use libc::ENOSPC; +use procfs::KernelVersion; use std::{ ffi::CString, io, @@ -105,7 +106,7 @@ pub use xdp::{Xdp, XdpError, XdpFlags}; use crate::{ generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type}, maps::MapError, - obj::{self, btf::BtfError, Function, KernelVersion}, + obj::{self, btf::BtfError, Function}, pin::PinError, sys::{ bpf_btf_get_fd_by_id, bpf_get_object, bpf_load_program, bpf_pin_object, @@ -573,13 +574,14 @@ fn load_program( }, ) = obj; - let target_kernel_version = match *kernel_version { - KernelVersion::Any => { - let (major, minor, patch) = crate::sys::kernel_version().unwrap(); - (major << 16) + (minor << 8) + patch - } - _ => (*kernel_version).into(), - }; + let target_kernel_version = kernel_version.unwrap_or_else(|| { + let KernelVersion { + major, + minor, + patch, + } = KernelVersion::current().unwrap(); + (u32::from(major) << 16) + (u32::from(minor) << 8) + u32::from(patch) + }); let mut logger = VerifierLog::new(); diff --git a/aya/src/programs/probe.rs b/aya/src/programs/probe.rs index f9d17bc0..c6320e1a 100644 --- a/aya/src/programs/probe.rs +++ b/aya/src/programs/probe.rs @@ -1,4 +1,5 @@ use libc::pid_t; +use procfs::KernelVersion; use std::{ fs::{self, OpenOptions}, io::{self, Write}, @@ -13,7 +14,7 @@ use crate::{ trace_point::read_sys_fs_trace_point_id, uprobe::UProbeError, utils::find_tracefs_path, Link, ProgramData, ProgramError, }, - sys::{kernel_version, perf_event_open_probe, perf_event_open_trace_point}, + sys::{perf_event_open_probe, perf_event_open_trace_point}, }; static PROBE_NAME_INDEX: AtomicUsize = AtomicUsize::new(0); @@ -49,8 +50,7 @@ pub(crate) fn attach>( ) -> Result { // https://github.com/torvalds/linux/commit/e12f03d7031a977356e3d7b75a68c2185ff8d155 // Use debugfs to create probe - let k_ver = kernel_version().unwrap(); - if k_ver < (4, 17, 0) { + if KernelVersion::current().unwrap() >= KernelVersion::new(4, 17, 0) { let (fd, event_alias) = create_as_trace_point(kind, fn_name, offset, pid)?; let link = T::from(perf_attach_debugfs( program_data.fd_or_err()?, diff --git a/aya/src/programs/xdp.rs b/aya/src/programs/xdp.rs index 4940fc7e..01843928 100644 --- a/aya/src/programs/xdp.rs +++ b/aya/src/programs/xdp.rs @@ -1,6 +1,8 @@ //! eXpress Data Path (XDP) programs. + use bitflags; use libc::if_nametoindex; +use procfs::KernelVersion; use std::{convert::TryFrom, ffi::CString, hash::Hash, io, mem, os::unix::io::RawFd}; use thiserror::Error; @@ -15,10 +17,7 @@ use crate::{ programs::{ define_link_wrapper, load_program, FdLink, Link, LinkError, ProgramData, ProgramError, }, - sys::{ - bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, kernel_version, - netlink_set_xdp_fd, - }, + sys::{bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, netlink_set_xdp_fd}, }; /// The type returned when attaching an [`Xdp`] program fails on kernels `< 5.9`. @@ -126,8 +125,7 @@ impl Xdp { let prog_fd = self.data.fd_or_err()?; let if_index = if_index as RawFd; - let k_ver = kernel_version().unwrap(); - if k_ver >= (5, 9, 0) { + if KernelVersion::current().unwrap() >= KernelVersion::new(5, 9, 0) { let link_fd = bpf_link_create(prog_fd, if_index, BPF_XDP, None, flags.bits()).map_err( |(_, io_error)| ProgramError::SyscallError { call: "bpf_link_create", @@ -224,8 +222,7 @@ impl Link for NlLink { } fn detach(self) -> Result<(), ProgramError> { - let k_ver = kernel_version().unwrap(); - let flags = if k_ver >= (5, 7, 0) { + let flags = if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { self.flags.bits() | XDP_FLAGS_REPLACE } else { self.flags.bits() diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index e0aa1669..542896cd 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -12,6 +12,7 @@ use obj::{ maps::{bpf_map_def, LegacyMap}, BpfSectionKind, }; +use procfs::KernelVersion; use crate::{ generated::{ @@ -27,12 +28,17 @@ use crate::{ }, copy_instructions, }, - sys::{kernel_version, syscall, SysResult, Syscall}, + sys::{syscall, SysResult, Syscall}, util::VerifierLog, Btf, Pod, BPF_OBJ_NAME_LEN, }; -pub(crate) fn bpf_create_map(name: &CStr, def: &obj::Map, btf_fd: Option) -> SysResult { +pub(crate) fn bpf_create_map( + name: &CStr, + def: &obj::Map, + btf_fd: Option, + kernel_version: KernelVersion, +) -> SysResult { let mut attr = unsafe { mem::zeroed::() }; let u = unsafe { &mut attr.__bindgen_anon_1 }; @@ -78,8 +84,7 @@ pub(crate) fn bpf_create_map(name: &CStr, def: &obj::Map, btf_fd: Option) // https://github.com/torvalds/linux/commit/ad5b177bd73f5107d97c36f56395c4281fb6f089 // The map name was added as a parameter in kernel 4.15+ so we skip adding it on // older kernels for compatibility - let k_ver = kernel_version().unwrap(); - if k_ver >= (4, 15, 0) { + if kernel_version >= KernelVersion::new(4, 15, 0) { // u.map_name is 16 bytes max and must be NULL terminated let name_len = cmp::min(name.to_bytes().len(), BPF_OBJ_NAME_LEN - 1); u.map_name[..name_len] diff --git a/aya/src/sys/mod.rs b/aya/src/sys/mod.rs index 294ff386..646d6cfe 100644 --- a/aya/src/sys/mod.rs +++ b/aya/src/sys/mod.rs @@ -5,15 +5,9 @@ mod perf_event; #[cfg(test)] mod fake; -use std::io; -#[cfg(not(test))] -use std::{ffi::CString, mem}; -#[cfg(not(test))] -use std::{fs::File, io::Read}; +use std::{io, mem}; -#[cfg(not(test))] -use libc::utsname; -use libc::{c_int, c_long, pid_t}; +use libc::{c_int, c_long, pid_t, SYS_bpf, SYS_perf_event_open}; pub(crate) use bpf::*; #[cfg(test)] @@ -25,7 +19,6 @@ use crate::generated::{bpf_attr, bpf_cmd, perf_event_attr}; pub(crate) type SysResult = Result; -#[cfg_attr(test, allow(dead_code))] pub(crate) enum Syscall<'a> { Bpf { cmd: bpf_cmd, @@ -46,126 +39,28 @@ pub(crate) enum Syscall<'a> { } fn syscall(call: Syscall) -> SysResult { - #[cfg(not(test))] - return unsafe { syscall_impl(call) }; - #[cfg(test)] return TEST_SYSCALL.with(|test_impl| unsafe { test_impl.borrow()(call) }); -} - -#[cfg(not(test))] -unsafe fn syscall_impl(call: Syscall) -> SysResult { - use libc::{SYS_bpf, SYS_perf_event_open}; - - use Syscall::*; - let ret = match call { - Bpf { cmd, attr } => libc::syscall(SYS_bpf, cmd, attr, mem::size_of::()), - PerfEventOpen { - attr, - pid, - cpu, - group, - flags, - } => libc::syscall(SYS_perf_event_open, &attr, pid, cpu, group, flags), - PerfEventIoctl { fd, request, arg } => { - libc::ioctl(fd, request.try_into().unwrap(), arg) as libc::c_long - } - }; - - if ret < 0 { - return Err((ret, io::Error::last_os_error())); - } - - Ok(ret) -} - -#[cfg(test)] -pub(crate) fn kernel_version() -> Result<(u32, u32, u32), ()> { - Ok((0xff, 0xff, 0xff)) -} -#[cfg(not(test))] -fn ubuntu_kernel_version() -> Result<(u32, u32, u32), ()> { - if let Ok(mut file) = File::open("/proc/version_signature") { - let mut buf = String::new(); - let mut major = 0u32; - let mut minor = 0u32; - let mut patch = 0u32; - let format = CString::new("%*s %*s %u.%u.%u\n").unwrap(); - - file.read_to_string(&mut buf).map_err(|_| ())?; - - unsafe { - if libc::sscanf( - buf.as_ptr() as *const _, - format.as_ptr(), - &mut major as *mut u32, - &mut minor as *mut _, - &mut patch as *mut _, - ) == 3 - { - return Ok((major, minor, patch)); + #[cfg_attr(test, allow(unreachable_code))] + match unsafe { + match call { + Syscall::Bpf { cmd, attr } => { + libc::syscall(SYS_bpf, cmd, attr, mem::size_of::()) } - } - } - - Err(()) -} - -#[cfg(not(test))] -pub(crate) fn kernel_version() -> Result<(u32, u32, u32), ()> { - if let Ok(version) = ubuntu_kernel_version() { - return Ok(version); - } - - unsafe { - let mut v = mem::zeroed::(); - if libc::uname(&mut v as *mut _) != 0 { - return Err(()); - } - - let mut major = 0u32; - let mut minor = 0u32; - let mut patch = 0u32; - - let debian_marker = CString::new("Debian").unwrap(); - - let p = libc::strstr(v.version.as_ptr(), debian_marker.as_ptr()); - - if !p.is_null() { - let debian_format = CString::new("Debian %u.%u.%u").map_err(|_| ())?; - - if libc::sscanf( - p, - debian_format.as_ptr(), - &mut major as *mut u32, - &mut minor as *mut _, - &mut patch as *mut _, - ) == 3 - { - // On Debian 10, kernels after 4.19.229 expect 4.19.255 due to broken Makefile patches. - let patch_level_limit = if major == 4 && minor == 19 { 230 } else { 255 }; - - if patch >= patch_level_limit { - patch = 255; - } - - return Ok((major, minor, patch)); + Syscall::PerfEventOpen { + attr, + pid, + cpu, + group, + flags, + } => libc::syscall(SYS_perf_event_open, &attr, pid, cpu, group, flags), + Syscall::PerfEventIoctl { fd, request, arg } => { + libc::ioctl(fd, request.try_into().unwrap(), arg) as libc::c_long } } - - let format = CString::new("%u.%u.%u").unwrap(); - if libc::sscanf( - v.release.as_ptr(), - format.as_ptr(), - &mut major as *mut u32, - &mut minor as *mut _, - &mut patch as *mut _, - ) != 3 - { - return Err(()); - } - - Ok((major, minor, patch)) + } { + ret @ 0.. => Ok(ret), + ret => Err((ret, io::Error::last_os_error())), } } diff --git a/test/integration-test/Cargo.toml b/test/integration-test/Cargo.toml index 07eaa927..36f8270b 100644 --- a/test/integration-test/Cargo.toml +++ b/test/integration-test/Cargo.toml @@ -12,7 +12,7 @@ aya-obj = { path = "../../aya-obj" } libc = { version = "0.2.105" } log = "0.4" object = { version = "0.31", default-features = false, features = ["std", "read_core", "elf"] } +procfs = "0.15.1" rbpf = "0.2.0" -regex = "1" tempfile = "3.3.0" tokio = { version = "1.24", features = ["rt", "rt-multi-thread", "sync", "time"] } diff --git a/test/integration-test/tests/common.rs b/test/integration-test/tests/common.rs deleted file mode 100644 index 3af8a597..00000000 --- a/test/integration-test/tests/common.rs +++ /dev/null @@ -1,23 +0,0 @@ -use anyhow::bail; -use libc::{uname, utsname}; -use regex::Regex; -use std::{cell::OnceCell, ffi::CStr, mem}; - -pub fn kernel_version() -> anyhow::Result<(u8, u8, u8)> { - static mut RE: OnceCell = OnceCell::new(); - let re = - unsafe { &mut RE }.get_or_init(|| Regex::new(r"^([0-9]+)\.([0-9]+)\.([0-9]+)").unwrap()); - let mut data: utsname = unsafe { mem::zeroed() }; - let ret = unsafe { uname(&mut data) }; - assert!(ret >= 0, "libc::uname failed."); - let release_cstr = unsafe { CStr::from_ptr(data.release.as_ptr()) }; - let release = release_cstr.to_string_lossy(); - if let Some(caps) = re.captures(&release) { - let major = caps.get(1).unwrap().as_str().parse().unwrap(); - let minor = caps.get(2).unwrap().as_str().parse().unwrap(); - let patch = caps.get(3).unwrap().as_str().parse().unwrap(); - Ok((major, minor, patch)) - } else { - bail!("no kernel version found"); - } -} diff --git a/test/integration-test/tests/load.rs b/test/integration-test/tests/load.rs index 110025b2..d242338d 100644 --- a/test/integration-test/tests/load.rs +++ b/test/integration-test/tests/load.rs @@ -1,3 +1,4 @@ +use procfs::KernelVersion; use std::{convert::TryInto as _, thread, time}; use aya::{ @@ -10,9 +11,6 @@ use aya::{ Bpf, }; -mod common; -use common::kernel_version; - const MAX_RETRIES: u32 = 100; const RETRY_DURATION_MS: u64 = 10; @@ -133,7 +131,7 @@ fn unload_kprobe() { #[test] fn pin_link() { - if kernel_version().unwrap() < (5, 9, 0) { + if KernelVersion::current().unwrap() < KernelVersion::new(5, 9, 0) { eprintln!("skipping test, XDP uses netlink"); return; } @@ -168,7 +166,7 @@ fn pin_link() { #[test] fn pin_lifecycle() { - if kernel_version().unwrap() < (5, 9, 0) { + if KernelVersion::current().unwrap() < KernelVersion::new(5, 9, 0) { eprintln!("skipping test, XDP uses netlink"); return; } diff --git a/test/integration-test/tests/smoke.rs b/test/integration-test/tests/smoke.rs index daeed03a..b3006072 100644 --- a/test/integration-test/tests/smoke.rs +++ b/test/integration-test/tests/smoke.rs @@ -1,12 +1,11 @@ +use procfs::KernelVersion; + use aya::{ include_bytes_aligned, programs::{Extension, Xdp, XdpFlags}, Bpf, BpfLoader, }; -mod common; -use common::kernel_version; - #[test] fn xdp() { let bytes = include_bytes_aligned!("../../../target/bpfel-unknown-none/release/pass"); @@ -18,15 +17,10 @@ fn xdp() { #[test] fn extension() { - let (major, minor, _) = kernel_version().unwrap(); - if major < 5 || (minor == 5 && minor < 9) { - eprintln!( - "skipping as {}.{} does not meet version requirement of 5.9", - major, minor - ); + if KernelVersion::current().unwrap() < KernelVersion::new(5, 9, 0) { + eprintln!("skipping test, XDP uses netlink"); return; } - // TODO: Check kernel version == 5.9 or later let main_bytes = include_bytes_aligned!("../../../target/bpfel-unknown-none/release/main.bpf.o"); let mut bpf = Bpf::load(main_bytes).unwrap();