From: Hannes Laimer <h.laimer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH proxmox v6 1/6] ebpf: add the crate with its BPF build
Date: Wed, 4 Nov 2026 14:49:00 +0100 [thread overview]
Message-ID: <20261104134905.1623451-2-h.laimer@proxmox.com> (raw)
In-Reply-To: <20261104134905.1623451-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 13:49 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 13:48 [PATCH proxmox v6 0/6] add proxmox-ebpf library Hannes Laimer
2026-11-04 13:49 ` Hannes Laimer [this message]
2026-11-04 13:49 ` [PATCH proxmox v6 2/6] ebpf: tc: add the link plumbing Hannes Laimer
2026-11-04 13:49 ` [PATCH proxmox v6 3/6] ebpf: subsystem: add the shared tc program handling Hannes Laimer
2026-11-04 13:49 ` [PATCH proxmox v6 4/6] ebpf: subsystem: place the links of different subsystems in a fixed order Hannes Laimer
2026-11-04 13:49 ` [PATCH proxmox v6 5/6] ebpf: tests: add a native harness for the BPF C programs Hannes Laimer
2026-11-04 13:49 ` [PATCH proxmox v6 6/6] ebpf: debian: package the crate as a Rust library Hannes Laimer
2026-11-04 16:02 ` superseded: [PATCH proxmox v6 0/6] add proxmox-ebpf 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=20261104134905.1623451-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox