bpf_loader_lib/meta/
mod.rs

1//!  SPDX-License-Identifier: MIT
2//!
3//! Copyright (c) 2023, eunomia-bpf
4//! All rights reserved.
5//!
6
7//! # bpf-loader-meta
8//!
9//! Here are metadata types used for deserilizing JSON skeletons
10//!
11//! A valid json skeleton is encoded using one of the two types:
12//! - `EunomiaObjectMeta`
13//! - `ComposedObject`
14//!
15//! In fact, the second one is `EunomiaObjectMeta` + ELF binary. So in another words, they are same
16//!
17//! ## EunomiaObjectMeta
18//! Two fields need extra explanation:
19//!
20//! ### `export_types` : `Vec<ExportedTypesStructMeta>`
21//!
22//! Here describes the types (usually a struct) of the data that this ebpf program exported to the userspace program. The types described here will be verified using BTF, and used to format the output the ebpf program gives, and passed to the user-callback or stdout. Due to a strange limitation, each program can only have one export types in the `Vec`
23//!
24//! ### `bpf_skel`: `BpfSkeletonMeta`
25//!
26//! Will be explained in the next words.
27//!
28//! ## BpfSkeletonMeta
29//!
30//! This struct describes the skeleton of an ebpf object.
31//! - `data_sections`: Describes `.rodata` and `.bss` sections, and variables in that
32//! - `maps`: Describes map declarations that are used in this ebpf object.
33//! - `progs`: Describes ebpf programs (functions) in this ebpf object
34//! - `obj_name`: The name of this ebpf object
35//! - `doc`: Docs, will be used to generate command line parser
36//!
37//! ## DataSectionMeta
38//!
39//! Describes a data section, and variables declared in it.
40//!
41//! ## DataSectionVariableMeta
42//!
43//! Describes a variable in the corresponding data section.
44//!
45//! - `name`: The name of this variable
46//! - `ty`: The C type of this variable
47//! - `value`: The default value of this variable. If not provided and not filled by command line parser, `bpf-loader` will fill the variable with zero bytes
48//! - `description`: The description of the variable. Will be used to generate command line parser
49//! - `cmdarg`: Detailed configuration on the command line argument of this variable
50//!
51//! ## VariableCommandArgument
52//!
53//! Describes the detailed configuration of this variable's command line argument.
54//!
55//! - `default`: The default value of this command line argument
56//! - `long`: The long name of this argument
57//! - `short`: The short name of this argument, in one char.
58//!
59//! ## MapMeta
60//!
61//! Describes an eBPF map
62//!
63//! ## ProgMeta
64//!
65//! Describes an eBPF program
66
67use base64::Engine;
68use deflate::deflate_bytes_zlib;
69use libbpf_rs::libbpf_sys::{BPF_TC_CUSTOM, BPF_TC_EGRESS, BPF_TC_INGRESS};
70use serde::{Deserialize, Serialize};
71use serde_json::Value;
72use serde_with::DefaultOnNull;
73/// Describe a struct member in an exported type
74#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
75pub struct ExportedTypesStructMemberMeta {
76    /// The name of the member
77    pub name: String,
78    #[serde(rename = "type")]
79    /// The type of the member
80    pub ty: String,
81}
82
83/// Describe an exported struct
84#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
85pub struct ExportedTypesStructMeta {
86    /// Name of the struct
87    pub name: String,
88    /// Members of the struct
89    pub members: Vec<ExportedTypesStructMemberMeta>,
90    /// Size of the struct
91    pub size: u32,
92    /// Btf type id of the struct
93    pub type_id: u32,
94}
95#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Default)]
96/// Sample types
97pub enum SampleMapType {
98    #[serde(rename = "log2_hist")]
99    /// print the event data as log2_hist plain text
100    Log2Hist,
101    #[serde(rename = "linear_hist")]
102    /// print the event data as linear hist plain text
103    LinearHist,
104    #[serde(rename = "default_kv")]
105    #[default]
106    /// print the event data as key-value format in plain text or json
107    DefaultKV,
108}
109
110/// Extra info for a map which will be used for sampling
111#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
112pub struct MapSampleMeta {
113    /// Sample interval, in milliseconds
114    pub interval: usize,
115    /// type of the map
116    #[serde(rename = "type", default)]
117    pub ty: SampleMapType,
118    /// Unit when printing hists
119    #[serde(default = "default_helpers::map_unit_default")]
120    pub unit: String,
121    /// Whether to clean up the map after sampling done
122    #[serde(default = "default_helpers::default_bool::<false>")]
123    pub clear_map: bool,
124}
125
126/// Describe a member of an overriding struct
127#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
128pub struct OverridedStructMember {
129    /// Name of the field
130    pub name: String,
131    /// Offset of the field. in bytes
132    pub offset: usize,
133    /// BTF type id of the field
134    pub btf_type_id: u32,
135}
136/// Describe whether and how a map's value will be exported
137#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
138pub enum MapExportConfig {
139    /// Don't export
140    #[serde(rename = "no_export")]
141    NoExport,
142    /// Use this btf type to specify the export value
143    #[serde(rename = "btf_type_id")]
144    ExportUseBtf(u32),
145    /// Use the custom members to specify the export value
146    #[serde(rename = "custom_members")]
147    ExportUseCustomMembers(Vec<OverridedStructMember>),
148    /// Use default configuration from BTF. This only applies to sample maps
149    #[serde(rename = "default")]
150    Default,
151}
152
153impl Default for MapExportConfig {
154    fn default() -> Self {
155        Self::NoExport
156    }
157}
158
159#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Default)]
160/// Specify which field in the provided input struct will be mapped to the corresponding field
161pub struct StackTraceFieldMapping {
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub pid: Option<String>,
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub cpu_id: Option<String>,
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub comm: Option<String>,
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub kstack_sz: Option<String>,
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub ustack_sz: Option<String>,
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub kstack: Option<String>,
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub ustack: Option<String>,
176}
177
178/// Indicate how to inteprete the buffer value polled by the userspace program
179/// DefaultStruct - Inteprete the data to a map constructed using BTF
180/// StackTrace - Inteprete the data that received as StackTrace data. Will also use BTF, but the user is responsible to provide a function to translate the fields to the corresponding requiring fields
181#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
182pub enum BufferValueInterpreter {
183    #[serde(rename = "default_struct")]
184    DefaultStruct,
185    #[serde(rename = "stack_trace")]
186    StackTrace {
187        #[serde(default)]
188        field_map: StackTraceFieldMapping,
189        #[serde(default = "default_helpers::default_bool::<true>")]
190        with_symbols: bool,
191    },
192}
193
194impl Default for BufferValueInterpreter {
195    fn default() -> Self {
196        Self::DefaultStruct
197    }
198}
199
200/// Describe a map
201#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
202pub struct MapMeta {
203    /// Name of the map
204    pub name: String,
205    /// TODO: get to know what's this
206    pub ident: String,
207    /// Whether the value of this map will be used to describe a data section
208    #[serde(default = "default_helpers::default_bool::<false>")]
209    pub mmaped: bool,
210    /// Extra info if this map will be used for sampling
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub sample: Option<MapSampleMeta>,
213    /// The export config of this map
214    #[serde(default)]
215    pub export_config: MapExportConfig,
216    /// How to intepreter the buffer value of this map. Only applies if this map if a buffer value map (perf event or ringbuf)
217    #[serde(default)]
218    pub intepreter: BufferValueInterpreter,
219}
220#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
221/// Describe the meta of a bpf program
222pub struct ProgMeta {
223    /// name of this bpf program
224    pub name: String,
225    /// Attach point of this program
226    pub attach: String,
227    /// Whether the attaching of this program will generate a bpf_link
228    pub link: bool,
229    #[serde(flatten)]
230    /// Other fields
231    pub others: Value,
232}
233
234#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
235/// Extra fields in prog meta for XDP programs
236pub struct XDPProgExtraMeta {
237    #[serde(default = "default_helpers::default_i32::<1>")]
238    /// Which interface to hook
239    pub ifindex: i32,
240    #[serde(default = "default_helpers::default_u32::<0>")]
241    /// XDP hook flags
242    pub flags: u32,
243    #[serde(default)]
244    /// XDP Hook options
245    pub xdpopts: XDPOpts,
246}
247
248#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
249/// XDP hook options
250#[derive(Default)]
251pub struct XDPOpts {
252    #[serde(default = "default_helpers::default_i32::<0>")]
253    /// old bpf program fd
254    pub old_prog_fd: i32,
255}
256
257#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
258/// Extra fields in prog meta for TC programs
259pub struct TCProgExtraMeta {
260    #[serde(default)]
261    /// TC Hook point
262    pub tchook: TCHook,
263    #[serde(default)]
264    /// TC Hook options
265    pub tcopts: TCOpts,
266}
267
268#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
269/// TC Hook options
270pub struct TCHook {
271    #[serde(default = "default_helpers::default_i32::<1>")]
272    /// Which interface to hook
273    pub ifindex: i32,
274    #[serde(default)]
275    /// Hook point
276    pub attach_point: TCAttachPoint,
277}
278impl Default for TCHook {
279    fn default() -> Self {
280        Self {
281            ifindex: 1,
282            attach_point: TCAttachPoint::default(),
283        }
284    }
285}
286
287#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
288/// TC attach point options
289pub enum TCAttachPoint {
290    #[serde(rename = "BPF_TC_INGRESS")]
291    ///
292    Ingress,
293    #[serde(rename = "BPF_TC_EGRESS")]
294    ///
295    Egress,
296    #[serde(rename = "BPF_TC_CUSTOM")]
297    ///
298    Custom,
299}
300impl Default for TCAttachPoint {
301    fn default() -> Self {
302        Self::Ingress
303    }
304}
305
306impl TCAttachPoint {
307    /// Get the BPF_TC_XXX values for this enum
308    pub fn to_value(&self) -> u32 {
309        match self {
310            TCAttachPoint::Ingress => BPF_TC_INGRESS,
311            TCAttachPoint::Egress => BPF_TC_EGRESS,
312            TCAttachPoint::Custom => BPF_TC_CUSTOM,
313        }
314    }
315}
316
317#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
318/// Options for TC program
319pub struct TCOpts {
320    #[serde(default = "default_helpers::default_u32::<1>")]
321    ///
322    pub handle: u32,
323    #[serde(default = "default_helpers::default_u32::<1>")]
324    ///
325    pub priority: u32,
326}
327impl Default for TCOpts {
328    fn default() -> Self {
329        Self {
330            handle: 1,
331            priority: 1,
332        }
333    }
334}
335
336#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Default)]
337/// The command line argument that can be used to retrive the value of this variable
338pub struct VariableCommandArgument {
339    #[serde(default)]
340    /// The default value of this option
341    pub default: Option<Value>,
342    /// The long name of this. If not provided, will use the variable name
343    pub long: Option<String>,
344    /// The short name of this
345    pub short: Option<String>,
346    /// The help string of this option. If not provided, will use the description
347    pub help: Option<String>,
348}
349
350#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
351/// Describe a variable in a data section
352pub struct DataSectionVariableMeta {
353    /// Name of this variable
354    pub name: String,
355    #[serde(rename = "type")]
356    /// Type of this variable
357    pub ty: String,
358    #[serde(skip_serializing_if = "Option::is_none")]
359    /// Value of this variable. This will be filled into the initial value of the corresponding map
360    pub value: Option<Value>,
361    #[serde(skip_serializing_if = "Option::is_none")]
362    /// Description of this variable. Will be used to display in generated command arguments
363    pub description: Option<String>,
364    #[serde(default)]
365    /// The command line argument to produce this variable
366    pub cmdarg: VariableCommandArgument,
367    #[serde(flatten)]
368    /// Other fields
369    pub others: Value,
370}
371#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
372/// Describe a data section
373pub struct DataSectionMeta {
374    /// Name of the section
375    pub name: String,
376    /// Variables in this section
377    pub variables: Vec<DataSectionVariableMeta>,
378}
379#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Default)]
380/// Docs of a bpf skeleton
381/// I'm sure you can understand the meaning of the fields without any docs...
382/// UPD: I've forgotten deep source..
383pub struct BpfSkelDoc {
384    ///
385    pub version: Option<String>,
386    ///
387    pub brief: Option<String>,
388    ///
389    pub details: Option<String>,
390    ///
391    pub description: Option<String>,
392}
393#[serde_with::serde_as]
394#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
395/// Describe a bpf skeleton elf
396pub struct BpfSkeletonMeta {
397    /// Data sections in this elf
398    pub data_sections: Vec<DataSectionMeta>,
399    /// Maps this program will use
400    pub maps: Vec<MapMeta>,
401    #[serde_as(deserialize_as = "DefaultOnNull")]
402    /// bpf programs in this object file
403    pub progs: Vec<ProgMeta>,
404    /// Object file name
405    pub obj_name: String,
406    #[serde(skip_serializing_if = "Option::is_none")]
407    /// Documents
408    pub doc: Option<BpfSkelDoc>,
409}
410impl BpfSkeletonMeta {
411    /// Find a map by its ident
412    pub fn find_map_by_ident(&self, ident: impl AsRef<str>) -> Option<&MapMeta> {
413        let str_ref = ident.as_ref();
414        self.maps.iter().find(|s| s.ident == str_ref)
415    }
416}
417
418/// global meta data config
419#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
420pub struct EunomiaObjectMeta {
421    /// Export types
422    #[serde(default)]
423    pub export_types: Vec<ExportedTypesStructMeta>,
424    /// The object skeleton
425    pub bpf_skel: BpfSkeletonMeta,
426
427    #[serde(default = "default_helpers::default_usize::<64>")]
428    /// perf buffer related config
429    pub perf_buffer_pages: usize,
430    #[serde(default = "default_helpers::default_usize::<10>")]
431    /// perf buffer related config
432    pub perf_buffer_time_ms: usize,
433    #[serde(default = "default_helpers::default_i32::<100>")]
434    /// poll config
435    pub poll_timeout_ms: i32,
436    #[serde(default = "default_helpers::default_bool::<false>")]
437    /// Whether libbpf should print debug info
438    /// This will only be apply to libbpf when start running
439    pub debug_verbose: bool,
440    #[serde(default = "default_helpers::default_bool::<false>")]
441    /// print config
442    /// print the types and names of export headers
443    pub print_header: bool,
444    /// Whether to enable support of multiple export types
445    /// If set to false, bpf-loader-rs will keep compatibility to the old version
446    /// If set to true, the field `export_config` of each map will be used
447    /// and the `export_types` field will be ignored
448    #[serde(default = "default_helpers::default_bool::<false>")]
449    pub enable_multiple_export_types: bool,
450}
451#[derive(Deserialize, Serialize, PartialEq, Eq)]
452pub(crate) struct ComposedObjectInner {
453    pub(crate) bpf_object: String,
454    pub(crate) bpf_object_size: usize,
455    pub(crate) meta: EunomiaObjectMeta,
456}
457
458#[derive(Clone, Debug, PartialEq, Eq)]
459/// Describe a full eunomia json(with ebpf object inside)
460/// The original json should be like:
461/// ```json
462/// {
463///    "bpf_object": "", // An base64-encoded, zlib deflate-compressed object file
464///    "bpf_object_size" : 0 , /// The uncompressed size of the object file, in bytes
465///    "meta": {} /// The meta object
466/// }
467/// ```
468pub struct ComposedObject {
469    /// The object binary
470    pub bpf_object: Vec<u8>,
471    /// The meta info
472    pub meta: EunomiaObjectMeta,
473}
474
475impl Serialize for ComposedObject {
476    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
477    where
478        S: serde::Serializer,
479    {
480        use serde::ser::Error;
481        let bpf_object_size = self.bpf_object.len();
482        let compressed = deflate_bytes_zlib(&self.bpf_object);
483        let bpf_object_base64 = base64::engine::general_purpose::STANDARD.encode(compressed);
484
485        let json_val = serde_json::to_value(ComposedObjectInner {
486            bpf_object: bpf_object_base64,
487            bpf_object_size,
488            meta: self.meta.clone(),
489        })
490        .map_err(|e| Error::custom(format!("Failed to serialize: {e}")))?;
491        json_val.serialize(serializer)
492    }
493}
494impl<'de> Deserialize<'de> for ComposedObject {
495    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
496    where
497        D: serde::Deserializer<'de>,
498    {
499        use serde::de::Error;
500        let json_val: ComposedObjectInner =
501            serde_json::from_value(Value::deserialize(deserializer)?)
502                .map_err(|e| Error::custom(format!("Malformed json provided: {e}")))?;
503        let base64_decoded = base64::engine::general_purpose::STANDARD
504            .decode(&json_val.bpf_object)
505            .map_err(|e| Error::custom(format!("Malformed base64: {e}")))?;
506        let uncompressed = inflate::inflate_bytes_zlib(&base64_decoded)
507            .map_err(|e| Error::custom(format!("Malformed compressed data: {e}")))?;
508        if uncompressed.len() != json_val.bpf_object_size {
509            return Err(Error::custom(format!(
510                "Unmatched size: {} in the json, but {} in the decompressed file",
511                json_val.bpf_object_size,
512                uncompressed.len()
513            )));
514        }
515        Ok(Self {
516            bpf_object: uncompressed,
517            meta: json_val.meta,
518        })
519    }
520}
521
522/// Global config to control the behavior of eunomia-bpf
523/// TODO: load config from json or config files
524#[derive(Deserialize, Serialize, Debug, Clone, Default)]
525pub struct RunnerConfig {
526    /// whether we should print the bpf_printk
527    /// from /sys/kernel/debug/tracing/trace_pipe
528    #[serde(default = "default_helpers::default_bool::<false>")]
529    pub print_kernel_debug: bool,
530}
531
532pub(crate) mod default_helpers {
533    pub(crate) fn default_bool<const V: bool>() -> bool {
534        V
535    }
536    pub(crate) fn default_usize<const V: usize>() -> usize {
537        V
538    }
539    pub(crate) fn default_i32<const V: i32>() -> i32 {
540        V
541    }
542    pub(crate) fn default_u32<const V: u32>() -> u32 {
543        V
544    }
545
546    pub(crate) fn map_unit_default() -> String {
547        "(unit)".into()
548    }
549}
550
551/// The builder of `Command`
552pub mod arg_builder;
553/// A parser that can parse values from command line
554pub mod arg_parser;
555#[cfg(test)]
556mod tests;