diff --git a/Cargo.lock b/Cargo.lock index e37f8288..835c0197 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,7 +151,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -327,6 +327,7 @@ name = "bpf-builder" version = "0.8.1" dependencies = [ "anyhow", + "bpf-features", ] [[package]] @@ -335,9 +336,10 @@ version = "0.8.1" dependencies = [ "anyhow", "aya", - "aya-ebpf-bindings", - "aya-obj", "bpf-builder", + "bpf-feature-autodetect", + "bpf-features", + "bpf-features-macros", "bytes", "cgroups-rs", "diesel", @@ -363,6 +365,48 @@ dependencies = [ "which", ] +[[package]] +name = "bpf-feature-autodetect" +version = "0.8.1" +dependencies = [ + "anyhow", + "aya", + "aya-ebpf-bindings", + "aya-obj", + "bpf-builder", + "bpf-features", + "libc", + "log", + "nix 0.27.1", + "thiserror", +] + +[[package]] +name = "bpf-features" +version = "0.8.1" +dependencies = [ + "anyhow", + "aya", + "aya-ebpf-bindings", + "aya-obj", + "libc", + "log", + "nix 0.27.1", + "proc-macro2", + "quote", + "thiserror", +] + +[[package]] +name = "bpf-features-macros" +version = "0.8.1" +dependencies = [ + "bpf-features", + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "bpf-filtering" version = "0.8.1" @@ -479,7 +523,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -676,7 +720,7 @@ dependencies = [ "diesel_table_macro_syntax", "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -685,7 +729,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn", + "syn 2.0.52", ] [[package]] @@ -974,7 +1018,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -1556,7 +1600,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -1755,7 +1799,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -1877,7 +1921,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -2002,13 +2046,16 @@ dependencies = [ "logger", "network-monitor", "nix 0.27.1", + "proc-macro2", "process-monitor", "pulsar-core", + "quote", "rules-engine", "rust-ini", "semver", "serde", "smtp-notifier", + "syn 1.0.109", "tokio", ] @@ -2399,7 +2446,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -2507,7 +2554,7 @@ checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -2592,7 +2639,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.52", ] [[package]] @@ -2601,6 +2648,17 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.52" @@ -2728,7 +2786,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -2832,7 +2890,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -2974,7 +3032,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -3129,7 +3187,7 @@ version = "0.8.1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -3190,7 +3248,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.52", "wasm-bindgen-shared", ] @@ -3224,7 +3282,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3515,7 +3573,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 92c6265e..81587e4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,9 +34,12 @@ env_logger = { workspace = true } futures-util = { workspace = true } log = { workspace = true } nix = { workspace = true } +quote = "1.0" +proc-macro2 = "1.0" rust-ini = { workspace = true } serde = { workspace = true } semver = { workspace = true, features = ["serde"] } +syn = { version = "1.0", features = ["full"] } tokio = { workspace = true, features = ["full"] } [features] @@ -65,6 +68,9 @@ members = [ "crates/engine-api", "crates/validatron", "crates/bpf-filtering", + "crates/bpf-features", + "crates/bpf-feature-autodetect", + "crates/bpf-features-macros", "xtask", "test-suite", ] @@ -82,6 +88,9 @@ bpf-common = { path = "crates/bpf-common", features = [ "test-utils", "test-suite", ] } +bpf-features = { path = "crates/bpf-features" } +bpf-feature-autodetect = { path = "crates/bpf-feature-autodetect" } +bpf-features-macros = { path = "crates/bpf-features-macros" } bpf-filtering = { path = "crates/bpf-filtering", features = ["test-suite"] } engine-api = { path = "crates/engine-api" } pulsar-core = { path = "crates/pulsar-core" } diff --git a/crates/bpf-builder/Cargo.toml b/crates/bpf-builder/Cargo.toml index 29045f14..88421788 100644 --- a/crates/bpf-builder/Cargo.toml +++ b/crates/bpf-builder/Cargo.toml @@ -7,3 +7,4 @@ repository.workspace = true [dependencies] anyhow = { workspace = true } +bpf-features = { workspace = true } diff --git a/crates/bpf-builder/src/lib.rs b/crates/bpf-builder/src/lib.rs index bba54b44..96f9ee2e 100644 --- a/crates/bpf-builder/src/lib.rs +++ b/crates/bpf-builder/src/lib.rs @@ -6,6 +6,7 @@ use std::{ }; use anyhow::{bail, Context}; +use bpf_features::BpfFeatures; static CLANG_DEFAULT: &str = "clang"; static LLVM_STRIP: &str = "llvm-strip"; @@ -24,35 +25,14 @@ pub fn build(name: &str, source: &str) -> Result<(), Box> println!("cargo:rerun-if-changed={INCLUDE_PATH}/get_path.bpf.h"); println!("cargo:rerun-if-changed={INCLUDE_PATH}/task.bpf.h"); - let features = [ - ("FEATURE_ATOMICS", "a"), - ("FEATURE_CGROUP_TASK_BTF", "c"), - ("FEATURE_FN_POINTERS", "f"), - ("FEATURE_LSM", "l"), - ]; - let out_dir = env::var("OUT_DIR").context("OUT_DIR not set")?; let out_path = Path::new(&out_dir).join(name); - for bits in 0..(1 << features.len()) { - let mut feature_args = Vec::new(); - let mut suffix = String::new(); - - for (i, (feature, code)) in features.iter().enumerate() { - if bits & (1 << i) != 0 { - feature_args.push(format!("-D{}", feature)); - suffix.push_str(code); - } - } - - if suffix.is_empty() { - suffix.push_str("none"); - } - - let filename = format!("{}.{}.bpf.o", name, suffix); + for (_, (bpf_objfile_suffix, build_args)) in BpfFeatures::all_combinations() { + let filename = format!("{name}.{bpf_objfile_suffix}"); let full_path = out_path.with_file_name(filename); - compile(source, full_path, &feature_args) - .context("Error compiling programs with features: {feature_args:?}")?; + compile(source, full_path, &build_args) + .context("Error compiling programs with features: {build_args:?}")?; } Ok(()) diff --git a/crates/bpf-common/Cargo.toml b/crates/bpf-common/Cargo.toml index b2f102c8..b7f367a8 100644 --- a/crates/bpf-common/Cargo.toml +++ b/crates/bpf-common/Cargo.toml @@ -13,8 +13,9 @@ sqlite3-vendored = ["libsqlite3-sys/bundled"] [dependencies] aya = { workspace = true, features = ["async_tokio"] } -aya-ebpf-bindings = { workspace = true } -aya-obj = { workspace = true } +bpf-features = { workspace = true } +bpf-feature-autodetect = { workspace = true } +bpf-features-macros = { workspace = true } bytes = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["full"] } diff --git a/crates/bpf-common/build.rs b/crates/bpf-common/build.rs deleted file mode 100644 index 922e9045..00000000 --- a/crates/bpf-common/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Result<(), Box> { - bpf_builder::build("test_lsm", "src/feature_autodetect/test_lsm.bpf.c") -} diff --git a/crates/bpf-common/src/lib.rs b/crates/bpf-common/src/lib.rs index de87709a..3bfb0c10 100644 --- a/crates/bpf-common/src/lib.rs +++ b/crates/bpf-common/src/lib.rs @@ -1,6 +1,5 @@ mod bpf_sender; pub mod containers; -pub mod insn; pub mod program; #[cfg(feature = "test-utils")] pub mod test_runner; @@ -14,16 +13,16 @@ mod bump_memlock_rlimit; pub mod parsing; pub mod time; +pub use bpf_feature_autodetect; +pub use bpf_features::BpfFeatures; +pub use bpf_features_macros::ebpf_programs_map; pub use bpf_sender::{BpfSender, BpfSenderWrapper}; pub use bump_memlock_rlimit::bump_memlock_rlimit; pub use program::{Program, ProgramBuilder, ProgramError}; pub use aya; -pub use aya_obj::generated::bpf_prog_type as BpfProgType; - pub mod bpf_fs; -pub mod feature_autodetect; /// Utility function to pretty print an error with its sources. /// diff --git a/crates/bpf-common/src/program.rs b/crates/bpf-common/src/program.rs index 58796524..f70e9b7d 100644 --- a/crates/bpf-common/src/program.rs +++ b/crates/bpf-common/src/program.rs @@ -2,18 +2,9 @@ //! - runs background thread which sets up the probe and waits for a shutdown signal //! - allows to read events events. //! -use core::fmt; use std::{ - collections::HashSet, - convert::TryFrom, - fmt::Display, - fs::File, - hash::{Hash, Hasher}, - io, - mem::size_of, - path::PathBuf, - sync::Arc, - time::Duration, + collections::HashSet, convert::TryFrom, fmt, fmt::Display, fs::File, io, mem::size_of, + path::PathBuf, sync::Arc, time::Duration, }; use aya::{ @@ -25,19 +16,16 @@ use aya::{ util::online_cpus, Bpf, BpfLoader, Btf, BtfError, Pod, }; -use aya_ebpf_bindings::bindings::bpf_func_id; +use bpf_feature_autodetect::{autodetect_features, kernel_version::KernelVersion}; +use bpf_features::BpfFeatures; use bytes::{Buf, Bytes, BytesMut}; use thiserror::Error; use tokio::{sync::watch, task::JoinError}; use crate::{ - feature_autodetect::{ - atomic::atomics_supported, func::func_id_supported, kernel_version::KernelVersion, - lsm::lsm_supported, - }, parsing::mountinfo::{get_cgroup2_mountpoint, MountinfoError}, time::Timestamp, - BpfProgType, BpfSender, Pid, + BpfSender, Pid, }; const PERF_HEADER_SIZE: usize = 4; @@ -83,36 +71,6 @@ pub enum BpfLogLevel { Debug = 2, } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct BpfFeatures { - pub atomics: bool, - pub cgroup_skb_task_btf: bool, - pub fn_pointers: bool, - pub lsm: bool, -} - -impl Hash for BpfFeatures { - fn hash(&self, state: &mut H) { - self.atomics.hash(state); - self.cgroup_skb_task_btf.hash(state); - self.fn_pointers.hash(state); - } -} - -impl BpfFeatures { - fn autodetect() -> Self { - Self { - atomics: atomics_supported(), - cgroup_skb_task_btf: func_id_supported( - bpf_func_id::BPF_FUNC_get_current_task_btf, - BpfProgType::BPF_PROG_TYPE_CGROUP_SKB, - ), - fn_pointers: true, - lsm: lsm_supported(), - } - } -} - impl BpfContext { pub fn new( pinning: Pinning, @@ -150,7 +108,7 @@ impl BpfContext { pinning_path, log_level, kernel_version, - features: BpfFeatures::autodetect(), + features: autodetect_features(), }) } @@ -174,159 +132,12 @@ impl BpfContext { /// at runtime. #[macro_export] macro_rules! ebpf_program { - ( $ctx: expr , $probe: expr) => {{ + ($ctx:expr , $probe:expr) => {{ use std::collections::HashMap; - use bpf_common::aya::include_bytes_aligned; - use bpf_common::feature_autodetect::kernel_version::KernelVersion; - use bpf_common::program::BpfFeatures; - - let programs: HashMap = HashMap::from([ - ( - BpfFeatures { - atomics: false, - cgroup_skb_task_btf: false, - fn_pointers: false, - lsm: false, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".none.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: true, - cgroup_skb_task_btf: false, - fn_pointers: false, - lsm: false, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".a.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: true, - cgroup_skb_task_btf: true, - fn_pointers: false, - lsm: false, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".ac.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: true, - cgroup_skb_task_btf: true, - fn_pointers: true, - lsm: false, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".acf.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: true, - cgroup_skb_task_btf: false, - fn_pointers: true, - lsm: false, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".af.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: false, - cgroup_skb_task_btf: true, - fn_pointers: false, - lsm: false, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".c.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: false, - cgroup_skb_task_btf: true, - fn_pointers: true, - lsm: false, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".cf.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: false, - cgroup_skb_task_btf: false, - fn_pointers: true, - lsm: false, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".f.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: false, - cgroup_skb_task_btf: false, - fn_pointers: false, - lsm: true, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".l.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: true, - cgroup_skb_task_btf: false, - fn_pointers: false, - lsm: true, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".al.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: true, - cgroup_skb_task_btf: true, - fn_pointers: false, - lsm: true, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".acl.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: true, - cgroup_skb_task_btf: true, - fn_pointers: true, - lsm: true, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".acfl.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: true, - cgroup_skb_task_btf: false, - fn_pointers: true, - lsm: true, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".afl.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: false, - cgroup_skb_task_btf: true, - fn_pointers: false, - lsm: true, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".cl.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: false, - cgroup_skb_task_btf: true, - fn_pointers: true, - lsm: true, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".cfl.bpf.o")).into(), - ), - ( - BpfFeatures { - atomics: false, - cgroup_skb_task_btf: false, - fn_pointers: true, - lsm: true, - }, - include_bytes_aligned!(concat!(env!("OUT_DIR"), "/", $probe, ".fl.bpf.o")).into(), - ), - ]); + use bpf_common::{ebpf_programs_map, BpfFeatures}; + + let programs: HashMap = HashMap::from(ebpf_programs_map!($probe)); programs .get($ctx.features()) diff --git a/crates/bpf-feature-autodetect/Cargo.toml b/crates/bpf-feature-autodetect/Cargo.toml new file mode 100644 index 00000000..ce0fb463 --- /dev/null +++ b/crates/bpf-feature-autodetect/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "bpf-feature-autodetect" +version.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true + +[dependencies] +anyhow = { workspace = true } +aya = { workspace = true } +aya-ebpf-bindings = { workspace = true } +aya-obj = { workspace = true } +bpf-features = { workspace = true } +libc = { workspace = true } +log = { workspace = true } +nix = { workspace = true } +thiserror = { workspace = true } + +[build-dependencies] +bpf-builder = { workspace = true } diff --git a/crates/bpf-feature-autodetect/build.rs b/crates/bpf-feature-autodetect/build.rs new file mode 100644 index 00000000..7eee7713 --- /dev/null +++ b/crates/bpf-feature-autodetect/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + bpf_builder::build("test_lsm", "src/test_lsm.bpf.c") +} diff --git a/crates/bpf-common/src/feature_autodetect/atomic.rs b/crates/bpf-feature-autodetect/src/atomic.rs similarity index 100% rename from crates/bpf-common/src/feature_autodetect/atomic.rs rename to crates/bpf-feature-autodetect/src/atomic.rs diff --git a/crates/bpf-common/src/feature_autodetect/func.rs b/crates/bpf-feature-autodetect/src/func.rs similarity index 100% rename from crates/bpf-common/src/feature_autodetect/func.rs rename to crates/bpf-feature-autodetect/src/func.rs diff --git a/crates/bpf-common/src/insn.rs b/crates/bpf-feature-autodetect/src/insn.rs similarity index 100% rename from crates/bpf-common/src/insn.rs rename to crates/bpf-feature-autodetect/src/insn.rs diff --git a/crates/bpf-common/src/feature_autodetect/kernel_version.rs b/crates/bpf-feature-autodetect/src/kernel_version.rs similarity index 100% rename from crates/bpf-common/src/feature_autodetect/kernel_version.rs rename to crates/bpf-feature-autodetect/src/kernel_version.rs diff --git a/crates/bpf-common/src/feature_autodetect/mod.rs b/crates/bpf-feature-autodetect/src/lib.rs similarity index 76% rename from crates/bpf-common/src/feature_autodetect/mod.rs rename to crates/bpf-feature-autodetect/src/lib.rs index f9847372..d1423705 100644 --- a/crates/bpf-common/src/feature_autodetect/mod.rs +++ b/crates/bpf-feature-autodetect/src/lib.rs @@ -3,16 +3,21 @@ use std::mem; use aya::BtfError; +use aya_ebpf_bindings::bindings::bpf_func_id; pub use aya_obj::generated::bpf_prog_type as BpfProgType; use aya_obj::generated::{bpf_attr, bpf_cmd, bpf_insn}; +use bpf_features::BpfFeatures; use libc::SYS_bpf; use thiserror::Error; pub mod atomic; pub mod func; +pub mod insn; pub mod kernel_version; pub mod lsm; +use crate::{atomic::atomics_supported, func::func_id_supported, lsm::lsm_supported}; + /// Size of the eBPF verifier log. const LOG_SIZE: usize = 4096; @@ -24,6 +29,18 @@ pub enum FeatureProbeError { Btf(#[from] BtfError), } +pub fn autodetect_features() -> BpfFeatures { + BpfFeatures { + atomics: atomics_supported(), + cgroup_skb_task_btf: func_id_supported( + bpf_func_id::BPF_FUNC_get_current_task_btf, + BpfProgType::BPF_PROG_TYPE_CGROUP_SKB, + ), + fn_pointers: true, + lsm: lsm_supported(), + } +} + /// Loads the given eBPF bytecode to the kernel. pub(crate) fn load_program( prog_type: BpfProgType, diff --git a/crates/bpf-common/src/feature_autodetect/lsm.rs b/crates/bpf-feature-autodetect/src/lsm.rs similarity index 100% rename from crates/bpf-common/src/feature_autodetect/lsm.rs rename to crates/bpf-feature-autodetect/src/lsm.rs diff --git a/crates/bpf-common/src/feature_autodetect/test_lsm.bpf.c b/crates/bpf-feature-autodetect/src/test_lsm.bpf.c similarity index 100% rename from crates/bpf-common/src/feature_autodetect/test_lsm.bpf.c rename to crates/bpf-feature-autodetect/src/test_lsm.bpf.c diff --git a/crates/bpf-features-macros/Cargo.toml b/crates/bpf-features-macros/Cargo.toml new file mode 100644 index 00000000..1dac6bcf --- /dev/null +++ b/crates/bpf-features-macros/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bpf-features-macros" +version.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true + +[dependencies] +bpf-features = { workspace = true } +proc-macro2 = { workspace = true } +quote = { workspace = true } +syn = { workspace = true } + +[lib] +proc-macro = true diff --git a/crates/bpf-features-macros/src/lib.rs b/crates/bpf-features-macros/src/lib.rs new file mode 100644 index 00000000..c1912da2 --- /dev/null +++ b/crates/bpf-features-macros/src/lib.rs @@ -0,0 +1,32 @@ +use bpf_features::BpfFeatures; +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, LitStr}; + +/// Generates a mapping of `BpfFeatures` to eBPF program bytes. The bytes are +/// embedded into the final binary thanks to `include_bytes_aligned`. +#[proc_macro] +pub fn ebpf_programs_map(input: TokenStream) -> TokenStream { + let probe = parse_macro_input!(input as LitStr).value(); + + let mut tokens = quote! {}; + + for (features, (bpf_objfile_suffix, _)) in BpfFeatures::all_combinations() { + let path = format!( + "{}/{}.{}", + std::env::var("OUT_DIR").unwrap(), + probe, + bpf_objfile_suffix + ); + tokens = quote! { + #tokens + (#features, bpf_common::aya::include_bytes_aligned!(#path)), + }; + } + + let tokens = quote! { + [#tokens] + }; + + tokens.into() +} diff --git a/crates/bpf-features/Cargo.toml b/crates/bpf-features/Cargo.toml new file mode 100644 index 00000000..c9cb759a --- /dev/null +++ b/crates/bpf-features/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "bpf-features" +version.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true + +[dependencies] +anyhow = { workspace = true } +aya = { workspace = true, features = ["async_tokio"] } +aya-ebpf-bindings = { workspace = true } +aya-obj = { workspace = true } +libc = { workspace = true } +log = { workspace = true } +nix = { workspace = true, features = ["fs"] } +proc-macro2 = { workspace = true } +quote = { workspace = true } +thiserror = { workspace = true } diff --git a/crates/bpf-features/src/lib.rs b/crates/bpf-features/src/lib.rs new file mode 100644 index 00000000..69ff9ead --- /dev/null +++ b/crates/bpf-features/src/lib.rs @@ -0,0 +1,182 @@ +use std::{collections::HashMap, hash::Hash}; + +pub use aya_obj::generated::bpf_prog_type as BpfProgType; +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct BpfFeatures { + pub atomics: bool, + pub cgroup_skb_task_btf: bool, + pub fn_pointers: bool, + pub lsm: bool, +} + +impl BpfFeatures { + pub fn bpf_objfile_suffix(&self) -> String { + let mut feature_codes = String::new(); + + if self.atomics { + feature_codes.push('a'); + } + if self.cgroup_skb_task_btf { + feature_codes.push('c'); + } + if self.fn_pointers { + feature_codes.push('f'); + } + if self.lsm { + feature_codes.push('l'); + } + + if feature_codes.is_empty() { + feature_codes.push_str("none"); + } + + format!("{}.bpf.o", feature_codes) + } + + pub fn build_args(&self) -> Vec { + let mut args = Vec::new(); + + if self.atomics { + args.push("-DFEATURE_ATOMICS".to_string()); + } + if self.cgroup_skb_task_btf { + args.push("-DFEATURE_CGROUP_TASK_BTF".to_string()); + } + if self.fn_pointers { + args.push("-DFEATURE_FN_POINTERS".to_string()); + } + if self.lsm { + args.push("-DFEATURE_LSM".to_string()); + } + + args + } + + pub fn all_combinations() -> HashMap)> { + let mut combinations = HashMap::new(); + + for atomics in [true, false] { + for cgroup_skb_task_btf in [true, false] { + for fn_pointers in [true, false] { + for lsm in [true, false] { + let features = Self { + atomics, + cgroup_skb_task_btf, + fn_pointers, + lsm, + }; + combinations.insert( + features.clone(), + (features.bpf_objfile_suffix(), features.build_args()), + ); + } + } + } + } + + combinations + } +} + +impl ToTokens for BpfFeatures { + fn to_tokens(&self, tokens: &mut TokenStream) { + let atomics = self.atomics; + let cgroup_skb_task_btf = self.cgroup_skb_task_btf; + let fn_pointers = self.fn_pointers; + let lsm = self.lsm; + + let generated = quote! { + BpfFeatures { + atomics: #atomics, + cgroup_skb_task_btf: #cgroup_skb_task_btf, + fn_pointers: #fn_pointers, + lsm: #lsm, + } + }; + + generated.to_tokens(tokens); + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_bpf_features() { + let features = BpfFeatures { + atomics: false, + cgroup_skb_task_btf: false, + fn_pointers: false, + lsm: false, + }; + assert_eq!(features.bpf_objfile_suffix(), "none.bpf.o"); + assert!(features.build_args().is_empty()); + + let features = BpfFeatures { + atomics: true, + cgroup_skb_task_btf: false, + fn_pointers: false, + lsm: false, + }; + assert_eq!(features.bpf_objfile_suffix(), "a.bpf.o"); + assert_eq!(features.build_args().as_slice(), &["-DFEATURE_ATOMICS"]); + + let features = BpfFeatures { + atomics: true, + cgroup_skb_task_btf: true, + fn_pointers: false, + lsm: false, + }; + assert_eq!(features.bpf_objfile_suffix(), "ac.bpf.o"); + assert_eq!( + features.build_args().as_slice(), + &["-DFEATURE_ATOMICS", "-DFEATURE_CGROUP_TASK_BTF"] + ); + + let features = BpfFeatures { + atomics: true, + cgroup_skb_task_btf: true, + fn_pointers: true, + lsm: false, + }; + assert_eq!(features.bpf_objfile_suffix(), "acf.bpf.o"); + assert_eq!( + features.build_args().as_slice(), + &[ + "-DFEATURE_ATOMICS", + "-DFEATURE_CGROUP_TASK_BTF", + "-DFEATURE_FN_POINTERS" + ] + ); + + let features = BpfFeatures { + atomics: true, + cgroup_skb_task_btf: true, + fn_pointers: true, + lsm: true, + }; + assert_eq!(features.bpf_objfile_suffix(), "acfl.bpf.o"); + assert_eq!( + features.build_args(), + &[ + "-DFEATURE_ATOMICS", + "-DFEATURE_CGROUP_TASK_BTF", + "-DFEATURE_FN_POINTERS", + "-DFEATURE_LSM" + ] + ); + + let features = BpfFeatures { + atomics: false, + cgroup_skb_task_btf: true, + fn_pointers: false, + lsm: false, + }; + assert_eq!(features.bpf_objfile_suffix(), "c.bpf.o"); + assert_eq!(features.build_args(), &["-DFEATURE_CGROUP_TASK_BTF"]); + } +} diff --git a/crates/bpf-filtering/src/test_suite.rs b/crates/bpf-filtering/src/test_suite.rs index a8c81d0d..444e1e1b 100644 --- a/crates/bpf-filtering/src/test_suite.rs +++ b/crates/bpf-filtering/src/test_suite.rs @@ -1,7 +1,7 @@ use crate::config::Rule; use crate::maps::{Cgroup, InterestMap, Map, PolicyDecision, RuleMap}; use bpf_common::aya::programs::RawTracePoint; -use bpf_common::aya::{self, include_bytes_aligned, Bpf, BpfLoader}; +use bpf_common::aya::{self, Bpf, BpfLoader}; use bpf_common::program::BpfContext; use bpf_common::test_runner::{TestCase, TestReport, TestSuite}; use bpf_common::test_utils::cgroup::fork_in_temp_cgroup; diff --git a/crates/modules/process-monitor/src/lib.rs b/crates/modules/process-monitor/src/lib.rs index b1528883..f62ad991 100644 --- a/crates/modules/process-monitor/src/lib.rs +++ b/crates/modules/process-monitor/src/lib.rs @@ -1,8 +1,8 @@ use anyhow::Context; use bpf_common::{ + bpf_feature_autodetect::kernel_version::KernelVersion, containers::ContainerError, ebpf_program, - feature_autodetect::kernel_version::KernelVersion, parsing::{BufferIndex, IndexError}, program::BpfContext, BpfSender, Gid, Pid, Program, ProgramBuilder, ProgramError, Uid,