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