zenith-ebpf 0.1.0

Zenith eBPF 程序管理:预编译字节码嵌入(include_bytes!)、libbpf-rs 高性能加载、bpf_link 原子挂载、双 Bank 热更新
//! eBPF 错误类型定义
//!
//! 统一的错误处理体系,支持详细错误信息和上下文

use std::fmt;

/// eBPF 操作错误
#[derive(Debug)]
pub enum EbpfError {
    /// 加载错误
    Load(LoadError),
    /// 附加错误
    Attach(AttachError),
    /// Map 操作错误
    Map(MapError),
    /// Bank 管理错误
    Bank(ManagerError),
    /// 内核不支持
    KernelNotSupported(String),
}

impl fmt::Display for EbpfError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            EbpfError::Load(e) => write!(f, "eBPF load error: {}", e),
            EbpfError::Attach(e) => write!(f, "eBPF attach error: {}", e),
            EbpfError::Map(e) => write!(f, "eBPF map error: {}", e),
            EbpfError::Bank(e) => write!(f, "eBPF bank manager error: {}", e),
            EbpfError::KernelNotSupported(msg) => {
                write!(f, "Kernel not supported: {}", msg)
            }
        }
    }
}

impl std::error::Error for EbpfError {}

impl From<LoadError> for EbpfError {
    fn from(e: LoadError) -> Self {
        EbpfError::Load(e)
    }
}

impl From<AttachError> for EbpfError {
    fn from(e: AttachError) -> Self {
        EbpfError::Attach(e)
    }
}

impl From<MapError> for EbpfError {
    fn from(e: MapError) -> Self {
        EbpfError::Map(e)
    }
}

impl From<ManagerError> for EbpfError {
    fn from(e: ManagerError) -> Self {
        EbpfError::Bank(e)
    }
}

/// 程序加载错误
#[derive(Debug)]
pub enum LoadError {
    /// libbpf 操作失败
    Libbpf(String),
    /// 程序未找到
    NotFound(String),
    /// 字节码为空
    EmptyBytecode,
    /// 验证失败
    VerificationFailed(String),
    /// 不支持的程序类型
    UnsupportedType(String),
}

impl fmt::Display for LoadError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            LoadError::Libbpf(msg) => write!(f, "libbpf operation failed: {}", msg),
            LoadError::NotFound(name) => write!(f, "Program not found: {}", name),
            LoadError::EmptyBytecode => write!(f, "Bytecode is empty"),
            LoadError::VerificationFailed(msg) => {
                write!(f, "Verification failed: {}", msg)
            }
            LoadError::UnsupportedType(ty) => {
                write!(f, "Unsupported program type: {}", ty)
            }
        }
    }
}

impl std::error::Error for LoadError {}

/// 程序附加错误
#[derive(Debug)]
pub enum AttachError {
    /// libbpf 操作失败
    Libbpf(String),
    /// 接口未找到
    InterfaceNotFound(String),
    /// 附加标志不支持
    InvalidFlags(String),
    /// 资源不足
    InsufficientResources(String),
}

impl fmt::Display for AttachError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AttachError::Libbpf(msg) => write!(f, "libbpf attach failed: {}", msg),
            AttachError::InterfaceNotFound(name) => {
                write!(f, "Interface not found: {}", name)
            }
            AttachError::InvalidFlags(msg) => write!(f, "Invalid flags: {}", msg),
            AttachError::InsufficientResources(msg) => {
                write!(f, "Insufficient resources: {}", msg)
            }
        }
    }
}

impl std::error::Error for AttachError {}

/// Map 操作错误
#[derive(Debug)]
pub enum MapError {
    /// libbpf 操作失败
    Libbpf(String),
    /// Map 未找到
    NotFound(String),
    /// 键或值大小不匹配
    SizeMismatch {
        /// 期望的字节数
        expected: usize,
        /// 实际的字节数
        actual: usize,
    },
    /// Map 已满
    MapFull,
    /// 计数器算术溢出(fail-closed,禁止回绕)
    CounterOverflow,
    /// 不支持的操作
    UnsupportedOperation(String),
}

impl fmt::Display for MapError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MapError::Libbpf(msg) => write!(f, "libbpf map operation failed: {}", msg),
            MapError::NotFound(name) => write!(f, "Map not found: {}", name),
            MapError::SizeMismatch { expected, actual } => {
                write!(
                    f,
                    "Size mismatch: expected {}, got {}",
                    expected, actual
                )
            }
            MapError::MapFull => write!(f, "Map is full"),
            MapError::CounterOverflow => write!(f, "Counter arithmetic overflow (fail-closed)"),
            MapError::UnsupportedOperation(op) => {
                write!(f, "Unsupported operation: {}", op)
            }
        }
    }
}

impl std::error::Error for MapError {}

/// Bank 管理器错误
#[derive(Debug)]
pub enum ManagerError {
    /// 状态无效
    InvalidState,
    /// 加载错误
    Load(String),
    /// 切换错误
    SwitchFailed(String),
    /// 程序不匹配
    ProgramMismatch,
    /// 活动程序不是 xdp_main(完整过滤链被绕过,fail-closed)
    NotXdpMain(String),
    /// 内部锁错误
    LockError(String),
}

impl fmt::Display for ManagerError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ManagerError::InvalidState => write!(f, "Invalid bank state"),
            ManagerError::Load(msg) => write!(f, "Load error: {}", msg),
            ManagerError::SwitchFailed(msg) => write!(f, "Switch failed: {}", msg),
            ManagerError::ProgramMismatch => write!(f, "Program mismatch"),
            ManagerError::NotXdpMain(msg) => {
                write!(f, "Active program is not xdp_main (filter chain bypass): {}", msg)
            }
            ManagerError::LockError(msg) => write!(f, "Lock error: {}", msg),
        }
    }
}

impl std::error::Error for ManagerError {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_ebpf_error_display_load() {
        let err = EbpfError::Load(LoadError::Libbpf("test error".to_string()));
        assert!(format!("{}", err).contains("eBPF load error"));
        assert!(format!("{}", err).contains("test error"));
    }

    #[test]
    fn test_ebpf_error_display_attach() {
        let err = EbpfError::Attach(AttachError::Libbpf("attach failed".to_string()));
        assert!(format!("{}", err).contains("eBPF attach error"));
        assert!(format!("{}", err).contains("attach failed"));
    }

    #[test]
    fn test_ebpf_error_display_map() {
        let err = EbpfError::Map(MapError::NotFound("test_map".to_string()));
        assert!(format!("{}", err).contains("eBPF map error"));
        assert!(format!("{}", err).contains("test_map"));
    }

    #[test]
    fn test_ebpf_error_display_bank() {
        let err = EbpfError::Bank(ManagerError::InvalidState);
        assert!(format!("{}", err).contains("eBPF bank manager error"));
    }

    #[test]
    fn test_ebpf_error_display_kernel() {
        let err = EbpfError::KernelNotSupported("AF_XDP not available".to_string());
        assert!(format!("{}", err).contains("Kernel not supported"));
        assert!(format!("{}", err).contains("AF_XDP not available"));
    }

    #[test]
    fn test_load_error_variants() {
        let e1 = LoadError::Libbpf("libbpf fail".to_string());
        assert!(format!("{}", e1).contains("libbpf operation failed"));

        let e2 = LoadError::NotFound("my_prog".to_string());
        assert!(format!("{}", e2).contains("Program not found"));
        assert!(format!("{}", e2).contains("my_prog"));

        let e3 = LoadError::EmptyBytecode;
        assert!(format!("{}", e3).contains("Bytecode is empty"));

        let e4 = LoadError::VerificationFailed("verifier error".to_string());
        assert!(format!("{}", e4).contains("Verification failed"));

        let e5 = LoadError::UnsupportedType("kprobe".to_string());
        assert!(format!("{}", e5).contains("Unsupported program type"));
    }

    #[test]
    fn test_attach_error_variants() {
        let e1 = AttachError::Libbpf("libbpf attach fail".to_string());
        assert!(format!("{}", e1).contains("libbpf attach failed"));

        let e2 = AttachError::InterfaceNotFound("eth0".to_string());
        assert!(format!("{}", e2).contains("Interface not found"));
        assert!(format!("{}", e2).contains("eth0"));

        let e3 = AttachError::InvalidFlags("bad flags".to_string());
        assert!(format!("{}", e3).contains("Invalid flags"));

        let e4 = AttachError::InsufficientResources("out of memory".to_string());
        assert!(format!("{}", e4).contains("Insufficient resources"));
    }

    #[test]
    fn test_map_error_variants() {
        let e1 = MapError::Libbpf("map op fail".to_string());
        assert!(format!("{}", e1).contains("libbpf map operation failed"));

        let e2 = MapError::NotFound("xsk_map".to_string());
        assert!(format!("{}", e2).contains("Map not found"));
        assert!(format!("{}", e2).contains("xsk_map"));

        let e3 = MapError::SizeMismatch { expected: 8, actual: 4 };
        let msg = format!("{}", e3);
        assert!(msg.contains("Size mismatch"));
        assert!(msg.contains("8"));
        assert!(msg.contains("4"));

        let e4 = MapError::MapFull;
        assert!(format!("{}", e4).contains("Map is full"));

        let e5 = MapError::UnsupportedOperation("delete".to_string());
        assert!(format!("{}", e5).contains("Unsupported operation"));
        assert!(format!("{}", e5).contains("delete"));
    }

    #[test]
    fn test_manager_error_variants() {
        let e1 = ManagerError::InvalidState;
        assert!(format!("{}", e1).contains("Invalid bank state"));

        let e2 = ManagerError::Load("load fail".to_string());
        assert!(format!("{}", e2).contains("Load error"));

        let e3 = ManagerError::SwitchFailed("switch fail".to_string());
        assert!(format!("{}", e3).contains("Switch failed"));

        let e4 = ManagerError::ProgramMismatch;
        assert!(format!("{}", e4).contains("Program mismatch"));

        let e5 = ManagerError::NotXdpMain("Bank A is Redirect".to_string());
        assert!(format!("{}", e5).contains("not xdp_main"));

        let e6 = ManagerError::LockError("lock poisoned".to_string());
        assert!(format!("{}", e6).contains("Lock error"));
    }

    #[test]
    fn test_error_from_conversions() {
        let load_err = LoadError::EmptyBytecode;
        let ebpf_err: EbpfError = load_err.into();
        assert!(matches!(ebpf_err, EbpfError::Load(_)));

        let attach_err = AttachError::InterfaceNotFound("eth0".to_string());
        let ebpf_err: EbpfError = attach_err.into();
        assert!(matches!(ebpf_err, EbpfError::Attach(_)));

        let map_err = MapError::MapFull;
        let ebpf_err: EbpfError = map_err.into();
        assert!(matches!(ebpf_err, EbpfError::Map(_)));

        let mgr_err = ManagerError::InvalidState;
        let ebpf_err: EbpfError = mgr_err.into();
        assert!(matches!(ebpf_err, EbpfError::Bank(_)));
    }

    #[test]
    fn test_errors_are_debug() {
        let e = EbpfError::KernelNotSupported("test".to_string());
        let debug_str = format!("{:?}", e);
        assert!(!debug_str.is_empty());

        let e = LoadError::EmptyBytecode;
        let debug_str = format!("{:?}", e);
        assert!(!debug_str.is_empty());

        let e = AttachError::InvalidFlags("test".to_string());
        let debug_str = format!("{:?}", e);
        assert!(!debug_str.is_empty());

        let e = MapError::SizeMismatch { expected: 4, actual: 8 };
        let debug_str = format!("{:?}", e);
        assert!(!debug_str.is_empty());

        let e = ManagerError::LockError("test".to_string());
        let debug_str = format!("{:?}", e);
        assert!(!debug_str.is_empty());
    }

    #[test]
    fn test_error_trait_implementations() {
        fn assert_error<T: std::error::Error>() {}
        assert_error::<EbpfError>();
        assert_error::<LoadError>();
        assert_error::<AttachError>();
        assert_error::<MapError>();
        assert_error::<ManagerError>();
    }

    #[test]
    fn test_load_error_debug() {
        let e = LoadError::NotFound("prog".to_string());
        let debug_str = format!("{:?}", e);
        assert!(!debug_str.is_empty());
    }

    #[test]
    fn test_attach_error_debug() {
        let e = AttachError::Libbpf("fail".to_string());
        let debug_str = format!("{:?}", e);
        assert!(!debug_str.is_empty());
    }

    #[test]
    fn test_map_error_display_match() {
        let e1 = MapError::MapFull;
        let e2 = MapError::MapFull;
        assert_eq!(format!("{}", e1), format!("{}", e2));
    }

    #[test]
    fn test_manager_error_debug() {
        let e = ManagerError::SwitchFailed("test".to_string());
        let debug_str = format!("{:?}", e);
        assert!(!debug_str.is_empty());
    }
}