From 4ce4de70eab3c464bde5a60e0bf746681c611012 Mon Sep 17 00:00:00 2001 From: mahesh bhatiya Date: Sat, 12 Jul 2025 23:11:48 +0530 Subject: [PATCH] feat: migrate all eBPF programs to per-crate xtask build system - Introduced isolated xtask-based builders for each eBPF program - Each xtask handles its own build logic using `cargo rustc` and `llc-20` - Standardized output path to `bin/{program}.o` - Eliminated the need for build_ebpf.sh script - Improved maintainability and modularity of eBPF build process --- Cargo.toml | 15 +++ bin/ssh_monitor.o | Bin 1880 -> 2776 bytes core/ssh_session_agent.go | 11 +- ebpf-programs/connect_count/Cargo.toml | 23 +--- .../program}/Cargo.toml | 9 +- .../{ => program}/rust-toolchain.toml | 0 .../connect_count/{ => program}/src/lib.rs | 0 ebpf-programs/connect_count/xtask/Cargo.toml | 8 ++ ebpf-programs/connect_count/xtask/src/main.rs | 103 ++++++++++++++++ ebpf-programs/ssh_fail_monitor/Cargo.toml | 24 +--- .../ssh_fail_monitor/program/Cargo.toml | 18 +++ .../{ => program}/rust-toolchain.toml | 0 .../ssh_fail_monitor/{ => program}/src/lib.rs | 37 +++--- .../ssh_fail_monitor/xtask/Cargo.toml | 8 ++ .../ssh_fail_monitor/xtask/src/main.rs | 103 ++++++++++++++++ ebpf-programs/ssh_monitor/Cargo.toml | 24 +--- ebpf-programs/ssh_monitor/program/Cargo.toml | 18 +++ .../{ => program}/rust-toolchain.toml | 0 .../ssh_monitor/{ => program}/src/lib.rs | 27 ++-- ebpf-programs/ssh_monitor/xtask/Cargo.toml | 8 ++ ebpf-programs/ssh_monitor/xtask/src/main.rs | 103 ++++++++++++++++ ebpf-programs/ssh_session_monitor/Cargo.toml | 20 +-- .../ssh_session_monitor/rust-toolchain.toml | 4 - ebpf-programs/ssh_session_monitor/src/lib.rs | 115 ------------------ .../ssh-session-ebpf/Cargo.toml | 11 ++ .../ssh-session-ebpf/src/lib.rs | 25 ++++ .../ssh_session_monitor/xtask/Cargo.toml | 8 ++ .../ssh_session_monitor/xtask/src/main.rs | 103 ++++++++++++++++ .../test_program/rust-toolchain.toml | 4 - ebpf-programs/test_program/src/lib.rs | 12 -- ebpf-programs/udp_monitor/.cargo/config.toml | 5 - ebpf-programs/udp_monitor/Cargo.toml | 24 +--- ebpf-programs/udp_monitor/program/Cargo.toml | 18 +++ .../{ => program}/rust-toolchain.toml | 0 .../udp_monitor/{ => program}/src/lib.rs | 0 ebpf-programs/udp_monitor/xtask/Cargo.toml | 8 ++ ebpf-programs/udp_monitor/xtask/src/main.rs | 103 ++++++++++++++++ scripts/build_ebpf.sh | 80 ------------ xtask/Cargo.toml | 9 ++ xtask/src/main.rs | 31 +++++ 40 files changed, 774 insertions(+), 345 deletions(-) create mode 100644 Cargo.toml rename ebpf-programs/{test_program => connect_count/program}/Cargo.toml (75%) rename ebpf-programs/connect_count/{ => program}/rust-toolchain.toml (100%) rename ebpf-programs/connect_count/{ => program}/src/lib.rs (100%) create mode 100644 ebpf-programs/connect_count/xtask/Cargo.toml create mode 100644 ebpf-programs/connect_count/xtask/src/main.rs create mode 100644 ebpf-programs/ssh_fail_monitor/program/Cargo.toml rename ebpf-programs/ssh_fail_monitor/{ => program}/rust-toolchain.toml (100%) rename ebpf-programs/ssh_fail_monitor/{ => program}/src/lib.rs (71%) create mode 100644 ebpf-programs/ssh_fail_monitor/xtask/Cargo.toml create mode 100644 ebpf-programs/ssh_fail_monitor/xtask/src/main.rs create mode 100644 ebpf-programs/ssh_monitor/program/Cargo.toml rename ebpf-programs/ssh_monitor/{ => program}/rust-toolchain.toml (100%) rename ebpf-programs/ssh_monitor/{ => program}/src/lib.rs (64%) create mode 100644 ebpf-programs/ssh_monitor/xtask/Cargo.toml create mode 100644 ebpf-programs/ssh_monitor/xtask/src/main.rs delete mode 100644 ebpf-programs/ssh_session_monitor/rust-toolchain.toml delete mode 100644 ebpf-programs/ssh_session_monitor/src/lib.rs create mode 100644 ebpf-programs/ssh_session_monitor/ssh-session-ebpf/Cargo.toml create mode 100644 ebpf-programs/ssh_session_monitor/ssh-session-ebpf/src/lib.rs create mode 100644 ebpf-programs/ssh_session_monitor/xtask/Cargo.toml create mode 100644 ebpf-programs/ssh_session_monitor/xtask/src/main.rs delete mode 100644 ebpf-programs/test_program/rust-toolchain.toml delete mode 100644 ebpf-programs/test_program/src/lib.rs delete mode 100644 ebpf-programs/udp_monitor/.cargo/config.toml create mode 100644 ebpf-programs/udp_monitor/program/Cargo.toml rename ebpf-programs/udp_monitor/{ => program}/rust-toolchain.toml (100%) rename ebpf-programs/udp_monitor/{ => program}/src/lib.rs (100%) create mode 100644 ebpf-programs/udp_monitor/xtask/Cargo.toml create mode 100644 ebpf-programs/udp_monitor/xtask/src/main.rs delete mode 100755 scripts/build_ebpf.sh create mode 100644 xtask/Cargo.toml create mode 100644 xtask/src/main.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5b1613e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] +members = [".", "xtask", "ssh-monitor-ebpf"] +resolver = "2" + +[package] +name = "ssh-monitor" +version = "0.1.0" +edition = "2021" + +[dependencies] +aya = "0.1.1" +anyhow = "1.0" +clap = { version = "4.4", features = ["derive"] } +tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "signal"] } +libc = "0.2" \ No newline at end of file diff --git a/bin/ssh_monitor.o b/bin/ssh_monitor.o index b2dc1d61a57a37688172cfaf2a0a551ce11d7c65..82978bb160b1b1db511da942914daaa57f95245c 100644 GIT binary patch literal 2776 zcmbVO&2QsG6rX<4WdYIc0aXE{aNtOY?KHNLK!I+$tyD_2rDz30BYQkfy-l28JG9vx z;J`|V6GB1)iA!J)`~fTVvQm2F*aHVHNP9ur1BY^=xANY2CNWkbB1UrF_ix_Jdw%g} z^_|W6xjCZN9Qm7!C8Z_g&b2XL6KsuK2hSyPb%o;n6$0GV)$l1{9Lvk8d3|RNOBjt~W^I`-L+hzW`^9 z<3n$W=)(UP@+<<^8>IgI!V?*n`u7Wuh1~xt#bx(?6*9Xk^ZHT1kY&i{5#+Td^G~(e z{L%{;{)hkC0wIek80ZptT1bw>T?C3OD_n{^@_56a(%@ylxtx&CsRSR@DU%Q0Yd-%| zz$pS`KF%4W_3IZd()+n9^U3Ug0eHg)G9TD?Qa)J60=V3V2V3T0>Q26u^w!>vs2863 zBPbm8ouK(~@nHMro3DDsJwdL6?M3M?2sTD$VJse799{fmj8oV@A+JU9mpEJAWkF<` z*AlWoWRqu1&jP;+7Pa;O7uQ>mR4z@S|7ME*3y7QKUzwsm0kj+B8hJ^ek`eu53jX)t zHwphsi6db~I2Tj=-vIsUygVfEYFs=4{swfjTKO_y4biG{KpFd?3=b}p@pd~Xz(QGI zV{u_2!8c_^%4o-@oU_+5GG_U8M#lJ`Gx5)We3r3eEgxs>&w*T&M@6-$aw*{_328Z< z82eVVD{ZyIAZbUW6!GR!Cu+O=^(5)h<0wctX$FjkF(>r)?x!2^2ko0>E41GYKcOb2 zH0s6)(@Z4P<&7Yu-S9XFJyMF3C~;h%y%y5_{Vlrr_Vy0lc<*5M{r$0gdwciRI|rMD zez>cdPS2s-?fBFMj38{p)f1~qs}=K*H#hHvP+YMQPwBjorlo9*^d zx1;XTu>eMMwTkB$x^B!0r|M5gk4c|#cevYDVcAbMC>Q=K#;D3Y+uczW_T2>pf}r z-yj^rZ)DqtB9^^E{-)ihes}TwEv$zs7%3Yk9Zh)nVb|n!_;$8!+t38p5Xa@gRDa}Y z!eztnZrXg0G8ZAqqdts3rY?$0Jx!QSOsSN!zC4(dZnS z!xlMc@~-bLHOx757TuqrvxazwSsw{`_-=Ymb92CtCb4k|*JX%{{Kow{7%u@o4qTsR z4ZXc`mMXV)m62o=7r(~wS&m2Se`b7wSi)bmn6&tq&nKYR$CUpi;hQSLAoQalxH{O- zfQ||@D7Dh^v<|{ZD+s+vfgX5%(84%T!rsdO*1CSMt^#le!P@g^&}-vT>4Emzh{KSu zaqo8h{)0MFdL2)DuCH{}ew5x2#nLxlW5!HMK>VcD(HW8MZ9Pcd&Ek&cRAoZVRDp~$;oy5Y*2u!A4z%j6`i@fW@16Fi3+(x>lWi39Sx akkq0RpZ?aAN`q diff --git a/core/ssh_session_agent.go b/core/ssh_session_agent.go index a483370..c444a44 100644 --- a/core/ssh_session_agent.go +++ b/core/ssh_session_agent.go @@ -14,7 +14,6 @@ import ( "github.com/cilium/ebpf/ringbuf" ) -// Match Rust layout (repr(C)) type SshSessionEvent struct { Pid uint32 Uid uint32 @@ -29,27 +28,23 @@ func RunSSHSessionMonitor() error { return fmt.Errorf("load spec: %w", err) } - // Load with no pinning or options coll, err := ebpf.NewCollectionWithOptions(spec, ebpf.CollectionOptions{}) if err != nil { return fmt.Errorf("create collection: %w", err) } defer coll.Close() - - // Get maps + ringbufMap := coll.Maps["SSH_SESSION_RINGBUF"] if ringbufMap == nil { return fmt.Errorf("map SSH_SESSION_RINGBUF not found") } - // Attach kprobe to track shell exec kp, err := link.Kprobe("do_execveat_common", coll.Programs["track_shell_start"], nil) if err != nil { return fmt.Errorf("attach kprobe: %w", err) } defer kp.Close() - // Attach tracepoint to track process exit tp, err := link.Tracepoint("sched", "sched_process_exit", coll.Programs["sched_process_exit"], nil) if err != nil { return fmt.Errorf("attach tracepoint: %w", err) @@ -57,15 +52,11 @@ func RunSSHSessionMonitor() error { defer tp.Close() log.Println("SSH Session Monitor started. Waiting for events...") - - // Set up ring buffer reader reader, err := ringbuf.NewReader(ringbufMap) if err != nil { return fmt.Errorf("open ringbuf: %w", err) } defer reader.Close() - - // Graceful shutdown on Ctrl+C sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) diff --git a/ebpf-programs/connect_count/Cargo.toml b/ebpf-programs/connect_count/Cargo.toml index 8ba861f..0dc9ff4 100644 --- a/ebpf-programs/connect_count/Cargo.toml +++ b/ebpf-programs/connect_count/Cargo.toml @@ -1,17 +1,6 @@ -[package] -name = "connect_count" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["staticlib"] - -[dependencies] -aya-ebpf = { version = "0.1.1", default-features = false } - -[profile.release] -opt-level = "z" -lto = true -panic = "abort" -codegen-units = 1 -strip = "debuginfo" +[workspace] +resolver = "2" +members = [ + "program", + "xtask" +] diff --git a/ebpf-programs/test_program/Cargo.toml b/ebpf-programs/connect_count/program/Cargo.toml similarity index 75% rename from ebpf-programs/test_program/Cargo.toml rename to ebpf-programs/connect_count/program/Cargo.toml index 0e9d03d..8ba861f 100644 --- a/ebpf-programs/test_program/Cargo.toml +++ b/ebpf-programs/connect_count/program/Cargo.toml @@ -1,14 +1,17 @@ [package] -name = "ebpf_test" +name = "connect_count" version = "0.1.0" edition = "2021" +[lib] +crate-type = ["staticlib"] + [dependencies] aya-ebpf = { version = "0.1.1", default-features = false } [profile.release] opt-level = "z" -lto = false +lto = true panic = "abort" -strip = "debuginfo" codegen-units = 1 +strip = "debuginfo" diff --git a/ebpf-programs/connect_count/rust-toolchain.toml b/ebpf-programs/connect_count/program/rust-toolchain.toml similarity index 100% rename from ebpf-programs/connect_count/rust-toolchain.toml rename to ebpf-programs/connect_count/program/rust-toolchain.toml diff --git a/ebpf-programs/connect_count/src/lib.rs b/ebpf-programs/connect_count/program/src/lib.rs similarity index 100% rename from ebpf-programs/connect_count/src/lib.rs rename to ebpf-programs/connect_count/program/src/lib.rs diff --git a/ebpf-programs/connect_count/xtask/Cargo.toml b/ebpf-programs/connect_count/xtask/Cargo.toml new file mode 100644 index 0000000..952245c --- /dev/null +++ b/ebpf-programs/connect_count/xtask/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +clap = { version = "4.5", features = ["derive"] } diff --git a/ebpf-programs/connect_count/xtask/src/main.rs b/ebpf-programs/connect_count/xtask/src/main.rs new file mode 100644 index 0000000..50d69af --- /dev/null +++ b/ebpf-programs/connect_count/xtask/src/main.rs @@ -0,0 +1,103 @@ +use std::{ + fs, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; + +use anyhow::{bail, Context, Result}; +use clap::Parser; + +#[derive(Parser)] +enum Args { + BuildEbpf, +} + +fn main() -> Result<()> { + match Args::parse() { + Args::BuildEbpf => build_ssh_session_monitor(), + } +} + +fn build_ssh_session_monitor() -> Result<()> { + let crate_name = "connect_count"; + let target = "bpfel-unknown-none"; + let deps_dir = Path::new("target").join(target).join("release").join("deps"); + + let output_bitcode: PathBuf; + let final_output = Path::new("../../bin/connect_count.o"); + + println!("[1/3] Building {crate_name} with `cargo rustc`..."); + + let status = Command::new("cargo") + .args([ + "+nightly", + "rustc", + "--release", + "--target", + target, + "-Z", + "build-std=core", + "-p", + crate_name, + "--", + "--emit=obj", + ]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to run cargo rustc")?; + + if !status.success() { + bail!("Build failed for {crate_name}"); + } + + println!("\nSearching for LLVM bitcode .o in {}", deps_dir.display()); + + let candidates: Vec<_> = fs::read_dir(&deps_dir)? + .filter_map(Result::ok) + .map(|entry| entry.path()) + .filter(|path| { + path.extension().map(|ext| ext == "o").unwrap_or(false) + && path.file_name() + .map(|name| name.to_string_lossy().contains("connect_count")) + .unwrap_or(false) + }) + .collect(); + + if candidates.is_empty() { + bail!( + "No matching .o files found for crate in {}", + deps_dir.display() + ); + } + + output_bitcode = candidates[0].clone(); + + println!("Found: {}", output_bitcode.display()); + + println!("\n[2/3] Compiling bitcode to BPF ELF with llc-20..."); + + let status = Command::new("llc-20") + .args([ + "-march=bpf", + "-filetype=obj", + "-o", + final_output.to_str().unwrap(), + output_bitcode.to_str().unwrap(), + ]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to execute llc-20")?; + + if !status.success() { + bail!("llc-20 failed to generate final ELF file."); + } + + println!( + "Done: Final ELF written to {}", + final_output.display() + ); + + Ok(()) +} diff --git a/ebpf-programs/ssh_fail_monitor/Cargo.toml b/ebpf-programs/ssh_fail_monitor/Cargo.toml index 38e4926..0dc9ff4 100644 --- a/ebpf-programs/ssh_fail_monitor/Cargo.toml +++ b/ebpf-programs/ssh_fail_monitor/Cargo.toml @@ -1,18 +1,6 @@ -[package] -name = "ssh_fail_monitor" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["staticlib"] -name = "ssh_fail_monitor" - -[dependencies] -aya-ebpf = { version = "0.1.1", default-features = false } - -[profile.release] -opt-level = "z" -lto = true -panic = "abort" -codegen-units = 1 -strip = "debuginfo" \ No newline at end of file +[workspace] +resolver = "2" +members = [ + "program", + "xtask" +] diff --git a/ebpf-programs/ssh_fail_monitor/program/Cargo.toml b/ebpf-programs/ssh_fail_monitor/program/Cargo.toml new file mode 100644 index 0000000..38e4926 --- /dev/null +++ b/ebpf-programs/ssh_fail_monitor/program/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ssh_fail_monitor" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] +name = "ssh_fail_monitor" + +[dependencies] +aya-ebpf = { version = "0.1.1", default-features = false } + +[profile.release] +opt-level = "z" +lto = true +panic = "abort" +codegen-units = 1 +strip = "debuginfo" \ No newline at end of file diff --git a/ebpf-programs/ssh_fail_monitor/rust-toolchain.toml b/ebpf-programs/ssh_fail_monitor/program/rust-toolchain.toml similarity index 100% rename from ebpf-programs/ssh_fail_monitor/rust-toolchain.toml rename to ebpf-programs/ssh_fail_monitor/program/rust-toolchain.toml diff --git a/ebpf-programs/ssh_fail_monitor/src/lib.rs b/ebpf-programs/ssh_fail_monitor/program/src/lib.rs similarity index 71% rename from ebpf-programs/ssh_fail_monitor/src/lib.rs rename to ebpf-programs/ssh_fail_monitor/program/src/lib.rs index 7887fbc..5c8d00d 100644 --- a/ebpf-programs/ssh_fail_monitor/src/lib.rs +++ b/ebpf-programs/ssh_fail_monitor/program/src/lib.rs @@ -1,20 +1,17 @@ #![no_std] #![no_main] +use aya_ebpf::bpf_printk; use aya_ebpf::{ + cty::c_int, + helpers::{ + bpf_get_current_comm, bpf_get_current_pid_tgid, bpf_get_current_uid_gid, bpf_ktime_get_ns, + }, macros::map, maps::{HashMap, RingBuf}, programs::ProbeContext, - helpers::{ - bpf_get_current_pid_tgid, - bpf_get_current_uid_gid, - bpf_get_current_comm, - bpf_ktime_get_ns, - }, EbpfContext, - cty::c_int, }; -use aya_ebpf::bpf_printk; #[repr(C)] #[derive(Clone, Copy)] @@ -31,8 +28,7 @@ pub struct SshFailEvent { static mut SSH_FAIL_RINGBUF: RingBuf = RingBuf::with_byte_size(4096, 0); #[map(name = "SSH_FAIL_COUNTS")] -static mut SSH_FAIL_COUNTS: HashMap = - HashMap::::with_max_entries(1024, 0); +static mut SSH_FAIL_COUNTS: HashMap = HashMap::::with_max_entries(1024, 0); #[no_mangle] #[link_section = "uretprobe/pam_authenticate"] @@ -43,11 +39,11 @@ pub fn ssh_fail_monitor(ctx: ProbeContext) -> u32 { fn try_monitor(ctx: ProbeContext) -> Result<(), ()> { // SAFELY extract return value from `ctx` - + let ret_val = unsafe { *(ctx.as_ptr() as *const c_int) }; - unsafe { - bpf_printk!(b"ssh_fail_monitor: ret_val seen\n"); - } + unsafe { + let _ = bpf_printk!(b"ssh_fail_monitor: ret_val seen\n"); +} if ret_val == 0 { return Ok(()); } @@ -55,7 +51,6 @@ fn try_monitor(ctx: ProbeContext) -> Result<(), ()> { let pid = (bpf_get_current_pid_tgid() >> 32) as u32; let uid = (bpf_get_current_uid_gid() >> 32) as u32; - unsafe { bpf_printk!(b"[ssh_fail] processing event\n"); } @@ -77,10 +72,14 @@ fn try_monitor(ctx: ProbeContext) -> Result<(), ()> { }; unsafe { - let count = SSH_FAIL_COUNTS.get(&pid).copied().unwrap_or(0); - SSH_FAIL_COUNTS.insert(&pid, &(count + 1), 0).ok(); - SSH_FAIL_RINGBUF.output(&event, 0); - bpf_printk!(b"[ssh_fail] event emitted\n"); + let map_ptr = core::ptr::addr_of_mut!(SSH_FAIL_COUNTS); + let ringbuf_ptr = core::ptr::addr_of_mut!(SSH_FAIL_RINGBUF); + + let count = (*map_ptr).get(&pid).copied().unwrap_or(0); + (*map_ptr).insert(&pid, &(count + 1), 0).ok(); + + let _ = (*ringbuf_ptr).output(&event, 0); + bpf_printk!(b"[ssh_fail] event emitted\n"); } Ok(()) diff --git a/ebpf-programs/ssh_fail_monitor/xtask/Cargo.toml b/ebpf-programs/ssh_fail_monitor/xtask/Cargo.toml new file mode 100644 index 0000000..952245c --- /dev/null +++ b/ebpf-programs/ssh_fail_monitor/xtask/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +clap = { version = "4.5", features = ["derive"] } diff --git a/ebpf-programs/ssh_fail_monitor/xtask/src/main.rs b/ebpf-programs/ssh_fail_monitor/xtask/src/main.rs new file mode 100644 index 0000000..a2e1fed --- /dev/null +++ b/ebpf-programs/ssh_fail_monitor/xtask/src/main.rs @@ -0,0 +1,103 @@ +use std::{ + fs, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; + +use anyhow::{bail, Context, Result}; +use clap::Parser; + +#[derive(Parser)] +enum Args { + BuildEbpf, +} + +fn main() -> Result<()> { + match Args::parse() { + Args::BuildEbpf => build_ssh_session_monitor(), + } +} + +fn build_ssh_session_monitor() -> Result<()> { + let crate_name = "ssh_fail_monitor"; + let target = "bpfel-unknown-none"; + let deps_dir = Path::new("target").join(target).join("release").join("deps"); + + let output_bitcode: PathBuf; + let final_output = Path::new("../../bin/ssh_fail_monitor.o"); + + println!("[1/3] Building {crate_name} with `cargo rustc`..."); + + let status = Command::new("cargo") + .args([ + "+nightly", + "rustc", + "--release", + "--target", + target, + "-Z", + "build-std=core", + "-p", + crate_name, + "--", + "--emit=obj", + ]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to run cargo rustc")?; + + if !status.success() { + bail!("Build failed for {crate_name}"); + } + + println!("\nSearching for LLVM bitcode .o in {}", deps_dir.display()); + + let candidates: Vec<_> = fs::read_dir(&deps_dir)? + .filter_map(Result::ok) + .map(|entry| entry.path()) + .filter(|path| { + path.extension().map(|ext| ext == "o").unwrap_or(false) + && path.file_name() + .map(|name| name.to_string_lossy().contains("ssh_fail_monitor")) + .unwrap_or(false) + }) + .collect(); + + if candidates.is_empty() { + bail!( + "No matching .o files found for crate in {}", + deps_dir.display() + ); + } + + output_bitcode = candidates[0].clone(); + + println!("Found: {}", output_bitcode.display()); + + println!("\n[2/3] Compiling bitcode to BPF ELF with llc-20..."); + + let status = Command::new("llc-20") + .args([ + "-march=bpf", + "-filetype=obj", + "-o", + final_output.to_str().unwrap(), + output_bitcode.to_str().unwrap(), + ]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to execute llc-20")?; + + if !status.success() { + bail!("llc-20 failed to generate final ELF file."); + } + + println!( + "Done: Final ELF written to {}", + final_output.display() + ); + + Ok(()) +} diff --git a/ebpf-programs/ssh_monitor/Cargo.toml b/ebpf-programs/ssh_monitor/Cargo.toml index ad43fd0..0dc9ff4 100644 --- a/ebpf-programs/ssh_monitor/Cargo.toml +++ b/ebpf-programs/ssh_monitor/Cargo.toml @@ -1,18 +1,6 @@ -[package] -name = "ssh_monitor" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["staticlib"] -name = "ssh_monitor" - -[dependencies] -aya-ebpf = { version = "0.1.1", default-features = false } - -[profile.release] -opt-level = "z" -lto = true -panic = "abort" -codegen-units = 1 -strip = "debuginfo" \ No newline at end of file +[workspace] +resolver = "2" +members = [ + "program", + "xtask" +] diff --git a/ebpf-programs/ssh_monitor/program/Cargo.toml b/ebpf-programs/ssh_monitor/program/Cargo.toml new file mode 100644 index 0000000..ad43fd0 --- /dev/null +++ b/ebpf-programs/ssh_monitor/program/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ssh_monitor" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] +name = "ssh_monitor" + +[dependencies] +aya-ebpf = { version = "0.1.1", default-features = false } + +[profile.release] +opt-level = "z" +lto = true +panic = "abort" +codegen-units = 1 +strip = "debuginfo" \ No newline at end of file diff --git a/ebpf-programs/ssh_monitor/rust-toolchain.toml b/ebpf-programs/ssh_monitor/program/rust-toolchain.toml similarity index 100% rename from ebpf-programs/ssh_monitor/rust-toolchain.toml rename to ebpf-programs/ssh_monitor/program/rust-toolchain.toml diff --git a/ebpf-programs/ssh_monitor/src/lib.rs b/ebpf-programs/ssh_monitor/program/src/lib.rs similarity index 64% rename from ebpf-programs/ssh_monitor/src/lib.rs rename to ebpf-programs/ssh_monitor/program/src/lib.rs index 686b1ce..037fd82 100644 --- a/ebpf-programs/ssh_monitor/src/lib.rs +++ b/ebpf-programs/ssh_monitor/program/src/lib.rs @@ -2,11 +2,13 @@ #![no_main] use aya_ebpf::{ + bpf_printk, + helpers::{ + bpf_get_current_comm, bpf_get_current_pid_tgid, bpf_get_current_uid_gid, + bpf_ktime_get_ns, + }, macros::map, maps::{HashMap, RingBuf}, - programs::ProbeContext, - helpers::{bpf_get_current_comm, bpf_get_current_pid_tgid, bpf_get_current_uid_gid, bpf_ktime_get_ns}, - bpf_printk, }; #[repr(C)] @@ -26,7 +28,7 @@ static mut SSH_FAIL_COUNTS: HashMap = HashMap::with_max_entries(1024, #[no_mangle] #[link_section = "kprobe/tty_write"] -pub fn ssh_fail_monitor(ctx: ProbeContext) -> u32 { +pub fn ssh_fail_monitor() -> u32 { let pid = (bpf_get_current_pid_tgid() >> 32) as u32; let uid = (bpf_get_current_uid_gid() >> 32) as u32; let ts = unsafe { bpf_ktime_get_ns() }; @@ -44,12 +46,21 @@ pub fn ssh_fail_monitor(ctx: ProbeContext) -> u32 { let event = SshFailEvent { pid, uid, ts, comm }; unsafe { - let count = SSH_FAIL_COUNTS.get(&uid).copied().unwrap_or(0); - SSH_FAIL_COUNTS.insert(&uid, &(count + 1), 0).ok(); - let _ = SSH_FAIL_RINGBUF.output(&event, 0); - bpf_printk!(b"[ssh_fail] UID=%d PID=%d\n", uid, pid); + let map_ptr = core::ptr::addr_of_mut!(SSH_FAIL_COUNTS); + let ringbuf_ptr = core::ptr::addr_of_mut!(SSH_FAIL_RINGBUF); + + match (*map_ptr).get(&uid).copied() { + Some(count) => { + let _ = (*map_ptr).insert(&uid, &(count + 1), 0); + } + None => { + let _ = (*map_ptr).insert(&uid, &1, 0); + } } + let _ = (*ringbuf_ptr).output(&event, 0); + let _ = bpf_printk!(b"[ssh_fail] UID=%d PID=%d\n", uid, pid); +} 0 } diff --git a/ebpf-programs/ssh_monitor/xtask/Cargo.toml b/ebpf-programs/ssh_monitor/xtask/Cargo.toml new file mode 100644 index 0000000..952245c --- /dev/null +++ b/ebpf-programs/ssh_monitor/xtask/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +clap = { version = "4.5", features = ["derive"] } diff --git a/ebpf-programs/ssh_monitor/xtask/src/main.rs b/ebpf-programs/ssh_monitor/xtask/src/main.rs new file mode 100644 index 0000000..c55e7e9 --- /dev/null +++ b/ebpf-programs/ssh_monitor/xtask/src/main.rs @@ -0,0 +1,103 @@ +use std::{ + fs, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; + +use anyhow::{bail, Context, Result}; +use clap::Parser; + +#[derive(Parser)] +enum Args { + BuildEbpf, +} + +fn main() -> Result<()> { + match Args::parse() { + Args::BuildEbpf => build_ssh_session_monitor(), + } +} + +fn build_ssh_session_monitor() -> Result<()> { + let crate_name = "ssh_monitor"; + let target = "bpfel-unknown-none"; + let deps_dir = Path::new("target").join(target).join("release").join("deps"); + + let output_bitcode: PathBuf; + let final_output = Path::new("../../bin/ssh_monitor.o"); + + println!("[1/3] Building {crate_name} with `cargo rustc`..."); + + let status = Command::new("cargo") + .args([ + "+nightly", + "rustc", + "--release", + "--target", + target, + "-Z", + "build-std=core", + "-p", + crate_name, + "--", + "--emit=obj", + ]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to run cargo rustc")?; + + if !status.success() { + bail!("Build failed for {crate_name}"); + } + + println!("\nSearching for LLVM bitcode .o in {}", deps_dir.display()); + + let candidates: Vec<_> = fs::read_dir(&deps_dir)? + .filter_map(Result::ok) + .map(|entry| entry.path()) + .filter(|path| { + path.extension().map(|ext| ext == "o").unwrap_or(false) + && path.file_name() + .map(|name| name.to_string_lossy().contains("ssh_monitor")) + .unwrap_or(false) + }) + .collect(); + + if candidates.is_empty() { + bail!( + "No matching .o files found for crate in {}", + deps_dir.display() + ); + } + + output_bitcode = candidates[0].clone(); + + println!("Found: {}", output_bitcode.display()); + + println!("\n[2/3] Compiling bitcode to BPF ELF with llc-20..."); + + let status = Command::new("llc-20") + .args([ + "-march=bpf", + "-filetype=obj", + "-o", + final_output.to_str().unwrap(), + output_bitcode.to_str().unwrap(), + ]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to execute llc-20")?; + + if !status.success() { + bail!("llc-20 failed to generate final ELF file."); + } + + println!( + "Done: Final ELF written to {}", + final_output.display() + ); + + Ok(()) +} diff --git a/ebpf-programs/ssh_session_monitor/Cargo.toml b/ebpf-programs/ssh_session_monitor/Cargo.toml index 4b791eb..82455ef 100644 --- a/ebpf-programs/ssh_session_monitor/Cargo.toml +++ b/ebpf-programs/ssh_session_monitor/Cargo.toml @@ -1,14 +1,6 @@ -[package] -name = "ssh_session_monitor" -version = "0.1.0" -edition = "2021" - -[dependencies] -aya-ebpf = { version = "0.1.1" } - -[profile.release] -opt-level = "z" -lto = false -panic = "abort" -strip = "debuginfo" -codegen-units = 1 +[workspace] +resolver = "2" +members = [ + "ssh-session-ebpf", + "xtask" +] diff --git a/ebpf-programs/ssh_session_monitor/rust-toolchain.toml b/ebpf-programs/ssh_session_monitor/rust-toolchain.toml deleted file mode 100644 index 78474db..0000000 --- a/ebpf-programs/ssh_session_monitor/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "nightly" -components = ["rust-src"] -targets = ["bpfel-unknown-none"] diff --git a/ebpf-programs/ssh_session_monitor/src/lib.rs b/ebpf-programs/ssh_session_monitor/src/lib.rs deleted file mode 100644 index 543a4eb..0000000 --- a/ebpf-programs/ssh_session_monitor/src/lib.rs +++ /dev/null @@ -1,115 +0,0 @@ -#![no_std] -#![no_main] - -use core::mem; -use aya_ebpf::{ - macros::{kprobe, map, tracepoint}, - programs::{ProbeContext, TracePointContext}, - helpers::{ - bpf_get_current_comm, - bpf_get_current_pid_tgid, - bpf_get_current_uid_gid, - bpf_ktime_get_ns, - bpf_ringbuf_output, - }, - maps::{HashMap, RingBuf}, - EbpfContext, - bpf_printk, -}; - -#[repr(C)] -#[derive(Clone, Copy)] -pub struct SshSessionEvent { - pub pid: u32, - pub uid: u32, - pub start_time_ns: u64, - pub duration_ns: u64, - pub comm: [u8; 16], -} - -#[map(name = "SSH_SESSION_STARTS")] -static SSH_SESSION_STARTS: HashMap = - HashMap::with_max_entries(1024, 0); - -#[map(name = "SSH_SESSION_RINGBUF")] -static SSH_SESSION_RINGBUF: RingBuf = - RingBuf::with_byte_size(4096, 0); - -#[kprobe] -pub fn track_shell_start(_ctx: ProbeContext) -> u32 { - let pid = (bpf_get_current_pid_tgid() >> 32) as u32; - let uid = (bpf_get_current_uid_gid() >> 32) as u32; - let ts = unsafe { bpf_ktime_get_ns() }; - - let comm = bpf_get_current_comm().unwrap_or([0u8; 16]); - - if !comm.starts_with(b"bash") && !comm.starts_with(b"sh") && !comm.starts_with(b"zsh") { - return 0; - } - - let mut comm_fixed = [0u8; 16]; - let len = core::cmp::min(comm.len(), 16); - comm_fixed[..len].copy_from_slice(&comm[..len]); - - let event = SshSessionEvent { - pid, - uid, - start_time_ns: ts, - duration_ns: 0, - comm: comm_fixed, - }; - - unsafe { - let _ = SSH_SESSION_STARTS.insert(&pid, &event, 0); - bpf_printk!(b"[ssh_session] Start PID=%d UID=%d\n", pid, uid); - } - - 0 -} - -#[tracepoint] -pub fn sched_process_exit(ctx: TracePointContext) -> i32 { - let pid = unsafe { - let ptr = ctx.as_ptr().add(16); - *(ptr as *const u32) - }; - - let now = unsafe { bpf_ktime_get_ns() }; - - unsafe { - if let Some(event) = SSH_SESSION_STARTS.get(&pid) { - let mut out = *event; - out.duration_ns = now - event.start_time_ns; - - let data_ptr = &out as *const _ as *mut core::ffi::c_void; - let size = mem::size_of::() as u64; - - bpf_ringbuf_output( - &SSH_SESSION_RINGBUF as *const _ as *mut core::ffi::c_void, - data_ptr, - size, - 0, - ); - - let _ = SSH_SESSION_STARTS.remove(&pid); - - bpf_printk!( - b"[ssh_session] End PID=%d UID=%d duration_ns=%llu\n", - pid, - out.uid, - out.duration_ns - ); - } - } - - 0 -} - -#[panic_handler] -fn panic(_: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[no_mangle] -#[link_section = "license"] -pub static LICENSE: [u8; 4] = *b"GPL\0"; \ No newline at end of file diff --git a/ebpf-programs/ssh_session_monitor/ssh-session-ebpf/Cargo.toml b/ebpf-programs/ssh_session_monitor/ssh-session-ebpf/Cargo.toml new file mode 100644 index 0000000..f64b8ac --- /dev/null +++ b/ebpf-programs/ssh_session_monitor/ssh-session-ebpf/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "ssh-session-ebpf" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +aya-ebpf = "0.1.1" +aya-ebpf-macros = "0.1.1" diff --git a/ebpf-programs/ssh_session_monitor/ssh-session-ebpf/src/lib.rs b/ebpf-programs/ssh_session_monitor/ssh-session-ebpf/src/lib.rs new file mode 100644 index 0000000..9a56e35 --- /dev/null +++ b/ebpf-programs/ssh_session_monitor/ssh-session-ebpf/src/lib.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +use aya_ebpf::{ + macros::kprobe, + programs::ProbeContext, + bpf_printk, +}; + +#[kprobe] +pub fn ssh_session_monitor(_ctx: ProbeContext) -> u32 { + unsafe { + bpf_printk!(b"SSH session monitor: probe hit!\0"); + } + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[no_mangle] +#[link_section = "license"] +pub static LICENSE: &[u8] = b"GPL\0"; diff --git a/ebpf-programs/ssh_session_monitor/xtask/Cargo.toml b/ebpf-programs/ssh_session_monitor/xtask/Cargo.toml new file mode 100644 index 0000000..952245c --- /dev/null +++ b/ebpf-programs/ssh_session_monitor/xtask/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +clap = { version = "4.5", features = ["derive"] } diff --git a/ebpf-programs/ssh_session_monitor/xtask/src/main.rs b/ebpf-programs/ssh_session_monitor/xtask/src/main.rs new file mode 100644 index 0000000..2668e07 --- /dev/null +++ b/ebpf-programs/ssh_session_monitor/xtask/src/main.rs @@ -0,0 +1,103 @@ +use std::{ + fs, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; + +use anyhow::{bail, Context, Result}; +use clap::Parser; + +#[derive(Parser)] +enum Args { + BuildEbpf, +} + +fn main() -> Result<()> { + match Args::parse() { + Args::BuildEbpf => build_ssh_session_monitor(), + } +} + +fn build_ssh_session_monitor() -> Result<()> { + let crate_name = "ssh-session-ebpf"; + let target = "bpfel-unknown-none"; + let deps_dir = Path::new("target").join(target).join("release").join("deps"); + + let output_bitcode: PathBuf; + let final_output = Path::new("../../bin/ssh_session_monitor.o"); + + println!("[1/3] Building {crate_name} with `cargo rustc`..."); + + let status = Command::new("cargo") + .args([ + "+nightly", + "rustc", + "--release", + "--target", + target, + "-Z", + "build-std=core", + "-p", + crate_name, + "--", + "--emit=obj", + ]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to run cargo rustc")?; + + if !status.success() { + bail!("Build failed for {crate_name}"); + } + + println!("\nSearching for LLVM bitcode .o in {}", deps_dir.display()); + + let candidates: Vec<_> = fs::read_dir(&deps_dir)? + .filter_map(Result::ok) + .map(|entry| entry.path()) + .filter(|path| { + path.extension().map(|ext| ext == "o").unwrap_or(false) + && path.file_name() + .map(|name| name.to_string_lossy().contains("ssh_session_ebpf")) + .unwrap_or(false) + }) + .collect(); + + if candidates.is_empty() { + bail!( + "No matching .o files found for crate in {}", + deps_dir.display() + ); + } + + output_bitcode = candidates[0].clone(); + + println!("Found: {}", output_bitcode.display()); + + println!("\n[2/3] Compiling bitcode to BPF ELF with llc-20..."); + + let status = Command::new("llc-20") + .args([ + "-march=bpf", + "-filetype=obj", + "-o", + final_output.to_str().unwrap(), + output_bitcode.to_str().unwrap(), + ]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to execute llc-20")?; + + if !status.success() { + bail!("llc-20 failed to generate final ELF file."); + } + + println!( + "Done: Final ELF written to {}", + final_output.display() + ); + + Ok(()) +} diff --git a/ebpf-programs/test_program/rust-toolchain.toml b/ebpf-programs/test_program/rust-toolchain.toml deleted file mode 100644 index 78474db..0000000 --- a/ebpf-programs/test_program/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "nightly" -components = ["rust-src"] -targets = ["bpfel-unknown-none"] diff --git a/ebpf-programs/test_program/src/lib.rs b/ebpf-programs/test_program/src/lib.rs deleted file mode 100644 index 269a99b..0000000 --- a/ebpf-programs/test_program/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![no_std] -#![no_main] - -use aya_ebpf::macros::map; -use aya_ebpf::maps::HashMap; - -#[no_mangle] -#[link_section = "license"] -pub static LICENSE: &[u8; 4] = b"GPL\0"; - -#[map(name = "test_map")] -static mut TEST_MAP: HashMap = HashMap::::with_max_entries(1, 0); diff --git a/ebpf-programs/udp_monitor/.cargo/config.toml b/ebpf-programs/udp_monitor/.cargo/config.toml deleted file mode 100644 index 82cb39c..0000000 --- a/ebpf-programs/udp_monitor/.cargo/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[build] -rustflags = [ - "-Zbuild-std=core,compiler_builtins", - "-Zbuild-std-features=compiler-builtins-mem" -] diff --git a/ebpf-programs/udp_monitor/Cargo.toml b/ebpf-programs/udp_monitor/Cargo.toml index 49a4076..0dc9ff4 100644 --- a/ebpf-programs/udp_monitor/Cargo.toml +++ b/ebpf-programs/udp_monitor/Cargo.toml @@ -1,18 +1,6 @@ -[package] -name = "udp_monitor" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["staticlib"] -name = "udp_monitor" - -[dependencies] -aya-ebpf = { version = "0.1.1", default-features = false } - -[profile.release] -opt-level = "z" -lto = true -panic = "abort" -codegen-units = 1 -strip = "debuginfo" \ No newline at end of file +[workspace] +resolver = "2" +members = [ + "program", + "xtask" +] diff --git a/ebpf-programs/udp_monitor/program/Cargo.toml b/ebpf-programs/udp_monitor/program/Cargo.toml new file mode 100644 index 0000000..49a4076 --- /dev/null +++ b/ebpf-programs/udp_monitor/program/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "udp_monitor" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] +name = "udp_monitor" + +[dependencies] +aya-ebpf = { version = "0.1.1", default-features = false } + +[profile.release] +opt-level = "z" +lto = true +panic = "abort" +codegen-units = 1 +strip = "debuginfo" \ No newline at end of file diff --git a/ebpf-programs/udp_monitor/rust-toolchain.toml b/ebpf-programs/udp_monitor/program/rust-toolchain.toml similarity index 100% rename from ebpf-programs/udp_monitor/rust-toolchain.toml rename to ebpf-programs/udp_monitor/program/rust-toolchain.toml diff --git a/ebpf-programs/udp_monitor/src/lib.rs b/ebpf-programs/udp_monitor/program/src/lib.rs similarity index 100% rename from ebpf-programs/udp_monitor/src/lib.rs rename to ebpf-programs/udp_monitor/program/src/lib.rs diff --git a/ebpf-programs/udp_monitor/xtask/Cargo.toml b/ebpf-programs/udp_monitor/xtask/Cargo.toml new file mode 100644 index 0000000..952245c --- /dev/null +++ b/ebpf-programs/udp_monitor/xtask/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +clap = { version = "4.5", features = ["derive"] } diff --git a/ebpf-programs/udp_monitor/xtask/src/main.rs b/ebpf-programs/udp_monitor/xtask/src/main.rs new file mode 100644 index 0000000..afe816a --- /dev/null +++ b/ebpf-programs/udp_monitor/xtask/src/main.rs @@ -0,0 +1,103 @@ +use std::{ + fs, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; + +use anyhow::{bail, Context, Result}; +use clap::Parser; + +#[derive(Parser)] +enum Args { + BuildEbpf, +} + +fn main() -> Result<()> { + match Args::parse() { + Args::BuildEbpf => build_ssh_session_monitor(), + } +} + +fn build_ssh_session_monitor() -> Result<()> { + let crate_name = "udp_monitor"; + let target = "bpfel-unknown-none"; + let deps_dir = Path::new("target").join(target).join("release").join("deps"); + + let output_bitcode: PathBuf; + let final_output = Path::new("../../bin/udp_monitor.o"); + + println!("[1/3] Building {crate_name} with `cargo rustc`..."); + + let status = Command::new("cargo") + .args([ + "+nightly", + "rustc", + "--release", + "--target", + target, + "-Z", + "build-std=core", + "-p", + crate_name, + "--", + "--emit=obj", + ]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to run cargo rustc")?; + + if !status.success() { + bail!("Build failed for {crate_name}"); + } + + println!("\nSearching for LLVM bitcode .o in {}", deps_dir.display()); + + let candidates: Vec<_> = fs::read_dir(&deps_dir)? + .filter_map(Result::ok) + .map(|entry| entry.path()) + .filter(|path| { + path.extension().map(|ext| ext == "o").unwrap_or(false) + && path.file_name() + .map(|name| name.to_string_lossy().contains("udp_monitor")) + .unwrap_or(false) + }) + .collect(); + + if candidates.is_empty() { + bail!( + "No matching .o files found for crate in {}", + deps_dir.display() + ); + } + + output_bitcode = candidates[0].clone(); + + println!("Found: {}", output_bitcode.display()); + + println!("\n[2/3] Compiling bitcode to BPF ELF with llc-20..."); + + let status = Command::new("llc-20") + .args([ + "-march=bpf", + "-filetype=obj", + "-o", + final_output.to_str().unwrap(), + output_bitcode.to_str().unwrap(), + ]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to execute llc-20")?; + + if !status.success() { + bail!("llc-20 failed to generate final ELF file."); + } + + println!( + "Done: Final ELF written to {}", + final_output.display() + ); + + Ok(()) +} diff --git a/scripts/build_ebpf.sh b/scripts/build_ebpf.sh deleted file mode 100755 index c7514eb..0000000 --- a/scripts/build_ebpf.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash -set -e - -# ------------------------------------- -# CONFIGURATION -# ------------------------------------- -BASE_DIR="ebpf-programs" -OUT_DIR="bin" -TARGET="bpfel-unknown-none" -RELEASE_PATH="target/${TARGET}/release" -DEPS_PATH="$RELEASE_PATH/deps" - -# ------------------------------------- -# TOOL CHECKS -# ------------------------------------- -command -v llc-20 >/dev/null 2>&1 || { - echo "Error: 'llc-20' not found. Install via LLVM 12–20." >&2 - exit 1 -} - -command -v cargo >/dev/null 2>&1 || { - echo "Error: 'cargo' not found in PATH." >&2 - exit 1 -} - -mkdir -p "$OUT_DIR" - -# ------------------------------------- -# MAIN LOOP -# ------------------------------------- -for PROBE_DIR in "$BASE_DIR"/*; do - [[ -d "$PROBE_DIR" && -f "$PROBE_DIR/Cargo.toml" ]] || continue - - echo "----------------------------------------" - - # Auto-detect probe (crate) name from Cargo.toml - PROBE_NAME=$(grep '^name' "$PROBE_DIR/Cargo.toml" | head -n1 | cut -d'"' -f2) - - if [[ -z "$PROBE_NAME" ]]; then - echo "Error: Could not detect crate name in $PROBE_DIR/Cargo.toml" >&2 - exit 1 - fi - - echo "Building eBPF probe: $PROBE_NAME" - echo "Source path : $PROBE_DIR" - echo "Target output : $OUT_DIR/$PROBE_NAME.o" - echo - - # Step 1: Build - echo "[1/3] Building with Cargo..." - cargo +nightly rustc --release \ - --manifest-path "$PROBE_DIR/Cargo.toml" \ - --target "$TARGET" -Z build-std=core \ - -- --emit=obj - - # Step 2: Locate compiled object - echo "[2/3] Locating compiled bitcode object..." - OBJ_PATH=$(find "$PROBE_DIR/$DEPS_PATH" -maxdepth 1 -name "${PROBE_NAME}_ebpf-*.o" -o -name "${PROBE_NAME}-*.o" | head -n1) - - if [[ -z "$OBJ_PATH" ]]; then - OBJ_PATH=$(find "$PROBE_DIR/$RELEASE_PATH" -maxdepth 1 -name "${PROBE_NAME}-*.o" | head -n1) - fi - - if [[ -z "$OBJ_PATH" ]]; then - echo "Error: Compiled object (.o) not found for $PROBE_NAME." >&2 - echo "Searched in:" >&2 - echo " $PROBE_DIR/$DEPS_PATH/" >&2 - echo " $PROBE_DIR/$RELEASE_PATH/" >&2 - exit 1 - fi - - # Step 3: Convert to final ELF format - echo "[3/3] Converting to ELF with llc-20..." - llc-20 -march=bpf -filetype=obj -o "$OUT_DIR/$PROBE_NAME.o" "$OBJ_PATH" - - echo "Success: $OUT_DIR/$PROBE_NAME.o created." - echo -done - -echo "All eBPF probes compiled and placed in '$OUT_DIR/'" diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..ddd688b --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +clap = { version = "4.4", features = ["derive"] } +glob = "0.3.1" \ No newline at end of file diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..8e542e4 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,31 @@ +use xshell::{cmd, Shell}; +use anyhow::Result; + +fn main() -> Result<()> { + let sh = Shell::new()?; + + println!("🔨 Building ssh_session_monitor..."); + + cmd!( + sh, + "cargo +nightly rustc -p ssh_session_monitor --release --target bpfel-unknown-none -Z build-std=core -- --emit=llvm-bc" + ) + .run()?; + + let bc_path = sh + .read_dir("ebpf-programs/ssh_session_monitor/target/bpfel-unknown-none/release/deps")? + .into_iter() + .find(|p| p.extension().map_or(false, |ext| ext == "bc")) + .expect("LLVM bitcode (.bc) file not found"); + + sh.create_dir("bin")?; + + let output = "bin/ssh_session_monitor.o"; + + println!("📦 Converting .bc -> .o: {output}"); + + cmd!(sh, "llc-20 -march=bpf -filetype=obj -o {output} {bc_path}").run()?; + + println!("✅ Done."); + Ok(()) +}