1#![deny(unsafe_code)]
29#![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#[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#[cfg(feature = "io_uring")]
60pub use io_uring::{Completion, IoUringBatcher, UdpBatchIo};
61
62use std::sync::atomic::{AtomicBool, Ordering};
63
64#[allow(unsafe_code)]
72pub fn page_size() -> usize {
73 let ps = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
75 if ps <= 0 {
76 4096usize
77 } else {
78 ps as usize
79 }
80}
81
82#[derive(Debug, Clone)]
84pub struct LinuxPlatform {
85 pub kernel_version: String,
87 pub architecture: String,
89}
90
91impl LinuxPlatform {
92 pub fn new(kernel_version: String, architecture: String) -> Self {
94 Self {
95 kernel_version,
96 architecture,
97 }
98 }
99
100 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 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 pub fn supports_hugepage(&self) -> bool {
128 if let Ok(content) = std::fs::read_to_string("/proc/meminfo") {
130 content.contains("HugePages_Total")
131 } else {
132 false
133 }
134 }
135
136 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 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(), mlock: true, }
162 }
163}
164
165#[derive(Debug, Clone, Copy)]
167#[derive(Default)]
168pub struct PlatformCapabilities {
169 pub ebpf: bool,
171 pub af_xdp: bool,
173 pub hugepage: bool,
175 pub xdp_native: bool,
177 pub xdp_generic: bool,
179 pub mlock: bool,
181}
182
183
184static INITIALIZED: AtomicBool = AtomicBool::new(false);
186
187pub fn init() -> Result<()> {
194 if INITIALIZED.swap(true, Ordering::SeqCst) {
195 return Ok(());
197 }
198
199 let kernel_version = get_kernel_version();
201
202 let platform = LinuxPlatform::new(kernel_version, get_architecture());
203
204 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#[allow(unsafe_code)]
228fn get_kernel_version() -> String {
229 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 std::fs::read_to_string("/proc/sys/kernel/osrelease")
241 .unwrap_or_else(|_| "unknown".to_string())
242 .trim()
243 .to_string()
244}
245
246fn 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 let result = init();
312 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}