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::CString;
15use std::io;
16use std::mem::size_of_val;
17use std::os::fd::AsFd;
18use std::os::fd::AsRawFd;
19use std::os::fd::BorrowedFd;
20use std::os::fd::FromRawFd;
21use std::os::fd::OwnedFd;
22use std::os::raw::c_char;
23use std::ptr;
24use std::time::Duration;
25
26use crate::util;
27use crate::MapType;
28use crate::ProgramAttachType;
29use crate::ProgramType;
30use crate::Result;
31
32macro_rules! gen_info_impl {
33    // This magic here allows us to embed doc comments into macro expansions
34    ($(#[$attr:meta])*
35     $name:ident, $info_ty:ty, $uapi_info_ty:ty, $next_id:expr, $fd_by_id:expr) => {
36        $(#[$attr])*
37        #[derive(Default, Debug)]
38        pub struct $name {
39            cur_id: u32,
40        }
41
42        impl $name {
43            // Returns Some(next_valid_fd), None on none left
44            fn next_valid_fd(&mut self) -> Option<OwnedFd> {
45                loop {
46                    if unsafe { $next_id(self.cur_id, &mut self.cur_id) } != 0 {
47                        return None;
48                    }
49
50                    let fd = unsafe { $fd_by_id(self.cur_id) };
51                    if fd < 0 {
52                        let err = io::Error::last_os_error();
53                        if err.kind() == io::ErrorKind::NotFound {
54                            continue;
55                        }
56
57                        return None;
58                    }
59
60                    return Some(unsafe { OwnedFd::from_raw_fd(fd)});
61                }
62            }
63        }
64
65        impl Iterator for $name {
66            type Item = $info_ty;
67
68            fn next(&mut self) -> Option<Self::Item> {
69                let fd = self.next_valid_fd()?;
70
71                // We need to use std::mem::zeroed() instead of just using
72                // ::default() because padding bytes need to be zero as well.
73                // Old kernels which know about fewer fields than we do will
74                // check to make sure every byte past what they know is zero
75                // and will return E2BIG otherwise.
76                let mut item: $uapi_info_ty = unsafe { std::mem::zeroed() };
77                let item_ptr: *mut $uapi_info_ty = &mut item;
78                let mut len = size_of_val(&item) as u32;
79
80                let ret = unsafe { libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr as *mut c_void, &mut len) };
81                let parsed_uapi = if ret != 0 {
82                    None
83                } else {
84                    <$info_ty>::from_uapi(fd.as_fd(), item)
85                };
86
87                parsed_uapi
88            }
89        }
90    };
91}
92
93/// BTF Line information
94#[derive(Clone, Debug)]
95pub struct LineInfo {
96    /// Offset of instruction in vector
97    pub insn_off: u32,
98    /// File name offset
99    pub file_name_off: u32,
100    /// Line offset in debug info
101    pub line_off: u32,
102    /// Line number
103    pub line_num: u32,
104    /// Line column number
105    pub line_col: u32,
106}
107
108impl From<&libbpf_sys::bpf_line_info> for LineInfo {
109    fn from(item: &libbpf_sys::bpf_line_info) -> Self {
110        LineInfo {
111            insn_off: item.insn_off,
112            file_name_off: item.file_name_off,
113            line_off: item.line_off,
114            line_num: item.line_col >> 10,
115            line_col: item.line_col & 0x3ff,
116        }
117    }
118}
119
120/// Bpf identifier tag
121#[derive(Debug, Clone, Default)]
122#[repr(C)]
123pub struct Tag(pub [u8; 8]);
124
125/// Information about a BPF program
126#[derive(Debug, Clone)]
127// TODO: Document members.
128#[allow(missing_docs)]
129pub struct ProgramInfo {
130    pub name: CString,
131    pub ty: ProgramType,
132    pub tag: Tag,
133    pub id: u32,
134    pub jited_prog_insns: Vec<u8>,
135    pub xlated_prog_insns: Vec<u8>,
136    /// Duration since system boot
137    pub load_time: Duration,
138    pub created_by_uid: u32,
139    pub map_ids: Vec<u32>,
140    pub ifindex: u32,
141    pub gpl_compatible: bool,
142    pub netns_dev: u64,
143    pub netns_ino: u64,
144    pub jited_ksyms: Vec<*const c_void>,
145    pub jited_func_lens: Vec<u32>,
146    pub btf_id: u32,
147    pub func_info_rec_size: u32,
148    pub func_info: Vec<libbpf_sys::bpf_func_info>,
149    pub line_info: Vec<LineInfo>,
150    pub jited_line_info: Vec<*const c_void>,
151    pub line_info_rec_size: u32,
152    pub jited_line_info_rec_size: u32,
153    pub prog_tags: Vec<Tag>,
154    pub run_time_ns: u64,
155    pub run_cnt: u64,
156    /// Skipped BPF executions due to recursion or concurrent execution prevention.
157    pub recursion_misses: u64,
158}
159
160/// An iterator for the information of loaded bpf programs
161#[derive(Default, Debug)]
162pub struct ProgInfoIter {
163    cur_id: u32,
164    opts: ProgInfoQueryOptions,
165}
166
167/// Options to query the program info currently loaded
168#[derive(Clone, Default, Debug)]
169pub struct ProgInfoQueryOptions {
170    /// Include the vector of bpf instructions in the result
171    include_xlated_prog_insns: bool,
172    /// Include the vector of jited instructions in the result
173    include_jited_prog_insns: bool,
174    /// Include the ids of maps associated with the program
175    include_map_ids: bool,
176    /// Include source line information corresponding to xlated code
177    include_line_info: bool,
178    /// Include function type information corresponding to xlated code
179    include_func_info: bool,
180    /// Include source line information corresponding to jited code
181    include_jited_line_info: bool,
182    /// Include function type information corresponding to jited code
183    include_jited_func_lens: bool,
184    /// Include program tags
185    include_prog_tags: bool,
186    /// Include the jited kernel symbols
187    include_jited_ksyms: bool,
188}
189
190impl ProgInfoIter {
191    /// Generate an iter from more specific query options
192    pub fn with_query_opts(opts: ProgInfoQueryOptions) -> Self {
193        Self {
194            opts,
195            ..Self::default()
196        }
197    }
198}
199
200impl ProgInfoQueryOptions {
201    /// Include the vector of jited bpf instructions in the result
202    pub fn include_xlated_prog_insns(mut self, v: bool) -> Self {
203        self.include_xlated_prog_insns = v;
204        self
205    }
206
207    /// Include the vector of jited instructions in the result
208    pub fn include_jited_prog_insns(mut self, v: bool) -> Self {
209        self.include_jited_prog_insns = v;
210        self
211    }
212
213    /// Include the ids of maps associated with the program
214    pub fn include_map_ids(mut self, v: bool) -> Self {
215        self.include_map_ids = v;
216        self
217    }
218
219    /// Include source line information corresponding to xlated code
220    pub fn include_line_info(mut self, v: bool) -> Self {
221        self.include_line_info = v;
222        self
223    }
224
225    /// Include function type information corresponding to xlated code
226    pub fn include_func_info(mut self, v: bool) -> Self {
227        self.include_func_info = v;
228        self
229    }
230
231    /// Include source line information corresponding to jited code
232    pub fn include_jited_line_info(mut self, v: bool) -> Self {
233        self.include_jited_line_info = v;
234        self
235    }
236
237    /// Include function type information corresponding to jited code
238    pub fn include_jited_func_lens(mut self, v: bool) -> Self {
239        self.include_jited_func_lens = v;
240        self
241    }
242
243    /// Include program tags
244    pub fn include_prog_tags(mut self, v: bool) -> Self {
245        self.include_prog_tags = v;
246        self
247    }
248
249    /// Include the jited kernel symbols
250    pub fn include_jited_ksyms(mut self, v: bool) -> Self {
251        self.include_jited_ksyms = v;
252        self
253    }
254
255    /// Include everything there is in the query results
256    pub fn include_all(self) -> Self {
257        Self {
258            include_xlated_prog_insns: true,
259            include_jited_prog_insns: true,
260            include_map_ids: true,
261            include_line_info: true,
262            include_func_info: true,
263            include_jited_line_info: true,
264            include_jited_func_lens: true,
265            include_prog_tags: true,
266            include_jited_ksyms: true,
267        }
268    }
269}
270
271impl ProgramInfo {
272    fn load_from_fd(fd: BorrowedFd<'_>, opts: &ProgInfoQueryOptions) -> Result<Self> {
273        let mut item = libbpf_sys::bpf_prog_info::default();
274
275        let mut xlated_prog_insns: Vec<u8> = Vec::new();
276        let mut jited_prog_insns: Vec<u8> = Vec::new();
277        let mut map_ids: Vec<u32> = Vec::new();
278        let mut jited_line_info: Vec<*const c_void> = Vec::new();
279        let mut line_info: Vec<libbpf_sys::bpf_line_info> = Vec::new();
280        let mut func_info: Vec<libbpf_sys::bpf_func_info> = Vec::new();
281        let mut jited_func_lens: Vec<u32> = Vec::new();
282        let mut prog_tags: Vec<Tag> = Vec::new();
283        let mut jited_ksyms: Vec<*const c_void> = Vec::new();
284
285        let item_ptr: *mut libbpf_sys::bpf_prog_info = &mut item;
286        let mut len = size_of_val(&item) as u32;
287
288        let ret = unsafe {
289            libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr as *mut c_void, &mut len)
290        };
291        util::parse_ret(ret)?;
292
293        // SANITY: `libbpf` should guarantee NUL termination.
294        let name = util::c_char_slice_to_cstr(&item.name).unwrap();
295        let ty = ProgramType::from(item.type_);
296
297        if opts.include_xlated_prog_insns {
298            xlated_prog_insns.resize(item.xlated_prog_len as usize, 0u8);
299            item.xlated_prog_insns = xlated_prog_insns.as_mut_ptr() as *mut c_void as u64;
300        } else {
301            item.xlated_prog_len = 0;
302        }
303
304        if opts.include_jited_prog_insns {
305            jited_prog_insns.resize(item.jited_prog_len as usize, 0u8);
306            item.jited_prog_insns = jited_prog_insns.as_mut_ptr() as *mut c_void as u64;
307        } else {
308            item.jited_prog_len = 0;
309        }
310
311        if opts.include_map_ids {
312            map_ids.resize(item.nr_map_ids as usize, 0u32);
313            item.map_ids = map_ids.as_mut_ptr() as *mut c_void as u64;
314        } else {
315            item.nr_map_ids = 0;
316        }
317
318        if opts.include_line_info {
319            line_info.resize(
320                item.nr_line_info as usize,
321                libbpf_sys::bpf_line_info::default(),
322            );
323            item.line_info = line_info.as_mut_ptr() as *mut c_void as u64;
324        } else {
325            item.nr_line_info = 0;
326        }
327
328        if opts.include_func_info {
329            func_info.resize(
330                item.nr_func_info as usize,
331                libbpf_sys::bpf_func_info::default(),
332            );
333            item.func_info = func_info.as_mut_ptr() as *mut c_void as u64;
334        } else {
335            item.nr_func_info = 0;
336        }
337
338        if opts.include_jited_line_info {
339            jited_line_info.resize(item.nr_jited_line_info as usize, ptr::null());
340            item.jited_line_info = jited_line_info.as_mut_ptr() as *mut c_void as u64;
341        } else {
342            item.nr_jited_line_info = 0;
343        }
344
345        if opts.include_jited_func_lens {
346            jited_func_lens.resize(item.nr_jited_func_lens as usize, 0);
347            item.jited_func_lens = jited_func_lens.as_mut_ptr() as *mut c_void as u64;
348        } else {
349            item.nr_jited_func_lens = 0;
350        }
351
352        if opts.include_prog_tags {
353            prog_tags.resize(item.nr_prog_tags as usize, Tag::default());
354            item.prog_tags = prog_tags.as_mut_ptr() as *mut c_void as u64;
355        } else {
356            item.nr_prog_tags = 0;
357        }
358
359        if opts.include_jited_ksyms {
360            jited_ksyms.resize(item.nr_jited_ksyms as usize, ptr::null());
361            item.jited_ksyms = jited_ksyms.as_mut_ptr() as *mut c_void as u64;
362        } else {
363            item.nr_jited_ksyms = 0;
364        }
365
366        let ret = unsafe {
367            libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr as *mut c_void, &mut len)
368        };
369        util::parse_ret(ret)?;
370
371        Ok(ProgramInfo {
372            name: name.to_owned(),
373            ty,
374            tag: Tag(item.tag),
375            id: item.id,
376            jited_prog_insns,
377            xlated_prog_insns,
378            load_time: Duration::from_nanos(item.load_time),
379            created_by_uid: item.created_by_uid,
380            map_ids,
381            ifindex: item.ifindex,
382            gpl_compatible: item._bitfield_1.get_bit(0),
383            netns_dev: item.netns_dev,
384            netns_ino: item.netns_ino,
385            jited_ksyms,
386            jited_func_lens,
387            btf_id: item.btf_id,
388            func_info_rec_size: item.func_info_rec_size,
389            func_info,
390            line_info: line_info.iter().map(|li| li.into()).collect(),
391            jited_line_info,
392            line_info_rec_size: item.line_info_rec_size,
393            jited_line_info_rec_size: item.jited_line_info_rec_size,
394            prog_tags,
395            run_time_ns: item.run_time_ns,
396            run_cnt: item.run_cnt,
397            recursion_misses: item.recursion_misses,
398        })
399    }
400}
401
402impl ProgInfoIter {
403    fn next_valid_fd(&mut self) -> Option<OwnedFd> {
404        loop {
405            if unsafe { libbpf_sys::bpf_prog_get_next_id(self.cur_id, &mut self.cur_id) } != 0 {
406                return None;
407            }
408
409            let fd = unsafe { libbpf_sys::bpf_prog_get_fd_by_id(self.cur_id) };
410            if fd < 0 {
411                let err = io::Error::last_os_error();
412                if err.kind() == io::ErrorKind::NotFound {
413                    continue;
414                }
415                return None;
416            }
417
418            return Some(unsafe { OwnedFd::from_raw_fd(fd) });
419        }
420    }
421}
422
423impl Iterator for ProgInfoIter {
424    type Item = ProgramInfo;
425
426    fn next(&mut self) -> Option<Self::Item> {
427        let fd = self.next_valid_fd()?;
428
429        let prog = ProgramInfo::load_from_fd(fd.as_fd(), &self.opts);
430
431        match prog {
432            Ok(p) => Some(p),
433            // TODO: We should consider bubbling up errors properly.
434            Err(_err) => None,
435        }
436    }
437}
438
439/// Information about a BPF map
440#[derive(Debug, Clone)]
441// TODO: Document members.
442#[allow(missing_docs)]
443pub struct MapInfo {
444    pub name: CString,
445    pub ty: MapType,
446    pub id: u32,
447    pub key_size: u32,
448    pub value_size: u32,
449    pub max_entries: u32,
450    pub map_flags: u32,
451    pub ifindex: u32,
452    pub btf_vmlinux_value_type_id: u32,
453    pub netns_dev: u64,
454    pub netns_ino: u64,
455    pub btf_id: u32,
456    pub btf_key_type_id: u32,
457    pub btf_value_type_id: u32,
458}
459
460impl MapInfo {
461    fn from_uapi(_fd: BorrowedFd<'_>, s: libbpf_sys::bpf_map_info) -> Option<Self> {
462        // SANITY: `libbpf` should guarantee NUL termination.
463        let name = util::c_char_slice_to_cstr(&s.name).unwrap();
464        let ty = MapType::from(s.type_);
465
466        Some(Self {
467            name: name.to_owned(),
468            ty,
469            id: s.id,
470            key_size: s.key_size,
471            value_size: s.value_size,
472            max_entries: s.max_entries,
473            map_flags: s.map_flags,
474            ifindex: s.ifindex,
475            btf_vmlinux_value_type_id: s.btf_vmlinux_value_type_id,
476            netns_dev: s.netns_dev,
477            netns_ino: s.netns_ino,
478            btf_id: s.btf_id,
479            btf_key_type_id: s.btf_key_type_id,
480            btf_value_type_id: s.btf_value_type_id,
481        })
482    }
483}
484
485gen_info_impl!(
486    /// Iterator that returns [`MapInfo`]s.
487    MapInfoIter,
488    MapInfo,
489    libbpf_sys::bpf_map_info,
490    libbpf_sys::bpf_map_get_next_id,
491    libbpf_sys::bpf_map_get_fd_by_id
492);
493
494/// Information about BPF type format
495#[derive(Debug, Clone)]
496pub struct BtfInfo {
497    /// The name associated with this btf information in the kernel
498    pub name: CString,
499    /// The raw btf bytes from the kernel
500    pub btf: Vec<u8>,
501    /// The btf id associated with this btf information in the kernel
502    pub id: u32,
503}
504
505impl BtfInfo {
506    fn load_from_fd(fd: BorrowedFd<'_>) -> Result<Self> {
507        let mut item = libbpf_sys::bpf_btf_info::default();
508        let mut btf: Vec<u8> = Vec::new();
509        let mut name: Vec<u8> = Vec::new();
510
511        let item_ptr: *mut libbpf_sys::bpf_btf_info = &mut item;
512        let mut len = size_of_val(&item) as u32;
513
514        let ret = unsafe {
515            libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr as *mut c_void, &mut len)
516        };
517        util::parse_ret(ret)?;
518
519        // The API gives you the ascii string length while expecting
520        // you to give it back space for a nul-terminator
521        item.name_len += 1;
522        name.resize(item.name_len as usize, 0u8);
523        item.name = name.as_mut_ptr() as *mut c_void as u64;
524
525        btf.resize(item.btf_size as usize, 0u8);
526        item.btf = btf.as_mut_ptr() as *mut c_void as u64;
527
528        let ret = unsafe {
529            libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr as *mut c_void, &mut len)
530        };
531        util::parse_ret(ret)?;
532
533        Ok(BtfInfo {
534            // SANITY: Our buffer contained space for a NUL byte and we set its
535            //         contents to 0. Barring a `libbpf` bug a NUL byte will be
536            //         present.
537            name: CString::from_vec_with_nul(name).unwrap(),
538            btf,
539            id: item.id,
540        })
541    }
542}
543
544#[derive(Debug, Default)]
545/// An iterator for the btf type information of modules and programs
546/// in the kernel
547pub struct BtfInfoIter {
548    cur_id: u32,
549}
550
551impl BtfInfoIter {
552    // Returns Some(next_valid_fd), None on none left
553    fn next_valid_fd(&mut self) -> Option<OwnedFd> {
554        loop {
555            if unsafe { libbpf_sys::bpf_btf_get_next_id(self.cur_id, &mut self.cur_id) } != 0 {
556                return None;
557            }
558
559            let fd = unsafe { libbpf_sys::bpf_btf_get_fd_by_id(self.cur_id) };
560            if fd < 0 {
561                let err = io::Error::last_os_error();
562                if err.kind() == io::ErrorKind::NotFound {
563                    continue;
564                }
565                return None;
566            }
567
568            return Some(unsafe { OwnedFd::from_raw_fd(fd) });
569        }
570    }
571}
572
573impl Iterator for BtfInfoIter {
574    type Item = BtfInfo;
575
576    fn next(&mut self) -> Option<Self::Item> {
577        let fd = self.next_valid_fd()?;
578
579        let info = BtfInfo::load_from_fd(fd.as_fd());
580
581        match info {
582            Ok(i) => Some(i),
583            // TODO: We should consider bubbling up errors properly.
584            Err(_err) => None,
585        }
586    }
587}
588
589#[derive(Debug, Clone)]
590// TODO: Document members.
591#[allow(missing_docs)]
592pub struct RawTracepointLinkInfo {
593    pub name: String,
594}
595
596#[derive(Debug, Clone)]
597// TODO: Document members.
598#[allow(missing_docs)]
599pub struct TracingLinkInfo {
600    pub attach_type: ProgramAttachType,
601}
602
603#[derive(Debug, Clone)]
604// TODO: Document members.
605#[allow(missing_docs)]
606pub struct CgroupLinkInfo {
607    pub cgroup_id: u64,
608    pub attach_type: ProgramAttachType,
609}
610
611#[derive(Debug, Clone)]
612// TODO: Document members.
613#[allow(missing_docs)]
614pub struct NetNsLinkInfo {
615    pub ino: u32,
616    pub attach_type: ProgramAttachType,
617}
618
619#[derive(Debug, Clone)]
620// TODO: Document variants.
621#[allow(missing_docs)]
622pub enum LinkTypeInfo {
623    RawTracepoint(RawTracepointLinkInfo),
624    Tracing(TracingLinkInfo),
625    Cgroup(CgroupLinkInfo),
626    Iter,
627    NetNs(NetNsLinkInfo),
628    Unknown,
629}
630
631/// Information about a BPF link
632#[derive(Debug, Clone)]
633// TODO: Document members.
634#[allow(missing_docs)]
635pub struct LinkInfo {
636    pub info: LinkTypeInfo,
637    pub id: u32,
638    pub prog_id: u32,
639}
640
641impl LinkInfo {
642    fn from_uapi(fd: BorrowedFd<'_>, mut s: libbpf_sys::bpf_link_info) -> Option<Self> {
643        let type_info = match s.type_ {
644            libbpf_sys::BPF_LINK_TYPE_RAW_TRACEPOINT => {
645                let mut buf = [0; 256];
646                s.__bindgen_anon_1.raw_tracepoint.tp_name = buf.as_mut_ptr() as u64;
647                s.__bindgen_anon_1.raw_tracepoint.tp_name_len = buf.len() as u32;
648                let item_ptr: *mut libbpf_sys::bpf_link_info = &mut s;
649                let mut len = size_of_val(&s) as u32;
650
651                let ret = unsafe {
652                    libbpf_sys::bpf_obj_get_info_by_fd(
653                        fd.as_raw_fd(),
654                        item_ptr as *mut c_void,
655                        &mut len,
656                    )
657                };
658                if ret != 0 {
659                    return None;
660                }
661
662                LinkTypeInfo::RawTracepoint(RawTracepointLinkInfo {
663                    name: util::c_ptr_to_string(
664                        unsafe { s.__bindgen_anon_1.raw_tracepoint.tp_name } as *const c_char,
665                    )
666                    .unwrap_or_else(|_| "?".to_string()),
667                })
668            }
669            libbpf_sys::BPF_LINK_TYPE_TRACING => LinkTypeInfo::Tracing(TracingLinkInfo {
670                attach_type: ProgramAttachType::from(unsafe {
671                    s.__bindgen_anon_1.tracing.attach_type
672                }),
673            }),
674            libbpf_sys::BPF_LINK_TYPE_CGROUP => LinkTypeInfo::Cgroup(CgroupLinkInfo {
675                cgroup_id: unsafe { s.__bindgen_anon_1.cgroup.cgroup_id },
676                attach_type: ProgramAttachType::from(unsafe {
677                    s.__bindgen_anon_1.cgroup.attach_type
678                }),
679            }),
680            libbpf_sys::BPF_LINK_TYPE_ITER => LinkTypeInfo::Iter,
681            libbpf_sys::BPF_LINK_TYPE_NETNS => LinkTypeInfo::NetNs(NetNsLinkInfo {
682                ino: unsafe { s.__bindgen_anon_1.netns.netns_ino },
683                attach_type: ProgramAttachType::from(unsafe {
684                    s.__bindgen_anon_1.netns.attach_type
685                }),
686            }),
687            _ => LinkTypeInfo::Unknown,
688        };
689
690        Some(Self {
691            info: type_info,
692            id: s.id,
693            prog_id: s.prog_id,
694        })
695    }
696}
697
698gen_info_impl!(
699    /// Iterator that returns [`LinkInfo`]s.
700    LinkInfoIter,
701    LinkInfo,
702    libbpf_sys::bpf_link_info,
703    libbpf_sys::bpf_link_get_next_id,
704    libbpf_sys::bpf_link_get_fd_by_id
705);