bpf_feature/
kernel_config.rs1use flate2::bufread::GzDecoder;
18use nix::sys::utsname;
19use std::{
20 collections::HashMap,
21 fmt::Display,
22 fs::File,
23 io::{BufRead, BufReader},
24};
25use thiserror::Error as ThisError;
26
27#[cfg(feature = "serde")]
28use bpf_rs_macros::SerializeFromDisplay;
29#[cfg(feature = "serde")]
30use serde::Serialize;
31
32pub const KERNEL_CONFIG_KEYS: [&str; 35] = [
34 "CONFIG_BPF",
35 "CONFIG_BPF_SYSCALL",
36 "CONFIG_HAVE_EBPF_JIT",
37 "CONFIG_BPF_JIT",
38 "CONFIG_BPF_JIT_ALWAYS_ON",
39 "CONFIG_DEBUG_INFO_BTF",
40 "CONFIG_DEBUG_INFO_BTF_MODULES",
41 "CONFIG_CGROUPS",
42 "CONFIG_CGROUP_BPF",
43 "CONFIG_CGROUP_NET_CLASSID",
44 "CONFIG_SOCK_CGROUP_DATA",
45 "CONFIG_BPF_EVENTS",
46 "CONFIG_KPROBE_EVENTS",
47 "CONFIG_UPROBE_EVENTS",
48 "CONFIG_TRACING",
49 "CONFIG_FTRACE_SYSCALLS",
50 "CONFIG_FUNCTION_ERROR_INJECTION",
51 "CONFIG_BPF_KPROBE_OVERRIDE",
52 "CONFIG_NET",
53 "CONFIG_XDP_SOCKETS",
54 "CONFIG_LWTUNNEL_BPF",
55 "CONFIG_NET_ACT_BPF",
56 "CONFIG_NET_CLS_BPF",
57 "CONFIG_NET_CLS_ACT",
58 "CONFIG_NET_SCH_INGRESS",
59 "CONFIG_XFRM",
60 "CONFIG_IP_ROUTE_CLASSID",
61 "CONFIG_IPV6_SEG6_BPF",
62 "CONFIG_BPF_LIRC_MODE2",
63 "CONFIG_BPF_STREAM_PARSER",
64 "CONFIG_NETFILTER_XT_MATCH_BPF",
65 "CONFIG_BPFILTER",
66 "CONFIG_BPFILTER_UMH",
67 "CONFIG_TEST_BPF",
68 "CONFIG_HZ",
69];
70
71#[non_exhaustive]
73#[derive(ThisError, Debug)]
74#[cfg_attr(feature = "serde", derive(Serialize))]
75pub enum KernelConfigError {
76 #[error("can't open file")]
78 NotFound,
79 #[error("file data format unknown")]
81 ContentsUnknown,
82 #[error("can't read from file")]
84 ReadFail,
85}
86
87#[derive(Debug)]
89#[cfg_attr(feature = "serde", derive(SerializeFromDisplay))]
90pub enum ConfigValue {
91 Y,
93 N,
99 M,
104 NotSet,
105 Other(String),
107}
108
109impl Display for ConfigValue {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 match self {
112 ConfigValue::Y => write!(f, "y"),
113 ConfigValue::N => write!(f, "n"),
114 ConfigValue::M => write!(f, "m"),
115 ConfigValue::NotSet => write!(f, "not set"),
116 ConfigValue::Other(value) => write!(f, "{}", value),
117 }
118 }
119}
120
121type KernelConfigValues = HashMap<&'static str, ConfigValue>;
122
123#[derive(Debug)]
125#[cfg_attr(feature = "serde", derive(Serialize))]
126pub struct KernelConfig {
127 #[cfg_attr(feature = "serde", serde(flatten))]
130 pub values: KernelConfigValues,
131}
132
133impl KernelConfig {
134 fn probe_kernel_config() -> Result<KernelConfigValues, KernelConfigError> {
135 let utsn = utsname::uname();
136
137 let config_reader: Box<dyn BufRead> =
138 match File::open(format!("/boot/config-{}", utsn.release())) {
139 Err(_) => {
140 let compressed_config =
141 File::open("/proc/config.gz").map_err(|_| KernelConfigError::NotFound)?;
142 let decoder = GzDecoder::new(BufReader::new(compressed_config));
143 Box::new(BufReader::new(decoder))
144 }
145 Ok(f) => Box::new(BufReader::new(f)),
146 };
147
148 let mut lines_iter = config_reader.lines();
149 let _ = lines_iter
150 .next()
151 .transpose()
152 .map_err(|_| KernelConfigError::ReadFail)?
153 .ok_or(KernelConfigError::ReadFail)?;
154 let line = lines_iter
155 .next()
156 .transpose()
157 .map_err(|_| KernelConfigError::ReadFail)?
158 .ok_or(KernelConfigError::ReadFail)?;
159
160 if !line.starts_with("# Automatically generated file; DO NOT EDIT.") {
161 return Err(KernelConfigError::ContentsUnknown);
162 }
163
164 let mut config = HashMap::from(KERNEL_CONFIG_KEYS.map(|key| (key, ConfigValue::NotSet)));
165
166 for line_item in lines_iter {
167 let line = line_item.map_err(|_| KernelConfigError::ReadFail)?;
168 if !line.starts_with("CONFIG_") {
169 continue;
170 }
171
172 let split_items: Vec<_> = line.split('=').collect();
173 if split_items.len() < 2 {
174 continue;
175 }
176
177 let line_key = split_items[0];
178 let line_value = split_items[1];
179
180 for key in KERNEL_CONFIG_KEYS {
181 if key != line_key {
182 continue;
183 }
184
185 config.insert(
186 key,
187 match line_value {
188 "y" => ConfigValue::Y,
189 "m" => ConfigValue::M,
190 "n" => ConfigValue::N,
191 _ => ConfigValue::Other(line_value.to_string()),
192 },
193 );
194 }
195 }
196
197 Ok(config)
198 }
199}
200
201pub fn features() -> Result<KernelConfig, KernelConfigError> {
204 Ok(KernelConfig {
205 values: KernelConfig::probe_kernel_config()?,
206 })
207}