1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! A library focused on detecting supported eBPF features on the current host
//!
//! # Background
//!
//! The approaches here taken are similar to the way [bpftool](https://github.com/libbpf/bpftool)
//! probes functionality on the host. We recommend users use the `bpftool feature`
//! subcommand for interactive display of eBPF feature support but we
//! developed this library for incorporating these detection techniques within Rust apps.
//!
//! ## Compatibility with bpftool
//!
//! This library is aimed to exactly match the detection features of `bpftool feature`. If
//! this is not the case, we consider [it a bug](https://github.com/bpfdeploy-io/bpf-rs/issues).
//!
//! As an example of this, we recreated the default output of `bpftool feature` here:
//! [`examples/feature-probe.rs`](https://github.com/bpfdeploy-io/bpf-rs/tree/main/bpf-feature/examples)
//!
//! # JSON
//!
//! We also support JSON output. This is powered by [Serde](https://github.com/serde-rs/serde),
//! a popular serialization crate. Note that the JSON output differs in structure
//! from the output of `bpftool feature probe --json` but semantically should be
//! identical.
//!
//! To see an example of JSON out, see the example
//! [`examples/json-dump.rs`](https://github.com/bpfdeploy-io/bpf-rs/tree/main/bpf-feature/examples).
//!
//! Serialization support is **NOT** enabled by default. Please pass in the `serde`
//! feature to enable.
//!
//! ## Other serialization formats
//!
//! Because of the abstraction Serde provides, we are not restricted to JSON and it
//! is possible to support other serialization formats. This should work out of the
//! box but if issues occur, please let us know.
//!
//! # Design
//!
//! For detecting all functionality, we've exported a singular function [`detect`]
//! that can be configured with options through [`DetectOpts`] (to pass in the
//! defaults you can use [`DetectOpts::default()`]):
//!
//! ```
//! use bpf_feature::{detect, DetectOpts};
//!
//! fn main() {
//!     let features = detect(DetectOpts::default());
//!     // ...
//! }
//! ```
//!
//! ## Modularity
//!
//! `detect` is not the only entrypoint publicly exported. We have organized
//! related features into modules that export specific detections through a
//! `features()` function:
//!
//! - [`bpf::features`]
//! - [`kernel_config::features`]
//! - [`runtime::features`]
//! - [`misc::features`]
//!
//! This means that in your application can choose which features to run:
//!
//! ```
//! use bpf_feature::kernel_config::{self, KERNEL_CONFIG_KEYS, KernelConfig};
//!
//! fn main() {
//!     match kernel_config::features() {
//!         Ok(KernelConfig { values }) => KERNEL_CONFIG_KEYS.iter().for_each(|&key| {
//!             match values.get(key) {
//!                 Some(value) => println!("{} is set to {}", key, value),
//!                 None => println!("{} is not set", key),
//!             };
//!         }),
//!         Err(err) => println!("skipping kernel config, {}", err),
//!     }
//! }
//! ```
//!
//!

#[cfg(feature = "serde")]
use serde::Serialize;
#[cfg(feature = "serde")]
mod serde_ext;

pub mod bpf;
pub mod kernel_config;
pub mod misc;
pub mod runtime;

/// Results of the entire feature detection set from [`detect`]
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Features {
    #[cfg_attr(feature = "serde", serde(serialize_with = "serde_ext::flatten_result"))]
    pub runtime: Result<runtime::Runtime, runtime::RuntimeError>,
    #[cfg_attr(feature = "serde", serde(serialize_with = "serde_ext::flatten_result"))]
    pub kernel_config: Result<kernel_config::KernelConfig, kernel_config::KernelConfigError>,
    #[cfg_attr(feature = "serde", serde(serialize_with = "serde_ext::flatten_result"))]
    pub bpf: Result<bpf::Bpf, bpf::BpfError>,
    pub misc: misc::Misc,
}

/// Options that can be passed into [`detect`]
pub struct DetectOpts {
    pub full_helpers: bool,
}

impl Default for DetectOpts {
    fn default() -> Self {
        Self {
            full_helpers: false,
        }
    }
}

/// Primary function to run entire feature detection set
pub fn detect(opts: DetectOpts) -> Features {
    Features {
        runtime: runtime::features(),
        kernel_config: kernel_config::features(),
        bpf: bpf::features(bpf::BpfFeaturesOpts {
            full_helpers: opts.full_helpers,
        }),
        misc: misc::features(),
    }
}