1use air_sys_types::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
18use core::ffi::CStr;
19use core::num::NonZeroI32;
20
21use air_sys_types::{
22 BpfAttachFlags, BpfAttachType, BpfBtfLoadFlags, BpfLinkCreateRequest, BpfMapBatchInput,
23 BpfMapBatchRequest, BpfMapCreateRequest, BpfMapLookupFlags, BpfMapUpdateFlags,
24 BpfObjectGetFlags, BpfProgramLoadRequest, BpfProgramQueryRequest, BpfProgramTestRunRequest,
25 BpfProgramTestRunResult, BpfStatsType, BpfTaskFdQueryRequest, Errno, PerfEventAttr,
26 PerfEventOpenFlags, PerfEventScope,
27};
28
29#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
30compile_error!("air-sys-syscall::ebpf supporte uniquement x86_64 et aarch64 (ADR-014).");
31
32const BPF_MAP_CREATE: i32 = 0;
37const BPF_MAP_LOOKUP_ELEM: i32 = 1;
38const BPF_MAP_UPDATE_ELEM: i32 = 2;
39const BPF_MAP_DELETE_ELEM: i32 = 3;
40const BPF_MAP_GET_NEXT_KEY: i32 = 4;
41const BPF_PROG_LOAD: i32 = 5;
42const BPF_OBJ_PIN: i32 = 6;
43const BPF_OBJ_GET: i32 = 7;
44const BPF_PROG_ATTACH: i32 = 8;
45const BPF_PROG_DETACH: i32 = 9;
46const BPF_PROG_TEST_RUN: i32 = 10;
47const BPF_PROG_GET_NEXT_ID: i32 = 11;
48const BPF_MAP_GET_NEXT_ID: i32 = 12;
49const BPF_PROG_GET_FD_BY_ID: i32 = 13;
50const BPF_MAP_GET_FD_BY_ID: i32 = 14;
51const BPF_OBJ_GET_INFO_BY_FD: i32 = 15;
52const BPF_PROG_QUERY: i32 = 16;
53const BPF_RAW_TRACEPOINT_OPEN: i32 = 17;
54const BPF_BTF_LOAD: i32 = 18;
55const BPF_BTF_GET_FD_BY_ID: i32 = 19;
56const BPF_TASK_FD_QUERY: i32 = 20;
57const BPF_MAP_LOOKUP_AND_DELETE_ELEM: i32 = 21;
58const BPF_MAP_FREEZE: i32 = 22;
59const BPF_BTF_GET_NEXT_ID: i32 = 23;
60const BPF_MAP_LOOKUP_BATCH: i32 = 24;
61const BPF_MAP_LOOKUP_AND_DELETE_BATCH: i32 = 25;
62const BPF_MAP_UPDATE_BATCH: i32 = 26;
63const BPF_MAP_DELETE_BATCH: i32 = 27;
64const BPF_LINK_CREATE: i32 = 28;
65const BPF_LINK_UPDATE: i32 = 29;
66const BPF_LINK_GET_FD_BY_ID: i32 = 30;
67const BPF_LINK_GET_NEXT_ID: i32 = 31;
68const BPF_ENABLE_STATS: i32 = 32;
69const BPF_ITER_CREATE: i32 = 33;
70const BPF_LINK_DETACH: i32 = 34;
71const BPF_PROG_BIND_MAP: i32 = 35;
72const BPF_TOKEN_CREATE: i32 = 36;
73
74const BPF_OBJ_NAME_LEN: usize = 16;
77
78macro_rules! raii_handle {
83 ($(#[$meta:meta])* $name:ident) => {
84 $(#[$meta])*
85 #[derive(Debug)]
86 pub struct $name(OwnedFd);
87
88 impl $name {
89 #[must_use]
91 #[inline]
92 pub const fn from_fd(fd: OwnedFd) -> Self {
93 Self(fd)
94 }
95
96 #[must_use]
98 pub fn as_fd(&self) -> BorrowedFd<'_> {
99 use air_sys_types::fd::AsFd;
100 self.0.as_fd()
101 }
102
103 #[must_use]
105 pub fn into_fd(self) -> OwnedFd {
106 self.0
107 }
108 }
109 };
110}
111
112raii_handle!(
113 BpfMap
115);
116raii_handle!(
117 BpfProgram
119);
120raii_handle!(
121 BpfLink
123);
124raii_handle!(
125 Btf
127);
128raii_handle!(
129 PerfEvent
131);
132
133impl PerfEvent {
134 pub fn read_count(&self) -> Result<u64, Errno> {
141 let mut buf = [0_u8; 8];
142 let ret = unsafe { raw_syscall_read(self.0.as_raw_fd(), buf.as_mut_ptr() as u64, 8) };
145 if ret < 0 {
146 return Err(errno_from_negative_syscall_ret(ret));
147 }
148 Ok(u64::from_ne_bytes(buf))
149 }
150}
151
152struct Attr {
161 bytes: [u8; 192],
162}
163
164impl Attr {
165 const fn new() -> Self {
166 Self { bytes: [0_u8; 192] }
167 }
168
169 fn put_u32(&mut self, offset: usize, value: u32) {
170 let end = offset.checked_add(4).expect("offset attr borné");
171 if let Some(slot) = self.bytes.get_mut(offset..end) {
172 slot.copy_from_slice(&value.to_ne_bytes());
173 }
174 }
175
176 fn put_u64(&mut self, offset: usize, value: u64) {
177 let end = offset.checked_add(8).expect("offset attr borné");
178 if let Some(slot) = self.bytes.get_mut(offset..end) {
179 slot.copy_from_slice(&value.to_ne_bytes());
180 }
181 }
182
183 fn read_u32(&self, offset: usize) -> u32 {
184 let end = offset.checked_add(4).unwrap_or(self.bytes.len());
185 match self.bytes.get(offset..end).and_then(|s| s.try_into().ok()) {
186 Some(array) => u32::from_ne_bytes(array),
187 None => 0,
188 }
189 }
190
191 fn put_name(&mut self, offset: usize, name: &CStr) {
193 let raw = name.to_bytes();
194 let take = raw.len().min(BPF_OBJ_NAME_LEN.saturating_sub(1));
195 let end = offset.checked_add(take).expect("offset nom borné");
196 if let (Some(dst), Some(src)) = (self.bytes.get_mut(offset..end), raw.get(..take)) {
197 dst.copy_from_slice(src);
198 }
199 }
200
201 fn call(&mut self, cmd: i32, size: usize) -> i64 {
203 let size_u32 = u32::try_from(size).expect("taille attr ≤ 192");
204 unsafe { raw_syscall_bpf(cmd, self.bytes.as_mut_ptr() as u64, size_u32) }
208 }
209}
210
211fn fd_bits(fd: BorrowedFd<'_>) -> u32 {
213 u32::from_ne_bytes(fd.as_raw_fd().to_ne_bytes())
214}
215
216fn owned_from_ret(ret: i64) -> Result<OwnedFd, Errno> {
217 if ret < 0 {
218 return Err(errno_from_negative_syscall_ret(ret));
219 }
220 let raw = i32::try_from(ret).map_err(|_| Errno::EINVAL)?;
221 Ok(unsafe { OwnedFd::from_raw_fd(raw) })
223}
224
225pub fn bpf_map_create(request: &BpfMapCreateRequest<'_>) -> Result<BpfMap, Errno> {
237 let mut attr = Attr::new();
238 attr.put_u32(0, request.map_type.to_raw());
239 attr.put_u32(4, request.key_size);
240 attr.put_u32(8, request.value_size);
241 attr.put_u32(12, request.max_entries);
242 attr.put_u32(16, request.flags.bits());
243 if let Some(inner) = request.inner_map {
244 attr.put_u32(20, fd_bits(inner));
245 }
246 if let Some(node) = request.numa_node {
247 attr.put_u32(24, node);
248 }
249 if let Some(name) = request.name {
250 attr.put_name(28, name);
251 }
252 if let Some(btf) = request.btf {
253 attr.put_u32(48, fd_bits(btf.btf_fd));
254 attr.put_u32(52, btf.key_type_id);
255 attr.put_u32(56, btf.value_type_id);
256 }
257 owned_from_ret(attr.call(BPF_MAP_CREATE, 64)).map(BpfMap::from_fd)
258}
259
260pub fn bpf_map_freeze(map: BorrowedFd<'_>) -> Result<(), Errno> {
266 let mut attr = Attr::new();
267 attr.put_u32(0, fd_bits(map));
268 ret_unit(attr.call(BPF_MAP_FREEZE, 4))
269}
270
271pub fn bpf_map_lookup_element(
286 map: BorrowedFd<'_>,
287 key: &[u8],
288 value_out: &mut [u8],
289 flags: BpfMapLookupFlags,
290) -> Result<(), Errno> {
291 let mut attr = Attr::new();
292 attr.put_u32(0, fd_bits(map));
293 attr.put_u64(8, key.as_ptr() as u64);
294 attr.put_u64(16, value_out.as_mut_ptr() as u64);
295 attr.put_u64(24, flags.bits());
296 ret_unit(attr.call(BPF_MAP_LOOKUP_ELEM, 32))
297}
298
299pub fn bpf_map_update_element(
306 map: BorrowedFd<'_>,
307 key: &[u8],
308 value: &[u8],
309 flags: BpfMapUpdateFlags,
310) -> Result<(), Errno> {
311 let mut attr = Attr::new();
312 attr.put_u32(0, fd_bits(map));
313 attr.put_u64(8, key.as_ptr() as u64);
314 attr.put_u64(16, value.as_ptr() as u64);
315 attr.put_u64(24, flags.bits());
316 ret_unit(attr.call(BPF_MAP_UPDATE_ELEM, 32))
317}
318
319pub fn bpf_map_delete_element(map: BorrowedFd<'_>, key: &[u8]) -> Result<(), Errno> {
325 let mut attr = Attr::new();
326 attr.put_u32(0, fd_bits(map));
327 attr.put_u64(8, key.as_ptr() as u64);
328 ret_unit(attr.call(BPF_MAP_DELETE_ELEM, 24))
329}
330
331pub fn bpf_map_get_next_key(
339 map: BorrowedFd<'_>,
340 key: Option<&[u8]>,
341 next_key_out: &mut [u8],
342) -> Result<bool, Errno> {
343 let mut attr = Attr::new();
344 attr.put_u32(0, fd_bits(map));
345 if let Some(k) = key {
346 attr.put_u64(8, k.as_ptr() as u64);
347 }
348 attr.put_u64(16, next_key_out.as_mut_ptr() as u64);
349 let ret = attr.call(BPF_MAP_GET_NEXT_KEY, 24);
350 if ret < 0 {
351 let err = errno_from_negative_syscall_ret(ret);
352 if err == Errno::ENOENT {
353 return Ok(false);
354 }
355 return Err(err);
356 }
357 Ok(true)
358}
359
360pub fn bpf_map_lookup_and_delete_element(
366 map: BorrowedFd<'_>,
367 key: Option<&[u8]>,
368 value_out: &mut [u8],
369 flags: BpfMapLookupFlags,
370) -> Result<(), Errno> {
371 let mut attr = Attr::new();
372 attr.put_u32(0, fd_bits(map));
373 if let Some(k) = key {
374 attr.put_u64(8, k.as_ptr() as u64);
375 }
376 attr.put_u64(16, value_out.as_mut_ptr() as u64);
377 attr.put_u64(24, flags.bits());
378 ret_unit(attr.call(BPF_MAP_LOOKUP_AND_DELETE_ELEM, 32))
379}
380
381fn batch_in(
386 cmd: i32,
387 map: BorrowedFd<'_>,
388 request: &mut BpfMapBatchRequest<'_>,
389) -> Result<u32, Errno> {
390 let mut attr = Attr::new();
391 if let Some(cursor) = request.in_batch {
392 attr.put_u64(0, cursor);
393 }
394 attr.put_u64(8, (&raw mut request.out_batch) as u64);
395 attr.put_u64(16, request.keys.as_mut_ptr() as u64);
396 attr.put_u64(24, request.values.as_mut_ptr() as u64);
397 attr.put_u32(32, request.count);
398 attr.put_u32(36, fd_bits(map));
399 attr.put_u64(40, request.elem_flags);
400 attr.put_u64(48, request.flags);
401 let ret = attr.call(cmd, 56);
402 if ret < 0 {
403 return Err(errno_from_negative_syscall_ret(ret));
404 }
405 Ok(attr.read_u32(32))
406}
407
408pub fn bpf_map_lookup_batch(
414 map: BorrowedFd<'_>,
415 request: &mut BpfMapBatchRequest<'_>,
416) -> Result<u32, Errno> {
417 batch_in(BPF_MAP_LOOKUP_BATCH, map, request)
418}
419
420pub fn bpf_map_lookup_and_delete_batch(
426 map: BorrowedFd<'_>,
427 request: &mut BpfMapBatchRequest<'_>,
428) -> Result<u32, Errno> {
429 batch_in(BPF_MAP_LOOKUP_AND_DELETE_BATCH, map, request)
430}
431
432fn batch_out(cmd: i32, map: BorrowedFd<'_>, request: &BpfMapBatchInput<'_>) -> Result<u32, Errno> {
433 let mut attr = Attr::new();
434 attr.put_u64(16, request.keys.as_ptr() as u64);
435 attr.put_u64(24, request.values.as_ptr() as u64);
436 attr.put_u32(32, request.count);
437 attr.put_u32(36, fd_bits(map));
438 attr.put_u64(40, request.elem_flags);
439 attr.put_u64(48, request.flags);
440 let ret = attr.call(cmd, 56);
441 if ret < 0 {
442 return Err(errno_from_negative_syscall_ret(ret));
443 }
444 Ok(attr.read_u32(32))
445}
446
447pub fn bpf_map_update_batch(
453 map: BorrowedFd<'_>,
454 request: &BpfMapBatchInput<'_>,
455) -> Result<u32, Errno> {
456 batch_out(BPF_MAP_UPDATE_BATCH, map, request)
457}
458
459pub fn bpf_map_delete_batch(
465 map: BorrowedFd<'_>,
466 request: &BpfMapBatchInput<'_>,
467) -> Result<u32, Errno> {
468 batch_out(BPF_MAP_DELETE_BATCH, map, request)
469}
470
471pub fn bpf_program_load(
489 request: &BpfProgramLoadRequest<'_>,
490 verifier_log: Option<&mut [u8]>,
491) -> Result<BpfProgram, Errno> {
492 let mut attr = Attr::new();
493 attr.put_u32(0, request.program_type.to_raw());
494 let insn_count = u32::try_from(request.instructions.len()).map_err(|_| Errno::EINVAL)?;
495 attr.put_u32(4, insn_count);
496 attr.put_u64(8, request.instructions.as_ptr() as u64);
497 attr.put_u64(16, request.license.as_ptr() as u64);
498 attr.put_u32(24, request.log_level.to_raw());
499 if let Some(log) = verifier_log {
500 let log_size = u32::try_from(log.len()).unwrap_or(u32::MAX);
501 if request.log_level.to_raw() == 0 {
503 attr.put_u32(24, 1);
504 }
505 attr.put_u32(28, log_size);
506 attr.put_u64(32, log.as_mut_ptr() as u64);
507 }
508 attr.put_u32(44, request.flags.bits());
509 if let Some(name) = request.name {
510 attr.put_name(48, name);
511 }
512 if let Some(attach) = request.expected_attach_type {
513 attr.put_u32(68, attach.to_raw());
514 }
515 if let Some(btf) = request.attach_btf {
516 attr.put_u32(72, fd_bits(btf));
517 }
518 if let Some(btf_id) = request.attach_btf_id {
519 attr.put_u32(108, btf_id);
520 }
521 if let Some(prog) = request.attach_program {
522 attr.put_u32(112, fd_bits(prog));
523 }
524 owned_from_ret(attr.call(BPF_PROG_LOAD, 120)).map(BpfProgram::from_fd)
525}
526
527pub fn bpf_program_test_run(
534 program: BorrowedFd<'_>,
535 request: &mut BpfProgramTestRunRequest<'_>,
536) -> Result<BpfProgramTestRunResult, Errno> {
537 let ptr_or_null = |slice: &[u8]| -> u64 {
541 if slice.is_empty() {
542 0
543 } else {
544 slice.as_ptr() as u64
545 }
546 };
547 let mut attr = Attr::new();
548 attr.put_u32(0, fd_bits(program));
549 attr.put_u32(8, u32::try_from(request.data_in.len()).unwrap_or(u32::MAX));
550 attr.put_u32(
551 12,
552 u32::try_from(request.data_out.len()).unwrap_or(u32::MAX),
553 );
554 attr.put_u64(16, ptr_or_null(request.data_in));
555 attr.put_u64(24, ptr_or_null(request.data_out));
556 attr.put_u32(32, request.repeat);
557 attr.put_u32(
558 40,
559 u32::try_from(request.context_in.len()).unwrap_or(u32::MAX),
560 );
561 attr.put_u32(
562 44,
563 u32::try_from(request.context_out.len()).unwrap_or(u32::MAX),
564 );
565 attr.put_u64(48, ptr_or_null(request.context_in));
566 attr.put_u64(56, ptr_or_null(request.context_out));
567 attr.put_u32(64, request.flags);
568 let ret = attr.call(BPF_PROG_TEST_RUN, 72);
569 if ret < 0 {
570 return Err(errno_from_negative_syscall_ret(ret));
571 }
572 Ok(BpfProgramTestRunResult {
573 return_value: attr.read_u32(4),
574 duration: attr.read_u32(36),
575 data_size_out: attr.read_u32(12),
576 context_size_out: attr.read_u32(44),
577 })
578}
579
580pub fn bpf_program_bind_map(
586 program: BorrowedFd<'_>,
587 map: BorrowedFd<'_>,
588 flags: u32,
589) -> Result<(), Errno> {
590 let mut attr = Attr::new();
591 attr.put_u32(0, fd_bits(program));
592 attr.put_u32(4, fd_bits(map));
593 attr.put_u32(8, flags);
594 ret_unit(attr.call(BPF_PROG_BIND_MAP, 12))
595}
596
597pub fn bpf_program_attach(
613 program: BorrowedFd<'_>,
614 target: BorrowedFd<'_>,
615 attach_type: BpfAttachType,
616 flags: BpfAttachFlags,
617) -> Result<(), Errno> {
618 let mut attr = Attr::new();
619 attr.put_u32(0, fd_bits(target));
620 attr.put_u32(4, fd_bits(program));
621 attr.put_u32(8, attach_type.to_raw());
622 attr.put_u32(12, flags.bits());
623 ret_unit(attr.call(BPF_PROG_ATTACH, 16))
624}
625
626pub fn bpf_program_detach(
632 target: BorrowedFd<'_>,
633 attach_type: BpfAttachType,
634 program: Option<BorrowedFd<'_>>,
635) -> Result<(), Errno> {
636 let mut attr = Attr::new();
637 attr.put_u32(0, fd_bits(target));
638 if let Some(prog) = program {
639 attr.put_u32(4, fd_bits(prog));
640 }
641 attr.put_u32(8, attach_type.to_raw());
642 ret_unit(attr.call(BPF_PROG_DETACH, 16))
643}
644
645pub fn bpf_link_create(request: &BpfLinkCreateRequest<'_>) -> Result<BpfLink, Errno> {
656 let mut attr = Attr::new();
657 attr.put_u32(0, fd_bits(request.program));
658 if let Some(target) = request.target {
659 attr.put_u32(4, fd_bits(target));
660 }
661 attr.put_u32(8, request.attach_type.to_raw());
662 attr.put_u32(12, request.flags);
663 owned_from_ret(attr.call(BPF_LINK_CREATE, 16)).map(BpfLink::from_fd)
664}
665
666pub fn bpf_link_update(
672 link: BorrowedFd<'_>,
673 new_program: BorrowedFd<'_>,
674 old_program: Option<BorrowedFd<'_>>,
675 flags: u32,
676) -> Result<(), Errno> {
677 let mut attr = Attr::new();
678 attr.put_u32(0, fd_bits(link));
679 attr.put_u32(4, fd_bits(new_program));
680 attr.put_u32(8, flags);
681 if let Some(old) = old_program {
682 attr.put_u32(12, fd_bits(old));
683 }
684 ret_unit(attr.call(BPF_LINK_UPDATE, 16))
685}
686
687pub fn bpf_link_detach(link: BorrowedFd<'_>) -> Result<(), Errno> {
693 let mut attr = Attr::new();
694 attr.put_u32(0, fd_bits(link));
695 ret_unit(attr.call(BPF_LINK_DETACH, 4))
696}
697
698pub fn bpf_raw_tracepoint_open(name: &CStr, program: BorrowedFd<'_>) -> Result<BpfLink, Errno> {
705 let mut attr = Attr::new();
706 attr.put_u64(0, name.as_ptr() as u64);
707 attr.put_u32(8, fd_bits(program));
708 owned_from_ret(attr.call(BPF_RAW_TRACEPOINT_OPEN, 12)).map(BpfLink::from_fd)
709}
710
711pub fn bpf_iterator_create(link: BorrowedFd<'_>, flags: u32) -> Result<OwnedFd, Errno> {
719 let mut attr = Attr::new();
720 attr.put_u32(0, fd_bits(link));
721 attr.put_u32(4, flags);
722 owned_from_ret(attr.call(BPF_ITER_CREATE, 8))
723}
724
725pub fn bpf_object_pin(object: BorrowedFd<'_>, path: &CStr) -> Result<(), Errno> {
736 let mut attr = Attr::new();
737 attr.put_u64(0, path.as_ptr() as u64);
738 attr.put_u32(8, fd_bits(object));
739 ret_unit(attr.call(BPF_OBJ_PIN, 16))
740}
741
742pub fn bpf_object_get(path: &CStr, flags: BpfObjectGetFlags) -> Result<OwnedFd, Errno> {
748 let mut attr = Attr::new();
749 attr.put_u64(0, path.as_ptr() as u64);
750 attr.put_u32(12, flags.bits());
751 owned_from_ret(attr.call(BPF_OBJ_GET, 16))
752}
753
754fn get_next_id(cmd: i32, after: Option<u32>) -> Result<Option<u32>, Errno> {
759 let mut attr = Attr::new();
760 attr.put_u32(0, after.unwrap_or(0));
761 let ret = attr.call(cmd, 12);
762 if ret < 0 {
763 let err = errno_from_negative_syscall_ret(ret);
764 if err == Errno::ENOENT {
765 return Ok(None);
766 }
767 return Err(err);
768 }
769 Ok(Some(attr.read_u32(4)))
770}
771
772pub fn bpf_program_get_next_id(after: Option<u32>) -> Result<Option<u32>, Errno> {
778 get_next_id(BPF_PROG_GET_NEXT_ID, after)
779}
780
781pub fn bpf_map_get_next_id(after: Option<u32>) -> Result<Option<u32>, Errno> {
787 get_next_id(BPF_MAP_GET_NEXT_ID, after)
788}
789
790pub fn bpf_btf_get_next_id(after: Option<u32>) -> Result<Option<u32>, Errno> {
796 get_next_id(BPF_BTF_GET_NEXT_ID, after)
797}
798
799pub fn bpf_link_get_next_id(after: Option<u32>) -> Result<Option<u32>, Errno> {
805 get_next_id(BPF_LINK_GET_NEXT_ID, after)
806}
807
808fn get_fd_by_id(cmd: i32, id: u32) -> Result<OwnedFd, Errno> {
809 let mut attr = Attr::new();
810 attr.put_u32(0, id);
811 owned_from_ret(attr.call(cmd, 12))
812}
813
814pub fn bpf_program_get_fd_by_id(id: u32) -> Result<BpfProgram, Errno> {
821 get_fd_by_id(BPF_PROG_GET_FD_BY_ID, id).map(BpfProgram::from_fd)
822}
823
824pub fn bpf_map_get_fd_by_id(id: u32) -> Result<BpfMap, Errno> {
831 get_fd_by_id(BPF_MAP_GET_FD_BY_ID, id).map(BpfMap::from_fd)
832}
833
834pub fn bpf_btf_get_fd_by_id(id: u32) -> Result<Btf, Errno> {
841 get_fd_by_id(BPF_BTF_GET_FD_BY_ID, id).map(Btf::from_fd)
842}
843
844pub fn bpf_link_get_fd_by_id(id: u32) -> Result<BpfLink, Errno> {
851 get_fd_by_id(BPF_LINK_GET_FD_BY_ID, id).map(BpfLink::from_fd)
852}
853
854pub fn bpf_object_get_info_by_fd(
860 object: BorrowedFd<'_>,
861 info_out: &mut [u8],
862) -> Result<u32, Errno> {
863 let mut attr = Attr::new();
864 attr.put_u32(0, fd_bits(object));
865 attr.put_u32(4, u32::try_from(info_out.len()).unwrap_or(u32::MAX));
866 attr.put_u64(8, info_out.as_mut_ptr() as u64);
867 let ret = attr.call(BPF_OBJ_GET_INFO_BY_FD, 16);
868 if ret < 0 {
869 return Err(errno_from_negative_syscall_ret(ret));
870 }
871 Ok(attr.read_u32(4))
872}
873
874pub fn bpf_program_query(request: &mut BpfProgramQueryRequest<'_>) -> Result<u32, Errno> {
882 let mut attr = Attr::new();
886 attr.put_u32(0, fd_bits(request.target));
887 attr.put_u32(4, request.attach_type.to_raw());
888 attr.put_u32(8, request.query_flags);
889 attr.put_u64(16, request.program_ids.as_mut_ptr() as u64);
890 attr.put_u32(
891 24,
892 u32::try_from(request.program_ids.len()).unwrap_or(u32::MAX),
893 );
894 let ret = attr.call(BPF_PROG_QUERY, 32);
895 if ret < 0 {
896 return Err(errno_from_negative_syscall_ret(ret));
897 }
898 Ok(attr.read_u32(24))
899}
900
901pub fn bpf_task_fd_query(request: &mut BpfTaskFdQueryRequest<'_>) -> Result<(), Errno> {
907 let mut attr = Attr::new();
908 let pid = u32::from_ne_bytes(request.pid.as_raw().to_ne_bytes());
909 attr.put_u32(0, pid);
910 attr.put_u32(4, fd_bits(request.fd));
911 attr.put_u32(
912 12,
913 u32::try_from(request.name_buffer.len()).unwrap_or(u32::MAX),
914 );
915 attr.put_u64(16, request.name_buffer.as_mut_ptr() as u64);
916 ret_unit(attr.call(BPF_TASK_FD_QUERY, 48))
917}
918
919pub fn bpf_btf_load(
930 btf_blob: &[u8],
931 verifier_log: Option<&mut [u8]>,
932 flags: BpfBtfLoadFlags,
933) -> Result<Btf, Errno> {
934 let mut attr = Attr::new();
935 attr.put_u64(0, btf_blob.as_ptr() as u64);
936 attr.put_u32(16, u32::try_from(btf_blob.len()).unwrap_or(u32::MAX));
937 if let Some(log) = verifier_log {
938 attr.put_u64(8, log.as_mut_ptr() as u64);
939 attr.put_u32(20, u32::try_from(log.len()).unwrap_or(u32::MAX));
940 attr.put_u32(24, 1); }
942 attr.put_u32(32, flags.bits());
943 owned_from_ret(attr.call(BPF_BTF_LOAD, 40)).map(Btf::from_fd)
944}
945
946pub fn bpf_enable_statistics(kind: BpfStatsType) -> Result<OwnedFd, Errno> {
957 let mut attr = Attr::new();
958 attr.put_u32(0, kind.to_raw());
959 owned_from_ret(attr.call(BPF_ENABLE_STATS, 4))
960}
961
962pub fn bpf_token_create(bpffs: BorrowedFd<'_>, flags: u32) -> Result<OwnedFd, Errno> {
969 let mut attr = Attr::new();
970 attr.put_u32(0, flags);
971 attr.put_u32(4, fd_bits(bpffs));
972 owned_from_ret(attr.call(BPF_TOKEN_CREATE, 8))
973}
974
975fn ret_unit(ret: i64) -> Result<(), Errno> {
976 if ret < 0 {
977 return Err(errno_from_negative_syscall_ret(ret));
978 }
979 Ok(())
980}
981
982const PERF_FLAG_PID_CGROUP: u64 = 1 << 2;
987
988pub fn perf_event_open(
999 attr: &PerfEventAttr,
1000 scope: PerfEventScope<'_>,
1001 group_leader: Option<BorrowedFd<'_>>,
1002 flags: PerfEventOpenFlags,
1003) -> Result<PerfEvent, Errno> {
1004 let mut local = *attr;
1005 local.size = u32::try_from(core::mem::size_of::<PerfEventAttr>()).expect("136 tient en u32");
1006
1007 let (pid, cpu, scope_flags) = match scope {
1008 PerfEventScope::CallingProcessAnyCpu => (0_i32, -1_i32, 0_u64),
1009 PerfEventScope::CallingProcessOnCpu(cpu) => {
1010 (0, i32::try_from(cpu).map_err(|_| Errno::EINVAL)?, 0)
1011 }
1012 PerfEventScope::ProcessAnyCpu(process) => (process.as_raw(), -1, 0),
1013 PerfEventScope::ProcessOnCpu { process, cpu } => (
1014 process.as_raw(),
1015 i32::try_from(cpu).map_err(|_| Errno::EINVAL)?,
1016 0,
1017 ),
1018 PerfEventScope::AllProcessesOnCpu(cpu) => {
1019 (-1, i32::try_from(cpu).map_err(|_| Errno::EINVAL)?, 0)
1020 }
1021 PerfEventScope::Cgroup { cgroup, cpu } => (
1022 cgroup.as_raw_fd(),
1023 i32::try_from(cpu).map_err(|_| Errno::EINVAL)?,
1024 PERF_FLAG_PID_CGROUP,
1025 ),
1026 };
1027
1028 let group_fd = group_leader.map_or(-1, |fd| fd.as_raw_fd());
1029 let open_flags = flags.bits() | PerfEventOpenFlags::FD_CLOEXEC.bits() | scope_flags;
1030
1031 let ret = unsafe {
1034 raw_syscall_perf_event_open((&raw const local) as u64, pid, cpu, group_fd, open_flags)
1035 };
1036 owned_from_ret(ret).map(PerfEvent::from_fd)
1037}
1038
1039const fn ioc(dir: u32, ty: u8, nr: u8, size: u32) -> u32 {
1041 (dir << 30) | (size << 16) | ((ty as u32) << 8) | (nr as u32)
1042}
1043
1044const PERF_IOC_TYPE: u8 = b'$';
1046const IOC_NONE: u32 = 0;
1047const IOC_WRITE: u32 = 1;
1048const IOC_READ: u32 = 2;
1049
1050fn perf_ioctl(event: BorrowedFd<'_>, request: u32, arg: u64) -> Result<(), Errno> {
1051 let ret = unsafe { raw_syscall_ioctl(event.as_raw_fd(), u64::from(request), arg) };
1055 if ret < 0 {
1056 return Err(errno_from_negative_syscall_ret(ret));
1057 }
1058 Ok(())
1059}
1060
1061pub fn perf_event_enable(event: BorrowedFd<'_>) -> Result<(), Errno> {
1067 perf_ioctl(event, ioc(IOC_NONE, PERF_IOC_TYPE, 0, 0), 0)
1068}
1069
1070pub fn perf_event_disable(event: BorrowedFd<'_>) -> Result<(), Errno> {
1076 perf_ioctl(event, ioc(IOC_NONE, PERF_IOC_TYPE, 1, 0), 0)
1077}
1078
1079pub fn perf_event_reset(event: BorrowedFd<'_>) -> Result<(), Errno> {
1085 perf_ioctl(event, ioc(IOC_NONE, PERF_IOC_TYPE, 3, 0), 0)
1086}
1087
1088pub fn perf_event_refresh(event: BorrowedFd<'_>, count: u32) -> Result<(), Errno> {
1094 perf_ioctl(event, ioc(IOC_NONE, PERF_IOC_TYPE, 2, 0), u64::from(count))
1095}
1096
1097pub fn perf_event_set_period(event: BorrowedFd<'_>, period: u64) -> Result<(), Errno> {
1103 let local = period;
1104 perf_ioctl(
1105 event,
1106 ioc(IOC_WRITE, PERF_IOC_TYPE, 4, 8),
1107 (&raw const local) as u64,
1108 )
1109}
1110
1111pub fn perf_event_set_filter(event: BorrowedFd<'_>, filter: &CStr) -> Result<(), Errno> {
1117 perf_ioctl(
1118 event,
1119 ioc(IOC_WRITE, PERF_IOC_TYPE, 6, 8),
1120 filter.as_ptr() as u64,
1121 )
1122}
1123
1124pub fn perf_event_id(event: BorrowedFd<'_>) -> Result<u64, Errno> {
1130 let mut id: u64 = 0;
1131 perf_ioctl(
1132 event,
1133 ioc(IOC_READ, PERF_IOC_TYPE, 7, 8),
1134 (&raw mut id) as u64,
1135 )?;
1136 Ok(id)
1137}
1138
1139pub fn perf_event_set_bpf_program(
1145 event: BorrowedFd<'_>,
1146 program: BorrowedFd<'_>,
1147) -> Result<(), Errno> {
1148 perf_ioctl(
1149 event,
1150 ioc(IOC_WRITE, PERF_IOC_TYPE, 8, 4),
1151 u64::from(fd_bits(program)),
1152 )
1153}
1154
1155pub fn perf_event_set_output(
1161 event: BorrowedFd<'_>,
1162 output: Option<BorrowedFd<'_>>,
1163) -> Result<(), Errno> {
1164 let output_fd = output.map_or(-1_i32, |fd| fd.as_raw_fd());
1165 let arg = u64::from(u32::from_ne_bytes(output_fd.to_ne_bytes()));
1166 perf_ioctl(event, ioc(IOC_NONE, PERF_IOC_TYPE, 5, 0), arg)
1167}
1168
1169pub fn perf_event_pause_output(event: BorrowedFd<'_>, pause: bool) -> Result<(), Errno> {
1175 perf_ioctl(
1176 event,
1177 ioc(IOC_WRITE, PERF_IOC_TYPE, 9, 4),
1178 u64::from(u32::from(pause)),
1179 )
1180}
1181
1182pub fn perf_event_query_bpf(event: BorrowedFd<'_>, ids_out: &mut [u32]) -> Result<u32, Errno> {
1190 let capacity = u32::try_from(ids_out.len()).unwrap_or(u32::MAX);
1192 let total = ids_out.len().checked_add(2).ok_or(Errno::EINVAL)?;
1193 let mut buffer = vec![0_u32; total];
1194 buffer[0] = capacity;
1195 perf_ioctl(
1196 event,
1197 ioc(IOC_READ | IOC_WRITE, PERF_IOC_TYPE, 10, 8),
1198 buffer.as_mut_ptr() as u64,
1199 )?;
1200 let prog_cnt = buffer[1];
1201 let copy = ids_out.len().min(buffer.len().saturating_sub(2));
1202 let src_end = copy.saturating_add(2);
1203 if let (Some(dst), Some(src)) = (ids_out.get_mut(..copy), buffer.get(2..src_end)) {
1204 dst.copy_from_slice(src);
1205 }
1206 Ok(prog_cnt)
1207}
1208
1209pub fn perf_event_modify_attributes(
1215 event: BorrowedFd<'_>,
1216 attr: &PerfEventAttr,
1217) -> Result<(), Errno> {
1218 let mut local = *attr;
1219 local.size = u32::try_from(core::mem::size_of::<PerfEventAttr>()).expect("136 tient en u32");
1220 perf_ioctl(
1221 event,
1222 ioc(IOC_WRITE, PERF_IOC_TYPE, 11, 8),
1223 (&raw const local) as u64,
1224 )
1225}
1226
1227#[cfg(target_arch = "x86_64")]
1232#[inline]
1233unsafe fn raw_syscall_bpf(cmd: i32, attr: u64, size: u32) -> i64 {
1234 let ret: i64;
1235 unsafe {
1238 core::arch::asm!(
1239 "syscall",
1240 in("rax") 321_i64,
1241 in("rdi") i64::from(cmd),
1242 in("rsi") attr,
1243 in("rdx") u64::from(size),
1244 lateout("rax") ret,
1245 lateout("rcx") _,
1246 lateout("r11") _,
1247 options(nostack, preserves_flags),
1248 );
1249 }
1250 ret
1251}
1252
1253#[cfg(target_arch = "aarch64")]
1254#[inline]
1255unsafe fn raw_syscall_bpf(cmd: i32, attr: u64, size: u32) -> i64 {
1256 let ret: i64;
1257 unsafe {
1259 core::arch::asm!(
1260 "svc 0",
1261 in("x8") 280_i64,
1262 inout("x0") i64::from(cmd) => ret,
1263 in("x1") attr,
1264 in("x2") u64::from(size),
1265 options(nostack, preserves_flags),
1266 );
1267 }
1268 ret
1269}
1270
1271#[cfg(target_arch = "x86_64")]
1272#[inline]
1273unsafe fn raw_syscall_perf_event_open(
1274 attr: u64,
1275 pid: i32,
1276 cpu: i32,
1277 group_fd: i32,
1278 flags: u64,
1279) -> i64 {
1280 let ret: i64;
1281 unsafe {
1283 core::arch::asm!(
1284 "syscall",
1285 in("rax") 298_i64,
1286 in("rdi") attr,
1287 in("rsi") i64::from(pid),
1288 in("rdx") i64::from(cpu),
1289 in("r10") i64::from(group_fd),
1290 in("r8") flags,
1291 lateout("rax") ret,
1292 lateout("rcx") _,
1293 lateout("r11") _,
1294 options(nostack, preserves_flags, readonly),
1295 );
1296 }
1297 ret
1298}
1299
1300#[cfg(target_arch = "aarch64")]
1301#[inline]
1302unsafe fn raw_syscall_perf_event_open(
1303 attr: u64,
1304 pid: i32,
1305 cpu: i32,
1306 group_fd: i32,
1307 flags: u64,
1308) -> i64 {
1309 let ret: i64;
1310 unsafe {
1312 core::arch::asm!(
1313 "svc 0",
1314 in("x8") 241_i64,
1315 inout("x0") attr => ret,
1316 in("x1") i64::from(pid),
1317 in("x2") i64::from(cpu),
1318 in("x3") i64::from(group_fd),
1319 in("x4") flags,
1320 options(nostack, preserves_flags, readonly),
1321 );
1322 }
1323 ret
1324}
1325
1326#[cfg(target_arch = "x86_64")]
1327#[inline]
1328unsafe fn raw_syscall_ioctl(fd: i32, request: u64, arg: u64) -> i64 {
1329 let ret: i64;
1330 unsafe {
1332 core::arch::asm!(
1333 "syscall",
1334 in("rax") 16_i64,
1335 in("rdi") i64::from(fd),
1336 in("rsi") request,
1337 in("rdx") arg,
1338 lateout("rax") ret,
1339 lateout("rcx") _,
1340 lateout("r11") _,
1341 options(nostack, preserves_flags),
1342 );
1343 }
1344 ret
1345}
1346
1347#[cfg(target_arch = "aarch64")]
1348#[inline]
1349unsafe fn raw_syscall_ioctl(fd: i32, request: u64, arg: u64) -> i64 {
1350 let ret: i64;
1351 unsafe {
1353 core::arch::asm!(
1354 "svc 0",
1355 in("x8") 29_i64,
1356 inout("x0") i64::from(fd) => ret,
1357 in("x1") request,
1358 in("x2") arg,
1359 options(nostack, preserves_flags),
1360 );
1361 }
1362 ret
1363}
1364
1365#[cfg(target_arch = "x86_64")]
1366#[inline]
1367unsafe fn raw_syscall_read(fd: i32, buffer: u64, count: u64) -> i64 {
1368 let ret: i64;
1369 unsafe {
1371 core::arch::asm!(
1372 "syscall",
1373 in("rax") 0_i64,
1374 in("rdi") i64::from(fd),
1375 in("rsi") buffer,
1376 in("rdx") count,
1377 lateout("rax") ret,
1378 lateout("rcx") _,
1379 lateout("r11") _,
1380 options(nostack, preserves_flags),
1381 );
1382 }
1383 ret
1384}
1385
1386#[cfg(target_arch = "aarch64")]
1387#[inline]
1388unsafe fn raw_syscall_read(fd: i32, buffer: u64, count: u64) -> i64 {
1389 let ret: i64;
1390 unsafe {
1392 core::arch::asm!(
1393 "svc 0",
1394 in("x8") 63_i64,
1395 inout("x0") i64::from(fd) => ret,
1396 in("x1") buffer,
1397 in("x2") count,
1398 options(nostack, preserves_flags),
1399 );
1400 }
1401 ret
1402}
1403
1404fn errno_from_negative_syscall_ret(ret: i64) -> Errno {
1409 debug_assert!(ret < 0 && ret > -4096);
1410 #[allow(clippy::cast_possible_truncation)]
1411 let raw = ret.wrapping_neg() as i32;
1412 let nz = NonZeroI32::new(raw).expect("errno strictement positif par construction");
1413 Errno::from_nonzero(nz)
1414}
1415
1416#[cfg(fuzzing)]
1419pub mod fuzz_api {
1420 use super::Attr;
1421
1422 pub fn probe_attr_readback(bytes: &[u8]) {
1428 let mut attr = Attr::new();
1429 let n = bytes.len().min(attr.bytes.len());
1430 if let (Some(dst), Some(src)) = (attr.bytes.get_mut(..n), bytes.get(..n)) {
1431 dst.copy_from_slice(src);
1432 }
1433 for off in [0_usize, 4, 12, 16, 24, 32, 36, 44, 180, 188, 200, 1024] {
1435 let _ = attr.read_u32(off);
1436 }
1437 }
1438}
1439
1440#[cfg(test)]
1441mod tests;