libbpf_rs/
skeleton.rs

1use core::ffi::c_void;
2use std::alloc::alloc_zeroed;
3use std::alloc::dealloc;
4use std::alloc::Layout;
5use std::ffi::CString;
6use std::mem::size_of;
7use std::mem::MaybeUninit;
8use std::os::raw::c_char;
9use std::os::raw::c_ulong;
10use std::ptr;
11use std::ptr::addr_of;
12use std::ptr::NonNull;
13
14use libbpf_sys::bpf_link;
15use libbpf_sys::bpf_map;
16use libbpf_sys::bpf_map_skeleton;
17use libbpf_sys::bpf_object;
18use libbpf_sys::bpf_object_skeleton;
19use libbpf_sys::bpf_prog_skeleton;
20use libbpf_sys::bpf_program;
21
22use crate::error::IntoError as _;
23use crate::util;
24use crate::AsRawLibbpf;
25use crate::Error;
26use crate::Object;
27use crate::ObjectBuilder;
28use crate::OpenObject;
29use crate::Result;
30
31#[derive(Debug)]
32struct MapSkelConfig {
33    name: String,
34    p: Box<*mut bpf_map>,
35    mmaped: Option<Box<*mut c_void>>,
36}
37
38#[derive(Debug)]
39struct ProgSkelConfig {
40    name: String,
41    p: Box<*mut bpf_program>,
42    link: Box<*mut bpf_link>,
43}
44
45#[allow(missing_docs)]
46#[derive(Debug)]
47pub struct ObjectSkeletonConfigBuilder<'dat> {
48    data: &'dat [u8],
49    p: Box<*mut bpf_object>,
50    name: Option<String>,
51    maps: Vec<MapSkelConfig>,
52    progs: Vec<ProgSkelConfig>,
53}
54
55fn str_to_cstring_and_pool(s: &str, pool: &mut Vec<CString>) -> Result<*const c_char> {
56    let cname = util::str_to_cstring(s)?;
57    let p = cname.as_ptr();
58    pool.push(cname);
59
60    Ok(p)
61}
62
63impl<'dat> ObjectSkeletonConfigBuilder<'dat> {
64    /// Construct a new instance
65    ///
66    /// `object_data` is the contents of the `.o` from clang
67    ///
68    /// `p` is a reference to the pointer where `libbpf_sys::bpf_object` should be
69    /// stored/retrieved
70    pub fn new(object_data: &'dat [u8]) -> Self {
71        Self {
72            data: object_data,
73            p: Box::new(ptr::null_mut()),
74            name: None,
75            maps: Vec::new(),
76            progs: Vec::new(),
77        }
78    }
79
80    #[allow(missing_docs)]
81    pub fn name<T: AsRef<str>>(&mut self, name: T) -> &mut Self {
82        self.name = Some(name.as_ref().to_string());
83        self
84    }
85
86    /// Adds a map to the config
87    ///
88    /// Set `mmaped` to `true` if the map is mmap'able to userspace
89    pub fn map<T: AsRef<str>>(&mut self, name: T, mmaped: bool) -> &mut Self {
90        let m = if mmaped {
91            Some(Box::new(ptr::null_mut()))
92        } else {
93            None
94        };
95
96        self.maps.push(MapSkelConfig {
97            name: name.as_ref().to_string(),
98            p: Box::new(ptr::null_mut()),
99            mmaped: m,
100        });
101
102        self
103    }
104
105    /// Adds a prog to the config
106    pub fn prog<T: AsRef<str>>(&mut self, name: T) -> &mut Self {
107        self.progs.push(ProgSkelConfig {
108            name: name.as_ref().to_string(),
109            p: Box::new(ptr::null_mut()),
110            link: Box::new(ptr::null_mut()),
111        });
112
113        self
114    }
115
116    fn build_maps(
117        maps: &mut [MapSkelConfig],
118        s: &mut bpf_object_skeleton,
119        string_pool: &mut Vec<CString>,
120    ) -> Option<Layout> {
121        if maps.is_empty() {
122            return None;
123        }
124
125        s.map_cnt = maps.len() as i32;
126        s.map_skel_sz = size_of::<bpf_map_skeleton>() as i32;
127
128        let layout = Layout::array::<bpf_map_skeleton>(maps.len())
129            .expect("Failed to allocate memory for maps skeleton");
130
131        unsafe {
132            s.maps = alloc_zeroed(layout) as *mut bpf_map_skeleton;
133            for (i, map) in maps.iter_mut().enumerate() {
134                let current_map = s.maps.add(i);
135
136                // Opt to panic on error here. We've already allocated memory and we'd rather not
137                // leak. Extremely unlikely to have invalid unicode anyways.
138                (*current_map).name = str_to_cstring_and_pool(&map.name, string_pool)
139                    .expect("Invalid unicode in map name");
140                (*current_map).map = &mut *map.p;
141                (*current_map).mmaped = if let Some(ref mut mmaped) = map.mmaped {
142                    &mut **mmaped
143                } else {
144                    ptr::null_mut()
145                };
146            }
147        }
148
149        Some(layout)
150    }
151
152    fn build_progs(
153        progs: &mut [ProgSkelConfig],
154        s: &mut bpf_object_skeleton,
155        string_pool: &mut Vec<CString>,
156    ) -> Option<Layout> {
157        if progs.is_empty() {
158            return None;
159        }
160
161        s.prog_cnt = progs.len() as i32;
162        s.prog_skel_sz = size_of::<bpf_prog_skeleton>() as i32;
163
164        let layout = Layout::array::<bpf_prog_skeleton>(progs.len())
165            .expect("Failed to allocate memory for progs skeleton");
166
167        unsafe {
168            s.progs = alloc_zeroed(layout) as *mut bpf_prog_skeleton;
169            for (i, prog) in progs.iter_mut().enumerate() {
170                let current_prog = s.progs.add(i);
171
172                // See above for `expect()` rationale
173                (*current_prog).name = str_to_cstring_and_pool(&prog.name, string_pool)
174                    .expect("Invalid unicode in prog name");
175                (*current_prog).prog = &mut *prog.p;
176                (*current_prog).link = &mut *prog.link;
177            }
178        }
179
180        Some(layout)
181    }
182
183    #[allow(missing_docs)]
184    pub fn build(mut self) -> Result<ObjectSkeletonConfig<'dat>> {
185        // Holds `CString`s alive so pointers to them stay valid
186        let mut string_pool = Vec::new();
187
188        let mut s = libbpf_sys::bpf_object_skeleton {
189            sz: size_of::<bpf_object_skeleton>() as c_ulong,
190            ..Default::default()
191        };
192
193        if let Some(ref n) = self.name {
194            s.name = str_to_cstring_and_pool(n, &mut string_pool)?;
195        }
196
197        // libbpf_sys will use it as const despite the signature
198        s.data = self.data.as_ptr() as *mut c_void;
199        s.data_sz = self.data.len() as c_ulong;
200
201        // Give s ownership over the box
202        s.obj = Box::into_raw(self.p);
203
204        let maps_layout = Self::build_maps(&mut self.maps, &mut s, &mut string_pool);
205        let progs_layout = Self::build_progs(&mut self.progs, &mut s, &mut string_pool);
206
207        Ok(ObjectSkeletonConfig {
208            inner: s,
209            maps: self.maps,
210            progs: self.progs,
211            maps_layout,
212            progs_layout,
213            _data: self.data,
214            _string_pool: string_pool,
215        })
216    }
217}
218
219/// Helper struct that wraps a `libbpf_sys::bpf_object_skeleton`.
220///
221/// This struct will:
222/// * ensure lifetimes are valid for dependencies (pointers, data buffer)
223/// * free any allocated memory on drop
224///
225/// This struct can be moved around at will. Upon drop, all allocated resources will be freed
226#[derive(Debug)]
227pub struct ObjectSkeletonConfig<'dat> {
228    inner: bpf_object_skeleton,
229    maps: Vec<MapSkelConfig>,
230    progs: Vec<ProgSkelConfig>,
231    /// Layout necessary to `dealloc` memory
232    maps_layout: Option<Layout>,
233    /// Same as above
234    progs_layout: Option<Layout>,
235    /// Hold this reference so that compiler guarantees buffer lives as long as us
236    _data: &'dat [u8],
237    /// Hold strings alive so pointers to them stay valid
238    _string_pool: Vec<CString>,
239}
240
241impl ObjectSkeletonConfig<'_> {
242    /// Returns the `mmaped` pointer for a map at the specified `index`.
243    ///
244    /// The index is determined by the order in which the map was passed to
245    /// `ObjectSkeletonConfigBuilder::map`. Index starts at 0.
246    ///
247    /// Warning: the returned pointer is only valid while the `ObjectSkeletonConfig` is alive.
248    pub fn map_mmap_ptr(&self, index: usize) -> Result<*mut c_void> {
249        if index >= self.maps.len() {
250            return Err(Error::with_invalid_data(format!(
251                "Invalid map index: {index}"
252            )));
253        }
254
255        let p = self.maps[index]
256            .mmaped
257            .as_ref()
258            .ok_or_invalid_data(|| "Map does not have mmaped ptr")?;
259        Ok(**p)
260    }
261
262    /// Returns the link pointer for a prog at the specified `index`.
263    ///
264    /// The index is determined by the order in which the prog was passed to
265    /// `ObjectSkeletonConfigBuilder::prog`. Index starts at 0.
266    ///
267    /// Warning: the returned pointer is only valid while the `ObjectSkeletonConfig` is alive.
268    pub fn prog_link_ptr(&self, index: usize) -> Result<*mut bpf_link> {
269        if index >= self.progs.len() {
270            return Err(Error::with_invalid_data(format!(
271                "Invalid prog index: {index}"
272            )));
273        }
274
275        Ok(*self.progs[index].link)
276    }
277}
278
279impl AsRawLibbpf for ObjectSkeletonConfig<'_> {
280    type LibbpfType = libbpf_sys::bpf_object_skeleton;
281
282    /// Retrieve the underlying [`libbpf_sys::bpf_object_skeleton`].
283    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
284        // SAFETY: A reference is always a valid pointer.
285        unsafe { NonNull::new_unchecked(addr_of!(self.inner).cast_mut()) }
286    }
287}
288
289impl Drop for ObjectSkeletonConfig<'_> {
290    // Note we do *not* run `libbpf_sys::bpf_object__destroy_skeleton` here.
291    //
292    // Couple reasons:
293    //
294    // 1) We did not allocate `libbpf_sys::bpf_object_skeleton` on the heap and
295    //    `libbpf_sys::bpf_object__destroy_skeleton` will try to free from heap
296    //
297    // 2) `libbpf_object_skeleton` assumes it "owns" the object and everything inside it.
298    //    libbpf-cargo's generated skeleton instead gives ownership of the object to
299    //    libbpf-rs::*Object. The destructors in libbpf-rs::*Object will know when and how to do
300    //    cleanup.
301    fn drop(&mut self) {
302        assert_eq!(self.maps_layout.is_none(), self.inner.maps.is_null());
303        assert_eq!(self.progs_layout.is_none(), self.inner.progs.is_null());
304
305        if let Some(layout) = self.maps_layout {
306            unsafe {
307                dealloc(self.inner.maps as _, layout);
308            }
309        }
310
311        if let Some(layout) = self.progs_layout {
312            unsafe {
313                dealloc(self.inner.progs as _, layout);
314            }
315        }
316
317        let _ = unsafe { Box::from_raw(self.inner.obj) };
318    }
319}
320
321/// A trait for skeleton builder.
322pub trait SkelBuilder<'obj> {
323    /// Define that when BPF object is opened, the returned type should implement the [`OpenSkel`]
324    /// trait
325    type Output: OpenSkel<'obj>;
326
327    /// Open eBPF object and return [`OpenSkel`]
328    fn open(self, object: &'obj mut MaybeUninit<OpenObject>) -> Result<Self::Output>;
329
330    /// Open eBPF object with [`libbpf_sys::bpf_object_open_opts`] and return [`OpenSkel`]
331    fn open_opts(
332        self,
333        open_opts: libbpf_sys::bpf_object_open_opts,
334        object: &'obj mut MaybeUninit<OpenObject>,
335    ) -> Result<Self::Output>;
336
337    /// Get a reference to [`ObjectBuilder`]
338    fn object_builder(&self) -> &ObjectBuilder;
339
340    /// Get a mutable reference to [`ObjectBuilder`]
341    fn object_builder_mut(&mut self) -> &mut ObjectBuilder;
342}
343
344/// A trait for opened skeleton.
345///
346/// In addition to the methods defined in this trait, skeletons that implement this trait will also
347/// have bespoke implementations of a few additional methods to facilitate access to global
348/// variables of the BPF program. These methods will be named `bss()`, `data()`, and `rodata()`.
349/// Each corresponds to the variables stored in the BPF ELF program section of the same name.
350/// However if your BPF program lacks one of these sections the corresponding rust method will not
351/// be generated.
352///
353/// The type of the value returned by each of these methods will be specific to your BPF program.
354/// A common convention is to define a single global variable in the BPF program with a struct type
355/// containing a field for each configuration parameter <sup>\[[source]\]</sup>. libbpf-rs
356/// auto-generates this pattern for you without you having to define such a struct type in your BPF
357/// program. It does this by examining each of the global variables in your BPF program's `.bss`,
358/// `.data`, and `.rodata` sections and then creating Rust struct types. Since these struct types
359/// are specific to the layout of your BPF program, they are not documented in this crate. However
360/// you can see documentation for them by running `cargo doc` in your own project and looking at
361/// the `imp` module. You can also view their implementation by looking at the generated skeleton
362/// rust source file. The use of these methods can also be seen in the examples 'capable',
363/// 'runqslower', and 'tproxy'.
364///
365/// If you ever doubt whether libbpf-rs has placed a particular variable in the correct struct
366/// type, you can see which section each global variable is stored in by examining the output of
367/// the following command (after a successful build):
368///
369/// ```sh
370/// bpf-objdump --syms ./target/bpf/*.bpf.o
371/// ```
372///
373/// [source]: https://nakryiko.com/posts/bcc-to-libbpf-howto-guide/#application-configuration
374pub trait OpenSkel<'obj> {
375    /// Define that when BPF object is loaded, the returned type should implement the [`Skel`] trait
376    type Output: Skel<'obj>;
377
378    /// Load BPF object and return [`Skel`].
379    fn load(self) -> Result<Self::Output>;
380
381    /// Get a reference to [`OpenObject`].
382    fn open_object(&self) -> &OpenObject;
383
384    /// Get a mutable reference to [`OpenObject`].
385    fn open_object_mut(&mut self) -> &mut OpenObject;
386}
387
388/// A trait for loaded skeleton.
389pub trait Skel<'obj> {
390    /// Attach BPF object.
391    fn attach(&mut self) -> Result<()> {
392        unimplemented!()
393    }
394    /// Get a reference to [`Object`].
395    fn object(&self) -> &Object;
396
397    /// Get a mutable reference to [`Object`].
398    fn object_mut(&mut self) -> &mut Object;
399}