1 use core::ffi::c_void;
2 use std::alloc::alloc_zeroed;
3 use std::alloc::dealloc;
4 use std::alloc::Layout;
5 use std::ffi::CString;
6 use std::mem::size_of;
7 use std::mem::MaybeUninit;
8 use std::os::raw::c_char;
9 use std::os::raw::c_ulong;
10 use std::ptr;
11 use std::ptr::addr_of;
12 use std::ptr::NonNull;
13 
14 use libbpf_sys::bpf_link;
15 use libbpf_sys::bpf_map;
16 use libbpf_sys::bpf_map_skeleton;
17 use libbpf_sys::bpf_object;
18 use libbpf_sys::bpf_object_skeleton;
19 use libbpf_sys::bpf_prog_skeleton;
20 use libbpf_sys::bpf_program;
21 
22 use crate::error::IntoError as _;
23 use crate::util;
24 use crate::AsRawLibbpf;
25 use crate::Error;
26 use crate::Object;
27 use crate::ObjectBuilder;
28 use crate::OpenObject;
29 use crate::Result;
30 
31 #[derive(Debug)]
32 struct MapSkelConfig {
33     name: String,
34     p: Box<*mut bpf_map>,
35     mmaped: Option<Box<*mut c_void>>,
36 }
37 
38 #[derive(Debug)]
39 struct 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)]
47 pub 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 
str_to_cstring_and_pool(s: &str, pool: &mut Vec<CString>) -> Result<*const c_char>55 fn 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 
63 impl<'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
new(object_data: &'dat [u8]) -> Self70     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)]
name<T: AsRef<str>>(&mut self, name: T) -> &mut Self81     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
map<T: AsRef<str>>(&mut self, name: T, mmaped: bool) -> &mut Self89     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
prog<T: AsRef<str>>(&mut self, name: T) -> &mut Self106     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 
build_maps( maps: &mut [MapSkelConfig], s: &mut bpf_object_skeleton, string_pool: &mut Vec<CString>, ) -> Option<Layout>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 
build_progs( progs: &mut [ProgSkelConfig], s: &mut bpf_object_skeleton, string_pool: &mut Vec<CString>, ) -> Option<Layout>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)]
build(mut self) -> Result<ObjectSkeletonConfig<'dat>>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)]
227 pub 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 
241 impl 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.
map_mmap_ptr(&self, index: usize) -> Result<*mut c_void>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.
prog_link_ptr(&self, index: usize) -> Result<*mut bpf_link>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 
279 impl AsRawLibbpf for ObjectSkeletonConfig<'_> {
280     type LibbpfType = libbpf_sys::bpf_object_skeleton;
281 
282     /// Retrieve the underlying [`libbpf_sys::bpf_object_skeleton`].
as_libbpf_object(&self) -> NonNull<Self::LibbpfType>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 
289 impl 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.
drop(&mut self)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.
322 pub 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`]
open(self, object: &'obj mut MaybeUninit<OpenObject>) -> Result<Self::Output>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`]
open_opts( self, open_opts: libbpf_sys::bpf_object_open_opts, object: &'obj mut MaybeUninit<OpenObject>, ) -> Result<Self::Output>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`]
object_builder(&self) -> &ObjectBuilder338     fn object_builder(&self) -> &ObjectBuilder;
339 
340     /// Get a mutable reference to [`ObjectBuilder`]
object_builder_mut(&mut self) -> &mut ObjectBuilder341     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
374 pub 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`].
load(self) -> Result<Self::Output>379     fn load(self) -> Result<Self::Output>;
380 
381     /// Get a reference to [`OpenObject`].
open_object(&self) -> &OpenObject382     fn open_object(&self) -> &OpenObject;
383 
384     /// Get a mutable reference to [`OpenObject`].
open_object_mut(&mut self) -> &mut OpenObject385     fn open_object_mut(&mut self) -> &mut OpenObject;
386 }
387 
388 /// A trait for loaded skeleton.
389 pub trait Skel<'obj> {
390     /// Attach BPF object.
attach(&mut self) -> Result<()>391     fn attach(&mut self) -> Result<()> {
392         unimplemented!()
393     }
394     /// Get a reference to [`Object`].
object(&self) -> &Object395     fn object(&self) -> &Object;
396 
397     /// Get a mutable reference to [`Object`].
object_mut(&mut self) -> &mut Object398     fn object_mut(&mut self) -> &mut Object;
399 }
400