From 4ddbc1cacbd19d7157299c343ef0672dde2ff533 Mon Sep 17 00:00:00 2001 From: Andrew Stoycos Date: Fri, 3 Feb 2023 15:16:44 -0500 Subject: [PATCH 01/11] Fix Aya build badge Signed-off-by: Andrew Stoycos --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2f8d48c..de31ec77 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [crates-badge]: https://img.shields.io/crates/v/aya.svg?style=for-the-badge&logo=rust [crates-url]: https://crates.io/crates/aya [license-badge]: https://img.shields.io/badge/license-MIT%2FApache--2.0-blue?style=for-the-badge -[build-badge]: https://img.shields.io/github/workflow/status/aya-rs/aya/build-aya?style=for-the-badge&logo=github +[build-badge]: https://img.shields.io/github/actions/workflow/status/aya-rs/aya/build-aya.yml?branch=main&style=for-the-badge [book-badge]: https://img.shields.io/badge/read%20the-book-9cf.svg?style=for-the-badge&logo=mdbook [book-url]: https://aya-rs.dev/book From c22014c757c88c40091e44a48e14920f6e6e0334 Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Tue, 7 Feb 2023 20:34:03 +1100 Subject: [PATCH 02/11] aya: fix Lru and LruPerCpu hash maps They were broken by https://github.com/aya-rs/aya/pull/397 --- aya/src/bpf.rs | 6 +++--- aya/src/maps/mod.rs | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 6ad6b084..2a751f36 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -608,10 +608,10 @@ fn parse_map(data: (String, MapData)) -> Result<(String, Map), BpfError> { BPF_MAP_TYPE_PERCPU_ARRAY => Ok(Map::PerCpuArray(map)), BPF_MAP_TYPE_PROG_ARRAY => Ok(Map::ProgramArray(map)), BPF_MAP_TYPE_HASH => Ok(Map::HashMap(map)), + BPF_MAP_TYPE_LRU_HASH => Ok(Map::LruHashMap(map)), BPF_MAP_TYPE_PERCPU_HASH => Ok(Map::PerCpuHashMap(map)), - BPF_MAP_TYPE_PERF_EVENT_ARRAY | BPF_MAP_TYPE_LRU_PERCPU_HASH => { - Ok(Map::PerfEventArray(map)) - } + BPF_MAP_TYPE_LRU_PERCPU_HASH => Ok(Map::PerCpuLruHashMap(map)), + BPF_MAP_TYPE_PERF_EVENT_ARRAY => Ok(Map::PerfEventArray(map)), BPF_MAP_TYPE_SOCKHASH => Ok(Map::SockHash(map)), BPF_MAP_TYPE_SOCKMAP => Ok(Map::SockMap(map)), BPF_MAP_TYPE_BLOOM_FILTER => Ok(Map::BloomFilter(map)), diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index e9d3009b..b7eafb6d 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -242,6 +242,10 @@ pub enum Map { HashMap(MapData), /// A [`PerCpuHashMap`] map PerCpuHashMap(MapData), + /// A [`HashMap`] map that uses a LRU eviction policy. + LruHashMap(MapData), + /// A [`PerCpuHashMap`] map that uses a LRU eviction policy. + PerCpuLruHashMap(MapData), /// A [`PerfEventArray`] map PerfEventArray(MapData), /// A [`SockMap`] map @@ -268,7 +272,9 @@ impl Map { Map::PerCpuArray(map) => map.obj.map_type(), Map::ProgramArray(map) => map.obj.map_type(), Map::HashMap(map) => map.obj.map_type(), + Map::LruHashMap(map) => map.obj.map_type(), Map::PerCpuHashMap(map) => map.obj.map_type(), + Map::PerCpuLruHashMap(map) => map.obj.map_type(), Map::PerfEventArray(map) => map.obj.map_type(), Map::SockHash(map) => map.obj.map_type(), Map::SockMap(map) => map.obj.map_type(), From 7a720ab0c1061b7a6f4e8e7bf862d86550bbdc7b Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Thu, 8 Sep 2022 19:32:54 +0000 Subject: [PATCH 03/11] aya: Add from_pin for Programs This commit adds from_pin() which allows the creation of a Program from a path on bpffs. This is useful to be able to call `attach` or other APIs for programs that are already loaded to the kernel. This differs from #444 since it implements this on the concrete program type, not the Program enum, allowing the user to pass in any additional context that isn't available from bpf_prog_info. Signed-off-by: Dave Tucker --- aya/src/programs/cgroup_skb.rs | 18 +++ aya/src/programs/cgroup_sock.rs | 15 ++ aya/src/programs/cgroup_sock_addr.rs | 15 ++ aya/src/programs/cgroup_sockopt.rs | 15 ++ aya/src/programs/kprobe.rs | 13 +- aya/src/programs/links.rs | 2 +- aya/src/programs/lsm.rs | 1 + aya/src/programs/mod.rs | 190 +++++++++++++++++++----- aya/src/programs/perf_event.rs | 1 + aya/src/programs/sk_skb.rs | 13 +- aya/src/programs/tc.rs | 15 ++ aya/src/programs/tp_btf.rs | 1 + aya/src/programs/uprobe.rs | 11 ++ test/integration-test/src/tests/load.rs | 24 ++- 14 files changed, 291 insertions(+), 43 deletions(-) diff --git a/aya/src/programs/cgroup_skb.rs b/aya/src/programs/cgroup_skb.rs index 3911a3c3..aa7541e1 100644 --- a/aya/src/programs/cgroup_skb.rs +++ b/aya/src/programs/cgroup_skb.rs @@ -2,6 +2,7 @@ use std::{ hash::Hash, os::unix::prelude::{AsRawFd, RawFd}, + path::Path, }; use crate::{ @@ -138,6 +139,23 @@ impl CgroupSkb { pub fn detach(&mut self, link_id: CgroupSkbLinkId) -> Result<(), ProgramError> { self.data.links.remove(link_id) } + + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>( + path: P, + expected_attach_type: CgroupSkbAttachType, + ) -> Result { + let data = ProgramData::from_pinned_path(path)?; + Ok(Self { + data, + expected_attach_type: Some(expected_attach_type), + }) + } } #[derive(Debug, Hash, Eq, PartialEq)] diff --git a/aya/src/programs/cgroup_sock.rs b/aya/src/programs/cgroup_sock.rs index 4463116c..2f391d95 100644 --- a/aya/src/programs/cgroup_sock.rs +++ b/aya/src/programs/cgroup_sock.rs @@ -4,6 +4,7 @@ pub use aya_obj::programs::CgroupSockAttachType; use std::{ hash::Hash, os::unix::prelude::{AsRawFd, RawFd}, + path::Path, }; use crate::{ @@ -113,6 +114,20 @@ impl CgroupSock { pub fn detach(&mut self, link_id: CgroupSockLinkId) -> Result<(), ProgramError> { self.data.links.remove(link_id) } + + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>( + path: P, + attach_type: CgroupSockAttachType, + ) -> Result { + let data = ProgramData::from_pinned_path(path)?; + Ok(Self { data, attach_type }) + } } #[derive(Debug, Hash, Eq, PartialEq)] diff --git a/aya/src/programs/cgroup_sock_addr.rs b/aya/src/programs/cgroup_sock_addr.rs index 12345b5e..e1817252 100644 --- a/aya/src/programs/cgroup_sock_addr.rs +++ b/aya/src/programs/cgroup_sock_addr.rs @@ -4,6 +4,7 @@ pub use aya_obj::programs::CgroupSockAddrAttachType; use std::{ hash::Hash, os::unix::prelude::{AsRawFd, RawFd}, + path::Path, }; use crate::{ @@ -119,6 +120,20 @@ impl CgroupSockAddr { pub fn detach(&mut self, link_id: CgroupSockAddrLinkId) -> Result<(), ProgramError> { self.data.links.remove(link_id) } + + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>( + path: P, + attach_type: CgroupSockAddrAttachType, + ) -> Result { + let data = ProgramData::from_pinned_path(path)?; + Ok(Self { data, attach_type }) + } } #[derive(Debug, Hash, Eq, PartialEq)] diff --git a/aya/src/programs/cgroup_sockopt.rs b/aya/src/programs/cgroup_sockopt.rs index 97231f17..8b744a8c 100644 --- a/aya/src/programs/cgroup_sockopt.rs +++ b/aya/src/programs/cgroup_sockopt.rs @@ -4,6 +4,7 @@ pub use aya_obj::programs::CgroupSockoptAttachType; use std::{ hash::Hash, os::unix::prelude::{AsRawFd, RawFd}, + path::Path, }; use crate::{ @@ -114,6 +115,20 @@ impl CgroupSockopt { pub fn detach(&mut self, link_id: CgroupSockoptLinkId) -> Result<(), ProgramError> { self.data.links.remove(link_id) } + + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>( + path: P, + attach_type: CgroupSockoptAttachType, + ) -> Result { + let data = ProgramData::from_pinned_path(path)?; + Ok(Self { data, attach_type }) + } } #[derive(Debug, Hash, Eq, PartialEq)] diff --git a/aya/src/programs/kprobe.rs b/aya/src/programs/kprobe.rs index 81bb783f..5bd2726e 100644 --- a/aya/src/programs/kprobe.rs +++ b/aya/src/programs/kprobe.rs @@ -1,5 +1,5 @@ //! Kernel space probes. -use std::io; +use std::{io, path::Path}; use thiserror::Error; use crate::{ @@ -83,6 +83,17 @@ impl KProbe { pub fn take_link(&mut self, link_id: KProbeLinkId) -> Result { self.data.take_link(link_id) } + + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>(path: P, kind: ProbeKind) -> Result { + let data = ProgramData::from_pinned_path(path)?; + Ok(Self { data, kind }) + } } define_link_wrapper!( diff --git a/aya/src/programs/links.rs b/aya/src/programs/links.rs index 79aa3c07..19177f35 100644 --- a/aya/src/programs/links.rs +++ b/aya/src/programs/links.rs @@ -181,7 +181,7 @@ impl PinnedLink { PinnedLink { inner: link, path } } - /// Creates a [`PinnedLink`] from a valid path on bpffs. + /// Creates a [`crate::programs::links::PinnedLink`] from a valid path on bpffs. pub fn from_pin>(path: P) -> Result { let path_string = CString::new(path.as_ref().to_string_lossy().to_string()).unwrap(); let fd = diff --git a/aya/src/programs/lsm.rs b/aya/src/programs/lsm.rs index 904019fc..3a455707 100644 --- a/aya/src/programs/lsm.rs +++ b/aya/src/programs/lsm.rs @@ -1,4 +1,5 @@ //! LSM probes. + use crate::{ generated::{bpf_attach_type::BPF_LSM_MAC, bpf_prog_type::BPF_PROG_TYPE_LSM}, obj::btf::{Btf, BtfKind}, diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 4454fcc3..479d61e4 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -69,7 +69,7 @@ use std::{ ffi::CString, io, os::unix::io::{AsRawFd, RawFd}, - path::Path, + path::{Path, PathBuf}, }; use thiserror::Error; @@ -108,8 +108,9 @@ use crate::{ obj::{self, btf::BtfError, Function, KernelVersion}, pin::PinError, sys::{ - bpf_get_object, bpf_load_program, bpf_pin_object, bpf_prog_get_fd_by_id, - bpf_prog_get_info_by_fd, bpf_prog_query, retry_with_verifier_logs, BpfLoadProgramAttrs, + bpf_btf_get_fd_by_id, bpf_get_object, bpf_load_program, bpf_pin_object, + bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd, bpf_prog_query, retry_with_verifier_logs, + BpfLoadProgramAttrs, }, util::VerifierLog, }; @@ -337,33 +338,33 @@ impl Program { } } - /// Unload the program - fn unload(&mut self) -> Result<(), ProgramError> { + /// Unloads the program from the kernel. + pub fn unload(self) -> Result<(), ProgramError> { match self { - Program::KProbe(p) => p.unload(), - Program::UProbe(p) => p.unload(), - Program::TracePoint(p) => p.unload(), - Program::SocketFilter(p) => p.unload(), - Program::Xdp(p) => p.unload(), - Program::SkMsg(p) => p.unload(), - Program::SkSkb(p) => p.unload(), - Program::SockOps(p) => p.unload(), - Program::SchedClassifier(p) => p.unload(), - Program::CgroupSkb(p) => p.unload(), - Program::CgroupSysctl(p) => p.unload(), - Program::CgroupSockopt(p) => p.unload(), - Program::LircMode2(p) => p.unload(), - Program::PerfEvent(p) => p.unload(), - Program::RawTracePoint(p) => p.unload(), - Program::Lsm(p) => p.unload(), - Program::BtfTracePoint(p) => p.unload(), - Program::FEntry(p) => p.unload(), - Program::FExit(p) => p.unload(), - Program::Extension(p) => p.unload(), - Program::CgroupSockAddr(p) => p.unload(), - Program::SkLookup(p) => p.unload(), - Program::CgroupSock(p) => p.unload(), - Program::CgroupDevice(p) => p.unload(), + Program::KProbe(mut p) => p.unload(), + Program::UProbe(mut p) => p.unload(), + Program::TracePoint(mut p) => p.unload(), + Program::SocketFilter(mut p) => p.unload(), + Program::Xdp(mut p) => p.unload(), + Program::SkMsg(mut p) => p.unload(), + Program::SkSkb(mut p) => p.unload(), + Program::SockOps(mut p) => p.unload(), + Program::SchedClassifier(mut p) => p.unload(), + Program::CgroupSkb(mut p) => p.unload(), + Program::CgroupSysctl(mut p) => p.unload(), + Program::CgroupSockopt(mut p) => p.unload(), + Program::LircMode2(mut p) => p.unload(), + Program::PerfEvent(mut p) => p.unload(), + Program::RawTracePoint(mut p) => p.unload(), + Program::Lsm(mut p) => p.unload(), + Program::BtfTracePoint(mut p) => p.unload(), + Program::FEntry(mut p) => p.unload(), + Program::FExit(mut p) => p.unload(), + Program::Extension(mut p) => p.unload(), + Program::CgroupSockAddr(mut p) => p.unload(), + Program::SkLookup(mut p) => p.unload(), + Program::CgroupSock(mut p) => p.unload(), + Program::CgroupDevice(mut p) => p.unload(), } } @@ -401,16 +402,10 @@ impl Program { } } -impl Drop for Program { - fn drop(&mut self) { - let _ = self.unload(); - } -} - #[derive(Debug)] pub(crate) struct ProgramData { pub(crate) name: Option, - pub(crate) obj: obj::Program, + pub(crate) obj: Option, pub(crate) fd: Option, pub(crate) links: LinkMap, pub(crate) expected_attach_type: Option, @@ -419,6 +414,7 @@ pub(crate) struct ProgramData { pub(crate) attach_prog_fd: Option, pub(crate) btf_fd: Option, pub(crate) verifier_log_level: u32, + pub(crate) path: Option, } impl ProgramData { @@ -430,7 +426,7 @@ impl ProgramData { ) -> ProgramData { ProgramData { name, - obj, + obj: Some(obj), fd: None, links: LinkMap::new(), expected_attach_type: None, @@ -439,8 +435,68 @@ impl ProgramData { attach_prog_fd: None, btf_fd, verifier_log_level, + path: None, } } + + pub(crate) fn from_bpf_prog_info( + name: Option, + fd: RawFd, + path: &Path, + info: bpf_prog_info, + ) -> Result, ProgramError> { + let attach_btf_id = if info.attach_btf_id > 0 { + Some(info.attach_btf_id) + } else { + None + }; + let attach_btf_obj_fd = if info.attach_btf_obj_id > 0 { + let fd = bpf_btf_get_fd_by_id(info.attach_btf_obj_id).map_err(|io_error| { + ProgramError::SyscallError { + call: "bpf_btf_get_fd_by_id".to_string(), + io_error, + } + })?; + Some(fd as u32) + } else { + None + }; + + Ok(ProgramData { + name, + obj: None, + fd: Some(fd), + links: LinkMap::new(), + expected_attach_type: None, + attach_btf_obj_fd, + attach_btf_id, + attach_prog_fd: None, + btf_fd: None, + verifier_log_level: 0, + path: Some(path.to_path_buf()), + }) + } + + pub(crate) fn from_pinned_path>( + path: P, + ) -> Result, ProgramError> { + let path_string = + CString::new(path.as_ref().as_os_str().to_string_lossy().as_bytes()).unwrap(); + let fd = + bpf_get_object(&path_string).map_err(|(_, io_error)| ProgramError::SyscallError { + call: "bpf_obj_get".to_owned(), + io_error, + })? as RawFd; + + let info = bpf_prog_get_info_by_fd(fd).map_err(|io_error| ProgramError::SyscallError { + call: "bpf_prog_get_info_by_fd".to_owned(), + io_error, + })?; + + let info = ProgramInfo(info); + let name = info.name_as_str().map(|s| s.to_string()); + ProgramData::from_bpf_prog_info(name, fd, path.as_ref(), info.0) + } } impl ProgramData { @@ -493,6 +549,11 @@ fn load_program( if fd.is_some() { return Err(ProgramError::AlreadyLoaded); } + if obj.is_none() { + // This program was loaded from a pin in bpffs + return Err(ProgramError::AlreadyLoaded); + } + let obj = obj.as_ref().unwrap(); let crate::obj::Program { function: Function { @@ -621,6 +682,12 @@ macro_rules! impl_program_unload { unload_program(&mut self.data) } } + + impl Drop for $struct_name { + fn drop(&mut self) { + let _ = self.unload(); + } + } )+ } } @@ -703,8 +770,17 @@ macro_rules! impl_program_pin{ /// To remove the program, the file on the BPF filesystem must be removed. /// Any directories in the the path provided should have been created by the caller. pub fn pin>(&mut self, path: P) -> Result<(), PinError> { + self.data.path = Some(path.as_ref().to_path_buf()); pin_program(&mut self.data, path) } + + /// Removes the pinned link from the filesystem. + pub fn unpin(mut self) -> Result<(), io::Error> { + if let Some(path) = self.data.path.take() { + std::fs::remove_file(path)?; + } + Ok(()) + } } )+ } @@ -737,6 +813,45 @@ impl_program_pin!( CgroupDevice, ); +macro_rules! impl_from_pin { + ($($struct_name:ident),+ $(,)?) => { + $( + impl $struct_name { + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>(path: P) -> Result { + let data = ProgramData::from_pinned_path(path)?; + Ok(Self { data }) + } + } + )+ + } +} + +// Use impl_from_pin if the program doesn't require additional data +impl_from_pin!( + TracePoint, + SocketFilter, + Xdp, + SkMsg, + CgroupSysctl, + LircMode2, + PerfEvent, + Lsm, + RawTracePoint, + BtfTracePoint, + FEntry, + FExit, + Extension, + SkLookup, + SockOps, + CgroupDevice, +); + macro_rules! impl_try_from_program { ($($ty:ident),+ $(,)?) => { $( @@ -849,7 +964,6 @@ impl ProgramInfo { unsafe { libc::close(fd); } - Ok(ProgramInfo(info)) } } diff --git a/aya/src/programs/perf_event.rs b/aya/src/programs/perf_event.rs index 0b8e7ae7..d4185375 100644 --- a/aya/src/programs/perf_event.rs +++ b/aya/src/programs/perf_event.rs @@ -1,4 +1,5 @@ //! Perf event programs. + pub use crate::generated::{ perf_hw_cache_id, perf_hw_cache_op_id, perf_hw_cache_op_result_id, perf_hw_id, perf_sw_ids, }; diff --git a/aya/src/programs/sk_skb.rs b/aya/src/programs/sk_skb.rs index a0470506..34aade6d 100644 --- a/aya/src/programs/sk_skb.rs +++ b/aya/src/programs/sk_skb.rs @@ -1,6 +1,6 @@ //! Skskb programs. -use std::os::unix::io::AsRawFd; +use std::{os::unix::io::AsRawFd, path::Path}; use crate::{ generated::{ @@ -105,6 +105,17 @@ impl SkSkb { pub fn take_link(&mut self, link_id: SkSkbLinkId) -> Result { self.data.take_link(link_id) } + + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>(path: P, kind: SkSkbKind) -> Result { + let data = ProgramData::from_pinned_path(path)?; + Ok(Self { data, kind }) + } } define_link_wrapper!( diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index cece3232..ad86b65c 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -4,6 +4,7 @@ use thiserror::Error; use std::{ ffi::{CStr, CString}, io, + path::Path, }; use crate::{ @@ -190,6 +191,20 @@ impl SchedClassifier { ) -> Result { self.data.take_link(link_id) } + + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>(path: P) -> Result { + let data = ProgramData::from_pinned_path(path)?; + let cname = CString::new(data.name.clone().unwrap_or_default()) + .unwrap() + .into_boxed_c_str(); + Ok(Self { data, name: cname }) + } } #[derive(Debug, Hash, Eq, PartialEq)] diff --git a/aya/src/programs/tp_btf.rs b/aya/src/programs/tp_btf.rs index e3f0b8cc..eb419ab1 100644 --- a/aya/src/programs/tp_btf.rs +++ b/aya/src/programs/tp_btf.rs @@ -1,4 +1,5 @@ //! BTF-enabled raw tracepoints. + use crate::{ generated::{bpf_attach_type::BPF_TRACE_RAW_TP, bpf_prog_type::BPF_PROG_TYPE_TRACING}, obj::btf::{Btf, BtfKind}, diff --git a/aya/src/programs/uprobe.rs b/aya/src/programs/uprobe.rs index d201ff5c..55629da0 100644 --- a/aya/src/programs/uprobe.rs +++ b/aya/src/programs/uprobe.rs @@ -136,6 +136,17 @@ impl UProbe { pub fn take_link(&mut self, link_id: UProbeLinkId) -> Result { self.data.take_link(link_id) } + + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>(path: P, kind: ProbeKind) -> Result { + let data = ProgramData::from_pinned_path(path)?; + Ok(Self { data, kind }) + } } define_link_wrapper!( diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index e593d9b5..8bb9dd0e 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -1,4 +1,4 @@ -use std::{process::Command, thread, time}; +use std::{convert::TryInto, process::Command, thread, time}; use aya::{ include_bytes_aligned, @@ -192,16 +192,36 @@ fn pin_lifecycle() { let mut bpf = Bpf::load(bytes).unwrap(); let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap(); prog.load().unwrap(); + prog.pin("/sys/fs/bpf/aya-xdp-test-prog").unwrap(); + } + + // should still be loaded since prog was pinned + assert_loaded!("pass", true); + + // 2. Load program from bpffs but don't attach it + { + let _ = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog").unwrap(); + } + + // should still be loaded since prog was pinned + assert_loaded!("pass", true); + + // 3. Load program from bpffs and attach + { + let mut prog = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog").unwrap(); let link_id = prog.attach("lo", XdpFlags::default()).unwrap(); let link = prog.take_link(link_id).unwrap(); let fd_link: FdLink = link.try_into().unwrap(); fd_link.pin("/sys/fs/bpf/aya-xdp-test-lo").unwrap(); + + // Unpin the program. It will stay attached since its links were pinned. + prog.unpin().unwrap(); } // should still be loaded since link was pinned assert_loaded!("pass", true); - // 2. Load a new version of the program, unpin link, and atomically replace old program + // 4. Load a new version of the program, unpin link, and atomically replace old program { let mut bpf = Bpf::load(bytes).unwrap(); let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap(); From 96b282d149bdafc91bd0455bc09ecf2ebf1e0c82 Mon Sep 17 00:00:00 2001 From: Dmitry Savintsev Date: Thu, 9 Feb 2023 11:07:42 +0100 Subject: [PATCH 04/11] aya-tool: remove outdated workaround The comment says that `.derive_debug` was needed as a workaround for https://github.com/rust-lang/rust-bindgen/issues/2083. This issue is now closed, and aya-tool compiles without derive_debug. Additionally, update bindgen dependency to 1.64. Signed-off-by: Dmitry Savintsev --- aya-tool/Cargo.toml | 2 +- aya-tool/src/bindgen.rs | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/aya-tool/Cargo.toml b/aya-tool/Cargo.toml index f1378e6f..8f7d9f93 100644 --- a/aya-tool/Cargo.toml +++ b/aya-tool/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Alessandro Decina "] edition = "2021" [dependencies] -bindgen = "0.63" +bindgen = "0.64" clap = { version = "4", features = ["derive"] } anyhow = "1" thiserror = "1" diff --git a/aya-tool/src/bindgen.rs b/aya-tool/src/bindgen.rs index 2d710534..eedff6d8 100644 --- a/aya-tool/src/bindgen.rs +++ b/aya-tool/src/bindgen.rs @@ -20,11 +20,4 @@ pub fn bpf_builder() -> Builder { .clang_arg("-Wno-unknown-attributes") .default_enum_style(EnumVariation::ModuleConsts) .prepend_enum_name(false) - // NOTE(vadorovsky): It's a workaround for the upstream bindgen issue: - // https://github.com/rust-lang/rust-bindgen/issues/2083 - // tl;dr: Rust nightly complains about #[repr(packed)] structs deriving - // Debug without Copy. - // It needs to be fixed properly upstream, but for now we have to - // disable Debug derive here. - .derive_debug(false) } From a18693b42dc986bde06b07540e261ecac59eef24 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Thu, 9 Feb 2023 14:19:47 +0000 Subject: [PATCH 05/11] aya: Add support for multibuffer programs This adds support for loading XDP programs that are multi-buffer capable, which is signalled using the xdp.frags section name. When this is set, we should set the BPF_F_XDP_HAS_FRAGS flag when loading the program into the kernel. Signed-off-by: Dave Tucker --- aya-obj/src/obj.rs | 28 +++++++++++++++++++++++++--- aya/src/bpf.rs | 14 +++++++++++--- aya/src/programs/mod.rs | 4 ++++ aya/src/sys/bpf.rs | 3 ++- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index 72f13ed7..b0668c27 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -160,7 +160,6 @@ pub struct Function { /// - `iter+`, `iter.s+` /// - `xdp.frags/cpumap`, `xdp/cpumap` /// - `xdp.frags/devmap`, `xdp/devmap` -/// - `xdp.frags` #[derive(Debug, Clone)] #[allow(missing_docs)] pub enum ProgramSection { @@ -184,6 +183,7 @@ pub enum ProgramSection { }, Xdp { name: String, + frags_supported: bool, }, SkMsg { name: String, @@ -266,7 +266,7 @@ impl ProgramSection { ProgramSection::URetProbe { name } => name, ProgramSection::TracePoint { name } => name, ProgramSection::SocketFilter { name } => name, - ProgramSection::Xdp { name } => name, + ProgramSection::Xdp { name, .. } => name, ProgramSection::SkMsg { name } => name, ProgramSection::SkSkbStreamParser { name } => name, ProgramSection::SkSkbStreamVerdict { name } => name, @@ -313,7 +313,8 @@ impl FromStr for ProgramSection { "kretprobe" => KRetProbe { name }, "uprobe" => UProbe { name }, "uretprobe" => URetProbe { name }, - "xdp" => Xdp { name }, + "xdp" => Xdp { name, frags_supported: false}, + "xdp.frags" => Xdp { name, frags_supported: true}, "tp_btf" => BtfTracePoint { name }, _ if kind.starts_with("tracepoint") || kind.starts_with("tp") => { // tracepoint sections are named `tracepoint/category/event_name`, @@ -1837,6 +1838,27 @@ mod tests { ); } + #[test] + fn test_parse_section_xdp_frags() { + let mut obj = fake_obj(); + + assert_matches!( + obj.parse_section(fake_section( + BpfSectionKind::Program, + "xdp.frags/foo", + bytes_of(&fake_ins()) + )), + Ok(()) + ); + assert_matches!( + obj.programs.get("foo"), + Some(Program { + section: ProgramSection::Xdp { frags_supported: true, .. }, + .. + }) + ); + } + #[test] fn test_parse_section_raw_tp() { let mut obj = fake_obj(); diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 2a751f36..d3e8059f 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -9,6 +9,7 @@ use std::{ use aya_obj::{ btf::{BtfFeatures, BtfRelocationError}, + generated::BPF_F_XDP_HAS_FRAGS, relocation::BpfRelocationError, }; use log::debug; @@ -485,9 +486,16 @@ impl<'a> BpfLoader<'a> { data: ProgramData::new(prog_name, obj, btf_fd, verifier_log_level), }) } - ProgramSection::Xdp { .. } => Program::Xdp(Xdp { - data: ProgramData::new(prog_name, obj, btf_fd, verifier_log_level), - }), + ProgramSection::Xdp { + frags_supported, .. + } => { + let mut data = + ProgramData::new(prog_name, obj, btf_fd, verifier_log_level); + if *frags_supported { + data.flags = BPF_F_XDP_HAS_FRAGS; + } + Program::Xdp(Xdp { data }) + } ProgramSection::SkMsg { .. } => Program::SkMsg(SkMsg { data: ProgramData::new(prog_name, obj, btf_fd, verifier_log_level), }), diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 479d61e4..2050f8a7 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -415,6 +415,7 @@ pub(crate) struct ProgramData { pub(crate) btf_fd: Option, pub(crate) verifier_log_level: u32, pub(crate) path: Option, + pub(crate) flags: u32, } impl ProgramData { @@ -436,6 +437,7 @@ impl ProgramData { btf_fd, verifier_log_level, path: None, + flags: 0, } } @@ -474,6 +476,7 @@ impl ProgramData { btf_fd: None, verifier_log_level: 0, path: Some(path.to_path_buf()), + flags: 0, }) } @@ -606,6 +609,7 @@ fn load_program( func_info: func_info.clone(), line_info_rec_size: *line_info_rec_size, line_info: line_info.clone(), + flags: data.flags, }; let verifier_log_level = data.verifier_log_level; diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index e7384976..ec3890d0 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -115,6 +115,7 @@ pub(crate) struct BpfLoadProgramAttrs<'a> { pub(crate) func_info: FuncSecInfo, pub(crate) line_info_rec_size: usize, pub(crate) line_info: LineSecInfo, + pub(crate) flags: u32, } pub(crate) fn bpf_load_program( @@ -136,6 +137,7 @@ pub(crate) fn bpf_load_program( u.prog_name = name; } + u.prog_flags = aya_attr.flags; u.prog_type = aya_attr.ty as u32; if let Some(v) = aya_attr.expected_attach_type { u.expected_attach_type = v as u32; @@ -179,7 +181,6 @@ pub(crate) fn bpf_load_program( if let Some(v) = aya_attr.attach_btf_id { u.attach_btf_id = v; } - sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr) } From 376c48640033fdbf8b5199641f353587273f8a32 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Thu, 9 Feb 2023 14:38:49 +0000 Subject: [PATCH 06/11] aya-bpf: Add multibuffer support for XDP Signed-off-by: Dave Tucker --- aya-bpf-macros/src/expand.rs | 23 ++++++++++++++++++----- aya-obj/src/obj.rs | 15 ++++++++++++--- test/integration-ebpf/src/map_test.rs | 2 +- test/integration-ebpf/src/pass.rs | 2 +- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/aya-bpf-macros/src/expand.rs b/aya-bpf-macros/src/expand.rs index 7c47e579..506cfa89 100644 --- a/aya-bpf-macros/src/expand.rs +++ b/aya-bpf-macros/src/expand.rs @@ -200,20 +200,33 @@ impl SkMsg { pub struct Xdp { item: ItemFn, name: Option, + frags: bool, } impl Xdp { pub fn from_syn(mut args: Args, item: ItemFn) -> Result { - let name = name_arg(&mut args)?; - - Ok(Xdp { item, name }) + let name = pop_arg(&mut args, "name"); + let mut frags = false; + if let Some(s) = pop_arg(&mut args, "frags") { + if let Ok(m) = s.parse() { + frags = m + } else { + return Err(Error::new_spanned( + "mutlibuffer", + "invalid value. should be 'true' or 'false'", + )); + } + } + err_on_unknown_args(&args)?; + Ok(Xdp { item, name, frags }) } pub fn expand(&self) -> Result { + let section_prefix = if self.frags { "xdp.frags" } else { "xdp" }; let section_name = if let Some(name) = &self.name { - format!("xdp/{name}") + format!("{section_prefix}/{name}") } else { - "xdp".to_owned() + section_prefix.to_string() }; let fn_vis = &self.item.vis; let fn_name = &self.item.sig.ident; diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index b0668c27..8491709b 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -313,8 +313,14 @@ impl FromStr for ProgramSection { "kretprobe" => KRetProbe { name }, "uprobe" => UProbe { name }, "uretprobe" => URetProbe { name }, - "xdp" => Xdp { name, frags_supported: false}, - "xdp.frags" => Xdp { name, frags_supported: true}, + "xdp" => Xdp { + name, + frags_supported: false, + }, + "xdp.frags" => Xdp { + name, + frags_supported: true, + }, "tp_btf" => BtfTracePoint { name }, _ if kind.starts_with("tracepoint") || kind.starts_with("tp") => { // tracepoint sections are named `tracepoint/category/event_name`, @@ -1853,7 +1859,10 @@ mod tests { assert_matches!( obj.programs.get("foo"), Some(Program { - section: ProgramSection::Xdp { frags_supported: true, .. }, + section: ProgramSection::Xdp { + frags_supported: true, + .. + }, .. }) ); diff --git a/test/integration-ebpf/src/map_test.rs b/test/integration-ebpf/src/map_test.rs index fc17d1f9..87c07557 100644 --- a/test/integration-ebpf/src/map_test.rs +++ b/test/integration-ebpf/src/map_test.rs @@ -14,7 +14,7 @@ static FOO: Array = Array::::with_max_entries(10, 0); #[map(name = "BAR")] static BAZ: Array = Array::::with_max_entries(10, 0); -#[xdp] +#[xdp(frags = "true")] pub fn pass(ctx: XdpContext) -> u32 { match unsafe { try_pass(ctx) } { Ok(ret) => ret, diff --git a/test/integration-ebpf/src/pass.rs b/test/integration-ebpf/src/pass.rs index 0979d557..b1bdde99 100644 --- a/test/integration-ebpf/src/pass.rs +++ b/test/integration-ebpf/src/pass.rs @@ -3,7 +3,7 @@ use aya_bpf::{bindings::xdp_action, macros::xdp, programs::XdpContext}; -#[xdp(name = "pass")] +#[xdp(name = "pass", frags = "true")] pub fn pass(ctx: XdpContext) -> u32 { match unsafe { try_pass(ctx) } { Ok(ret) => ret, From ce22ca668f3e7c0f9832d28370457204537d2e50 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Sun, 26 Jun 2022 22:26:19 +0100 Subject: [PATCH 07/11] aya: Make features a lazy_static Signed-off-by: Dave Tucker --- aya-obj/src/btf/btf.rs | 19 +++++++++++ aya/src/bpf.rs | 74 ++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index f439ee1a..4dd4716c 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -172,6 +172,25 @@ pub struct BtfFeatures { pub btf_type_tag: bool, } +impl std::fmt::Display for BtfFeatures { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_fmt(format_args!( + "[FEAT PROBE] BTF func support: {}\n\ + [FEAT PROBE] BTF global func support: {}\n\ + [FEAT PROBE] BTF var and datasec support: {}\n\ + [FEAT PROBE] BTF float support: {}\n\ + [FEAT PROBE] BTF decl_tag support: {}\n\ + [FEAT PROBE] BTF type_tag support: {}", + self.btf_func, + self.btf_func_global, + self.btf_datasec, + self.btf_float, + self.btf_decl_tag, + self.btf_type_tag, + )) + } +} + /// Bpf Type Format metadata. /// /// BTF is a kind of debug metadata that allows eBPF programs compiled against one kernel version diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index d3e8059f..cf593b2a 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -65,6 +65,10 @@ unsafe impl Pod for [T; N] {} pub use aya_obj::maps::{bpf_map_def, PinningType}; +lazy_static! { + pub(crate) static ref FEATURES: Features = Features::new(); +} + // Features implements BPF and BTF feature detection #[derive(Default, Debug)] pub(crate) struct Features { @@ -73,42 +77,40 @@ pub(crate) struct Features { } impl Features { - fn probe_features(&mut self) { - self.bpf_name = is_prog_name_supported(); - debug!("[FEAT PROBE] BPF program name support: {}", self.bpf_name); - - self.btf = if is_btf_supported() { - Some(BtfFeatures::default()) + fn new() -> Self { + let btf = if is_btf_supported() { + Some(BtfFeatures { + btf_func: is_btf_func_supported(), + btf_func_global: is_btf_func_global_supported(), + btf_datasec: is_btf_datasec_supported(), + btf_float: is_btf_float_supported(), + btf_decl_tag: is_btf_decl_tag_supported(), + btf_type_tag: is_btf_type_tag_supported(), + }) } else { None }; - debug!("[FEAT PROBE] BTF support: {}", self.btf.is_some()); - - if let Some(ref mut btf) = self.btf { - btf.btf_func = is_btf_func_supported(); - debug!("[FEAT PROBE] BTF func support: {}", btf.btf_func); - - btf.btf_func_global = is_btf_func_global_supported(); - debug!( - "[FEAT PROBE] BTF global func support: {}", - btf.btf_func_global - ); - - btf.btf_datasec = is_btf_datasec_supported(); - debug!( - "[FEAT PROBE] BTF var and datasec support: {}", - btf.btf_datasec - ); - - btf.btf_float = is_btf_float_supported(); - debug!("[FEAT PROBE] BTF float support: {}", btf.btf_float); - - btf.btf_decl_tag = is_btf_decl_tag_supported(); - debug!("[FEAT PROBE] BTF decl_tag support: {}", btf.btf_decl_tag); + let f = Features { + bpf_name: is_prog_name_supported(), + btf, + }; - btf.btf_type_tag = is_btf_type_tag_supported(); - debug!("[FEAT PROBE] BTF type_tag support: {}", btf.btf_type_tag); + debug!("{}", f); + if let Some(btf) = f.btf.as_ref() { + debug!("{}", btf) } + f + } +} + +impl std::fmt::Display for Features { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "[FEAT PROBE] BPF program name support: {}\n\ + [FEAT PROBE] BTF support: {}", + self.bpf_name, + self.btf.is_some() + )) } } @@ -139,7 +141,6 @@ pub struct BpfLoader<'a> { map_pin_path: Option, globals: HashMap<&'a str, &'a [u8]>, max_entries: HashMap<&'a str, u32>, - features: Features, extensions: HashSet<&'a str>, verifier_log_level: VerifierLogLevel, } @@ -169,14 +170,11 @@ impl Default for VerifierLogLevel { impl<'a> BpfLoader<'a> { /// Creates a new loader instance. pub fn new() -> BpfLoader<'a> { - let mut features = Features::default(); - features.probe_features(); BpfLoader { btf: Btf::from_sys_fs().ok().map(Cow::Owned), map_pin_path: None, globals: HashMap::new(), max_entries: HashMap::new(), - features, extensions: HashSet::new(), verifier_log_level: VerifierLogLevel::default(), } @@ -360,8 +358,8 @@ impl<'a> BpfLoader<'a> { let mut obj = Object::parse(data)?; obj.patch_map_data(self.globals.clone())?; - let btf_fd = if let Some(ref btf) = self.features.btf { - if let Some(btf) = obj.fixup_and_sanitize_btf(btf)? { + let btf_fd = if let Some(ref features) = FEATURES.btf { + if let Some(btf) = obj.fixup_and_sanitize_btf(features)? { // load btf to the kernel Some(load_btf(btf.to_bytes())?) } else { @@ -449,7 +447,7 @@ impl<'a> BpfLoader<'a> { .programs .drain() .map(|(name, obj)| { - let prog_name = if self.features.bpf_name { + let prog_name = if FEATURES.bpf_name { Some(name.clone()) } else { None From 763b92a2e007a17cc2b6a17929dcb6a5c26c9f88 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 27 Jun 2022 22:20:10 +0100 Subject: [PATCH 08/11] aya: Add probe for bpf_link_create for perf programs Signed-off-by: Dave Tucker --- aya/src/bpf.rs | 10 +++++++--- aya/src/sys/bpf.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index cf593b2a..2d71d88c 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -35,8 +35,8 @@ use crate::{ sys::{ bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_btf_datasec_supported, is_btf_decl_tag_supported, is_btf_float_supported, is_btf_func_global_supported, - is_btf_func_supported, is_btf_supported, is_btf_type_tag_supported, is_prog_name_supported, - retry_with_verifier_logs, + is_btf_func_supported, is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported, + is_prog_name_supported, retry_with_verifier_logs, }, util::{bytes_of, possible_cpus, VerifierLog, POSSIBLE_CPUS}, }; @@ -73,6 +73,7 @@ lazy_static! { #[derive(Default, Debug)] pub(crate) struct Features { pub bpf_name: bool, + pub bpf_perf_link: bool, pub btf: Option, } @@ -92,6 +93,7 @@ impl Features { }; let f = Features { bpf_name: is_prog_name_supported(), + bpf_perf_link: is_perf_link_supported(), btf, }; @@ -107,8 +109,10 @@ impl std::fmt::Display for Features { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( "[FEAT PROBE] BPF program name support: {}\n\ + [FEAT PROBE] bpf_link support for kprobe/uprobe/tracepoint: {}\n\ [FEAT PROBE] BTF support: {}", self.bpf_name, + self.bpf_perf_link, self.btf.is_some() )) } @@ -358,7 +362,7 @@ impl<'a> BpfLoader<'a> { let mut obj = Object::parse(data)?; obj.patch_map_data(self.globals.clone())?; - let btf_fd = if let Some(ref features) = FEATURES.btf { + let btf_fd = if let Some(features) = &FEATURES.btf { if let Some(btf) = obj.fixup_and_sanitize_btf(features)? { // load btf to the kernel Some(load_btf(btf.to_bytes())?) diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index ec3890d0..20a88f26 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -599,6 +599,37 @@ pub(crate) fn is_prog_name_supported() -> bool { } } +pub(crate) fn is_perf_link_supported() -> bool { + let mut attr = unsafe { mem::zeroed::() }; + let u = unsafe { &mut attr.__bindgen_anon_3 }; + + let prog: &[u8] = &[ + 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r0 = 0 + 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit + ]; + + let gpl = b"GPL\0"; + u.license = gpl.as_ptr() as u64; + + let insns = copy_instructions(prog).unwrap(); + u.insn_cnt = insns.len() as u32; + u.insns = insns.as_ptr() as u64; + u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32; + + if let Ok(fd) = sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr) { + if let Err((code, _)) = + // Uses an invalid target FD so we get EBADF if supported. + bpf_link_create(fd as i32, -1, bpf_attach_type::BPF_PERF_EVENT, None, 0) + { + // Returns EINVAL if unsupported. EBADF if supported. + let res = code == (-libc::EBADF).into(); + unsafe { libc::close(fd as i32) }; + return res; + } + } + false +} + pub(crate) fn is_btf_supported() -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("int".to_string()); From d0b3d3b2fac955ed0e1e3d885fcd3ba67941dc8c Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Fri, 10 Feb 2023 23:20:37 +0000 Subject: [PATCH 09/11] aya: Enable bpf_link for perf_attach programs This adds support for bpf_link to PerfEvent, Tracepoint, Kprobe and Uprobe programs. Signed-off-by: Dave Tucker --- aya/src/programs/kprobe.rs | 6 +-- aya/src/programs/perf_attach.rs | 83 +++++++++++++++++++++++---------- aya/src/programs/perf_event.rs | 23 ++++++--- aya/src/programs/probe.rs | 17 ++++--- aya/src/programs/trace_point.rs | 9 ++-- aya/src/programs/uprobe.rs | 6 +-- 6 files changed, 98 insertions(+), 46 deletions(-) diff --git a/aya/src/programs/kprobe.rs b/aya/src/programs/kprobe.rs index 5bd2726e..65a505d2 100644 --- a/aya/src/programs/kprobe.rs +++ b/aya/src/programs/kprobe.rs @@ -6,7 +6,7 @@ use crate::{ generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE, programs::{ define_link_wrapper, load_program, - perf_attach::{PerfLink, PerfLinkId}, + perf_attach::{PerfLinkIdInner, PerfLinkInner}, probe::{attach, ProbeKind}, ProgramData, ProgramError, }, @@ -101,8 +101,8 @@ define_link_wrapper!( KProbeLink, /// The type returned by [KProbe::attach]. Can be passed to [KProbe::detach]. KProbeLinkId, - PerfLink, - PerfLinkId + PerfLinkInner, + PerfLinkIdInner ); /// The type returned when attaching a [`KProbe`] fails. diff --git a/aya/src/programs/perf_attach.rs b/aya/src/programs/perf_attach.rs index 7bd15f23..78609110 100644 --- a/aya/src/programs/perf_attach.rs +++ b/aya/src/programs/perf_attach.rs @@ -3,11 +3,42 @@ use libc::close; use std::os::unix::io::RawFd; use crate::{ - programs::{probe::detach_debug_fs, Link, ProbeKind, ProgramData, ProgramError}, - sys::perf_event_ioctl, - PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_SET_BPF, + generated::bpf_attach_type::BPF_PERF_EVENT, + programs::{probe::detach_debug_fs, FdLink, Link, ProbeKind, ProgramError}, + sys::{bpf_link_create, perf_event_ioctl}, + FEATURES, PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_SET_BPF, }; +#[derive(Debug, Hash, Eq, PartialEq)] +pub(crate) enum PerfLinkIdInner { + FdLinkId(::Id), + PerfLinkId(::Id), +} + +#[derive(Debug)] +pub(crate) enum PerfLinkInner { + FdLink(FdLink), + PerfLink(PerfLink), +} + +impl Link for PerfLinkInner { + type Id = PerfLinkIdInner; + + fn id(&self) -> Self::Id { + match self { + PerfLinkInner::FdLink(link) => PerfLinkIdInner::FdLinkId(link.id()), + PerfLinkInner::PerfLink(link) => PerfLinkIdInner::PerfLinkId(link.id()), + } + } + + fn detach(self) -> Result<(), ProgramError> { + match self { + PerfLinkInner::FdLink(link) => link.detach(), + PerfLinkInner::PerfLink(link) => link.detach(), + } + } +} + /// The identifer of a PerfLink. #[derive(Debug, Hash, Eq, PartialEq)] pub struct PerfLinkId(RawFd); @@ -41,29 +72,36 @@ impl Link for PerfLink { } } -pub(crate) fn perf_attach>( - data: &mut ProgramData, - fd: RawFd, -) -> Result { - perf_attach_either(data, fd, None, None) +pub(crate) fn perf_attach(prog_fd: RawFd, fd: RawFd) -> Result { + if FEATURES.bpf_perf_link { + let link_fd = + bpf_link_create(prog_fd, fd, BPF_PERF_EVENT, None, 0).map_err(|(_, io_error)| { + ProgramError::SyscallError { + call: "bpf_link_create".to_owned(), + io_error, + } + })? as RawFd; + Ok(PerfLinkInner::FdLink(FdLink::new(link_fd))) + } else { + perf_attach_either(prog_fd, fd, None, None) + } } -pub(crate) fn perf_attach_debugfs>( - data: &mut ProgramData, +pub(crate) fn perf_attach_debugfs( + prog_fd: RawFd, fd: RawFd, probe_kind: ProbeKind, event_alias: String, -) -> Result { - perf_attach_either(data, fd, Some(probe_kind), Some(event_alias)) +) -> Result { + perf_attach_either(prog_fd, fd, Some(probe_kind), Some(event_alias)) } -fn perf_attach_either>( - data: &mut ProgramData, +fn perf_attach_either( + prog_fd: RawFd, fd: RawFd, probe_kind: Option, event_alias: Option, -) -> Result { - let prog_fd = data.fd_or_err()?; +) -> Result { perf_event_ioctl(fd, PERF_EVENT_IOC_SET_BPF, prog_fd).map_err(|(_, io_error)| { ProgramError::SyscallError { call: "PERF_EVENT_IOC_SET_BPF".to_owned(), @@ -77,12 +115,9 @@ fn perf_attach_either>( } })?; - data.links.insert( - PerfLink { - perf_fd: fd, - probe_kind, - event_alias, - } - .into(), - ) + Ok(PerfLinkInner::PerfLink(PerfLink { + perf_fd: fd, + probe_kind, + event_alias, + })) } diff --git a/aya/src/programs/perf_event.rs b/aya/src/programs/perf_event.rs index d4185375..177b3544 100644 --- a/aya/src/programs/perf_event.rs +++ b/aya/src/programs/perf_event.rs @@ -13,8 +13,9 @@ use crate::{ }, }, programs::{ + links::define_link_wrapper, load_program, perf_attach, - perf_attach::{PerfLink, PerfLinkId}, + perf_attach::{PerfLinkIdInner, PerfLinkInner}, ProgramData, ProgramError, }, sys::perf_event_open, @@ -119,7 +120,7 @@ pub enum PerfEventScope { #[derive(Debug)] #[doc(alias = "BPF_PROG_TYPE_PERF_EVENT")] pub struct PerfEvent { - pub(crate) data: ProgramData, + pub(crate) data: ProgramData, } impl PerfEvent { @@ -141,7 +142,7 @@ impl PerfEvent { config: u64, scope: PerfEventScope, sample_policy: SamplePolicy, - ) -> Result { + ) -> Result { let (sample_period, sample_frequency) = match sample_policy { SamplePolicy::Period(period) => (period, None), SamplePolicy::Frequency(frequency) => (0, Some(frequency)), @@ -168,13 +169,14 @@ impl PerfEvent { io_error, })? as i32; - perf_attach(&mut self.data, fd) + let link = perf_attach(self.data.fd_or_err()?, fd)?; + self.data.links.insert(PerfEventLink::new(link)) } /// Detaches the program. /// /// See [PerfEvent::attach]. - pub fn detach(&mut self, link_id: PerfLinkId) -> Result<(), ProgramError> { + pub fn detach(&mut self, link_id: PerfEventLinkId) -> Result<(), ProgramError> { self.data.links.remove(link_id) } @@ -182,7 +184,16 @@ impl PerfEvent { /// /// The link will be detached on `Drop` and the caller is now responsible /// for managing its lifetime. - pub fn take_link(&mut self, link_id: PerfLinkId) -> Result { + pub fn take_link(&mut self, link_id: PerfEventLinkId) -> Result { self.data.take_link(link_id) } } + +define_link_wrapper!( + /// The link used by [PerfEvent] programs. + PerfEventLink, + /// The type returned by [PerfEvent::attach]. Can be passed to [PerfEvent::detach]. + PerfEventLinkId, + PerfLinkInner, + PerfLinkIdInner +); diff --git a/aya/src/programs/probe.rs b/aya/src/programs/probe.rs index f595ca53..b1690ecd 100644 --- a/aya/src/programs/probe.rs +++ b/aya/src/programs/probe.rs @@ -8,7 +8,7 @@ use std::{ use crate::{ programs::{ - kprobe::KProbeError, perf_attach, perf_attach::PerfLink, perf_attach_debugfs, + kprobe::KProbeError, perf_attach, perf_attach::PerfLinkInner, perf_attach_debugfs, trace_point::read_sys_fs_trace_point_id, uprobe::UProbeError, utils::find_tracefs_path, Link, ProgramData, ProgramError, }, @@ -37,7 +37,7 @@ impl ProbeKind { } } -pub(crate) fn attach>( +pub(crate) fn attach>( program_data: &mut ProgramData, kind: ProbeKind, fn_name: &str, @@ -49,13 +49,18 @@ pub(crate) fn attach>( let k_ver = kernel_version().unwrap(); if k_ver < (4, 17, 0) { let (fd, event_alias) = create_as_trace_point(kind, fn_name, offset, pid)?; - - return perf_attach_debugfs(program_data, fd, kind, event_alias); + let link = T::from(perf_attach_debugfs( + program_data.fd_or_err()?, + fd, + kind, + event_alias, + )?); + return program_data.links.insert(link); }; let fd = create_as_probe(kind, fn_name, offset, pid)?; - - perf_attach(program_data, fd) + let link = T::from(perf_attach(program_data.fd_or_err()?, fd)?); + program_data.links.insert(link) } pub(crate) fn detach_debug_fs(kind: ProbeKind, event_alias: &str) -> Result<(), ProgramError> { diff --git a/aya/src/programs/trace_point.rs b/aya/src/programs/trace_point.rs index d4bf158d..f43cd77e 100644 --- a/aya/src/programs/trace_point.rs +++ b/aya/src/programs/trace_point.rs @@ -6,7 +6,7 @@ use crate::{ generated::bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT, programs::{ define_link_wrapper, load_program, - perf_attach::{perf_attach, PerfLink, PerfLinkId}, + perf_attach::{perf_attach, PerfLinkIdInner, PerfLinkInner}, utils::find_tracefs_path, ProgramData, ProgramError, }, @@ -87,7 +87,8 @@ impl TracePoint { } })? as i32; - perf_attach(&mut self.data, fd) + let link = perf_attach(self.data.fd_or_err()?, fd)?; + self.data.links.insert(TracePointLink::new(link)) } /// Detaches from a trace point. @@ -111,8 +112,8 @@ define_link_wrapper!( TracePointLink, /// The type returned by [TracePoint::attach]. Can be passed to [TracePoint::detach]. TracePointLinkId, - PerfLink, - PerfLinkId + PerfLinkInner, + PerfLinkIdInner ); pub(crate) fn read_sys_fs_trace_point_id( diff --git a/aya/src/programs/uprobe.rs b/aya/src/programs/uprobe.rs index 55629da0..c8a3446d 100644 --- a/aya/src/programs/uprobe.rs +++ b/aya/src/programs/uprobe.rs @@ -17,7 +17,7 @@ use crate::{ generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE, programs::{ define_link_wrapper, load_program, - perf_attach::{PerfLink, PerfLinkId}, + perf_attach::{PerfLinkIdInner, PerfLinkInner}, probe::{attach, ProbeKind}, ProgramData, ProgramError, }, @@ -154,8 +154,8 @@ define_link_wrapper!( UProbeLink, /// The type returned by [UProbe::attach]. Can be passed to [UProbe::detach]. UProbeLinkId, - PerfLink, - PerfLinkId + PerfLinkInner, + PerfLinkIdInner ); /// The type returned when attaching an [`UProbe`] fails. From 7479c1dd6c1356bddb0401dbeea65618674524c9 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Fri, 10 Feb 2023 23:30:00 +0000 Subject: [PATCH 10/11] aya: More discrete feature logging Just use the Debug formatter vs. printing a message for each probe. Signed-off-by: Dave Tucker --- aya-obj/src/btf/btf.rs | 19 ------------------- aya/src/bpf.rs | 19 +------------------ 2 files changed, 1 insertion(+), 37 deletions(-) diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index 4dd4716c..f439ee1a 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -172,25 +172,6 @@ pub struct BtfFeatures { pub btf_type_tag: bool, } -impl std::fmt::Display for BtfFeatures { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_fmt(format_args!( - "[FEAT PROBE] BTF func support: {}\n\ - [FEAT PROBE] BTF global func support: {}\n\ - [FEAT PROBE] BTF var and datasec support: {}\n\ - [FEAT PROBE] BTF float support: {}\n\ - [FEAT PROBE] BTF decl_tag support: {}\n\ - [FEAT PROBE] BTF type_tag support: {}", - self.btf_func, - self.btf_func_global, - self.btf_datasec, - self.btf_float, - self.btf_decl_tag, - self.btf_type_tag, - )) - } -} - /// Bpf Type Format metadata. /// /// BTF is a kind of debug metadata that allows eBPF programs compiled against one kernel version diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 2d71d88c..719f1195 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -96,28 +96,11 @@ impl Features { bpf_perf_link: is_perf_link_supported(), btf, }; - - debug!("{}", f); - if let Some(btf) = f.btf.as_ref() { - debug!("{}", btf) - } + debug!("BPF Feature Detection: {:#?}", f); f } } -impl std::fmt::Display for Features { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!( - "[FEAT PROBE] BPF program name support: {}\n\ - [FEAT PROBE] bpf_link support for kprobe/uprobe/tracepoint: {}\n\ - [FEAT PROBE] BTF support: {}", - self.bpf_name, - self.bpf_perf_link, - self.btf.is_some() - )) - } -} - /// Builder style API for advanced loading of eBPF programs. /// /// Loading eBPF code involves a few steps, including loading maps and applying From ce79de7ff6b965efa25840b35b0d051c3087db0a Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 13 Feb 2023 14:30:21 +0000 Subject: [PATCH 11/11] aya: Fix is_perf_link_supported This was mistakenly comparing the exit code of the syscall, which is always -1 and not the corresponding error-code. Added unit tests to ensure we don't regress. Signed-off-by: Dave Tucker --- aya/src/sys/bpf.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index 20a88f26..366b000f 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -617,12 +617,12 @@ pub(crate) fn is_perf_link_supported() -> bool { u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32; if let Ok(fd) = sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr) { - if let Err((code, _)) = + if let Err((_, e)) = // Uses an invalid target FD so we get EBADF if supported. bpf_link_create(fd as i32, -1, bpf_attach_type::BPF_PERF_EVENT, None, 0) { // Returns EINVAL if unsupported. EBADF if supported. - let res = code == (-libc::EBADF).into(); + let res = e.raw_os_error() == Some(libc::EBADF); unsafe { libc::close(fd as i32) }; return res; } @@ -892,3 +892,33 @@ where } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::sys::override_syscall; + use libc::{EBADF, EINVAL}; + + #[test] + fn test_perf_link_supported() { + override_syscall(|call| match call { + Syscall::Bpf { + cmd: bpf_cmd::BPF_LINK_CREATE, + .. + } => Err((-1, io::Error::from_raw_os_error(EBADF))), + _ => Ok(42), + }); + let supported = is_perf_link_supported(); + assert!(supported); + + override_syscall(|call| match call { + Syscall::Bpf { + cmd: bpf_cmd::BPF_LINK_CREATE, + .. + } => Err((-1, io::Error::from_raw_os_error(EINVAL))), + _ => Ok(42), + }); + let supported = is_perf_link_supported(); + assert!(!supported); + } +}