public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
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





  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal