From: Hannes Laimer <h.laimer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH proxmox v8 1/6] ebpf: add the crate with its BPF build
Date: Wed, 4 Nov 2026 18:29:13 +0100 [thread overview]
Message-ID: <20261104172918.2643810-2-h.laimer@proxmox.com> (raw)
In-Reply-To: <20261104172918.2643810-1-h.laimer@proxmox.com>
Each subsystem is a cargo feature. The build script compiles the C
programs of the enabled ones with clang, against the kernel's UAPI
headers.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
Cargo.toml | 2 +
proxmox-ebpf/Cargo.toml | 21 +++++++
proxmox-ebpf/build.rs | 99 ++++++++++++++++++++++++++++++++
proxmox-ebpf/include/bpf_debug.h | 12 ++++
proxmox-ebpf/src/lib.rs | 3 +
5 files changed, 137 insertions(+)
create mode 100644 proxmox-ebpf/Cargo.toml
create mode 100644 proxmox-ebpf/build.rs
create mode 100644 proxmox-ebpf/include/bpf_debug.h
create mode 100644 proxmox-ebpf/src/lib.rs
diff --git a/Cargo.toml b/Cargo.toml
index 87e43b545793..a98c05810e3a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,7 @@ members = [
"proxmox-dns-api",
"proxmox-fixed-string",
"proxmox-docgen",
+ "proxmox-ebpf",
"proxmox-http",
"proxmox-http-error",
"proxmox-human-byte",
@@ -90,6 +91,7 @@ rust-version = "1.88"
# external dependencies
anyhow = "1.0"
+aya = "0.13"
base32 = "0.4"
base64 = "0.22"
bitflags = "2.4"
diff --git a/proxmox-ebpf/Cargo.toml b/proxmox-ebpf/Cargo.toml
new file mode 100644
index 000000000000..4cfd0b3258ed
--- /dev/null
+++ b/proxmox-ebpf/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "proxmox-ebpf"
+version = "0.1.0"
+authors.workspace = true
+edition.workspace = true
+exclude.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[features]
+# When enabled, BPF programs compile with -DBPF_DEBUG so their DBG() macros
+# expand to bpf_printk. Off by default
+bpf-debug = []
+
+[dependencies]
+anyhow.workspace = true
+aya.workspace = true
+log.workspace = true
+nix = { workspace = true, features = ["fs"] }
diff --git a/proxmox-ebpf/build.rs b/proxmox-ebpf/build.rs
new file mode 100644
index 000000000000..9cb143d955a8
--- /dev/null
+++ b/proxmox-ebpf/build.rs
@@ -0,0 +1,99 @@
+use std::env;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+// one clang version wherever the crate is built, the package depends on it by name
+const CLANG: &str = "clang-21";
+
+fn main() {
+ let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
+ let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
+ let src_dir = manifest_dir.join("src");
+ let global_include = manifest_dir.join("include");
+ let bpf_debug = env::var_os("CARGO_FEATURE_BPF_DEBUG").is_some();
+
+ // re-run when a subsystem appears
+ println!("cargo:rerun-if-changed={}", src_dir.display());
+ println!("cargo:rerun-if-changed={}", global_include.display());
+ println!("cargo:rerun-if-changed=build.rs");
+ println!("cargo:rerun-if-env-changed=CARGO_FEATURE_BPF_DEBUG");
+
+ for sub_entry in std::fs::read_dir(&src_dir).expect("read src/") {
+ let sub_path = sub_entry.expect("read src/ entry").path();
+ let bpf_dir = sub_path.join("bpf");
+ if !bpf_dir.is_dir() {
+ continue;
+ }
+ // each subsystem is a cargo feature of the same name, skip disabled ones
+ let feature = sub_path
+ .file_name()
+ .and_then(|s| s.to_str())
+ .expect("subsystem dir name")
+ .to_uppercase()
+ .replace('-', "_");
+ if env::var_os(format!("CARGO_FEATURE_{feature}")).is_none() {
+ continue;
+ }
+ println!("cargo:rerun-if-changed={}", bpf_dir.display());
+
+ for entry in std::fs::read_dir(&bpf_dir).expect("read bpf dir") {
+ let path = entry.expect("read bpf entry").path();
+ let is_program = path
+ .file_name()
+ .and_then(|s| s.to_str())
+ .is_some_and(|s| s.ends_with(".bpf.c"));
+ if !is_program {
+ continue;
+ }
+ compile_bpf(&path, &bpf_dir, &global_include, &out_dir, bpf_debug);
+ }
+ }
+}
+
+fn multiarch() -> String {
+ let out = Command::new("dpkg-architecture")
+ .arg("-qDEB_HOST_MULTIARCH")
+ .output()
+ .expect("failed to invoke dpkg-architecture, is dpkg-dev installed?");
+ assert!(out.status.success(), "dpkg-architecture failed");
+ String::from_utf8(out.stdout).unwrap().trim().to_owned()
+}
+
+fn compile_bpf(
+ src: &Path,
+ local_include: &Path,
+ global_include: &Path,
+ out_dir: &Path,
+ debug: bool,
+) {
+ // foo.bpf.c -> foo.bpf.o (file_stem strips only the final extension)
+ let stem = src.file_stem().unwrap().to_str().unwrap();
+ let obj_path = out_dir.join(format!("{stem}.o"));
+
+ let mut cmd = Command::new(CLANG);
+ // we need -g for clang to emit BTF
+ cmd.args(["-O2", "-g", "-Wall", "-target", "bpf"])
+ .arg("-I")
+ .arg(global_include)
+ .arg("-I")
+ .arg(local_include)
+ // the UAPI headers reach asm/ through the multiarch directory, which clang searches for
+ // the host target only
+ .arg("-I")
+ .arg(format!("/usr/include/{}", multiarch()));
+ if debug {
+ cmd.arg("-DBPF_DEBUG");
+ }
+ let status = cmd
+ .arg("-c")
+ .arg(src)
+ .arg("-o")
+ .arg(&obj_path)
+ .status()
+ .unwrap_or_else(|e| panic!("failed to invoke {CLANG}: {e}"));
+ assert!(
+ status.success(),
+ "clang failed to compile {}",
+ src.display()
+ );
+}
diff --git a/proxmox-ebpf/include/bpf_debug.h b/proxmox-ebpf/include/bpf_debug.h
new file mode 100644
index 000000000000..29bd0b0f7ef9
--- /dev/null
+++ b/proxmox-ebpf/include/bpf_debug.h
@@ -0,0 +1,12 @@
+#ifndef BPF_DEBUG_H
+#define BPF_DEBUG_H
+
+#include <bpf/bpf_helpers.h>
+
+#ifdef BPF_DEBUG
+#define DBG(fmt, ...) bpf_printk(fmt, ##__VA_ARGS__)
+#else
+#define DBG(fmt, ...) ((void)0)
+#endif
+
+#endif
diff --git a/proxmox-ebpf/src/lib.rs b/proxmox-ebpf/src/lib.rs
new file mode 100644
index 000000000000..b19895faaa18
--- /dev/null
+++ b/proxmox-ebpf/src/lib.rs
@@ -0,0 +1,3 @@
+#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
+//! eBPF subsystems, one per concern, each behind a cargo feature of its name. Nothing here runs
+//! on its own, consumers pull in only the subsystem they drive.
--
2.47.3
next prev parent reply other threads:[~2026-11-04 17:29 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 17:29 [PATCH proxmox v8 0/6] add proxmox-ebpf library Hannes Laimer
2026-11-04 17:29 ` Hannes Laimer [this message]
2026-11-04 17:29 ` [PATCH proxmox v8 2/6] ebpf: tc: add the link plumbing Hannes Laimer
2026-11-04 17:29 ` [PATCH proxmox v8 3/6] ebpf: subsystem: add the shared tc program handling Hannes Laimer
2026-11-05 16:20 ` Wolfgang Bumiller
2026-11-04 17:29 ` [PATCH proxmox v8 4/6] ebpf: subsystem: place subsystem links in a fixed order Hannes Laimer
2026-11-06 13:30 ` Wolfgang Bumiller
2026-11-04 17:29 ` [PATCH proxmox v8 5/6] ebpf: tests: add a native harness for the BPF C programs Hannes Laimer
2026-11-04 17:29 ` [PATCH proxmox v8 6/6] ebpf: debian: package the crate as a Rust library Hannes Laimer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261104172918.2643810-2-h.laimer@proxmox.com \
--to=h.laimer@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.