From 47f764c19185a69a00f3925239797caa98cd5afe Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Sun, 26 Jun 2022 21:35:13 +0100 Subject: [PATCH] aya: Make Features part of the public API This commit adds a new probe for bpf_attach_cookie, which would be used to implement USDT probes. Since USDT probes aren't currently supported, we this triggers a dead_code warning in clippy. There are cases where exposing FEATURES - our lazy static - is actually helpful to users of the library. For example, they may wish to choose to load a different version of their bytecode based on current features. Or, in the case of an orchestrator like bpfd, we might want to allow users to describe which features their program needs and return nice error message is one or more nodes in their cluster doesn't support the necessary feature set. To do this without breaking the API, we make all the internal members of the `Features` and `BtfFeatures` structs private, and add accessors for them. We then add a `features()` API to avoid leaking the lazy_static. Signed-off-by: Dave Tucker --- aya-obj/src/btf/btf.rs | 68 ++++++++++++++++++++++++++++++--- aya-obj/src/obj.rs | 62 +++++++++++++++++++++++++++--- aya-obj/src/relocation.rs | 5 +-- aya/src/bpf.rs | 50 +++++++++++++----------- aya/src/programs/perf_attach.rs | 2 +- aya/src/sys/bpf.rs | 27 +++++++++++++ 6 files changed, 176 insertions(+), 38 deletions(-) diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index 360079f0..cf68e403 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -166,12 +166,68 @@ pub enum BtfError { #[derive(Default, Debug)] #[allow(missing_docs)] pub struct BtfFeatures { - pub btf_func: bool, - pub btf_func_global: bool, - pub btf_datasec: bool, - pub btf_float: bool, - pub btf_decl_tag: bool, - pub btf_type_tag: bool, + btf_func: bool, + btf_func_global: bool, + btf_datasec: bool, + btf_float: bool, + btf_decl_tag: bool, + btf_type_tag: bool, +} + +impl BtfFeatures { + #[doc(hidden)] + pub fn new( + btf_func: bool, + btf_func_global: bool, + btf_datasec: bool, + btf_float: bool, + btf_decl_tag: bool, + btf_type_tag: bool, + ) -> Self { + BtfFeatures { + btf_func, + btf_func_global, + btf_datasec, + btf_float, + btf_decl_tag, + btf_type_tag, + } + } + + /// Returns true if the BTF_TYPE_FUNC is supported. + pub fn btf_func(&self) -> bool { + self.btf_func + } + + /// Returns true if the BTF_TYPE_FUNC_GLOBAL is supported. + pub fn btf_func_global(&self) -> bool { + self.btf_func_global + } + + /// Returns true if the BTF_TYPE_DATASEC is supported. + pub fn btf_datasec(&self) -> bool { + self.btf_datasec + } + + /// Returns true if the BTF_FLOAT is supported. + pub fn btf_float(&self) -> bool { + self.btf_float + } + + /// Returns true if the BTF_DECL_TAG is supported. + pub fn btf_decl_tag(&self) -> bool { + self.btf_decl_tag + } + + /// Returns true if the BTF_TYPE_TAG is supported. + pub fn btf_type_tag(&self) -> bool { + self.btf_type_tag + } + + /// Returns true if the BTF_KIND_FUNC_PROTO is supported. + pub fn btf_kind_func_proto(&self) -> bool { + self.btf_func && self.btf_decl_tag + } } /// Bpf Type Format metadata. diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index 5ff4f1c3..f066d01e 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -42,11 +42,63 @@ const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE; #[derive(Default, Debug)] #[allow(missing_docs)] pub struct Features { - pub bpf_name: bool, - pub bpf_probe_read_kernel: bool, - pub bpf_perf_link: bool, - pub bpf_global_data: bool, - pub btf: Option, + bpf_name: bool, + bpf_probe_read_kernel: bool, + bpf_perf_link: bool, + bpf_global_data: bool, + bpf_cookie: bool, + btf: Option, +} + +impl Features { + #[doc(hidden)] + pub fn new( + bpf_name: bool, + bpf_probe_read_kernel: bool, + bpf_perf_link: bool, + bpf_global_data: bool, + bpf_cookie: bool, + btf: Option, + ) -> Self { + Self { + bpf_name, + bpf_probe_read_kernel, + bpf_perf_link, + bpf_global_data, + bpf_cookie, + btf, + } + } + + /// Returns whether BPF program names are supported. + pub fn bpf_name(&self) -> bool { + self.bpf_name + } + + /// Returns whether the bpf_probe_read_kernel helper is supported. + pub fn bpf_probe_read_kernel(&self) -> bool { + self.bpf_probe_read_kernel + } + + /// Returns whether bpf_links are supported for Kprobes/Uprobes/Tracepoints. + pub fn bpf_perf_link(&self) -> bool { + self.bpf_perf_link + } + + /// Returns whether BPF program global data is supported. + pub fn bpf_global_data(&self) -> bool { + self.bpf_global_data + } + + /// Returns whether BPF program cookie is supported. + pub fn bpf_cookie(&self) -> bool { + self.bpf_cookie + } + + /// If BTF is supported, returns which BTF features are supported. + pub fn btf(&self) -> Option<&BtfFeatures> { + self.btf.as_ref() + } } /// The loaded object file representation diff --git a/aya-obj/src/relocation.rs b/aya-obj/src/relocation.rs index 7fb3bc0d..f310557e 100644 --- a/aya-obj/src/relocation.rs +++ b/aya-obj/src/relocation.rs @@ -242,10 +242,7 @@ fn relocate_maps<'a, I: Iterator>( m } else { let Some(m) = maps_by_section.get(§ion_index) else { - debug!( - "failed relocating map by section index {}", - section_index - ); + debug!("failed relocating map by section index {}", section_index); return Err(RelocationError::SectionNotFound { symbol_index: rel.symbol_index, symbol_name: sym.name.clone(), diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 961b0f06..3c2aa495 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -33,11 +33,11 @@ use crate::{ SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp, }, sys::{ - bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_bpf_global_data_supported, - 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_perf_link_supported, is_probe_read_kernel_supported, - is_prog_name_supported, retry_with_verifier_logs, + bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_bpf_cookie_supported, + is_bpf_global_data_supported, 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_perf_link_supported, + is_probe_read_kernel_supported, is_prog_name_supported, retry_with_verifier_logs, }, util::{bytes_of, bytes_of_slice, possible_cpus, VerifierLog, POSSIBLE_CPUS}, }; @@ -72,28 +72,34 @@ lazy_static! { fn detect_features() -> Features { 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(), - }) + Some(BtfFeatures::new( + is_btf_func_supported(), + is_btf_func_global_supported(), + is_btf_datasec_supported(), + is_btf_float_supported(), + is_btf_decl_tag_supported(), + is_btf_type_tag_supported(), + )) } else { None }; - let f = Features { - bpf_name: is_prog_name_supported(), - bpf_probe_read_kernel: is_probe_read_kernel_supported(), - bpf_perf_link: is_perf_link_supported(), - bpf_global_data: is_bpf_global_data_supported(), + let f = Features::new( + is_prog_name_supported(), + is_probe_read_kernel_supported(), + is_perf_link_supported(), + is_bpf_global_data_supported(), + is_bpf_cookie_supported(), btf, - }; + ); debug!("BPF Feature Detection: {:#?}", f); f } +/// Returns a reference to the detected BPF features. +pub fn features() -> &'static Features { + &FEATURES +} + /// Builder style API for advanced loading of eBPF programs. /// /// Loading eBPF code involves a few steps, including loading maps and applying @@ -347,7 +353,7 @@ impl<'a> BpfLoader<'a> { let mut obj = Object::parse(data)?; obj.patch_map_data(self.globals.clone())?; - let btf_fd = if let Some(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())?) @@ -364,7 +370,7 @@ impl<'a> BpfLoader<'a> { let mut maps = HashMap::new(); for (name, mut obj) in obj.maps.drain() { if let (false, BpfSectionKind::Bss | BpfSectionKind::Data | BpfSectionKind::Rodata) = - (FEATURES.bpf_global_data, obj.section_kind()) + (FEATURES.bpf_global_data(), obj.section_kind()) { continue; } @@ -452,7 +458,7 @@ impl<'a> BpfLoader<'a> { .map(|(name, prog_obj)| { let function_obj = obj.functions.get(&prog_obj.function_key()).unwrap().clone(); - let prog_name = if FEATURES.bpf_name { + let prog_name = if FEATURES.bpf_name() { Some(name.clone()) } else { None diff --git a/aya/src/programs/perf_attach.rs b/aya/src/programs/perf_attach.rs index 78609110..fe4fafe3 100644 --- a/aya/src/programs/perf_attach.rs +++ b/aya/src/programs/perf_attach.rs @@ -73,7 +73,7 @@ impl Link for PerfLink { } pub(crate) fn perf_attach(prog_fd: RawFd, fd: RawFd) -> Result { - if FEATURES.bpf_perf_link { + 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 { diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index aac2e1b9..0bbdf943 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -719,6 +719,33 @@ pub(crate) fn is_bpf_global_data_supported() -> bool { false } +pub(crate) fn is_bpf_cookie_supported() -> bool { + let mut attr = unsafe { mem::zeroed::() }; + let u = unsafe { &mut attr.__bindgen_anon_3 }; + + let prog: &[u8] = &[ + 0x85, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, // call bpf_get_attach_cookie + 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_KPROBE as u32; + + match sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr) { + Ok(v) => { + let fd = v as RawFd; + unsafe { close(fd) }; + true + } + Err(_) => false, + } +} + pub(crate) fn is_btf_supported() -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("int".to_string());