Skip to main content

zenith_linux/
lib.rs

1//! Zenith Linux - Linux 平台抽象层
2//!
3//! 本 crate 提供 Linux 平台特有的系统抽象,包括:
4//! - AF_XDP Socket 管理与零拷贝数据通路
5//! - UMEM 内存管理(mmap、锁页、HugePage)
6//! - 四环操作封装(Fill/RX/TX/Completion)
7//! - 描述符安全校验引擎与事务化所有权迁移
8//! - 系统调用封装与能力探测
9//!
10//! # 架构原则
11//! 1. 上层接口零 unsafe,所有 unsafe 封装在本 crate 内部
12//! 2. 单队列单 Owner,无锁、无共享、无阻塞
13//! 3. 预分配优先,热路径零堆分配
14//! 4. 描述符全生命周期追踪,守恒等式恒成立
15//!
16//! # unsafe 使用
17//! crate 顶层 `#![deny(unsafe_code)]`(与 workspace lint 一致),unsafe 仅
18//! 在以下位置精确放开,绝不 crate 级全开:
19//! - 本文件 `page_size()` / `get_kernel_version()`:libc FFI 的安全封装,
20//!   以 item 级 `#[allow(unsafe_code)]` 精确放开;
21//! - affinity / netif / ring / syscalls / umem / xsk / io_uring(feature 门控)
22//!   七个模块:文件顶部 `#![allow(unsafe_code)]` + 模块文档说明 unsafe 不可
23//!   避免的原因(系统调用 / mmap 共享内存 / C 结构体 FFI)。
24//!
25//! descriptor / error 模块不含任何 unsafe,不做任何放开。
26//! 所有 unsafe 块均附 SAFETY 注释。
27
28#![deny(unsafe_code)]
29// AF_XDP/io_uring/libbpf 仅 Linux 可用:非 Linux 目标下本 crate 整体为空,
30// 依赖方(zenith-runtime 等)以同样的 cfg(target_os = "linux") 门控使用点。
31#![cfg(target_os = "linux")]
32
33pub mod affinity;
34pub mod descriptor;
35pub mod error;
36pub mod netif;
37pub mod ring;
38pub mod syscalls;
39pub mod umem;
40pub mod xsk;
41
42// O2: io_uring 异步 IO 批量化封装(可选,需启用 io_uring feature)
43#[cfg(feature = "io_uring")]
44pub mod io_uring;
45
46pub use affinity::{online_cpu_count, set_thread_affinity, set_thread_affinity_range, AffinityError};
47pub use descriptor::{Descriptor, DescriptorEngine, DescriptorType, XdpDesc};
48pub use error::{LinuxError, Result};
49pub use netif::{if_nametoindex, NetIfError};
50pub use ring::{RingOffsets, RingType, XDP_RING_NEED_WAKEUP, XskRing};
51pub use syscalls::{
52    pipe2, recvmmsg, sendfile, sendmmsg, splice, splice_bidirectional, FdGuard, SpliceFlags,
53    MAX_BATCH_DATAGRAMS,
54};
55pub use umem::{UmemConfig, UmemManager};
56pub use xsk::{XskConfig, XskSocket, XskState};
57
58// O2: io_uring 批量 IO 提交器重导出
59#[cfg(feature = "io_uring")]
60pub use io_uring::{Completion, IoUringBatcher, UdpBatchIo};
61
62use std::sync::atomic::{AtomicBool, Ordering};
63
64/// 返回系统页面大小(字节)。
65///
66/// # 实现
67/// 通过 `libc::sysconf(_SC_PAGESIZE)` 查询,失败时回退到 4096(大多数 Linux 平台的默认)。
68/// 该值为只读系统属性,首次调用后不变,因此由调用方缓存。
69///
70/// unsafe 不可避免:`libc::sysconf` 为 libc FFI 查询只读系统属性。
71#[allow(unsafe_code)]
72pub fn page_size() -> usize {
73    // SAFETY: sysconf 仅对只读全局系统属性进行查询,无副作用,无内存安全影响。
74    let ps = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
75    if ps <= 0 {
76        4096usize
77    } else {
78        ps as usize
79    }
80}
81
82/// Linux 平台信息
83#[derive(Debug, Clone)]
84pub struct LinuxPlatform {
85    /// 内核版本
86    pub kernel_version: String,
87    /// 架构
88    pub architecture: String,
89}
90
91impl LinuxPlatform {
92    /// 创建新的 Linux 平台实例
93    pub fn new(kernel_version: String, architecture: String) -> Self {
94        Self {
95            kernel_version,
96            architecture,
97        }
98    }
99
100    /// 检查是否支持 eBPF
101    ///
102    /// eBPF 需要内核 4.4+
103    pub fn supports_ebpf(&self) -> bool {
104        let major_minor: Vec<&str> = self.kernel_version.split('.').collect();
105        if major_minor.len() >= 2
106            && let Ok(major) = major_minor[0].parse::<u32>()
107                && let Ok(minor) = major_minor[1].parse::<u32>() {
108                    return major > 4 || (major == 4 && minor >= 4);
109                }
110        false
111    }
112
113    /// 检查是否支持 AF_XDP
114    ///
115    /// AF_XDP 需要内核 4.18+
116    pub fn supports_af_xdp(&self) -> bool {
117        let major_minor: Vec<&str> = self.kernel_version.split('.').collect();
118        if major_minor.len() >= 2
119            && let Ok(major) = major_minor[0].parse::<u32>()
120                && let Ok(minor) = major_minor[1].parse::<u32>() {
121                    return major > 4 || (major == 4 && minor >= 18);
122                }
123        false
124    }
125
126    /// 检查是否支持 HugePage
127    pub fn supports_hugepage(&self) -> bool {
128        // 检查 /proc/meminfo 中是否存在 HugePages
129        if let Ok(content) = std::fs::read_to_string("/proc/meminfo") {
130            content.contains("HugePages_Total")
131        } else {
132            false
133        }
134    }
135
136    /// 检查是否支持 XDP Native 模式
137    ///
138    /// 需要内核 5.3+ 和网卡驱动支持
139    pub fn supports_xdp_native(&self) -> bool {
140        let major_minor: Vec<&str> = self.kernel_version.split('.').collect();
141        if major_minor.len() >= 2
142            && let Ok(major) = major_minor[0].parse::<u32>()
143                && let Ok(minor) = major_minor[1].parse::<u32>() {
144                    return major > 5 || (major == 5 && minor >= 3);
145                }
146        false
147    }
148
149    /// 检测当前平台能力
150    ///
151    /// # 返回
152    /// * `PlatformCapabilities` - 平台能力集合
153    pub fn detect_capabilities(&self) -> PlatformCapabilities {
154        PlatformCapabilities {
155            ebpf: self.supports_ebpf(),
156            af_xdp: self.supports_af_xdp(),
157            hugepage: self.supports_hugepage(),
158            xdp_native: self.supports_xdp_native(),
159            xdp_generic: self.supports_ebpf(), // Generic 模式依赖 eBPF
160            mlock: true, // 通常都支持
161        }
162    }
163}
164
165/// 平台能力集合
166#[derive(Debug, Clone, Copy)]
167#[derive(Default)]
168pub struct PlatformCapabilities {
169    /// 是否支持 eBPF
170    pub ebpf: bool,
171    /// 是否支持 AF_XDP
172    pub af_xdp: bool,
173    /// 是否支持 HugePage
174    pub hugepage: bool,
175    /// 是否支持 XDP Native 模式
176    pub xdp_native: bool,
177    /// 是否支持 XDP Generic 模式
178    pub xdp_generic: bool,
179    /// 是否支持 mlock
180    pub mlock: bool,
181}
182
183
184/// 初始化状态
185static INITIALIZED: AtomicBool = AtomicBool::new(false);
186
187/// 初始化 Zenith Linux 平台
188///
189/// 必须在使用任何 AF_XDP 功能之前调用。
190///
191/// # 返回
192/// * `Result<()>` - 初始化结果
193pub fn init() -> Result<()> {
194    if INITIALIZED.swap(true, Ordering::SeqCst) {
195        // 已初始化
196        return Ok(());
197    }
198
199    // 检测内核版本
200    let kernel_version = get_kernel_version();
201
202    let platform = LinuxPlatform::new(kernel_version, get_architecture());
203
204    // 验证必要能力
205    let caps = platform.detect_capabilities();
206
207    if !caps.ebpf {
208        return Err(LinuxError::Unsupported(
209            "eBPF not supported (kernel 4.4+ required)".to_string(),
210        ));
211    }
212
213    Ok(())
214}
215
216/// 获取内核版本
217///
218/// unsafe 不可避免:`uname` 系统调用与 `CStr::from_ptr` 均为 libc FFI,
219/// 本函数是其安全封装(含 /proc 回退路径),故 item 级精确放开。
220// SAFETY: 以下 unsafe 块涉及三个不安全操作,均已验证为安全:
221// 1. std::mem::zeroed::<utsname>():utsname 为栈分配,在调用期间有效;
222//    zeroed() 返回全零初始化的结构体,满足 C 结构体要求。
223// 2. libc::uname(&mut utsname):uname() 是纯系统调用,向提供的缓冲区写入内核信息;
224//    缓冲区为栈分配,生命周期覆盖整个调用,不可能发生 use-after-free。
225// 3. CStr::from_ptr(utsname.release.as_ptr()):POSIX 规范保证 uname() 返回时
226//    release 字段以 null 结尾,因此 from_ptr 不会读取越界内存。
227#[allow(unsafe_code)]
228fn get_kernel_version() -> String {
229    // 尝试从 uname 获取
230    let mut utsname: libc::utsname = unsafe { std::mem::zeroed() };
231    let ret = unsafe { libc::uname(&mut utsname) };
232    if ret == 0 {
233        let release = unsafe {
234            std::ffi::CStr::from_ptr(utsname.release.as_ptr())
235        };
236        return release.to_string_lossy().to_string();
237    }
238
239    // 尝试从 /proc/sys/kernel/osrelease 读取
240    std::fs::read_to_string("/proc/sys/kernel/osrelease")
241        .unwrap_or_else(|_| "unknown".to_string())
242        .trim()
243        .to_string()
244}
245
246/// 获取架构
247fn get_architecture() -> String {
248    if cfg!(target_arch = "x86_64") {
249        "x86_64".to_string()
250    } else if cfg!(target_arch = "aarch64") {
251        "aarch64".to_string()
252    } else {
253        "unknown".to_string()
254    }
255}
256
257#[cfg(test)]
258mod tests {
259    use super::*;
260
261    #[test]
262    fn test_linux_platform_creation() {
263        let platform = LinuxPlatform::new("5.15.0".to_string(), "x86_64".to_string());
264        assert_eq!(platform.kernel_version, "5.15.0");
265        assert_eq!(platform.architecture, "x86_64");
266    }
267
268    #[test]
269    fn test_supports_ebpf() {
270        let modern = LinuxPlatform::new("5.15.0".to_string(), "x86_64".to_string());
271        assert!(modern.supports_ebpf());
272
273        let old = LinuxPlatform::new("4.1.0".to_string(), "x86_64".to_string());
274        assert!(!old.supports_ebpf());
275
276        let edge = LinuxPlatform::new("4.4.0".to_string(), "x86_64".to_string());
277        assert!(edge.supports_ebpf());
278    }
279
280    #[test]
281    fn test_supports_af_xdp() {
282        let modern = LinuxPlatform::new("5.15.0".to_string(), "x86_64".to_string());
283        assert!(modern.supports_af_xdp());
284
285        let old = LinuxPlatform::new("4.17.0".to_string(), "x86_64".to_string());
286        assert!(!old.supports_af_xdp());
287    }
288
289    #[test]
290    fn test_supports_xdp_native() {
291        let modern = LinuxPlatform::new("5.15.0".to_string(), "x86_64".to_string());
292        assert!(modern.supports_xdp_native());
293
294        let old = LinuxPlatform::new("5.2.0".to_string(), "x86_64".to_string());
295        assert!(!old.supports_xdp_native());
296    }
297
298    #[test]
299    fn test_detect_capabilities() {
300        let platform = LinuxPlatform::new("5.15.0".to_string(), "x86_64".to_string());
301        let caps = platform.detect_capabilities();
302        assert!(caps.ebpf);
303        assert!(caps.af_xdp);
304        assert!(caps.xdp_native);
305        assert!(caps.xdp_generic);
306    }
307
308    #[test]
309    fn test_platform_init() {
310        // 测试在支持的平台上初始化
311        let result = init();
312        // 可能成功也可能失败(取决于内核版本)
313        let _ = result;
314    }
315
316    #[test]
317    fn test_public_api_reexports() {
318        let _desc: Descriptor = Descriptor::default();
319        let _xdp: XdpDesc = XdpDesc::zero();
320        let _desc_type: DescriptorType = DescriptorType::DataFrame;
321        let _engine: DescriptorEngine = DescriptorEngine::new(1024).unwrap();
322        let _err: LinuxError = LinuxError::Unsupported("test".to_string());
323        let _result: Result<()> = Ok(());
324        let _ring: XskRing = XskRing::new(RingType::Rx, 16).unwrap();
325        let _ring_type: RingType = RingType::Fill;
326        let _offsets: RingOffsets = RingOffsets {
327            producer: 0,
328            consumer: 0,
329            desc: 0,
330            flags: 0,
331            len: 0,
332        };
333        let _umem_config: UmemConfig = UmemConfig::default();
334        let _xsk_config: XskConfig = XskConfig::default();
335        let _state: XskState = XskState::Created;
336    }
337
338    #[test]
339    fn test_platform_capabilities_default() {
340        let caps = PlatformCapabilities::default();
341        assert!(!caps.ebpf);
342        assert!(!caps.af_xdp);
343        assert!(!caps.hugepage);
344        assert!(!caps.xdp_native);
345        assert!(!caps.xdp_generic);
346        assert!(!caps.mlock);
347    }
348
349    #[test]
350    fn test_platform_capabilities_copy() {
351        let caps = PlatformCapabilities {
352            ebpf: true,
353            af_xdp: true,
354            hugepage: false,
355            xdp_native: true,
356            xdp_generic: true,
357            mlock: true,
358        };
359        let copied = caps;
360        assert!(copied.ebpf);
361        assert!(copied.af_xdp);
362        assert!(copied.xdp_native);
363    }
364
365    #[test]
366    fn test_linux_platform_clone() {
367        let platform = LinuxPlatform::new("5.15.0".to_string(), "x86_64".to_string());
368        let cloned = platform.clone();
369        assert_eq!(cloned.kernel_version, "5.15.0");
370        assert_eq!(cloned.architecture, "x86_64");
371    }
372
373    #[test]
374    fn test_kernel_version_boundary_4_4() {
375        let platform = LinuxPlatform::new("4.4.0".to_string(), "x86_64".to_string());
376        assert!(platform.supports_ebpf());
377    }
378
379    #[test]
380    fn test_kernel_version_boundary_4_3() {
381        let platform = LinuxPlatform::new("4.3.9".to_string(), "x86_64".to_string());
382        assert!(!platform.supports_ebpf());
383    }
384
385    #[test]
386    fn test_kernel_version_boundary_4_18() {
387        let platform = LinuxPlatform::new("4.18.0".to_string(), "x86_64".to_string());
388        assert!(platform.supports_af_xdp());
389    }
390
391    #[test]
392    fn test_kernel_version_boundary_4_17() {
393        let platform = LinuxPlatform::new("4.17.99".to_string(), "x86_64".to_string());
394        assert!(!platform.supports_af_xdp());
395    }
396
397    #[test]
398    fn test_kernel_version_boundary_5_3() {
399        let platform = LinuxPlatform::new("5.3.0".to_string(), "x86_64".to_string());
400        assert!(platform.supports_xdp_native());
401    }
402
403    #[test]
404    fn test_kernel_version_boundary_5_2() {
405        let platform = LinuxPlatform::new("5.2.99".to_string(), "x86_64".to_string());
406        assert!(!platform.supports_xdp_native());
407    }
408
409    #[test]
410    fn test_kernel_version_major_gt_4() {
411        let platform = LinuxPlatform::new("6.1.0".to_string(), "x86_64".to_string());
412        assert!(platform.supports_ebpf());
413        assert!(platform.supports_af_xdp());
414        assert!(platform.supports_xdp_native());
415    }
416
417    #[test]
418    fn test_invalid_kernel_version_format() {
419        let platform = LinuxPlatform::new("invalid".to_string(), "x86_64".to_string());
420        assert!(!platform.supports_ebpf());
421        assert!(!platform.supports_af_xdp());
422        assert!(!platform.supports_xdp_native());
423    }
424
425    #[test]
426    fn test_partial_kernel_version() {
427        let platform = LinuxPlatform::new("5".to_string(), "x86_64".to_string());
428        assert!(!platform.supports_ebpf());
429    }
430
431    #[test]
432    fn test_xdp_generic_equals_ebpf() {
433        let modern = LinuxPlatform::new("5.15.0".to_string(), "x86_64".to_string());
434        let caps = modern.detect_capabilities();
435        assert_eq!(caps.xdp_generic, caps.ebpf);
436    }
437}