Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a228989
test: add bpf_printk variadic argument integration test
heeen Jan 20, 2026
66dc131
aya-ebpf: fix bpf_printk variadic argument passing
heeen Jan 20, 2026
3580462
aya-obj: add struct_ops program section parsing
heeen Jan 5, 2026
ca9d899
aya: add struct_ops program type
heeen Jan 5, 2026
9e92b78
aya-ebpf: add struct_ops context and macro
heeen Jan 5, 2026
3b671b5
aya-obj: add struct_ops map parsing
heeen Jan 6, 2026
342de23
aya: add struct_ops map support
heeen Jan 6, 2026
e120dc1
aya-obj: add kfunc/ksyms section support
heeen Jan 10, 2026
b515a39
aya-obj: handle kfunc relocations
heeen Jan 10, 2026
97d8a9c
aya-obj: parse all functions in program sections
heeen Jan 10, 2026
deea8aa
aya: add BPF link support for struct_ops
heeen Jan 11, 2026
0bee867
test: add struct_ops integration tests
heeen Jan 5, 2026
894db30
aya: add Syscall program type support
heeen Jan 10, 2026
bbe10a1
aya-obj: update relocate_calls API
heeen Jan 17, 2026
beeb911
xtask: update public-api for struct_ops support
heeen Jan 17, 2026
53c7999
refactor: extract define_link_types! macro for programs without attach()
heeen Jan 19, 2026
30cc288
test: add unit tests for StructOpsMap and struct_ops loading
heeen Jan 19, 2026
2e961d7
aya-ebpf: add HID-BPF program support
heeen Jan 6, 2026
c238b9e
aya-ebpf: add safe abstractions for HID-BPF
heeen Jan 12, 2026
54fb398
test: add HID-BPF integration test
heeen Jan 6, 2026
aefe758
aya: replace transmute with ExpectedAttachType enum for struct_ops
heeen Jan 19, 2026
abbdb3d
aya-obj: use BPF_F_LINK constant instead of magic number
heeen Jan 19, 2026
daffafa
aya-obj: return error for unresolved kfunc calls
heeen Jan 19, 2026
4e6d786
test: improve struct_ops integration tests
heeen Jan 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions aya-ebpf-macros/src/hid_bpf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use std::borrow::Cow;

use proc_macro2::TokenStream;
use quote::quote;
use syn::{ItemFn, Result};

use crate::args::err_on_unknown_args;

/// The type of HID-BPF callback.
#[derive(Debug, Clone, Copy)]
pub(crate) enum HidBpfKind {
/// Called for each HID input report.
DeviceEvent,
/// Called to fix report descriptor at probe time.
RdescFixup,
/// Called for hardware requests (feature reports, etc).
HwRequest,
/// Called for output reports.
HwOutputReport,
}

impl HidBpfKind {
fn section_name(&self) -> &'static str {
match self {
Self::DeviceEvent => "struct_ops/hid_device_event",
Self::RdescFixup => "struct_ops/hid_rdesc_fixup",
Self::HwRequest => "struct_ops/hid_hw_request",
Self::HwOutputReport => "struct_ops/hid_hw_output_report",
}
}
}

pub(crate) struct HidBpf {
kind: HidBpfKind,
item: ItemFn,
}

impl HidBpf {
pub(crate) fn parse(kind: HidBpfKind, attrs: TokenStream, item: TokenStream) -> Result<Self> {
let item = syn::parse2(item)?;
let args = syn::parse2(attrs)?;
err_on_unknown_args(&args)?;
Ok(Self { kind, item })
}

pub(crate) fn expand(&self) -> TokenStream {
let Self { kind, item } = self;
let ItemFn {
attrs: _,
vis,
sig,
block: _,
} = item;
let fn_name = &sig.ident;
let section_name: Cow<'_, _> = kind.section_name().into();

// IMPORTANT: The entry point MUST use *mut hid_bpf_ctx (not c_void)
// to generate correct BTF for kfuncs like hid_bpf_get_data.
// The kernel verifier checks that kfunc args match BTF struct types.
quote! {
#[unsafe(no_mangle)]
#[unsafe(link_section = #section_name)]
#vis fn #fn_name(ctx: *mut ::aya_ebpf::programs::hid_bpf::hid_bpf_ctx) -> i32 {
return #fn_name(unsafe { ::aya_ebpf::programs::HidBpfContext::new(ctx) });

#item
}
}
}
}

#[cfg(test)]
mod tests {
use syn::parse_quote;

use super::*;

#[test]
fn test_hid_device_event() {
let prog = HidBpf::parse(
HidBpfKind::DeviceEvent,
parse_quote! {},
parse_quote! {
fn device_event(ctx: HidBpfContext) -> i32 {
0
}
},
)
.unwrap();
let expanded = prog.expand();
let expected = quote! {
#[unsafe(no_mangle)]
#[unsafe(link_section = "struct_ops/hid_device_event")]
fn device_event(ctx: *mut ::aya_ebpf::programs::hid_bpf::hid_bpf_ctx) -> i32 {
return device_event(unsafe { ::aya_ebpf::programs::HidBpfContext::new(ctx) });

fn device_event(ctx: HidBpfContext) -> i32 {
0
}
}
};
assert_eq!(expected.to_string(), expanded.to_string());
}

#[test]
fn test_hid_rdesc_fixup() {
let prog = HidBpf::parse(
HidBpfKind::RdescFixup,
parse_quote! {},
parse_quote! {
fn rdesc_fixup(ctx: HidBpfContext) -> i32 {
0
}
},
)
.unwrap();
let expanded = prog.expand();
let expected = quote! {
#[unsafe(no_mangle)]
#[unsafe(link_section = "struct_ops/hid_rdesc_fixup")]
fn rdesc_fixup(ctx: *mut ::aya_ebpf::programs::hid_bpf::hid_bpf_ctx) -> i32 {
return rdesc_fixup(unsafe { ::aya_ebpf::programs::HidBpfContext::new(ctx) });

fn rdesc_fixup(ctx: HidBpfContext) -> i32 {
0
}
}
};
assert_eq!(expected.to_string(), expanded.to_string());
}
}
152 changes: 152 additions & 0 deletions aya-ebpf-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod cgroup_sysctl;
mod fentry;
mod fexit;
mod flow_dissector;
mod hid_bpf;
mod kprobe;
mod lsm;
mod lsm_cgroup;
Expand All @@ -23,6 +24,7 @@ mod sk_msg;
mod sk_skb;
mod sock_ops;
mod socket_filter;
mod struct_ops;
mod tc;
mod tracepoint;
mod uprobe;
Expand All @@ -39,6 +41,7 @@ use cgroup_sysctl::CgroupSysctl;
use fentry::FEntry;
use fexit::FExit;
use flow_dissector::FlowDissector;
use hid_bpf::{HidBpf, HidBpfKind};
use kprobe::{KProbe, KProbeKind};
use lsm::Lsm;
use lsm_cgroup::LsmCgroup;
Expand All @@ -51,6 +54,7 @@ use sk_msg::SkMsg;
use sk_skb::{SkSkb, SkSkbKind};
use sock_ops::SockOps;
use socket_filter::SocketFilter;
use struct_ops::StructOps;
use tc::SchedClassifier;
use tracepoint::TracePoint;
use uprobe::{UProbe, UProbeKind};
Expand Down Expand Up @@ -601,6 +605,154 @@ pub fn fexit(attrs: TokenStream, item: TokenStream) -> TokenStream {
.into()
}

/// Marks a function as an eBPF struct_ops program.
///
/// Struct ops programs implement kernel interfaces like `hid_bpf_ops` for
/// HID device handling or `sched_ext_ops` for custom schedulers.
///
/// # Arguments
///
/// * `name` - Optional name for the struct_ops callback. If not specified,
/// the function name is used.
/// * `sleepable` - Mark the program as sleepable.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 5.6.
///
/// # Example
///
/// ```no_run
/// use aya_ebpf::{macros::struct_ops, programs::StructOpsContext};
///
/// #[struct_ops]
/// fn my_callback(ctx: StructOpsContext) -> i32 {
/// 0
/// }
/// ```
#[proc_macro_attribute]
pub fn struct_ops(attrs: TokenStream, item: TokenStream) -> TokenStream {
match StructOps::parse(attrs.into(), item.into()) {
Ok(prog) => prog.expand(),
Err(err) => err.into_compile_error(),
}
.into()
}

/// Marks a function as an HID-BPF device event handler.
///
/// This callback is invoked for each HID input report received from the device.
/// The function should return 0 to pass the event through, or modify the report
/// data and return a new size.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 6.3.
///
/// # Example
///
/// ```no_run
/// use aya_ebpf::{macros::hid_device_event, programs::HidBpfContext};
///
/// #[hid_device_event]
/// fn device_event(ctx: HidBpfContext) -> i32 {
/// 0
/// }
/// ```
#[proc_macro_attribute]
pub fn hid_device_event(attrs: TokenStream, item: TokenStream) -> TokenStream {
match HidBpf::parse(HidBpfKind::DeviceEvent, attrs.into(), item.into()) {
Ok(prog) => prog.expand(),
Err(err) => err.into_compile_error(),
}
.into()
}

/// Marks a function as an HID-BPF report descriptor fixup handler.
///
/// This callback is invoked once at device probe time to allow modifying the
/// HID report descriptor. The function should return 0 to keep the original
/// descriptor, or return a positive value indicating the new descriptor size.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 6.3.
///
/// # Example
///
/// ```no_run
/// use aya_ebpf::{macros::hid_rdesc_fixup, programs::HidBpfContext};
///
/// #[hid_rdesc_fixup]
/// fn rdesc_fixup(ctx: HidBpfContext) -> i32 {
/// 0
/// }
/// ```
#[proc_macro_attribute]
pub fn hid_rdesc_fixup(attrs: TokenStream, item: TokenStream) -> TokenStream {
match HidBpf::parse(HidBpfKind::RdescFixup, attrs.into(), item.into()) {
Ok(prog) => prog.expand(),
Err(err) => err.into_compile_error(),
}
.into()
}

/// Marks a function as an HID-BPF hardware request handler.
///
/// This callback is invoked when the kernel sends hardware requests like
/// GET_REPORT or SET_REPORT. Useful for intercepting and modifying feature
/// reports.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 6.3.
///
/// # Example
///
/// ```no_run
/// use aya_ebpf::{macros::hid_hw_request, programs::HidBpfContext};
///
/// #[hid_hw_request]
/// fn hw_request(ctx: HidBpfContext) -> i32 {
/// 0
/// }
/// ```
#[proc_macro_attribute]
pub fn hid_hw_request(attrs: TokenStream, item: TokenStream) -> TokenStream {
match HidBpf::parse(HidBpfKind::HwRequest, attrs.into(), item.into()) {
Ok(prog) => prog.expand(),
Err(err) => err.into_compile_error(),
}
.into()
}

/// Marks a function as an HID-BPF output report handler.
///
/// This callback is invoked when an output report is sent to the device.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 6.3.
///
/// # Example
///
/// ```no_run
/// use aya_ebpf::{macros::hid_hw_output_report, programs::HidBpfContext};
///
/// #[hid_hw_output_report]
/// fn hw_output_report(ctx: HidBpfContext) -> i32 {
/// 0
/// }
/// ```
#[proc_macro_attribute]
pub fn hid_hw_output_report(attrs: TokenStream, item: TokenStream) -> TokenStream {
match HidBpf::parse(HidBpfKind::HwOutputReport, attrs.into(), item.into()) {
Ok(prog) => prog.expand(),
Err(err) => err.into_compile_error(),
}
.into()
}

/// Marks a function as an eBPF Flow Dissector program.
///
/// Flow dissector is a program type that parses metadata out of the packets.
Expand Down
Loading
Loading