From fe339f0c59008798523a43c7fb4d8b63147ebe65 Mon Sep 17 00:00:00 2001 From: AzulEterno <75287037+AzulEterno@users.noreply.github.com> Date: Sun, 17 May 2026 11:34:20 +0800 Subject: [PATCH 1/2] Added android basic support. --- .gitignore | 7 ++ Cargo.toml | 10 +- scripts/cargo-android.sh | 112 ++++++++++++++++++ src/app.rs | 20 ++-- src/cli.rs | 4 +- src/main.rs | 12 ++ src/network/capture.rs | 4 +- src/network/platform/linux/interface_stats.rs | 2 +- src/network/platform/mod.rs | 50 ++++---- src/network/privileges.rs | 10 +- src/ui.rs | 2 +- 11 files changed, 190 insertions(+), 43 deletions(-) create mode 100755 scripts/cargo-android.sh diff --git a/.gitignore b/.gitignore index c93f8780..4a7dcfd6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ debug/ target/ +pcap.pc + +.cargo/ + # These are backup files generated by rustfmt **/*.rs.bk @@ -22,3 +26,6 @@ target/ .aider* /logs .venv + +#Artifact +libpcap.pc \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index a2727c09..69985393 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ test = false [dependencies] anyhow = "1.0" libc = "0.2" -arboard = { version = "3.6", features = ["wayland-data-control"] } +# arboard (clipboard) not available on Android - added in target-specific section below crossterm = "0.29" crossbeam = "0.8" dashmap = "6.1" @@ -51,12 +51,20 @@ flate2 = "1" maxminddb = "0.28" regex-lite = "0.1" +# Clipboard support for non-Android platforms +[target.'cfg(not(target_os = "android"))'.dependencies] +arboard = { version = "3.6", features = ["wayland-data-control"] } + [target.'cfg(target_os = "linux")'.dependencies] procfs = "0.18" libbpf-rs = { version = "0.26", optional = true } landlock = { version = "0.4", optional = true } caps = { version = "0.5", optional = true } +# Android uses Linux platform module but without eBPF/landlock +[target.'cfg(target_os = "android")'.dependencies] +procfs = "0.18" + [target.'cfg(windows)'.dependencies] windows = { version = "0.62", features = [ "Win32_Foundation", diff --git a/scripts/cargo-android.sh b/scripts/cargo-android.sh new file mode 100755 index 00000000..30891eff --- /dev/null +++ b/scripts/cargo-android.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +NDK_ROOT=${RUSTNET_ANDROID_NDK:-${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT:-}}} +ANDROID_TARGET=${RUSTNET_ANDROID_TARGET:-aarch64-linux-android} +ANDROID_API=${RUSTNET_ANDROID_API:-} +LIBPCAP_ROOT=${RUSTNET_ANDROID_LIBPCAP_ROOT:-${ROOT_DIR}/../libpcap-install} +LIBPCAP_VER=${RUSTNET_ANDROID_LIBPCAP_VER:-1.10.5} + +if [[ -z "${NDK_ROOT}" ]]; then + cat >&2 <<'EOF' +Android NDK path is required. + +Set one of: + RUSTNET_ANDROID_NDK + ANDROID_NDK_HOME + ANDROID_NDK_ROOT +EOF + exit 1 +fi + +find_prebuilt_dir() { + local prebuilt_root="${NDK_ROOT}/toolchains/llvm/prebuilt" + local host_tag=${RUSTNET_ANDROID_HOST_TAG:-} + + if [[ -n "${host_tag}" ]]; then + echo "${prebuilt_root}/${host_tag}" + return + fi + + case "$(uname -s)-$(uname -m)" in + Linux-x86_64) host_tag="linux-x86_64" ;; + Darwin-arm64) host_tag="darwin-x86_64" ;; + Darwin-x86_64) host_tag="darwin-x86_64" ;; + *) + host_tag=$(find "${prebuilt_root}" -mindepth 1 -maxdepth 1 -type d | head -n 1) + echo "${host_tag}" + return + ;; + esac + + echo "${prebuilt_root}/${host_tag}" +} + +default_api_for_target() { + case "$1" in + aarch64-linux-android|x86_64-linux-android) echo 21 ;; + armv7-linux-androideabi|i686-linux-android) echo 16 ;; + *) echo 21 ;; + esac +} + +PREBUILT_DIR=$(find_prebuilt_dir) +TOOLCHAIN_DIR="${PREBUILT_DIR}/bin" + +if [[ ! -d "${TOOLCHAIN_DIR}" ]]; then + echo "Android NDK toolchain directory not found: ${TOOLCHAIN_DIR}" >&2 + exit 1 +fi + +if [[ -z "${ANDROID_API}" ]]; then + ANDROID_API=$(default_api_for_target "${ANDROID_TARGET}") +fi + +CLANG="${TOOLCHAIN_DIR}/${ANDROID_TARGET}${ANDROID_API}-clang" +AR="${TOOLCHAIN_DIR}/llvm-ar" + +if [[ ! -x "${CLANG}" ]]; then + echo "Android clang not found: ${CLANG}" >&2 + echo "Available ${ANDROID_TARGET} toolchains:" >&2 + find "${TOOLCHAIN_DIR}" -maxdepth 1 -type f -name "${ANDROID_TARGET}*-clang" -printf ' %f\n' | sort >&2 || true + exit 1 +fi + +if [[ ! -x "${AR}" ]]; then + echo "Android llvm-ar not found: ${AR}" >&2 + exit 1 +fi + +if [[ ! -d "${LIBPCAP_ROOT}" ]]; then + echo "Android libpcap root not found: ${LIBPCAP_ROOT}" >&2 + exit 1 +fi + +if [[ ! -d "${LIBPCAP_ROOT}/lib" ]]; then + echo "Android libpcap library directory not found: ${LIBPCAP_ROOT}/lib" >&2 + exit 1 +fi + +if [[ ! -d "${LIBPCAP_ROOT}/include" ]]; then + echo "Android libpcap include directory not found: ${LIBPCAP_ROOT}/include" >&2 + exit 1 +fi + +target_env=${ANDROID_TARGET//-/_} +export "CC_${target_env}=${CLANG}" +export "AR_${target_env}=${AR}" +export "CARGO_TARGET_${target_env^^}_LINKER=${CLANG}" +export "CARGO_TARGET_${target_env^^}_AR=${AR}" +export LIBPCAP_LIBDIR="${LIBPCAP_ROOT}/lib" +export LIBPCAP_VER="${LIBPCAP_VER}" + +cat >&2 <( terminal: &mut ui::Terminal, app: &app::App, diff --git a/src/network/capture.rs b/src/network/capture.rs index fbc1d5d5..2d7e7522 100644 --- a/src/network/capture.rs +++ b/src/network/capture.rs @@ -326,7 +326,7 @@ fn find_capture_device(interface_name: &Option) -> Result { // Special handling for 'any' interface if name == "any" { - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", target_os = "android")))] { return Err(anyhow!( "The 'any' interface is only supported on Linux.\n\ @@ -335,7 +335,7 @@ fn find_capture_device(interface_name: &Option) -> Result { )); } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { log::info!("Using 'any' pseudo-interface to capture on all interfaces"); } diff --git a/src/network/platform/linux/interface_stats.rs b/src/network/platform/linux/interface_stats.rs index c4aa410c..2bb99b64 100644 --- a/src/network/platform/linux/interface_stats.rs +++ b/src/network/platform/linux/interface_stats.rs @@ -67,7 +67,7 @@ fn read_stat(base_path: &str, stat_name: &str) -> Result { } #[cfg(test)] -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] mod tests { use super::*; diff --git a/src/network/platform/mod.rs b/src/network/platform/mod.rs index 16115710..00890c0e 100644 --- a/src/network/platform/mod.rs +++ b/src/network/platform/mod.rs @@ -19,38 +19,38 @@ pub enum DegradationReason { None, // Linux eBPF reasons /// Missing CAP_BPF capability (Linux 5.8+) - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] MissingCapBpf, /// Missing CAP_PERFMON capability (Linux 5.8+) - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] MissingCapPerfmon, /// Missing both CAP_BPF and CAP_PERFMON (and no CAP_SYS_ADMIN fallback) - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] MissingBpfCapabilities, /// eBPF feature not compiled in - #[cfg(all(target_os = "linux", not(feature = "ebpf")))] + #[cfg(all(any(target_os = "linux", target_os = "android"), not(feature = "ebpf")))] EbpfFeatureDisabled, /// Kernel doesn't support required eBPF features (e.g. ENOSYS from bpf(2)) - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] KernelUnsupported, /// BPF syscall denied despite caps - typically AppArmor, kernel lockdown, /// or unprivileged_bpf_disabled interactions - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] BpfPermissionDenied, /// Failed to attach a kprobe (e.g. symbol missing from kernel). The /// String carries the symbol name where known. - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] KprobeAttachFailed(String), /// Kernel BTF unavailable / CO-RE relocation failed (no /sys/kernel/btf/vmlinux) - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] BtfUnavailable, /// Generic eBPF load failure carrying the truncated libbpf error text - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] EbpfLoadFailed(String), /// Binary lives on a filesystem mounted with `nosuid`, which makes the /// kernel silently ignore file capabilities set via `setcap`. Common when /// the binary is under `/home`, `/tmp`, or a removable mount. - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] BinaryOnNosuidMount, // macOS PKTAP reasons /// No root privileges for PKTAP @@ -72,21 +72,21 @@ impl DegradationReason { pub fn description(&self) -> Cow<'_, str> { match self { Self::None => Cow::Borrowed(""), - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] Self::MissingCapBpf => Cow::Borrowed("needs CAP_BPF"), - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] Self::MissingCapPerfmon => Cow::Borrowed("needs CAP_PERFMON"), - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] Self::MissingBpfCapabilities => Cow::Borrowed("needs CAP_BPF+CAP_PERFMON"), - #[cfg(all(target_os = "linux", not(feature = "ebpf")))] + #[cfg(all(any(target_os = "linux", target_os = "android"), not(feature = "ebpf")))] Self::EbpfFeatureDisabled => Cow::Borrowed("eBPF feature disabled"), - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] Self::KernelUnsupported => Cow::Borrowed("kernel unsupported"), - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] Self::BpfPermissionDenied => Cow::Borrowed( "BPF denied (check perf_event_paranoid / AppArmor / unprivileged_bpf_disabled)", ), - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] Self::KprobeAttachFailed(sym) => { if sym.is_empty() { Cow::Borrowed("kprobe attach failed") @@ -94,11 +94,11 @@ impl DegradationReason { Cow::Owned(format!("kprobe attach failed: {sym}")) } } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] Self::BtfUnavailable => Cow::Borrowed("kernel BTF unavailable"), - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] Self::EbpfLoadFailed(s) => Cow::Owned(format!("eBPF load failed: {s}")), - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] Self::BinaryOnNosuidMount => { Cow::Borrowed("file caps ignored: binary on a nosuid mount") } @@ -117,7 +117,7 @@ impl DegradationReason { pub fn unavailable_feature(&self) -> Option<&str> { match self { Self::None => None, - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] Self::MissingCapBpf | Self::MissingCapPerfmon | Self::MissingBpfCapabilities @@ -127,7 +127,7 @@ impl DegradationReason { | Self::BtfUnavailable | Self::EbpfLoadFailed(_) | Self::BinaryOnNosuidMount => Some("eBPF"), - #[cfg(all(target_os = "linux", not(feature = "ebpf")))] + #[cfg(all(any(target_os = "linux", target_os = "android"), not(feature = "ebpf")))] Self::EbpfFeatureDisabled => Some("eBPF"), #[cfg(target_os = "macos")] Self::MissingRootPrivileges @@ -141,7 +141,7 @@ impl DegradationReason { // Platform-specific modules (one cfg per platform instead of many) #[cfg(target_os = "freebsd")] mod freebsd; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] mod linux; #[cfg(target_os = "macos")] mod macos; @@ -151,9 +151,9 @@ mod windows; // Re-export factory functions and types from platform modules #[cfg(target_os = "freebsd")] pub use freebsd::{FreeBSDStatsProvider, create_process_lookup}; -#[cfg(all(target_os = "linux", feature = "landlock"))] +#[cfg(all(any(target_os = "linux", target_os = "android"), feature = "landlock"))] pub use linux::sandbox; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] pub use linux::{LinuxStatsProvider, create_process_lookup}; #[cfg(all(target_os = "macos", feature = "macos-sandbox"))] pub use macos::sandbox; diff --git a/src/network/privileges.rs b/src/network/privileges.rs index 9f94579b..4a040a10 100644 --- a/src/network/privileges.rs +++ b/src/network/privileges.rs @@ -4,7 +4,7 @@ //! network packets on different platforms (Linux, macOS, Windows). use anyhow::Result; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use anyhow::anyhow; #[cfg(any( not(any( @@ -42,6 +42,7 @@ impl PrivilegeStatus { /// Create a status indicating insufficient privileges #[cfg(any( target_os = "linux", + target_os = "android", target_os = "macos", target_os = "windows", target_os = "freebsd", @@ -84,7 +85,7 @@ impl PrivilegeStatus { /// Check if the current process has sufficient privileges for packet capture pub fn check_packet_capture_privileges() -> Result { - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { check_linux_privileges() } @@ -106,6 +107,7 @@ pub fn check_packet_capture_privileges() -> Result { #[cfg(not(any( target_os = "linux", + target_os = "android", target_os = "macos", target_os = "windows", target_os = "freebsd" @@ -117,7 +119,7 @@ pub fn check_packet_capture_privileges() -> Result { } } -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn check_linux_privileges() -> Result { use std::fs; @@ -181,7 +183,7 @@ fn check_linux_privileges() -> Result { } /// Detect if running inside a container -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] fn is_running_in_container() -> bool { use std::fs; diff --git a/src/ui.rs b/src/ui.rs index 5f1845a9..f9778e24 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1974,7 +1974,7 @@ fn draw_stats_panel( // Build the security/sandbox text up front so the chunk height can match // its content. Otherwise long feature lists get clipped on narrow columns. - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] let security_text: Vec = { let sandbox_info = app.get_sandbox_info(); let status_style = match sandbox_info.status.as_str() { From 6e01c848b05d3aabe432cb38ac54d6965254b05d Mon Sep 17 00:00:00 2001 From: AzulEterno <75287037+AzulEterno@users.noreply.github.com> Date: Sun, 17 May 2026 15:40:04 +0800 Subject: [PATCH 2/2] Enabled Android experimental ebpf support. --- .cargo/android-ebpf-config.toml | 2 + .gitignore | 8 +++- Cargo.toml | 6 +++ build.rs | 16 ++++++-- scripts/cargo-android.sh | 51 ++++++++++++++++++++++++-- src/app.rs | 4 +- src/network/platform/linux/enhanced.rs | 25 ++++++++++--- src/network/platform/linux/mod.rs | 17 +++++++-- src/network/platform/mod.rs | 17 +++++++-- 9 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 .cargo/android-ebpf-config.toml diff --git a/.cargo/android-ebpf-config.toml b/.cargo/android-ebpf-config.toml new file mode 100644 index 00000000..6a88d32b --- /dev/null +++ b/.cargo/android-ebpf-config.toml @@ -0,0 +1,2 @@ +[patch.crates-io] +libbpf-sys = { path = "third_party/libbpf-sys-android" } diff --git a/.gitignore b/.gitignore index 4a7dcfd6..b4f5f35e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ target/ pcap.pc .cargo/ +!.cargo/ +!.cargo/android-ebpf-config.toml # These are backup files generated by rustfmt **/*.rs.bk @@ -28,4 +30,8 @@ pcap.pc .venv #Artifact -libpcap.pc \ No newline at end of file +libpcap.pc + +third_party/ + +.cargo/config.toml \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 69985393..89f24fcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ caps = { version = "0.5", optional = true } # Android uses Linux platform module but without eBPF/landlock [target.'cfg(target_os = "android")'.dependencies] procfs = "0.18" +libbpf-rs = { version = "0.26", optional = true, features = ["vendored"] } [target.'cfg(windows)'.dependencies] windows = { version = "0.62", features = [ @@ -104,6 +105,9 @@ windows = { version = "0.62", features = [ [target.'cfg(target_os = "linux")'.build-dependencies] libbpf-cargo = { version = "0.26", optional = true } +[target.'cfg(target_os = "android")'.build-dependencies] +libbpf-cargo = { version = "0.26", optional = true } + [features] # eBPF is enabled by default for enhanced performance on Linux. # On non-Linux platforms, this feature has no effect as all eBPF code @@ -113,6 +117,7 @@ libbpf-cargo = { version = "0.26", optional = true } default = ["ebpf", "landlock", "macos-sandbox"] linux-default = ["ebpf"] # Deprecated: kept for backwards compatibility ebpf = ["libbpf-rs", "dep:libbpf-cargo"] +android-ebpf = ["libbpf-rs", "dep:libbpf-cargo"] landlock = ["dep:landlock", "dep:caps"] macos-sandbox = [] @@ -210,6 +215,7 @@ harness = false name = "rate_tracker" harness = false + [profile.release] lto = "thin" codegen-units = 1 diff --git a/build.rs b/build.rs index 36eef452..947a4dc7 100644 --- a/build.rs +++ b/build.rs @@ -11,7 +11,15 @@ fn main() -> Result<()> { // Compile eBPF programs on Linux when the feature is enabled // Check TARGET environment variable, not cfg!, to handle cross-compilation correctly let target = env::var("TARGET").unwrap_or_default(); - if target.contains("linux") && env::var("CARGO_FEATURE_EBPF").is_ok() { + let build_ebpf = if target.contains("android") { + env::var("CARGO_FEATURE_ANDROID_EBPF").is_ok() + } else if target.contains("linux") { + env::var("CARGO_FEATURE_EBPF").is_ok() + } else { + false + }; + + if build_ebpf { compile_ebpf_programs(); } @@ -186,7 +194,7 @@ fn download_windows_npcap_sdk() -> Result<()> { Ok(()) } -#[cfg(all(target_os = "linux", feature = "ebpf"))] +#[cfg(any(feature = "ebpf", feature = "android-ebpf"))] fn get_vmlinux_header(arch: &str) -> Result { // Use bundled vmlinux.h from resources directory let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); @@ -205,7 +213,7 @@ fn get_vmlinux_header(arch: &str) -> Result { } } -#[cfg(all(target_os = "linux", feature = "ebpf"))] +#[cfg(any(feature = "ebpf", feature = "android-ebpf"))] fn compile_ebpf_programs() { use libbpf_cargo::SkeletonBuilder; use std::ffi::OsStr; @@ -247,7 +255,7 @@ fn compile_ebpf_programs() { println!("cargo:rerun-if-changed={}", src); } -#[cfg(not(all(target_os = "linux", feature = "ebpf")))] +#[cfg(not(any(feature = "ebpf", feature = "android-ebpf")))] fn compile_ebpf_programs() { // No-op when not on Linux or eBPF feature is not enabled } diff --git a/scripts/cargo-android.sh b/scripts/cargo-android.sh index 30891eff..87533f9c 100755 --- a/scripts/cargo-android.sh +++ b/scripts/cargo-android.sh @@ -7,6 +7,8 @@ ANDROID_TARGET=${RUSTNET_ANDROID_TARGET:-aarch64-linux-android} ANDROID_API=${RUSTNET_ANDROID_API:-} LIBPCAP_ROOT=${RUSTNET_ANDROID_LIBPCAP_ROOT:-${ROOT_DIR}/../libpcap-install} LIBPCAP_VER=${RUSTNET_ANDROID_LIBPCAP_VER:-1.10.5} +ENABLE_ANDROID_EBPF=${RUSTNET_ANDROID_ENABLE_EBPF:-1} +ANDROID_EBPF_CONFIG="${ROOT_DIR}/.cargo/android-ebpf-config.toml" if [[ -z "${NDK_ROOT}" ]]; then cat >&2 <<'EOF' @@ -45,9 +47,8 @@ find_prebuilt_dir() { default_api_for_target() { case "$1" in - aarch64-linux-android|x86_64-linux-android) echo 21 ;; - armv7-linux-androideabi|i686-linux-android) echo 16 ;; - *) echo 21 ;; + aarch64-linux-android|x86_64-linux-android|armv7-linux-androideabi|i686-linux-android) echo 24 ;; + *) echo 24 ;; esac } @@ -63,6 +64,11 @@ if [[ -z "${ANDROID_API}" ]]; then ANDROID_API=$(default_api_for_target "${ANDROID_TARGET}") fi +if (( ANDROID_API < 24 )); then + echo "Android API ${ANDROID_API} is too low for current libpcap/pnet support; using 24 instead." >&2 + ANDROID_API=24 +fi + CLANG="${TOOLCHAIN_DIR}/${ANDROID_TARGET}${ANDROID_API}-clang" AR="${TOOLCHAIN_DIR}/llvm-ar" @@ -107,6 +113,43 @@ Android API: ${ANDROID_API} NDK prebuilt: ${PREBUILT_DIR} Clang: ${CLANG} libpcap: ${LIBPCAP_ROOT} +android-ebpf: ${ENABLE_ANDROID_EBPF} EOF -exec cargo "$@" +args=("$@") + +has_no_default_features=0 +has_features=0 +for arg in "${args[@]}"; do + case "${arg}" in + --no-default-features) has_no_default_features=1 ;; + --features|--all-features) has_features=1 ;; + --features=*) has_features=1 ;; + esac +done + +if [[ ${has_no_default_features} -eq 0 ]]; then + args+=(--no-default-features) +fi + +if [[ ${ENABLE_ANDROID_EBPF} == 1 && ${has_features} -eq 0 ]]; then + args+=(--features android-ebpf) +fi + +restore_lockfile() { + if [[ -n "${lock_backup_path:-}" && -f "${lock_backup_path}" ]]; then + mv "${lock_backup_path}" "${ROOT_DIR}/Cargo.lock" + fi +} + +if [[ ${ENABLE_ANDROID_EBPF} == 1 ]]; then + args+=(--config "${ANDROID_EBPF_CONFIG}") + + if [[ -f "${ROOT_DIR}/Cargo.lock" ]]; then + lock_backup_path=$(mktemp "${ROOT_DIR}/Cargo.lock.android-ebpf.XXXXXX") + cp "${ROOT_DIR}/Cargo.lock" "${lock_backup_path}" + trap restore_lockfile EXIT + fi +fi + +cargo "${args[@]}" diff --git a/src/app.rs b/src/app.rs index d9dbe71b..32ef049b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -698,7 +698,7 @@ impl App { *linktype_storage.write().unwrap() = Some(linktype); // Drop CAP_NET_RAW now that the socket is open (Linux only) - #[cfg(all(any(target_os = "linux", target_os = "android"), feature = "landlock"))] + #[cfg(all(target_os = "linux", feature = "landlock"))] { if let Err(e) = crate::network::platform::sandbox::capabilities::drop_cap_net_raw() @@ -924,7 +924,7 @@ impl App { info!("Packet processor {} started", id); // Drop CAP_NET_RAW immediately as this thread doesn't need it (Linux only) - #[cfg(all(any(target_os = "linux", target_os = "android"), feature = "landlock"))] + #[cfg(all(target_os = "linux", feature = "landlock"))] { if let Err(e) = crate::network::platform::sandbox::capabilities::drop_cap_net_raw() diff --git a/src/network/platform/linux/enhanced.rs b/src/network/platform/linux/enhanced.rs index ce7e2612..5d592b45 100644 --- a/src/network/platform/linux/enhanced.rs +++ b/src/network/platform/linux/enhanced.rs @@ -11,11 +11,17 @@ use std::net::IpAddr; use std::sync::RwLock; use std::time::{Duration, Instant}; -#[cfg(feature = "ebpf")] +#[cfg(any( + all(target_os = "linux", feature = "ebpf"), + all(target_os = "android", feature = "android-ebpf") +))] use super::ebpf::EbpfSocketTracker; // When eBPF is enabled, use the full enhanced implementation -#[cfg(feature = "ebpf")] +#[cfg(any( + all(target_os = "linux", feature = "ebpf"), + all(target_os = "android", feature = "android-ebpf") +))] mod ebpf_enhanced { use super::*; use crate::network::types::ProtocolState; @@ -484,7 +490,10 @@ mod ebpf_enhanced { } // When eBPF is disabled, use a simpler procfs-only implementation -#[cfg(not(feature = "ebpf"))] +#[cfg(not(any( + all(target_os = "linux", feature = "ebpf"), + all(target_os = "android", feature = "android-ebpf") +)))] mod procfs_only { use super::*; @@ -713,8 +722,14 @@ mod procfs_only { } // Re-export the appropriate implementation based on feature flag -#[cfg(feature = "ebpf")] +#[cfg(any( + all(target_os = "linux", feature = "ebpf"), + all(target_os = "android", feature = "android-ebpf") +))] pub use ebpf_enhanced::*; -#[cfg(not(feature = "ebpf"))] +#[cfg(not(any( + all(target_os = "linux", feature = "ebpf"), + all(target_os = "android", feature = "android-ebpf") +)))] pub use procfs_only::*; diff --git a/src/network/platform/linux/mod.rs b/src/network/platform/linux/mod.rs index 682afb7f..3a14ac0f 100644 --- a/src/network/platform/linux/mod.rs +++ b/src/network/platform/linux/mod.rs @@ -3,12 +3,18 @@ mod interface_stats; mod process; -#[cfg(feature = "ebpf")] +#[cfg(any( + all(target_os = "linux", feature = "ebpf"), + all(target_os = "android", feature = "android-ebpf") +))] pub mod ebpf; -#[cfg(feature = "ebpf")] +#[cfg(any( + all(target_os = "linux", feature = "ebpf"), + all(target_os = "android", feature = "android-ebpf") +))] mod enhanced; -#[cfg(feature = "landlock")] +#[cfg(all(target_os = "linux", feature = "landlock"))] pub mod sandbox; pub use interface_stats::LinuxStatsProvider; @@ -21,7 +27,10 @@ use anyhow::Result; /// Tries enhanced eBPF lookup first (if feature enabled), falls back to procfs /// The `_use_pktap` parameter is ignored on Linux (only used on macOS) pub fn create_process_lookup(_use_pktap: bool) -> Result> { - #[cfg(feature = "ebpf")] + #[cfg(any( + all(target_os = "linux", feature = "ebpf"), + all(target_os = "android", feature = "android-ebpf") + ))] { // Try enhanced lookup first (with eBPF if available), fall back to basic match enhanced::EnhancedLinuxProcessLookup::new() { diff --git a/src/network/platform/mod.rs b/src/network/platform/mod.rs index 00890c0e..b813beb4 100644 --- a/src/network/platform/mod.rs +++ b/src/network/platform/mod.rs @@ -28,7 +28,10 @@ pub enum DegradationReason { #[cfg(any(target_os = "linux", target_os = "android"))] MissingBpfCapabilities, /// eBPF feature not compiled in - #[cfg(all(any(target_os = "linux", target_os = "android"), not(feature = "ebpf")))] + #[cfg(any( + all(target_os = "linux", not(feature = "ebpf")), + all(target_os = "android", not(feature = "android-ebpf")) + ))] EbpfFeatureDisabled, /// Kernel doesn't support required eBPF features (e.g. ENOSYS from bpf(2)) #[cfg(any(target_os = "linux", target_os = "android"))] @@ -78,7 +81,10 @@ impl DegradationReason { Self::MissingCapPerfmon => Cow::Borrowed("needs CAP_PERFMON"), #[cfg(any(target_os = "linux", target_os = "android"))] Self::MissingBpfCapabilities => Cow::Borrowed("needs CAP_BPF+CAP_PERFMON"), - #[cfg(all(any(target_os = "linux", target_os = "android"), not(feature = "ebpf")))] + #[cfg(any( + all(target_os = "linux", not(feature = "ebpf")), + all(target_os = "android", not(feature = "android-ebpf")) + ))] Self::EbpfFeatureDisabled => Cow::Borrowed("eBPF feature disabled"), #[cfg(any(target_os = "linux", target_os = "android"))] Self::KernelUnsupported => Cow::Borrowed("kernel unsupported"), @@ -127,7 +133,10 @@ impl DegradationReason { | Self::BtfUnavailable | Self::EbpfLoadFailed(_) | Self::BinaryOnNosuidMount => Some("eBPF"), - #[cfg(all(any(target_os = "linux", target_os = "android"), not(feature = "ebpf")))] + #[cfg(any( + all(target_os = "linux", not(feature = "ebpf")), + all(target_os = "android", not(feature = "android-ebpf")) + ))] Self::EbpfFeatureDisabled => Some("eBPF"), #[cfg(target_os = "macos")] Self::MissingRootPrivileges @@ -151,7 +160,7 @@ mod windows; // Re-export factory functions and types from platform modules #[cfg(target_os = "freebsd")] pub use freebsd::{FreeBSDStatsProvider, create_process_lookup}; -#[cfg(all(any(target_os = "linux", target_os = "android"), feature = "landlock"))] +#[cfg(all(target_os = "linux", feature = "landlock"))] pub use linux::sandbox; #[cfg(any(target_os = "linux", target_os = "android"))] pub use linux::{LinuxStatsProvider, create_process_lookup};