Browse Source

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.
Tamir Duberstein 1 year ago
parent
commit
b611038d5b

+ 23 - 47
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<u32>,
     /// Program BTF
     pub btf: Option<Btf>,
     /// 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<u32>,
     /// 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(&section)?.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<u32>) -> Object {
         Object {
             endianness,
             license,
@@ -1256,7 +1256,7 @@ fn parse_license(data: &[u8]) -> Result<CString, ParseError> {
         .to_owned())
 }
 
-fn parse_version(data: &[u8], endianness: object::Endianness) -> Result<KernelVersion, ParseError> {
+fn parse_version(data: &[u8], endianness: object::Endianness) -> Result<Option<u32>, ParseError> {
     let data = match data.len() {
         4 => data.try_into().unwrap(),
         _ => {
@@ -1271,9 +1271,10 @@ fn parse_version(data: &[u8], endianness: object::Endianness) -> Result<KernelVe
         object::Endianness::Little => 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<u32, BtfError> {
     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<KernelVersion> 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<Map, ParseError> {
@@ -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,

+ 18 - 8
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"]

+ 18 - 12
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);
 

+ 4 - 3
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",

+ 4 - 3
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",

+ 4 - 3
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",

+ 4 - 3
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",

+ 4 - 3
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",

+ 4 - 3
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",

+ 10 - 8
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<T: Link>(
         },
     ) = 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();
 

+ 3 - 3
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<T: Link + From<PerfLinkInner>>(
 ) -> Result<T::Id, ProgramError> {
     // 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()?,

+ 5 - 8
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()

+ 9 - 4
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<RawFd>) -> SysResult {
+pub(crate) fn bpf_create_map(
+    name: &CStr,
+    def: &obj::Map,
+    btf_fd: Option<RawFd>,
+    kernel_version: KernelVersion,
+) -> SysResult {
     let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
 
     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<RawFd>)
     // 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]

+ 19 - 124
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<c_long, (c_long, io::Error)>;
 
-#[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::<bpf_attr>()),
-        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::<bpf_attr>())
             }
-        }
-    }
-
-    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::<utsname>();
-        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())),
     }
 }

+ 1 - 1
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"] }

+ 0 - 23
test/integration-test/tests/common.rs

@@ -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<Regex> = 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");
-    }
-}

+ 3 - 5
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;
     }

+ 4 - 10
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();