bpf_loader_lib/skeleton/preload/
mod.rs

1//!  SPDX-License-Identifier: MIT
2//!
3//! Copyright (c) 2023, eunomia-bpf
4//! All rights reserved.
5//!
6
7use std::{collections::HashMap, sync::Arc};
8
9use crate::{
10    btf_container::BtfContainer,
11    elf_container::ElfContainer,
12    meta::{EunomiaObjectMeta, RunnerConfig},
13    skeleton::preload::{
14        attach::{attach_perf_event, attach_tc, attach_xdp, AttachLink},
15        section_loader::load_section_data_with_skel_value,
16    },
17};
18use anyhow::{anyhow, bail, Context, Result};
19use libbpf_rs::OpenObject;
20use log::debug;
21use object::{Object, ObjectSection};
22
23use super::{handle::PollingHandle, BpfSkeleton};
24pub(crate) mod attach;
25pub(crate) mod section_loader;
26/// Represents an initialized bpf skeleton. It's waiting for the loading and attaching of bpf programs
27pub struct PreLoadBpfSkeleton {
28    ///   data storage
29    /// meta data control the behavior of ebpf program:
30    /// eg. types of the eBPF maps and prog, export data types
31    pub(crate) meta: EunomiaObjectMeta,
32    /// config of eunomia itself,
33    /// for how we creating, loading and interacting with the eBPF program
34    /// eg. poll maps timeout in ms
35    pub(crate) config_data: RunnerConfig,
36
37    pub(crate) bpf_object: OpenObject,
38
39    // Btf for the loaded program
40    pub(crate) btf: BtfContainer,
41
42    // Value sizes of maps this program hold
43    // This is a workaround for libbpf-rs not exposing bpf_map* in OpenMap
44    pub(crate) map_value_sizes: HashMap<String, u32>,
45
46    pub(crate) raw_elf: ElfContainer,
47}
48
49impl PreLoadBpfSkeleton {
50    /// start running the ebpf program
51
52    /// load and attach the ebpf program to the kernel to run the ebpf program
53    /// if the ebpf program has maps to export to user space, you need to call
54    /// the wait and export.
55    pub fn load_and_attach(mut self) -> Result<BpfSkeleton> {
56        // This function differs from the C++ version `int bpf_skeleton::load_and_attach_prog(void)`
57        // Because we put the call to bpf_object_open in `BpfSkeletonBuilder::build`
58        // So Here are just the calls to load and attach
59
60        debug!(
61            "Section names of ELF: {:?}",
62            self.raw_elf
63                .borrow_elf()
64                .sections()
65                .map(|v| v.name().map(|t| t.to_string()))
66                .collect::<Vec<_>>()
67        );
68
69        // Initialize data for sections
70        for section in self.meta.bpf_skel.data_sections.iter() {
71            debug!("Loading section: {:?}", section);
72            let map_meta = match section.name.as_str() {
73                ".rodata" => self
74                    .meta
75                    .bpf_skel
76                    .find_map_by_ident("rodata")
77                    .ok_or_else(|| {
78                        anyhow!("Failed to find map with ident `rodata` for section .rodata")
79                    })?,
80                ".bss" => self.meta.bpf_skel.find_map_by_ident("bss").ok_or_else(|| {
81                    anyhow!("Failed to find map with ident `bss` for section .bss")
82                })?,
83                s => bail!("Unsupported section: {}", s),
84            };
85            let map_name = map_meta.name.as_str();
86            let map = self.bpf_object.map_mut(map_name).ok_or_else(|| {
87                anyhow!(
88                    "Map named `{}` doesn't exist, cannot map section `{}`",
89                    map_name,
90                    section.name
91                )
92            })?;
93            // Set a buffer to hold the data
94            let buffer_size = *self
95                .map_value_sizes
96                .get(map_name)
97                .ok_or_else(|| anyhow!("Map name {} not found in value sizes", map_name))?
98                as usize;
99            debug!("Buffer size: {}", buffer_size);
100            let mut buffer = if let Some(v) = self
101                .raw_elf
102                .borrow_elf()
103                .section_data_by_name(&section.name)
104            {
105                debug!("Using buffer from ELF");
106                v.to_vec()
107            } else {
108                debug!("Using empty buffer");
109                vec![0; buffer_size]
110            };
111            if buffer.len() < buffer_size {
112                // Ensure correct size for zero-filled sections, e.g bss
113                buffer.resize(buffer_size, 0);
114            }
115            debug!("Buffer before filling: {:?}", buffer);
116            load_section_data_with_skel_value(self.btf.borrow_btf(), section, &mut buffer)
117                .with_context(|| anyhow!("Failed to load section {}", section.name))?;
118            debug!("Loaded buffer: {:?}", buffer);
119            map.set_initial_value(&buffer[..])
120                .map_err(|e| anyhow!("Failed to set initial value of map `{}`: {}", map_name, e))?;
121        }
122
123        let mut bpf_object = self
124            .bpf_object
125            .load()
126            .with_context(|| anyhow!("Failed to load bpf object"))?;
127        // Next steps are attaching...
128        let mut not_attached = vec![];
129        let mut links = vec![];
130        for prog_meta in self.meta.bpf_skel.progs.iter() {
131            let bpf_prog = bpf_object
132                .prog_mut(&prog_meta.name)
133                .ok_or_else(|| anyhow!("Program named `{}` not found in libbpf", prog_meta.name))?;
134            match bpf_prog.attach() {
135                Ok(link) => links.push(AttachLink::BpfLink(link)),
136                // EOPNOTSUPP 95 Operation not supported
137                Err(_) if errno::errno().0 == 95 => {
138                    // Not supported for auto-attaching, needs manually operations
139                    not_attached.push(prog_meta);
140                    continue;
141                }
142                Err(err) => bail!("Failed to attach program `{}`: {}", prog_meta.name, err),
143            };
144        }
145        for prog_meta in not_attached.into_iter() {
146            let bpf_prog = bpf_object
147                .prog_mut(&prog_meta.name)
148                .ok_or_else(|| anyhow!("Program named `{}` not found", prog_meta.name))?;
149            match bpf_prog.section() {
150                "tc" => links.push(attach_tc(bpf_prog, prog_meta).with_context(|| {
151                    anyhow!("Failed to attach tc program `{}`", prog_meta.name)
152                })?),
153                "xdp" => links.push(attach_xdp(bpf_prog, prog_meta).with_context(|| {
154                    anyhow!("Failed to attach xdp program `{}`", prog_meta.name)
155                })?),
156                "perf_event" => {
157                    let mut perf_links =
158                        attach_perf_event(bpf_prog, prog_meta).with_context(|| {
159                            anyhow!("Failed to attach perf event program `{}`", prog_meta.name)
160                        })?;
161                    links.append(&mut perf_links);
162                }
163                s => bail!("Unsupported attach type: {}", s),
164            }
165        }
166        Ok(BpfSkeleton {
167            handle: PollingHandle::new(),
168            meta: self.meta,
169            config_data: self.config_data,
170            btf: Arc::new(self.btf),
171            links,
172            prog: bpf_object,
173        })
174    }
175}
176
177#[cfg(test)]
178#[cfg(not(feature = "no-load-bpf-tests"))]
179mod tests {
180    use crate::{
181        meta::ComposedObject, skeleton::builder::BpfSkeletonBuilder, tests::get_assets_dir,
182    };
183
184    #[test]
185    fn test_load_and_attach() {
186        let skel: ComposedObject = serde_json::from_str(
187            &std::fs::read_to_string(get_assets_dir().join("bootstrap.json")).unwrap(),
188        )
189        .unwrap();
190        let pre_load_skel = BpfSkeletonBuilder::from_json_package(&skel, None)
191            .build()
192            .unwrap();
193        let loaded = pre_load_skel.load_and_attach().unwrap();
194        let prog = loaded.prog;
195        for map_meta in skel.meta.bpf_skel.maps.iter() {
196            let _map_bpf = prog.map(map_meta.name.as_str()).unwrap();
197        }
198        for prog_meta in skel.meta.bpf_skel.progs.iter() {
199            let prog_bpf = prog.prog(&prog_meta.name).unwrap();
200            assert_eq!(prog_bpf.section(), prog_meta.attach);
201        }
202        assert_eq!(loaded.links.len(), skel.meta.bpf_skel.progs.len());
203    }
204}