From 58895db9b46f810d8dd1550951d490ff56dcb9b4 Mon Sep 17 00:00:00 2001 From: Andrew Stoycos Date: Mon, 27 Mar 2023 11:25:45 -0400 Subject: [PATCH 1/5] Implement FdLink conversions Implement TryFrom functions to convert Fdlink to PerfLink/TracePointLink/ KprobeLink, and UprobeLink and vice-versa. This allows us to pin taken links for perf programs, ultimately ensuring the link isn't dropped when the loading process exits. Signed-off-by: Andrew Stoycos --- aya/src/programs/kprobe.rs | 34 ++++++++++++++++++++++++++++++-- aya/src/programs/perf_event.rs | 34 ++++++++++++++++++++++++++++++-- aya/src/programs/trace_point.rs | 35 ++++++++++++++++++++++++++++++--- aya/src/programs/uprobe.rs | 34 ++++++++++++++++++++++++++++++-- 4 files changed, 128 insertions(+), 9 deletions(-) diff --git a/aya/src/programs/kprobe.rs b/aya/src/programs/kprobe.rs index 53dac216..a61f084f 100644 --- a/aya/src/programs/kprobe.rs +++ b/aya/src/programs/kprobe.rs @@ -3,13 +3,14 @@ use std::{io, path::Path}; use thiserror::Error; use crate::{ - generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE, + generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_KPROBE}, programs::{ define_link_wrapper, load_program, perf_attach::{PerfLinkIdInner, PerfLinkInner}, probe::{attach, ProbeKind}, - ProgramData, ProgramError, + FdLink, LinkError, ProgramData, ProgramError, }, + sys::bpf_link_get_info_by_fd, VerifierLogLevel, }; @@ -119,3 +120,32 @@ pub enum KProbeError { io_error: io::Error, }, } + +impl TryFrom for FdLink { + type Error = LinkError; + + fn try_from(value: KProbeLink) -> Result { + if let PerfLinkInner::FdLink(fd) = value.into_inner() { + Ok(fd) + } else { + Err(LinkError::InvalidLink) + } + } +} + +impl TryFrom for KProbeLink { + type Error = LinkError; + + fn try_from(fd_link: FdLink) -> Result { + let info = + bpf_link_get_info_by_fd(fd_link.fd).map_err(|io_error| LinkError::SyscallError { + call: "BPF_OBJ_GET_INFO_BY_FD", + code: 0, + io_error, + })?; + if info.type_ == (bpf_link_type::BPF_LINK_TYPE_KPROBE_MULTI as u32) { + return Ok(KProbeLink::new(PerfLinkInner::FdLink(fd_link))); + } + Err(LinkError::InvalidLink) + } +} diff --git a/aya/src/programs/perf_event.rs b/aya/src/programs/perf_event.rs index 3c245573..5de7eeef 100644 --- a/aya/src/programs/perf_event.rs +++ b/aya/src/programs/perf_event.rs @@ -6,6 +6,7 @@ pub use crate::generated::{ use crate::{ generated::{ + bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_PERF_EVENT, perf_type_id::{ PERF_TYPE_BREAKPOINT, PERF_TYPE_HARDWARE, PERF_TYPE_HW_CACHE, PERF_TYPE_RAW, @@ -16,9 +17,9 @@ use crate::{ links::define_link_wrapper, load_program, perf_attach, perf_attach::{PerfLinkIdInner, PerfLinkInner}, - ProgramData, ProgramError, + FdLink, LinkError, ProgramData, ProgramError, }, - sys::perf_event_open, + sys::{bpf_link_get_info_by_fd, perf_event_open}, }; /// The type of perf event @@ -189,6 +190,35 @@ impl PerfEvent { } } +impl TryFrom for FdLink { + type Error = LinkError; + + fn try_from(value: PerfEventLink) -> Result { + if let PerfLinkInner::FdLink(fd) = value.into_inner() { + Ok(fd) + } else { + Err(LinkError::InvalidLink) + } + } +} + +impl TryFrom for PerfEventLink { + type Error = LinkError; + + fn try_from(fd_link: FdLink) -> Result { + let info = + bpf_link_get_info_by_fd(fd_link.fd).map_err(|io_error| LinkError::SyscallError { + call: "BPF_OBJ_GET_INFO_BY_FD", + code: 0, + io_error, + })?; + if info.type_ == (bpf_link_type::BPF_LINK_TYPE_PERF_EVENT as u32) { + return Ok(PerfEventLink::new(PerfLinkInner::FdLink(fd_link))); + } + Err(LinkError::InvalidLink) + } +} + define_link_wrapper!( /// The link used by [PerfEvent] programs. PerfEventLink, diff --git a/aya/src/programs/trace_point.rs b/aya/src/programs/trace_point.rs index d67c6a41..b23594b7 100644 --- a/aya/src/programs/trace_point.rs +++ b/aya/src/programs/trace_point.rs @@ -3,14 +3,14 @@ use std::{fs, io, path::Path}; use thiserror::Error; use crate::{ - generated::bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT, + generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT}, programs::{ define_link_wrapper, load_program, perf_attach::{perf_attach, PerfLinkIdInner, PerfLinkInner}, utils::find_tracefs_path, - ProgramData, ProgramError, + FdLink, LinkError, ProgramData, ProgramError, }, - sys::perf_event_open_trace_point, + sys::{bpf_link_get_info_by_fd, perf_event_open_trace_point}, }; /// The type returned when attaching a [`TracePoint`] fails. @@ -116,6 +116,35 @@ define_link_wrapper!( PerfLinkIdInner ); +impl TryFrom for FdLink { + type Error = LinkError; + + fn try_from(value: TracePointLink) -> Result { + if let PerfLinkInner::FdLink(fd) = value.into_inner() { + Ok(fd) + } else { + Err(LinkError::InvalidLink) + } + } +} + +impl TryFrom for TracePointLink { + type Error = LinkError; + + fn try_from(fd_link: FdLink) -> Result { + let info = + bpf_link_get_info_by_fd(fd_link.fd).map_err(|io_error| LinkError::SyscallError { + call: "BPF_OBJ_GET_INFO_BY_FD", + code: 0, + io_error, + })?; + if info.type_ == (bpf_link_type::BPF_LINK_TYPE_TRACING as u32) { + return Ok(TracePointLink::new(PerfLinkInner::FdLink(fd_link))); + } + Err(LinkError::InvalidLink) + } +} + pub(crate) fn read_sys_fs_trace_point_id( tracefs: &Path, category: &str, diff --git a/aya/src/programs/uprobe.rs b/aya/src/programs/uprobe.rs index b632407e..bab82d4b 100644 --- a/aya/src/programs/uprobe.rs +++ b/aya/src/programs/uprobe.rs @@ -14,13 +14,14 @@ use std::{ use thiserror::Error; use crate::{ - generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE, + generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_KPROBE}, programs::{ define_link_wrapper, load_program, perf_attach::{PerfLinkIdInner, PerfLinkInner}, probe::{attach, ProbeKind}, - ProgramData, ProgramError, + FdLink, LinkError, ProgramData, ProgramError, }, + sys::bpf_link_get_info_by_fd, VerifierLogLevel, }; @@ -160,6 +161,35 @@ define_link_wrapper!( PerfLinkIdInner ); +impl TryFrom for FdLink { + type Error = LinkError; + + fn try_from(value: UProbeLink) -> Result { + if let PerfLinkInner::FdLink(fd) = value.into_inner() { + Ok(fd) + } else { + Err(LinkError::InvalidLink) + } + } +} + +impl TryFrom for UProbeLink { + type Error = LinkError; + + fn try_from(fd_link: FdLink) -> Result { + let info = + bpf_link_get_info_by_fd(fd_link.fd).map_err(|io_error| LinkError::SyscallError { + call: "BPF_OBJ_GET_INFO_BY_FD", + code: 0, + io_error, + })?; + if info.type_ == (bpf_link_type::BPF_LINK_TYPE_TRACING as u32) { + return Ok(UProbeLink::new(PerfLinkInner::FdLink(fd_link))); + } + Err(LinkError::InvalidLink) + } +} + /// The type returned when attaching an [`UProbe`] fails. #[derive(Debug, Error)] pub enum UProbeError { From e668ae0c1b56c306db944cf7e07314262d522b2b Mon Sep 17 00:00:00 2001 From: Andrew Stoycos Date: Mon, 10 Jul 2023 16:56:50 -0400 Subject: [PATCH 2/5] re-add bpftool to integration tests We need bpftool to add tests for the link APIs since we don't yet have and aya API for listing links. Signed-off-by: Andrew Stoycos --- test/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/run.sh b/test/run.sh index 23c0dd1a..9def9a22 100755 --- a/test/run.sh +++ b/test/run.sh @@ -192,7 +192,7 @@ EOF exec_vm sudo dnf config-manager --set-enabled updates-testing exec_vm sudo dnf config-manager --set-enabled updates-testing-modular echo "Installing dependencies" - exec_vm sudo dnf install -qy llvm llvm-devel clang clang-devel zlib-devel + exec_vm sudo dnf install -qy bpftool llvm llvm-devel clang clang-devel zlib-devel exec_vm 'curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \ -y --profile minimal --default-toolchain nightly --component rust-src --component clippy' exec_vm 'echo source ~/.cargo/env >> ~/.bashrc' From fb075636c1ed7f5089dc55f9a4e609b2cd9923f5 Mon Sep 17 00:00:00 2001 From: Andrew Stoycos Date: Tue, 2 May 2023 15:58:27 -0400 Subject: [PATCH 3/5] Add integration test for perf link pin Add integration testing for link pinning and loading/unloading of tracepoint, kprobe, and uprobe programs. Redo how we utilize bpftool to verify that programs are loaded to be explicit with names. Also add a helper to verify that a program is loaded AND linked. Signed-off-by: Andrew Stoycos --- test/integration-ebpf/src/test.rs | 19 +- test/integration-test/src/tests/load.rs | 316 +++++++++++++++++++++--- 2 files changed, 297 insertions(+), 38 deletions(-) diff --git a/test/integration-ebpf/src/test.rs b/test/integration-ebpf/src/test.rs index 2c4efc91..187a3926 100644 --- a/test/integration-ebpf/src/test.rs +++ b/test/integration-ebpf/src/test.rs @@ -3,11 +3,11 @@ use aya_bpf::{ bindings::xdp_action, - macros::{kprobe, xdp}, - programs::{ProbeContext, XdpContext}, + macros::{kprobe, tracepoint, uprobe, xdp}, + programs::{ProbeContext, TracePointContext, XdpContext}, }; -#[xdp(name = "test_unload_xdp")] +#[xdp(name = "test_xdp")] pub fn pass(ctx: XdpContext) -> u32 { match unsafe { try_pass(ctx) } { Ok(ret) => ret, @@ -20,8 +20,17 @@ unsafe fn try_pass(_ctx: XdpContext) -> Result { } #[kprobe] -// truncated name to match bpftool output -pub fn test_unload_kpr(_ctx: ProbeContext) -> u32 { +pub fn test_kprobe(_ctx: ProbeContext) -> u32 { + 0 +} + +#[tracepoint] +pub fn test_tracepoint(_ctx: TracePointContext) -> u32 { + 0 +} + +#[uprobe] +pub fn test_uprobe(_ctx: ProbeContext) -> u32 { 0 } diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index ede471e2..5a390b6b 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -1,10 +1,10 @@ -use std::{convert::TryInto as _, thread, time}; +use std::{convert::TryInto as _, process::Command, thread, time}; use aya::{ maps::Array, programs::{ links::{FdLink, PinnedLink}, - loaded_programs, KProbe, TracePoint, Xdp, XdpFlags, + loaded_programs, KProbe, TracePoint, UProbe, Xdp, XdpFlags, }, util::KernelVersion, Bpf, @@ -50,6 +50,40 @@ fn multiple_btf_maps() { assert_eq!(val_2, 42); } +fn is_linked(prog_id: &u32) -> bool { + let output = Command::new("bpftool").args(["link"]).output(); + let output = output.expect("Failed to run 'bpftool link'"); + let stdout = String::from_utf8(output.stdout).unwrap(); + stdout.contains(&prog_id.to_string()) +} + +macro_rules! assert_loaded_and_linked { + ($name:literal, $loaded:expr) => { + for i in 0..(MAX_RETRIES + 1) { + let id = loaded_programs() + .find(|prog| prog.as_ref().unwrap().name() == $name.as_bytes()) + .map(|prog| Some(prog.unwrap().id())); + let mut linked = false; + if let Some(prog_id) = id { + linked = is_linked(&prog_id.unwrap()); + if linked == $loaded { + break; + } + } + + if i == MAX_RETRIES { + panic!( + "Expected (loaded/linked: {}) but found (id: {}, linked: {}", + $loaded, + id.is_some(), + linked + ); + } + thread::sleep(time::Duration::from_millis(RETRY_DURATION_MS)); + } + }; +} + macro_rules! assert_loaded { ($name:literal, $loaded:expr) => { for i in 0..(MAX_RETRIES + 1) { @@ -68,59 +102,109 @@ macro_rules! assert_loaded { #[test] fn unload_xdp() { let mut bpf = Bpf::load(crate::TEST).unwrap(); - let prog: &mut Xdp = bpf - .program_mut("test_unload_xdp") - .unwrap() - .try_into() - .unwrap(); + let prog: &mut Xdp = bpf.program_mut("test_xdp").unwrap().try_into().unwrap(); prog.load().unwrap(); - assert_loaded!("test_unload_xdp", true); + assert_loaded!("test_xdp", true); let link = prog.attach("lo", XdpFlags::default()).unwrap(); { let _link_owned = prog.take_link(link).unwrap(); prog.unload().unwrap(); - assert_loaded!("test_unload_xdp", true); + assert_loaded_and_linked!("test_xdp", true); }; - assert_loaded!("test_unload_xdp", false); + assert_loaded!("test_xdp", false); prog.load().unwrap(); - assert_loaded!("test_unload_xdp", true); + assert_loaded!("test_xdp", true); prog.attach("lo", XdpFlags::default()).unwrap(); - assert_loaded!("test_unload_xdp", true); + assert_loaded!("test_xdp", true); prog.unload().unwrap(); - assert_loaded!("test_unload_xdp", false); + assert_loaded!("test_xdp", false); } #[test] fn unload_kprobe() { let mut bpf = Bpf::load(crate::TEST).unwrap(); - let prog: &mut KProbe = bpf - .program_mut("test_unload_kpr") + let prog: &mut KProbe = bpf.program_mut("test_kprobe").unwrap().try_into().unwrap(); + prog.load().unwrap(); + assert_loaded!("test_kprobe", true); + let link = prog.attach("try_to_wake_up", 0).unwrap(); + { + let _link_owned = prog.take_link(link).unwrap(); + prog.unload().unwrap(); + assert_loaded_and_linked!("test_kprobe", true); + }; + + assert_loaded!("test_kprobe", false); + prog.load().unwrap(); + + assert_loaded!("test_kprobe", true); + prog.attach("try_to_wake_up", 0).unwrap(); + + assert_loaded!("test_kprobe", true); + prog.unload().unwrap(); + + assert_loaded!("test_kprobe", false); +} + +#[test] +fn basic_tracepoint() { + let mut bpf = Bpf::load(crate::TEST).unwrap(); + let prog: &mut TracePoint = bpf + .program_mut("test_tracepoint") .unwrap() .try_into() .unwrap(); + prog.load().unwrap(); - assert_loaded!("test_unload_kpr", true); - let link = prog.attach("try_to_wake_up", 0).unwrap(); + assert_loaded!("test_tracepoint", true); + let link = prog.attach("syscalls", "sys_enter_kill").unwrap(); + { let _link_owned = prog.take_link(link).unwrap(); prog.unload().unwrap(); - assert_loaded!("test_unload_kpr", true); + assert_loaded_and_linked!("test_tracepoint", true); }; - assert_loaded!("test_unload_kpr", false); + assert_loaded!("test_tracepoint", false); prog.load().unwrap(); - assert_loaded!("test_unload_kpr", true); - prog.attach("try_to_wake_up", 0).unwrap(); + assert_loaded!("test_tracepoint", true); + prog.attach("syscalls", "sys_enter_kill").unwrap(); - assert_loaded!("test_unload_kpr", true); + assert_loaded!("test_tracepoint", true); prog.unload().unwrap(); - assert_loaded!("test_unload_kpr", false); + assert_loaded!("test_tracepoint", false); +} + +#[test] +fn basic_uprobe() { + let mut bpf = Bpf::load(crate::TEST).unwrap(); + let prog: &mut UProbe = bpf.program_mut("test_uprobe").unwrap().try_into().unwrap(); + + prog.load().unwrap(); + assert_loaded!("test_uprobe", true); + let link = prog.attach(Some("sleep"), 0, "libc", None).unwrap(); + + { + let _link_owned = prog.take_link(link).unwrap(); + prog.unload().unwrap(); + assert_loaded_and_linked!("test_uprobe", true); + }; + + assert_loaded!("test_uprobe", false); + prog.load().unwrap(); + + assert_loaded!("test_uprobe", true); + prog.attach(Some("sleep"), 0, "libc", None).unwrap(); + + assert_loaded!("test_uprobe", true); + prog.unload().unwrap(); + + assert_loaded!("test_uprobe", false); } #[test] @@ -132,30 +216,26 @@ fn pin_link() { } let mut bpf = Bpf::load(crate::TEST).unwrap(); - let prog: &mut Xdp = bpf - .program_mut("test_unload_xdp") - .unwrap() - .try_into() - .unwrap(); + let prog: &mut Xdp = bpf.program_mut("test_xdp").unwrap().try_into().unwrap(); prog.load().unwrap(); let link_id = prog.attach("lo", XdpFlags::default()).unwrap(); let link = prog.take_link(link_id).unwrap(); - assert_loaded!("test_unload_xdp", true); + assert_loaded!("test_xdp", true); let fd_link: FdLink = link.try_into().unwrap(); let pinned = fd_link.pin("/sys/fs/bpf/aya-xdp-test-lo").unwrap(); // because of the pin, the program is still attached prog.unload().unwrap(); - assert_loaded!("test_unload_xdp", true); + assert_loaded!("test_xdp", true); // delete the pin, but the program is still attached let new_link = pinned.unpin().unwrap(); - assert_loaded!("test_unload_xdp", true); + assert_loaded!("test_xdp", true); // finally when new_link is dropped we're detached drop(new_link); - assert_loaded!("test_unload_xdp", false); + assert_loaded!("test_xdp", false); } #[test] @@ -198,7 +278,7 @@ fn pin_lifecycle() { } // should still be loaded since link was pinned - assert_loaded!("pass", true); + assert_loaded_and_linked!("pass", true); // 4. Load a new version of the program, unpin link, and atomically replace old program { @@ -217,3 +297,173 @@ fn pin_lifecycle() { // program should be unloaded assert_loaded!("pass", false); } + +#[test] +fn pin_lifecycle_tracepoint() { + // 1. Load Program and Pin + { + let mut bpf = Bpf::load(crate::TEST).unwrap(); + let prog: &mut TracePoint = bpf + .program_mut("test_tracepoint") + .unwrap() + .try_into() + .unwrap(); + prog.load().unwrap(); + prog.pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap(); + } + + // should still be loaded since prog was pinned + assert_loaded!("test_tracepoint", true); + + // 2. Load program from bpffs but don't attach it + { + let _ = TracePoint::from_pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap(); + } + + // should still be loaded since prog was pinned + assert_loaded!("test_tracepoint", true); + + // 3. Load program from bpffs and attach + { + let mut prog = TracePoint::from_pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap(); + let link_id = prog.attach("syscalls", "sys_enter_kill").unwrap(); + let link = prog.take_link(link_id).unwrap(); + let fd_link: FdLink = link.try_into().unwrap(); + fd_link + .pin("/sys/fs/bpf/aya-tracepoint-test-sys-enter-kill") + .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_and_linked!("test_tracepoint", true); + + // 4. unpin link, and make sure everything is unloaded + { + PinnedLink::from_pin("/sys/fs/bpf/aya-tracepoint-test-sys-enter-kill") + .unwrap() + .unpin() + .unwrap(); + } + + // program should be unloaded + assert_loaded!("test_tracepoint", false); +} + +#[test] +fn pin_lifecycle_kprobe() { + // 1. Load Program and Pin + { + let mut bpf = Bpf::load(crate::TEST).unwrap(); + let prog: &mut KProbe = bpf.program_mut("test_kprobe").unwrap().try_into().unwrap(); + prog.load().unwrap(); + prog.pin("/sys/fs/bpf/aya-kprobe-test-prog").unwrap(); + } + + // should still be loaded since prog was pinned + assert_loaded!("test_kprobe", true); + + // 2. Load program from bpffs but don't attach it + { + let _ = KProbe::from_pin( + "/sys/fs/bpf/aya-kprobe-test-prog", + aya::programs::ProbeKind::KProbe, + ) + .unwrap(); + } + + // should still be loaded since prog was pinned + assert_loaded!("test_kprobe", true); + + // 3. Load program from bpffs and attach + { + let mut prog = KProbe::from_pin( + "/sys/fs/bpf/aya-kprobe-test-prog", + aya::programs::ProbeKind::KProbe, + ) + .unwrap(); + let link_id = prog.attach("try_to_wake_up", 0).unwrap(); + let link = prog.take_link(link_id).unwrap(); + let fd_link: FdLink = link.try_into().unwrap(); + fd_link + .pin("/sys/fs/bpf/aya-kprobe-test-try-to-wake-up") + .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_and_linked!("test_kprobe", true); + + // 4. unpin link, and make sure everything is unloaded + { + PinnedLink::from_pin("/sys/fs/bpf/aya-kprobe-test-try-to-wake-up") + .unwrap() + .unpin() + .unwrap(); + } + + // program should be unloaded + assert_loaded!("test_kprobe", false); +} + +#[test] +fn pin_lifecycle_uprobe() { + // 1. Load Program and Pin + { + let mut bpf = Bpf::load(crate::TEST).unwrap(); + let prog: &mut UProbe = bpf.program_mut("test_uprobe").unwrap().try_into().unwrap(); + prog.load().unwrap(); + prog.pin("/sys/fs/bpf/aya-uprobe-test-prog").unwrap(); + } + + // should still be loaded since prog was pinned + assert_loaded!("test_uprobe", true); + + // 2. Load program from bpffs but don't attach it + { + let _ = UProbe::from_pin( + "/sys/fs/bpf/aya-uprobe-test-prog", + aya::programs::ProbeKind::UProbe, + ) + .unwrap(); + } + + // should still be loaded since prog was pinned + assert_loaded!("test_uprobe", true); + + // 3. Load program from bpffs and attach + { + let mut prog = UProbe::from_pin( + "/sys/fs/bpf/aya-uprobe-test-prog", + aya::programs::ProbeKind::UProbe, + ) + .unwrap(); + let link_id = prog.attach(Some("sleep"), 0, "libc", None).unwrap(); + let link = prog.take_link(link_id).unwrap(); + let fd_link: FdLink = link.try_into().unwrap(); + fd_link + .pin("/sys/fs/bpf/aya-uprobe-test-bash-sleep") + .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_and_linked!("test_uprobe", true); + + // 4. unpin link, and make sure everything is unloaded + { + PinnedLink::from_pin("/sys/fs/bpf/aya-uprobe-test-bash-sleep") + .unwrap() + .unpin() + .unwrap(); + } + + // program should be unloaded + assert_loaded!("test_uprobe", false); +} From 80b371f6d134aeba0f31716bf091d03cc9ca13fe Mon Sep 17 00:00:00 2001 From: Andrew Stoycos Date: Wed, 3 May 2023 12:05:30 -0400 Subject: [PATCH 4/5] add FdLink documentation and example Signed-off-by: Andrew Stoycos --- aya/src/programs/links.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/aya/src/programs/links.rs b/aya/src/programs/links.rs index 5f5c188f..7b3055f4 100644 --- a/aya/src/programs/links.rs +++ b/aya/src/programs/links.rs @@ -17,6 +17,10 @@ use crate::{ sys::{bpf_get_object, bpf_pin_object, bpf_prog_detach}, }; +// for docs link +#[allow(unused)] +use crate::programs::cgroup_skb::CgroupSkb; + /// A Link. pub trait Link: std::fmt::Debug + 'static { /// Unique Id @@ -82,6 +86,30 @@ impl Drop for LinkMap { pub struct FdLinkId(pub(crate) RawFd); /// A file descriptor link. +/// +/// Fd links are returned directly when attaching some program types (for +/// instance [`CgroupSkb`]), or can be obtained by converting other link +/// types (see the `TryFrom` implementations). +/// +/// An important property of fd links is that they can be pinned. Pinning +/// can be used keep a link attached "in background" even after the program +/// that has created the link terminates. +/// +/// # Example +/// +///```no_run +/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?; +/// use aya::{Bpf, programs::{links::FdLink, KProbe}}; +/// +/// let program: &mut KProbe = bpf.program_mut("intercept_wakeups").unwrap().try_into()?; +/// program.load()?; +/// let link_id = program.attach("try_to_wake_up", 0)?; +/// let link = program.take_link(link_id).unwrap(); +/// let fd_link: FdLink = link.try_into().unwrap(); +/// fd_link.pin("/sys/fs/bpf/intercept_wakeups_link").unwrap(); +/// +/// # Ok::<(), aya::BpfError>(()) +/// ``` #[derive(Debug)] pub struct FdLink { pub(crate) fd: RawFd, From 94f554a52f388a62ebdffea69bc3440ab8d0509f Mon Sep 17 00:00:00 2001 From: Andrew Stoycos Date: Wed, 12 Jul 2023 14:13:22 -0400 Subject: [PATCH 5/5] fix loaded_programs() race in int-tests in the integration tests we recenctly switched to using our internal api to list programs. I was seeing times when this would race and panic internally (program fd was deleted by aya WHILE we were trying to get it). This ensures that the list succeeded without panicking. Signed-off-by: Andrew Stoycos --- test/integration-test/src/tests/load.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index 5a390b6b..8958ae59 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -60,9 +60,12 @@ fn is_linked(prog_id: &u32) -> bool { macro_rules! assert_loaded_and_linked { ($name:literal, $loaded:expr) => { for i in 0..(MAX_RETRIES + 1) { + // Ignore race failures which can happen when the tests delete a + // program in the middle of a `loaded_programs()` call. let id = loaded_programs() - .find(|prog| prog.as_ref().unwrap().name() == $name.as_bytes()) - .map(|prog| Some(prog.unwrap().id())); + .filter_map(|prog| prog.ok()) + .find(|prog| prog.name() == $name.as_bytes()) + .map(|prog| Some(prog.id())); let mut linked = false; if let Some(prog_id) = id { linked = is_linked(&prog_id.unwrap()); @@ -87,7 +90,12 @@ macro_rules! assert_loaded_and_linked { macro_rules! assert_loaded { ($name:literal, $loaded:expr) => { for i in 0..(MAX_RETRIES + 1) { - let state = loaded_programs().any(|prog| prog.unwrap().name() == $name.as_bytes()); + // Ignore race failures which can happen when the tests delete a + // program in the middle of a `loaded_programs()` call. + let state = loaded_programs() + .filter_map(|prog| prog.ok()) + .any(|prog| prog.name() == $name.as_bytes()); + if state == $loaded { break; }