Skip to main content

libbpf_rs/
query.rs

1//! Query the host about BPF
2//!
3//! For example, to list the name of every bpf program running on the system:
4//! ```
5//! use libbpf_rs::query::ProgInfoIter;
6//!
7//! let mut iter = ProgInfoIter::default();
8//! for prog in iter {
9//!     println!("{}", prog.name.to_string_lossy());
10//! }
11//! ```
12
13use std::ffi::c_void;
14use std::ffi::CStr;
15use std::ffi::OsStr;
16use std::ffi::OsString;
17use std::io;
18use std::mem::size_of_val;
19use std::mem::zeroed;
20use std::os::fd::AsFd;
21use std::os::fd::AsRawFd;
22use std::os::fd::BorrowedFd;
23use std::os::fd::FromRawFd;
24use std::os::fd::OwnedFd;
25use std::os::raw::c_char;
26use std::os::unix::ffi::OsStrExt;
27use std::path::PathBuf;
28use std::ptr;
29use std::time::Duration;
30
31use crate::util;
32use crate::CgroupIterOrder;
33use crate::MapType;
34use crate::ProgramAttachType;
35use crate::ProgramType;
36use crate::Result;
37
38/// Convert a [`CStr`] into an owned [`OsString`].
39fn cstr_to_os_string(s: &CStr) -> OsString {
40    OsStr::from_bytes(s.to_bytes()).to_owned()
41}
42
43macro_rules! gen_info_impl {
44    // This magic here allows us to embed doc comments into macro expansions
45    ($(#[$attr:meta])*
46     $name:ident, $info_ty:ty, $uapi_info_ty:ty, $next_id:expr, $fd_by_id:expr) => {
47        $(#[$attr])*
48        #[derive(Default, Debug)]
49        pub struct $name {
50            cur_id: u32,
51        }
52
53        impl $name {
54            // Returns Some(next_valid_fd), None on none left
55            fn next_valid_fd(&mut self) -> Option<OwnedFd> {
56                loop {
57                    if unsafe { $next_id(self.cur_id, &mut self.cur_id) } != 0 {
58                        return None;
59                    }
60
61                    let fd = unsafe { $fd_by_id(self.cur_id) };
62                    if fd < 0 {
63                        let err = io::Error::last_os_error();
64                        if err.kind() == io::ErrorKind::NotFound {
65                            continue;
66                        }
67
68                        return None;
69                    }
70
71                    return Some(unsafe { OwnedFd::from_raw_fd(fd)});
72                }
73            }
74        }
75
76        impl Iterator for $name {
77            type Item = $info_ty;
78
79            fn next(&mut self) -> Option<Self::Item> {
80                let fd = self.next_valid_fd()?;
81
82                // We need to use std::mem::zeroed() instead of just using
83                // ::default() because padding bytes need to be zero as well.
84                // Old kernels which know about fewer fields than we do will
85                // check to make sure every byte past what they know is zero
86                // and will return E2BIG otherwise.
87                let mut item: $uapi_info_ty = unsafe { std::mem::zeroed() };
88                let item_ptr: *mut $uapi_info_ty = &mut item;
89                let mut len = size_of_val(&item) as u32;
90
91                let ret = unsafe { libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr.cast(), &mut len) };
92                let parsed_uapi = if ret != 0 {
93                    None
94                } else {
95                    <$info_ty>::from_uapi(fd.as_fd(), item)
96                };
97
98                parsed_uapi
99            }
100        }
101    };
102}
103
104/// BTF Line information.
105#[derive(Clone, Debug)]
106#[doc(alias = "bpf_line_info")]
107pub struct LineInfo {
108    /// Offset of instruction in vector.
109    pub insn_off: u32,
110    /// File name offset.
111    pub file_name_off: u32,
112    /// Line offset in debug info.
113    pub line_off: u32,
114    /// Line number.
115    pub line_num: u32,
116    /// Line column number.
117    pub line_col: u32,
118}
119
120impl From<&libbpf_sys::bpf_line_info> for LineInfo {
121    fn from(item: &libbpf_sys::bpf_line_info) -> Self {
122        Self {
123            insn_off: item.insn_off,
124            file_name_off: item.file_name_off,
125            line_off: item.line_off,
126            line_num: item.line_col >> 10,
127            line_col: item.line_col & 0x3ff,
128        }
129    }
130}
131
132/// Bpf identifier tag.
133#[derive(Debug, Clone, Default)]
134#[repr(C)]
135pub struct Tag(pub [u8; 8]);
136
137/// Information about a BPF program. Maps to `struct bpf_prog_info` in kernel uapi.
138#[derive(Debug, Clone)]
139#[doc(alias = "bpf_prog_info")]
140pub struct ProgramInfo {
141    /// A user-defined name for the BPF program.
142    pub name: OsString,
143    /// The type of the program.
144    pub ty: ProgramType,
145    /// An 8-byte hash (`BPF_TAG_SIZE`) computed from the program's
146    /// contents; used to detect changes in the program code.
147    pub tag: Tag,
148    /// A unique identifier for the program instance.
149    pub id: u32,
150    /// JIT-compiled instructions.
151    pub jited_prog_insns: Vec<u8>,
152    /// Translated BPF instructions in an intermediate representation.
153    pub xlated_prog_insns: Vec<u8>,
154    /// Time (since system boot) at which the program was loaded.
155    pub load_time: Duration,
156    /// UID of the user who loaded the program.
157    pub created_by_uid: u32,
158    /// Array of map IDs associated with this program.
159    pub map_ids: Vec<u32>,
160    /// Network interface index if the program is attached to a specific device.
161    pub ifindex: u32,
162    /// Whether the program is GPL compatible.
163    pub gpl_compatible: bool,
164    /// Device ID of the network namespace that the program is associated with.
165    pub netns_dev: u64,
166    /// Inode number of the network namespace associated with the program.
167    pub netns_ino: u64,
168    /// Number of kernel symbols in the JITed code (if available).
169    pub jited_ksyms: Vec<*const c_void>,
170    /// Number of function length records available for the JITed code.
171    pub jited_func_lens: Vec<u32>,
172    /// Identifier of the associated BTF (BPF Type Format) data.
173    pub btf_id: u32,
174    /// Size (in bytes) of each record in the function info array.
175    pub func_info_rec_size: u32,
176    /// Array of function info records for this program.
177    pub func_info: Vec<libbpf_sys::bpf_func_info>,
178    /// Array of line info records mapping BPF instructions to source code lines.
179    pub line_info: Vec<LineInfo>,
180    /// Line info records for the JIT-compiled code.
181    pub jited_line_info: Vec<*const c_void>,
182    /// Size (in bytes) of each line info record.
183    pub line_info_rec_size: u32,
184    /// Size (in bytes) of each record in the JITed line info array.
185    pub jited_line_info_rec_size: u32,
186    /// Array of program tags.
187    pub prog_tags: Vec<Tag>,
188    /// Total accumulated run time (in nanoseconds) for the program's execution.
189    pub run_time_ns: u64,
190    /// Total number of times the program has been executed.
191    pub run_cnt: u64,
192    /// Skipped BPF executions due to recursion or concurrent execution prevention.
193    pub recursion_misses: u64,
194    /// Number of instructions that were verified by the verifier.
195    pub verified_insns: u32,
196    /// The struct is non-exhaustive and open to extension.
197    #[doc(hidden)]
198    pub _non_exhaustive: (),
199}
200
201/// An iterator for the information of loaded bpf programs.
202#[derive(Default, Debug)]
203#[doc(alias = "bpf_prog_get_next_id")]
204pub struct ProgInfoIter {
205    cur_id: u32,
206    opts: ProgInfoQueryOptions,
207}
208
209/// Options to query the program info currently loaded.
210#[derive(Clone, Default, Debug)]
211pub struct ProgInfoQueryOptions {
212    /// Include the vector of bpf instructions in the result.
213    include_xlated_prog_insns: bool,
214    /// Include the vector of jited instructions in the result.
215    include_jited_prog_insns: bool,
216    /// Include the ids of maps associated with the program.
217    include_map_ids: bool,
218    /// Include source line information corresponding to xlated code.
219    include_line_info: bool,
220    /// Include function type information corresponding to xlated code.
221    include_func_info: bool,
222    /// Include source line information corresponding to jited code.
223    include_jited_line_info: bool,
224    /// Include function type information corresponding to jited code.
225    include_jited_func_lens: bool,
226    /// Include program tags.
227    include_prog_tags: bool,
228    /// Include the jited kernel symbols.
229    include_jited_ksyms: bool,
230}
231
232impl ProgInfoIter {
233    /// Generate an iter from more specific query options.
234    pub fn with_query_opts(opts: ProgInfoQueryOptions) -> Self {
235        Self {
236            opts,
237            ..Self::default()
238        }
239    }
240}
241
242impl ProgInfoQueryOptions {
243    /// Include the vector of jited bpf instructions in the result.
244    pub fn include_xlated_prog_insns(mut self, v: bool) -> Self {
245        self.include_xlated_prog_insns = v;
246        self
247    }
248
249    /// Include the vector of jited instructions in the result.
250    pub fn include_jited_prog_insns(mut self, v: bool) -> Self {
251        self.include_jited_prog_insns = v;
252        self
253    }
254
255    /// Include the ids of maps associated with the program.
256    pub fn include_map_ids(mut self, v: bool) -> Self {
257        self.include_map_ids = v;
258        self
259    }
260
261    /// Include source line information corresponding to xlated code.
262    pub fn include_line_info(mut self, v: bool) -> Self {
263        self.include_line_info = v;
264        self
265    }
266
267    /// Include function type information corresponding to xlated code.
268    pub fn include_func_info(mut self, v: bool) -> Self {
269        self.include_func_info = v;
270        self
271    }
272
273    /// Include source line information corresponding to jited code.
274    pub fn include_jited_line_info(mut self, v: bool) -> Self {
275        self.include_jited_line_info = v;
276        self
277    }
278
279    /// Include function type information corresponding to jited code.
280    pub fn include_jited_func_lens(mut self, v: bool) -> Self {
281        self.include_jited_func_lens = v;
282        self
283    }
284
285    /// Include program tags.
286    pub fn include_prog_tags(mut self, v: bool) -> Self {
287        self.include_prog_tags = v;
288        self
289    }
290
291    /// Include the jited kernel symbols.
292    pub fn include_jited_ksyms(mut self, v: bool) -> Self {
293        self.include_jited_ksyms = v;
294        self
295    }
296
297    /// Include everything there is in the query results.
298    pub fn include_all(self) -> Self {
299        Self {
300            include_xlated_prog_insns: true,
301            include_jited_prog_insns: true,
302            include_map_ids: true,
303            include_line_info: true,
304            include_func_info: true,
305            include_jited_line_info: true,
306            include_jited_func_lens: true,
307            include_prog_tags: true,
308            include_jited_ksyms: true,
309        }
310    }
311}
312
313impl ProgramInfo {
314    fn load_from_fd(fd: BorrowedFd<'_>, opts: &ProgInfoQueryOptions) -> Result<Self> {
315        let mut item = libbpf_sys::bpf_prog_info::default();
316
317        let mut xlated_prog_insns: Vec<u8> = Vec::new();
318        let mut jited_prog_insns: Vec<u8> = Vec::new();
319        let mut map_ids: Vec<u32> = Vec::new();
320        let mut jited_line_info: Vec<*const c_void> = Vec::new();
321        let mut line_info: Vec<libbpf_sys::bpf_line_info> = Vec::new();
322        let mut func_info: Vec<libbpf_sys::bpf_func_info> = Vec::new();
323        let mut jited_func_lens: Vec<u32> = Vec::new();
324        let mut prog_tags: Vec<Tag> = Vec::new();
325        let mut jited_ksyms: Vec<*const c_void> = Vec::new();
326
327        let item_ptr: *mut libbpf_sys::bpf_prog_info = &mut item;
328        let mut len = size_of_val(&item) as u32;
329
330        let ret = unsafe {
331            libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr.cast::<c_void>(), &mut len)
332        };
333        util::parse_ret(ret)?;
334
335        // SANITY: `libbpf` should guarantee NUL termination.
336        let name = util::c_char_slice_to_cstr(&item.name).unwrap();
337        let ty = ProgramType::from(item.type_);
338
339        if opts.include_xlated_prog_insns {
340            xlated_prog_insns.resize(item.xlated_prog_len as usize, 0u8);
341            item.xlated_prog_insns = xlated_prog_insns.as_mut_ptr().cast::<c_void>() as u64;
342        } else {
343            item.xlated_prog_len = 0;
344        }
345
346        if opts.include_jited_prog_insns {
347            jited_prog_insns.resize(item.jited_prog_len as usize, 0u8);
348            item.jited_prog_insns = jited_prog_insns.as_mut_ptr().cast::<c_void>() as u64;
349        } else {
350            item.jited_prog_len = 0;
351        }
352
353        if opts.include_map_ids {
354            map_ids.resize(item.nr_map_ids as usize, 0u32);
355            item.map_ids = map_ids.as_mut_ptr().cast::<c_void>() as u64;
356        } else {
357            item.nr_map_ids = 0;
358        }
359
360        if opts.include_line_info {
361            line_info.resize(
362                item.nr_line_info as usize,
363                libbpf_sys::bpf_line_info::default(),
364            );
365            item.line_info = line_info.as_mut_ptr().cast::<c_void>() as u64;
366        } else {
367            item.nr_line_info = 0;
368        }
369
370        if opts.include_func_info {
371            func_info.resize(
372                item.nr_func_info as usize,
373                libbpf_sys::bpf_func_info::default(),
374            );
375            item.func_info = func_info.as_mut_ptr().cast::<c_void>() as u64;
376        } else {
377            item.nr_func_info = 0;
378        }
379
380        if opts.include_jited_line_info {
381            jited_line_info.resize(item.nr_jited_line_info as usize, ptr::null());
382            item.jited_line_info = jited_line_info.as_mut_ptr().cast::<c_void>() as u64;
383        } else {
384            item.nr_jited_line_info = 0;
385        }
386
387        if opts.include_jited_func_lens {
388            jited_func_lens.resize(item.nr_jited_func_lens as usize, 0);
389            item.jited_func_lens = jited_func_lens.as_mut_ptr().cast::<c_void>() as u64;
390        } else {
391            item.nr_jited_func_lens = 0;
392        }
393
394        if opts.include_prog_tags {
395            prog_tags.resize(item.nr_prog_tags as usize, Tag::default());
396            item.prog_tags = prog_tags.as_mut_ptr().cast::<c_void>() as u64;
397        } else {
398            item.nr_prog_tags = 0;
399        }
400
401        if opts.include_jited_ksyms {
402            jited_ksyms.resize(item.nr_jited_ksyms as usize, ptr::null());
403            item.jited_ksyms = jited_ksyms.as_mut_ptr().cast::<c_void>() as u64;
404        } else {
405            item.nr_jited_ksyms = 0;
406        }
407
408        let ret = unsafe {
409            libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr.cast::<c_void>(), &mut len)
410        };
411        util::parse_ret(ret)?;
412
413        Ok(Self {
414            name: cstr_to_os_string(name),
415            ty,
416            tag: Tag(item.tag),
417            id: item.id,
418            jited_prog_insns,
419            xlated_prog_insns,
420            load_time: Duration::from_nanos(item.load_time),
421            created_by_uid: item.created_by_uid,
422            map_ids,
423            ifindex: item.ifindex,
424            gpl_compatible: item._bitfield_1.get_bit(0),
425            netns_dev: item.netns_dev,
426            netns_ino: item.netns_ino,
427            jited_ksyms,
428            jited_func_lens,
429            btf_id: item.btf_id,
430            func_info_rec_size: item.func_info_rec_size,
431            func_info,
432            line_info: line_info.iter().map(Into::into).collect(),
433            jited_line_info,
434            line_info_rec_size: item.line_info_rec_size,
435            jited_line_info_rec_size: item.jited_line_info_rec_size,
436            prog_tags,
437            run_time_ns: item.run_time_ns,
438            run_cnt: item.run_cnt,
439            recursion_misses: item.recursion_misses,
440            verified_insns: item.verified_insns,
441            _non_exhaustive: (),
442        })
443    }
444}
445
446impl ProgInfoIter {
447    fn next_valid_fd(&mut self) -> Option<OwnedFd> {
448        loop {
449            if unsafe { libbpf_sys::bpf_prog_get_next_id(self.cur_id, &mut self.cur_id) } != 0 {
450                return None;
451            }
452
453            let fd = unsafe { libbpf_sys::bpf_prog_get_fd_by_id(self.cur_id) };
454            if fd < 0 {
455                let err = io::Error::last_os_error();
456                if err.kind() == io::ErrorKind::NotFound {
457                    continue;
458                }
459                return None;
460            }
461
462            return Some(unsafe { OwnedFd::from_raw_fd(fd) });
463        }
464    }
465}
466
467impl Iterator for ProgInfoIter {
468    type Item = ProgramInfo;
469
470    fn next(&mut self) -> Option<Self::Item> {
471        let fd = self.next_valid_fd()?;
472        let prog = ProgramInfo::load_from_fd(fd.as_fd(), &self.opts);
473        prog.ok()
474    }
475}
476
477/// Information about a BPF map. Maps to `struct bpf_map_info` in kernel uapi.
478#[derive(Debug, Clone)]
479#[doc(alias = "bpf_map_info")]
480pub struct MapInfo {
481    /// A user-defined name for the BPF Map.
482    pub name: OsString,
483    /// The BPF map type.
484    pub ty: MapType,
485    /// A unique identifier for this map instance.
486    pub id: u32,
487    /// Size (in bytes) of the keys stored in the map.
488    pub key_size: u32,
489    /// Size (in bytes) of the values stored in the map.
490    pub value_size: u32,
491    /// Maximum number of entries that the map can hold.
492    pub max_entries: u32,
493    /// Map flags indicating specific properties (e.g., `BPF_F_NO_PREALLOC`).
494    pub map_flags: u32,
495    /// Network interface index if the map is associated with a specific device. Otherwise, this
496    /// may be zero.
497    pub ifindex: u32,
498    /// BTF (BPF Type Format) type ID for the value type as defined in the vmlinux BTF data.
499    pub btf_vmlinux_value_type_id: u32,
500    /// Device identifier of the network namespace.
501    pub netns_dev: u64,
502    /// Inode number of the network namespace.
503    pub netns_ino: u64,
504    /// BTF ID referencing the BTF data for this map. This helps to verify the correctness of the
505    /// map's data structure as per BTF metadata.
506    pub btf_id: u32,
507    /// BTF type ID for the key type.
508    pub btf_key_type_id: u32,
509    /// BTF type ID for the value type.
510    pub btf_value_type_id: u32,
511}
512
513impl MapInfo {
514    fn from_uapi(_fd: BorrowedFd<'_>, s: libbpf_sys::bpf_map_info) -> Option<Self> {
515        // SANITY: `libbpf` should guarantee NUL termination.
516        let name = util::c_char_slice_to_cstr(&s.name).unwrap();
517        let ty = MapType::from(s.type_);
518
519        Some(Self {
520            name: cstr_to_os_string(name),
521            ty,
522            id: s.id,
523            key_size: s.key_size,
524            value_size: s.value_size,
525            max_entries: s.max_entries,
526            map_flags: s.map_flags,
527            ifindex: s.ifindex,
528            btf_vmlinux_value_type_id: s.btf_vmlinux_value_type_id,
529            netns_dev: s.netns_dev,
530            netns_ino: s.netns_ino,
531            btf_id: s.btf_id,
532            btf_key_type_id: s.btf_key_type_id,
533            btf_value_type_id: s.btf_value_type_id,
534        })
535    }
536}
537
538gen_info_impl!(
539    /// Iterator that returns [`MapInfo`]s.
540    #[doc(alias = "bpf_map_get_next_id")]
541    MapInfoIter,
542    MapInfo,
543    libbpf_sys::bpf_map_info,
544    libbpf_sys::bpf_map_get_next_id,
545    libbpf_sys::bpf_map_get_fd_by_id
546);
547
548/// Information about BPF type format.
549#[derive(Debug, Clone)]
550#[doc(alias = "bpf_btf_info")]
551pub struct BtfInfo {
552    /// The name associated with this btf information in the kernel.
553    pub name: OsString,
554    /// The raw btf bytes from the kernel.
555    pub btf: Vec<u8>,
556    /// The btf id associated with this btf information in the kernel.
557    pub id: u32,
558}
559
560impl BtfInfo {
561    fn load_from_fd(fd: BorrowedFd<'_>) -> Result<Self> {
562        let mut item = libbpf_sys::bpf_btf_info::default();
563        let mut btf: Vec<u8> = Vec::new();
564        let mut name: Vec<u8> = Vec::new();
565
566        let item_ptr: *mut libbpf_sys::bpf_btf_info = &mut item;
567        let mut len = size_of_val(&item) as u32;
568
569        let ret = unsafe {
570            libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr.cast::<c_void>(), &mut len)
571        };
572        util::parse_ret(ret)?;
573
574        // The API gives you the ascii string length while expecting
575        // you to give it back space for a nul-terminator
576        item.name_len += 1;
577        name.resize(item.name_len as usize, 0u8);
578        item.name = name.as_mut_ptr().cast::<c_void>() as u64;
579
580        btf.resize(item.btf_size as usize, 0u8);
581        item.btf = btf.as_mut_ptr().cast::<c_void>() as u64;
582
583        let ret = unsafe {
584            libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr.cast::<c_void>(), &mut len)
585        };
586        util::parse_ret(ret)?;
587
588        Ok(Self {
589            // SANITY: Our buffer contained space for a NUL byte and we set its
590            //         contents to 0. Barring a `libbpf` bug a NUL byte will be
591            //         present.
592            name: cstr_to_os_string(CStr::from_bytes_with_nul(&name).unwrap()),
593            btf,
594            id: item.id,
595        })
596    }
597}
598
599#[derive(Debug, Default)]
600/// An iterator for the btf type information of modules and programs
601/// in the kernel
602#[doc(alias = "bpf_btf_get_next_id")]
603#[doc(alias = "bpf_btf_get_fd_by_id")]
604pub struct BtfInfoIter {
605    cur_id: u32,
606}
607
608impl BtfInfoIter {
609    // Returns Some(next_valid_fd), None on none left
610    fn next_valid_fd(&mut self) -> Option<OwnedFd> {
611        loop {
612            if unsafe { libbpf_sys::bpf_btf_get_next_id(self.cur_id, &mut self.cur_id) } != 0 {
613                return None;
614            }
615
616            let fd = unsafe { libbpf_sys::bpf_btf_get_fd_by_id(self.cur_id) };
617            if fd < 0 {
618                let err = io::Error::last_os_error();
619                if err.kind() == io::ErrorKind::NotFound {
620                    continue;
621                }
622                return None;
623            }
624
625            return Some(unsafe { OwnedFd::from_raw_fd(fd) });
626        }
627    }
628}
629
630impl Iterator for BtfInfoIter {
631    type Item = BtfInfo;
632
633    fn next(&mut self) -> Option<Self::Item> {
634        let fd = self.next_valid_fd()?;
635        let info = BtfInfo::load_from_fd(fd.as_fd());
636        info.ok()
637    }
638}
639
640/// Information about a raw tracepoint.
641#[derive(Debug, Clone)]
642pub struct RawTracepointLinkInfo {
643    /// The name of the raw tracepoint.
644    pub name: String,
645    /// The struct is non-exhaustive and open to extension.
646    #[doc(hidden)]
647    pub _non_exhaustive: (),
648}
649
650/// Information about a tracing link
651#[derive(Debug, Clone)]
652pub struct TracingLinkInfo {
653    /// Attach type of the tracing link.
654    pub attach_type: ProgramAttachType,
655    /// Target object ID (`prog_id` for [`ProgramType::Ext`], otherwise
656    /// BTF object id).
657    pub target_obj_id: u32,
658    /// BTF type id inside the target object.
659    pub target_btf_id: u32,
660    /// The struct is non-exhaustive and open to extension.
661    #[doc(hidden)]
662    pub _non_exhaustive: (),
663}
664
665/// Information about a cgroup link
666#[derive(Debug, Clone)]
667pub struct CgroupLinkInfo {
668    /// Identifier of the target cgroup.
669    pub cgroup_id: u64,
670    /// Attachment type for cgroup-based programs.
671    pub attach_type: ProgramAttachType,
672    /// The struct is non-exhaustive and open to extension.
673    #[doc(hidden)]
674    pub _non_exhaustive: (),
675}
676
677/// Information about a BPF iterator link.
678#[derive(Debug, Clone)]
679pub struct IterLinkInfo {
680    /// The `bpf_iter__*` target name.
681    pub target_name: OsString,
682    /// Specific BPF iterator information.
683    pub iter_type: IterType,
684    /// The struct is non-exhaustive and open to extension.
685    #[doc(hidden)]
686    pub _non_exhaustive: (),
687}
688
689/// Specific BPF iterator types with decoded information.
690#[derive(Debug, Clone)]
691pub enum IterType {
692    /// A map type iterator.
693    Map {
694        /// The ID of the map being iterated.
695        map_id: u32,
696    },
697    /// A cgroup type iterator.
698    Cgroup {
699        /// The cgroup ID of where the iterator starts.
700        cgroup_id: u64,
701        /// The order in how the iterator traverses.
702        order: CgroupIterOrder,
703    },
704    /// A task type iterator.
705    Task {
706        /// Specific thread that is iterated over.
707        tid: u32,
708        /// Specific process that is iterated over.
709        pid: u32,
710    },
711    /// An unknown or unsupported iterator type.
712    ///
713    /// The [`target_name`](IterLinkInfo::target_name) can still be used to
714    /// identify the iterator.
715    Unknown,
716}
717
718/// Information about a network namespace link.
719#[derive(Debug, Clone)]
720pub struct NetNsLinkInfo {
721    /// Inode number of the network namespace.
722    pub ino: u32,
723    /// Attachment type for network namespace programs.
724    pub attach_type: ProgramAttachType,
725    /// The struct is non-exhaustive and open to extension.
726    #[doc(hidden)]
727    pub _non_exhaustive: (),
728}
729
730/// Information about a BPF netfilter link.
731#[derive(Debug, Clone)]
732pub struct NetfilterLinkInfo {
733    /// Protocol family of the netfilter hook.
734    pub protocol_family: u32,
735    /// Netfilter hook number.
736    pub hooknum: u32,
737    /// Priority of the netfilter link.
738    pub priority: i32,
739    /// Flags used for the netfilter link.
740    pub flags: u32,
741    /// The struct is non-exhaustive and open to extension.
742    #[doc(hidden)]
743    pub _non_exhaustive: (),
744}
745
746/// Information about a XDP link.
747#[derive(Debug, Clone)]
748pub struct XdpLinkInfo {
749    /// Interface index to which the XDP link is attached.
750    pub ifindex: u32,
751    /// The struct is non-exhaustive and open to extension.
752    #[doc(hidden)]
753    pub _non_exhaustive: (),
754}
755
756/// Information about a BPF sockmap link.
757#[derive(Debug, Clone)]
758pub struct SockMapLinkInfo {
759    /// The ID of the BPF sockmap.
760    pub map_id: u32,
761    /// The type of program attached to the sockmap.
762    pub attach_type: ProgramAttachType,
763    /// The struct is non-exhaustive and open to extension.
764    #[doc(hidden)]
765    pub _non_exhaustive: (),
766}
767
768/// Information about a BPF netkit link.
769#[derive(Debug, Clone)]
770pub struct NetkitLinkInfo {
771    /// Interface index to which the netkit link is attached.
772    pub ifindex: u32,
773    /// Type of program attached to the netkit link.
774    pub attach_type: ProgramAttachType,
775    /// The struct is non-exhaustive and open to extension.
776    #[doc(hidden)]
777    pub _non_exhaustive: (),
778}
779
780/// Information about a BPF tc link.
781#[derive(Debug, Clone)]
782pub struct TcxLinkInfo {
783    /// Interface index to which the tc link is attached.
784    pub ifindex: u32,
785    /// Type of program attached to the tc link.
786    pub attach_type: ProgramAttachType,
787    /// The struct is non-exhaustive and open to extension.
788    #[doc(hidden)]
789    pub _non_exhaustive: (),
790}
791
792/// Information about a BPF `struct_ops` link.
793#[derive(Debug, Clone)]
794pub struct StructOpsLinkInfo {
795    /// The ID of the BPF map to which the `struct_ops` link is attached.
796    pub map_id: u32,
797    /// The struct is non-exhaustive and open to extension.
798    #[doc(hidden)]
799    pub _non_exhaustive: (),
800}
801
802/// Information about a multi-kprobe link.
803#[derive(Debug, Clone)]
804pub struct KprobeMultiLinkInfo {
805    /// Count of kprobe targets.
806    pub count: u32,
807    /// Flags for the link.
808    pub flags: u32,
809    /// Missed probes count.
810    pub missed: u64,
811    /// Addresses of the probe.
812    pub addrs: Vec<u64>,
813    /// Cookies corresponding to the attach addresses.
814    pub cookies: Vec<u64>,
815    /// The struct is non-exhaustive and open to extension.
816    #[doc(hidden)]
817    pub _non_exhaustive: (),
818}
819
820/// Information about a multi-uprobe link.
821#[derive(Debug, Clone)]
822pub struct UprobeMultiLinkInfo {
823    /// Size of the path.
824    pub path_size: u32,
825    /// The absolute file path of the binary being probed.
826    pub path: Option<PathBuf>,
827    /// Count of uprobe targets.
828    pub count: u32,
829    /// Flags for the link.
830    pub flags: u32,
831    /// PID to which the uprobe is attached.
832    pub pid: u32,
833    /// Offsets from the binary.
834    pub offsets: Vec<u64>,
835    /// Offsets of kernel reference counted USDT semaphore.
836    pub ref_ctr_offsets: Vec<u64>,
837    /// Cookies corresponding to the attach addresses.
838    pub cookies: Vec<u64>,
839    /// The struct is non-exhaustive and open to extension.
840    #[doc(hidden)]
841    pub _non_exhaustive: (),
842}
843
844/// Information about a perf event link.
845#[derive(Debug, Clone)]
846pub struct PerfEventLinkInfo {
847    /// The specific type of perf event with decoded information.
848    pub event_type: PerfEventType,
849    /// The struct is non-exhaustive and open to extension.
850    #[doc(hidden)]
851    pub _non_exhaustive: (),
852}
853
854/// Specific types of perf events with decoded information.
855#[derive(Debug, Clone)]
856pub enum PerfEventType {
857    /// A tracepoint event.
858    Tracepoint {
859        /// The tracepoint name.
860        name: Option<OsString>,
861        /// Attach cookie value for this link.
862        cookie: u64,
863    },
864    /// A kprobe event (includes both kprobe and kretprobe).
865    Kprobe {
866        /// The function being probed.
867        func_name: Option<OsString>,
868        /// Whether this is a return probe (kretprobe).
869        is_retprobe: bool,
870        /// Address of the probe.
871        addr: u64,
872        /// Offset from the function.
873        offset: u32,
874        /// Number of missed events.
875        missed: u64,
876        /// Cookie value for the kprobe.
877        cookie: u64,
878    },
879    /// A uprobe event (includes both uprobe and uretprobe).
880    Uprobe {
881        /// The absolute file path of the binary being probed.
882        file_name: Option<OsString>,
883        /// Whether this is a return probe (uretprobe).
884        is_retprobe: bool,
885        /// Offset from the binary.
886        offset: u32,
887        /// Cookie value for the uprobe.
888        cookie: u64,
889        /// Offset of kernel reference counted USDT semaphore.
890        ref_ctr_offset: u64,
891    },
892    /// A perf event.
893    Event {
894        /// The specific event of the perf event type.
895        config: u64,
896        /// The perf event type.
897        event_type: u32,
898        /// Cookie value for the perf event program.
899        cookie: u64,
900    },
901    /// An unknown or unsupported perf event type.
902    Unknown(u32),
903}
904
905/// Information about BPF link types. Maps to the anonymous union in `struct bpf_link_info` in
906/// kernel uapi.
907#[derive(Debug, Clone)]
908pub enum LinkTypeInfo {
909    /// Link type for raw tracepoints.
910    ///
911    /// Contains information about the BPF program directly to a raw tracepoint.
912    RawTracepoint(RawTracepointLinkInfo),
913    /// Tracing link type.
914    Tracing(TracingLinkInfo),
915    /// Link type for cgroup programs.
916    ///
917    /// Contains information about the cgroups and its attachment type.
918    Cgroup(CgroupLinkInfo),
919    /// Iterator link type.
920    Iter(IterLinkInfo),
921    /// Network namespace link type.
922    NetNs(NetNsLinkInfo),
923    /// Link type for XDP programs.
924    ///
925    /// Contains information about the XDP link, such as the interface index
926    /// to which the XDP link is attached.
927    Xdp(XdpLinkInfo),
928    /// Link type for `struct_ops` programs.
929    ///
930    /// Contains information about the BPF map to which the `struct_ops` link is
931    /// attached.
932    StructOps(StructOpsLinkInfo),
933    /// Link type for netfilter programs.
934    Netfilter(NetfilterLinkInfo),
935    /// Link type for kprobe-multi links.
936    KprobeMulti(KprobeMultiLinkInfo),
937    /// Link type for multi-uprobe links.
938    UprobeMulti(UprobeMultiLinkInfo),
939    /// Link type for TC programs.
940    Tcx(TcxLinkInfo),
941    /// Link type for netkit programs.
942    Netkit(NetkitLinkInfo),
943    /// Link type for sockmap programs.
944    SockMap(SockMapLinkInfo),
945    /// Link type for perf-event programs.
946    ///
947    /// Contains information about the perf event configuration including type and config
948    /// which can be used to identify tracepoints, kprobes, uprobes, etc.
949    PerfEvent(PerfEventLinkInfo),
950    /// Unknown link type.
951    Unknown,
952}
953
954/// Information about a BPF link. Maps to `struct bpf_link_info` in kernel uapi.
955#[derive(Debug, Clone)]
956#[doc(alias = "bpf_link_info")]
957pub struct LinkInfo {
958    /// Information about the BPF link type.
959    pub info: LinkTypeInfo,
960    /// Unique identifier of the BPF link.
961    pub id: u32,
962    /// ID of the BPF program attached via this link.
963    pub prog_id: u32,
964}
965
966impl LinkInfo {
967    /// Create a `LinkInfo` object from a fd.
968    #[doc(alias = "bpf_obj_get_info_by_fd")]
969    pub fn from_fd(fd: BorrowedFd<'_>) -> Result<Self> {
970        // See comment in gen_info_impl!() for why we use std::mem::zeroed()
971        let mut link_info: libbpf_sys::bpf_link_info = unsafe { zeroed() };
972        let item_ptr: *mut libbpf_sys::bpf_link_info = &mut link_info;
973        let mut len = size_of_val(&link_info) as u32;
974
975        let ret = unsafe {
976            libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr.cast::<c_void>(), &mut len)
977        };
978        util::parse_ret(ret)?;
979
980        Self::from_uapi(fd, link_info)
981            .ok_or_else(|| crate::Error::with_invalid_data("failed to parse link info"))
982    }
983
984    fn from_uapi(fd: BorrowedFd<'_>, mut s: libbpf_sys::bpf_link_info) -> Option<Self> {
985        let type_info = match s.type_ {
986            libbpf_sys::BPF_LINK_TYPE_RAW_TRACEPOINT => {
987                let mut buf = [0u8; 256];
988                s.__bindgen_anon_1.raw_tracepoint.tp_name = buf.as_mut_ptr() as u64;
989                s.__bindgen_anon_1.raw_tracepoint.tp_name_len = buf.len() as u32;
990                let item_ptr: *mut libbpf_sys::bpf_link_info = &mut s;
991                let mut len = size_of_val(&s) as u32;
992
993                let ret = unsafe {
994                    libbpf_sys::bpf_obj_get_info_by_fd(
995                        fd.as_raw_fd(),
996                        item_ptr.cast::<c_void>(),
997                        &mut len,
998                    )
999                };
1000                if ret != 0 {
1001                    return None;
1002                }
1003
1004                LinkTypeInfo::RawTracepoint(RawTracepointLinkInfo {
1005                    name: util::c_ptr_to_string(
1006                        unsafe { s.__bindgen_anon_1.raw_tracepoint.tp_name } as *const c_char,
1007                    )
1008                    .unwrap_or_else(|_| "?".to_string()),
1009                    _non_exhaustive: (),
1010                })
1011            }
1012            libbpf_sys::BPF_LINK_TYPE_TRACING => LinkTypeInfo::Tracing(TracingLinkInfo {
1013                attach_type: ProgramAttachType::from(unsafe {
1014                    s.__bindgen_anon_1.tracing.attach_type
1015                }),
1016                target_obj_id: unsafe { s.__bindgen_anon_1.tracing.target_obj_id },
1017                target_btf_id: unsafe { s.__bindgen_anon_1.tracing.target_btf_id },
1018                _non_exhaustive: (),
1019            }),
1020            libbpf_sys::BPF_LINK_TYPE_CGROUP => LinkTypeInfo::Cgroup(CgroupLinkInfo {
1021                cgroup_id: unsafe { s.__bindgen_anon_1.cgroup.cgroup_id },
1022                attach_type: ProgramAttachType::from(unsafe {
1023                    s.__bindgen_anon_1.cgroup.attach_type
1024                }),
1025                _non_exhaustive: (),
1026            }),
1027            libbpf_sys::BPF_LINK_TYPE_ITER => {
1028                let mut buf = [0u8; 256];
1029                s.__bindgen_anon_1.iter.target_name = buf.as_mut_ptr() as u64;
1030                s.__bindgen_anon_1.iter.target_name_len = buf.len() as u32;
1031                let item_ptr: *mut libbpf_sys::bpf_link_info = &mut s;
1032                let mut len = size_of_val(&s) as u32;
1033
1034                let ret = unsafe {
1035                    libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr.cast(), &mut len)
1036                };
1037                if ret != 0 {
1038                    return None;
1039                }
1040
1041                let iter_info = unsafe { s.__bindgen_anon_1.iter };
1042                // On a successful call the kernel always reports the target name
1043                // (its length is `strlen(target) + 1`) and copies it into `buf`,
1044                // so it is guaranteed to be present here.
1045                let target_name = unsafe {
1046                    cstr_to_os_string(CStr::from_ptr(iter_info.target_name as *const c_char))
1047                };
1048
1049                let iter_type = match target_name.as_bytes() {
1050                    b"bpf_map_elem" | b"bpf_sk_storage_map" => IterType::Map {
1051                        map_id: unsafe { iter_info.__bindgen_anon_1.map.map_id },
1052                    },
1053                    b"cgroup" => {
1054                        let order = match unsafe { iter_info.__bindgen_anon_2.cgroup.order } {
1055                            libbpf_sys::BPF_CGROUP_ITER_SELF_ONLY => CgroupIterOrder::SelfOnly,
1056                            libbpf_sys::BPF_CGROUP_ITER_DESCENDANTS_PRE => {
1057                                CgroupIterOrder::DescendantsPre
1058                            }
1059                            libbpf_sys::BPF_CGROUP_ITER_DESCENDANTS_POST => {
1060                                CgroupIterOrder::DescendantsPost
1061                            }
1062                            libbpf_sys::BPF_CGROUP_ITER_ANCESTORS_UP => {
1063                                CgroupIterOrder::AncestorsUp
1064                            }
1065                            _ => CgroupIterOrder::Default,
1066                        };
1067                        IterType::Cgroup {
1068                            cgroup_id: unsafe { iter_info.__bindgen_anon_2.cgroup.cgroup_id },
1069                            order,
1070                        }
1071                    }
1072                    b"task" | b"task_file" | b"task_vma" => IterType::Task {
1073                        tid: unsafe { iter_info.__bindgen_anon_2.task.tid },
1074                        pid: unsafe { iter_info.__bindgen_anon_2.task.pid },
1075                    },
1076                    _ => IterType::Unknown,
1077                };
1078
1079                LinkTypeInfo::Iter(IterLinkInfo {
1080                    target_name,
1081                    iter_type,
1082                    _non_exhaustive: (),
1083                })
1084            }
1085            libbpf_sys::BPF_LINK_TYPE_NETNS => LinkTypeInfo::NetNs(NetNsLinkInfo {
1086                ino: unsafe { s.__bindgen_anon_1.netns.netns_ino },
1087                attach_type: ProgramAttachType::from(unsafe {
1088                    s.__bindgen_anon_1.netns.attach_type
1089                }),
1090                _non_exhaustive: (),
1091            }),
1092            libbpf_sys::BPF_LINK_TYPE_NETFILTER => LinkTypeInfo::Netfilter(NetfilterLinkInfo {
1093                protocol_family: unsafe { s.__bindgen_anon_1.netfilter.pf },
1094                hooknum: unsafe { s.__bindgen_anon_1.netfilter.hooknum },
1095                priority: unsafe { s.__bindgen_anon_1.netfilter.priority },
1096                flags: unsafe { s.__bindgen_anon_1.netfilter.flags },
1097                _non_exhaustive: (),
1098            }),
1099            libbpf_sys::BPF_LINK_TYPE_XDP => LinkTypeInfo::Xdp(XdpLinkInfo {
1100                ifindex: unsafe { s.__bindgen_anon_1.xdp.ifindex },
1101                _non_exhaustive: (),
1102            }),
1103            libbpf_sys::BPF_LINK_TYPE_NETKIT => LinkTypeInfo::Netkit(NetkitLinkInfo {
1104                ifindex: unsafe { s.__bindgen_anon_1.netkit.ifindex },
1105                attach_type: ProgramAttachType::from(unsafe {
1106                    s.__bindgen_anon_1.netkit.attach_type
1107                }),
1108                _non_exhaustive: (),
1109            }),
1110            libbpf_sys::BPF_LINK_TYPE_TCX => LinkTypeInfo::Tcx(TcxLinkInfo {
1111                ifindex: unsafe { s.__bindgen_anon_1.tcx.ifindex },
1112                attach_type: ProgramAttachType::from(unsafe { s.__bindgen_anon_1.tcx.attach_type }),
1113                _non_exhaustive: (),
1114            }),
1115            libbpf_sys::BPF_LINK_TYPE_STRUCT_OPS => LinkTypeInfo::StructOps(StructOpsLinkInfo {
1116                map_id: unsafe { s.__bindgen_anon_1.struct_ops.map_id },
1117                _non_exhaustive: (),
1118            }),
1119            libbpf_sys::BPF_LINK_TYPE_KPROBE_MULTI => {
1120                let count = unsafe { s.__bindgen_anon_1.kprobe_multi.count } as usize;
1121                let mut addrs = vec![0; count];
1122                let mut cookies = vec![0; count];
1123
1124                s.__bindgen_anon_1.kprobe_multi.addrs = addrs.as_mut_ptr() as u64;
1125                s.__bindgen_anon_1.kprobe_multi.cookies = cookies.as_mut_ptr() as u64;
1126                let item_ptr: *mut libbpf_sys::bpf_link_info = &mut s;
1127                let mut len = size_of_val(&s) as u32;
1128                let ret = unsafe {
1129                    libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr.cast(), &mut len)
1130                };
1131                if ret != 0 {
1132                    return None;
1133                }
1134
1135                LinkTypeInfo::KprobeMulti(KprobeMultiLinkInfo {
1136                    count: unsafe { s.__bindgen_anon_1.kprobe_multi.count },
1137                    flags: unsafe { s.__bindgen_anon_1.kprobe_multi.flags },
1138                    missed: unsafe { s.__bindgen_anon_1.kprobe_multi.missed },
1139                    addrs,
1140                    cookies,
1141                    _non_exhaustive: (),
1142                })
1143            }
1144            libbpf_sys::BPF_LINK_TYPE_UPROBE_MULTI => {
1145                let mut buf = [0u8; libc::PATH_MAX as usize];
1146                let count = unsafe { s.__bindgen_anon_1.uprobe_multi.count } as usize;
1147                let mut offsets = vec![0; count];
1148                let mut ref_ctr_offsets = vec![0; count];
1149                let mut cookies = vec![0; count];
1150
1151                s.__bindgen_anon_1.uprobe_multi.path = buf.as_mut_ptr() as u64;
1152                s.__bindgen_anon_1.uprobe_multi.path_size = buf.len() as u32;
1153                s.__bindgen_anon_1.uprobe_multi.offsets = offsets.as_mut_ptr() as u64;
1154                s.__bindgen_anon_1.uprobe_multi.ref_ctr_offsets =
1155                    ref_ctr_offsets.as_mut_ptr() as u64;
1156                s.__bindgen_anon_1.uprobe_multi.cookies = cookies.as_mut_ptr() as u64;
1157                let item_ptr: *mut libbpf_sys::bpf_link_info = &mut s;
1158                let mut len = size_of_val(&s) as u32;
1159                let ret = unsafe {
1160                    libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr.cast(), &mut len)
1161                };
1162                if ret != 0 {
1163                    return None;
1164                }
1165
1166                let path_size = unsafe { s.__bindgen_anon_1.uprobe_multi.path_size };
1167                let path = if path_size != 0 {
1168                    let path_ptr = unsafe { s.__bindgen_anon_1.uprobe_multi.path } as *const c_char;
1169                    let c_str = unsafe { CStr::from_ptr(path_ptr) };
1170                    Some(PathBuf::from(OsStr::from_bytes(c_str.to_bytes())))
1171                } else {
1172                    None
1173                };
1174
1175                LinkTypeInfo::UprobeMulti(UprobeMultiLinkInfo {
1176                    path_size,
1177                    path,
1178                    count: unsafe { s.__bindgen_anon_1.uprobe_multi.count },
1179                    flags: unsafe { s.__bindgen_anon_1.uprobe_multi.flags },
1180                    pid: unsafe { s.__bindgen_anon_1.uprobe_multi.pid },
1181                    offsets,
1182                    ref_ctr_offsets,
1183                    cookies,
1184                    _non_exhaustive: (),
1185                })
1186            }
1187            libbpf_sys::BPF_LINK_TYPE_SOCKMAP => LinkTypeInfo::SockMap(SockMapLinkInfo {
1188                map_id: unsafe { s.__bindgen_anon_1.sockmap.map_id },
1189                attach_type: ProgramAttachType::from(unsafe {
1190                    s.__bindgen_anon_1.sockmap.attach_type
1191                }),
1192                _non_exhaustive: (),
1193            }),
1194            libbpf_sys::BPF_LINK_TYPE_PERF_EVENT => {
1195                // Get the BPF perf event type (BPF_PERF_EVENT_*) from the link info.
1196                let bpf_perf_event_type = unsafe { s.__bindgen_anon_1.perf_event.type_ };
1197
1198                // Handle two-phase call for perf event string data if needed (this mimics the
1199                // behavior of bpftool):
1200                // For tracepoints, kprobes, and uprobes, we need to pass in a buffer to get the
1201                // name. So we initialize the struct with a buffer pointer, and call
1202                // `bpf_obj_get_info_by_fd` again to populate the name.
1203                let mut buf = [0u8; libc::PATH_MAX as usize];
1204                let call_get_info_again = match bpf_perf_event_type {
1205                    libbpf_sys::BPF_PERF_EVENT_TRACEPOINT => {
1206                        s.__bindgen_anon_1
1207                            .perf_event
1208                            .__bindgen_anon_1
1209                            .tracepoint
1210                            .tp_name = buf.as_mut_ptr() as u64;
1211                        s.__bindgen_anon_1
1212                            .perf_event
1213                            .__bindgen_anon_1
1214                            .tracepoint
1215                            .name_len = buf.len() as u32;
1216                        true
1217                    }
1218                    libbpf_sys::BPF_PERF_EVENT_KPROBE | libbpf_sys::BPF_PERF_EVENT_KRETPROBE => {
1219                        s.__bindgen_anon_1
1220                            .perf_event
1221                            .__bindgen_anon_1
1222                            .kprobe
1223                            .func_name = buf.as_mut_ptr() as u64;
1224                        s.__bindgen_anon_1
1225                            .perf_event
1226                            .__bindgen_anon_1
1227                            .kprobe
1228                            .name_len = buf.len() as u32;
1229                        true
1230                    }
1231                    libbpf_sys::BPF_PERF_EVENT_UPROBE | libbpf_sys::BPF_PERF_EVENT_URETPROBE => {
1232                        // SAFETY: This field is valid to access in `bpf_link_info`.
1233                        let uprobe =
1234                            unsafe { &mut s.__bindgen_anon_1.perf_event.__bindgen_anon_1.uprobe };
1235                        uprobe.file_name = buf.as_mut_ptr() as u64;
1236                        uprobe.name_len = buf.len() as u32;
1237                        true
1238                    }
1239                    _ => false,
1240                };
1241
1242                if call_get_info_again {
1243                    let item_ptr: *mut libbpf_sys::bpf_link_info = &mut s;
1244                    let mut len = size_of_val(&s) as u32;
1245                    let ret = unsafe {
1246                        libbpf_sys::bpf_obj_get_info_by_fd(
1247                            fd.as_raw_fd(),
1248                            item_ptr.cast::<c_void>(),
1249                            &mut len,
1250                        )
1251                    };
1252                    if ret != 0 {
1253                        return None;
1254                    }
1255                }
1256
1257                let event_type = match bpf_perf_event_type {
1258                    libbpf_sys::BPF_PERF_EVENT_TRACEPOINT => {
1259                        let tp_name = unsafe {
1260                            s.__bindgen_anon_1
1261                                .perf_event
1262                                .__bindgen_anon_1
1263                                .tracepoint
1264                                .tp_name
1265                        };
1266                        let cookie = unsafe {
1267                            s.__bindgen_anon_1
1268                                .perf_event
1269                                .__bindgen_anon_1
1270                                .tracepoint
1271                                .cookie
1272                        };
1273                        let name = (tp_name != 0).then(|| unsafe {
1274                            cstr_to_os_string(CStr::from_ptr(tp_name as *const c_char))
1275                        });
1276
1277                        PerfEventType::Tracepoint { name, cookie }
1278                    }
1279                    libbpf_sys::BPF_PERF_EVENT_KPROBE | libbpf_sys::BPF_PERF_EVENT_KRETPROBE => {
1280                        let func_name = unsafe {
1281                            s.__bindgen_anon_1
1282                                .perf_event
1283                                .__bindgen_anon_1
1284                                .kprobe
1285                                .func_name
1286                        };
1287                        let addr =
1288                            unsafe { s.__bindgen_anon_1.perf_event.__bindgen_anon_1.kprobe.addr };
1289                        let offset =
1290                            unsafe { s.__bindgen_anon_1.perf_event.__bindgen_anon_1.kprobe.offset };
1291                        let missed =
1292                            unsafe { s.__bindgen_anon_1.perf_event.__bindgen_anon_1.kprobe.missed };
1293                        let cookie =
1294                            unsafe { s.__bindgen_anon_1.perf_event.__bindgen_anon_1.kprobe.cookie };
1295                        let func_name = (func_name != 0).then(|| unsafe {
1296                            cstr_to_os_string(CStr::from_ptr(func_name as *const c_char))
1297                        });
1298
1299                        let is_retprobe =
1300                            bpf_perf_event_type == libbpf_sys::BPF_PERF_EVENT_KRETPROBE;
1301                        PerfEventType::Kprobe {
1302                            func_name,
1303                            is_retprobe,
1304                            addr,
1305                            offset,
1306                            missed,
1307                            cookie,
1308                        }
1309                    }
1310                    libbpf_sys::BPF_PERF_EVENT_UPROBE | libbpf_sys::BPF_PERF_EVENT_URETPROBE => {
1311                        // SAFETY: This field is valid to access in `bpf_link_info`.
1312                        let uprobe =
1313                            unsafe { s.__bindgen_anon_1.perf_event.__bindgen_anon_1.uprobe };
1314                        // SAFETY: `file_name_ptr` is a valid nul terminated string pointer.
1315                        let file_name = (uprobe.file_name != 0).then(|| unsafe {
1316                            cstr_to_os_string(CStr::from_ptr(uprobe.file_name as *const c_char))
1317                        });
1318
1319                        PerfEventType::Uprobe {
1320                            file_name,
1321                            is_retprobe: bpf_perf_event_type
1322                                == libbpf_sys::BPF_PERF_EVENT_URETPROBE,
1323                            offset: uprobe.offset,
1324                            cookie: uprobe.cookie,
1325                            ref_ctr_offset: uprobe.ref_ctr_offset,
1326                        }
1327                    }
1328                    libbpf_sys::BPF_PERF_EVENT_EVENT => {
1329                        // SAFETY: This field is valid to access in `bpf_link_info`.
1330                        let event = unsafe { s.__bindgen_anon_1.perf_event.__bindgen_anon_1.event };
1331
1332                        PerfEventType::Event {
1333                            config: event.config,
1334                            event_type: event.type_,
1335                            cookie: event.cookie,
1336                        }
1337                    }
1338                    ty => PerfEventType::Unknown(ty),
1339                };
1340
1341                LinkTypeInfo::PerfEvent(PerfEventLinkInfo {
1342                    event_type,
1343                    _non_exhaustive: (),
1344                })
1345            }
1346            _ => LinkTypeInfo::Unknown,
1347        };
1348
1349        Some(Self {
1350            info: type_info,
1351            id: s.id,
1352            prog_id: s.prog_id,
1353        })
1354    }
1355}
1356
1357gen_info_impl!(
1358    /// Iterator that returns [`LinkInfo`]s.
1359    #[doc(alias = "bpf_link_get_next_id")]
1360    #[doc(alias = "bpf_link_get_fd_by_id")]
1361    LinkInfoIter,
1362    LinkInfo,
1363    libbpf_sys::bpf_link_info,
1364    libbpf_sys::bpf_link_get_next_id,
1365    libbpf_sys::bpf_link_get_fd_by_id
1366);