use serde::{Deserialize, Serialize};
use tatara_lisp_derive::TataraDomain;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum BpfProgramKind {
#[serde(rename = ":xdp")]
Xdp,
#[serde(rename = ":tc")]
Tc,
#[serde(rename = ":socket-filter")]
SocketFilter,
#[serde(rename = ":kprobe")]
Kprobe,
#[serde(rename = ":tracepoint")]
Tracepoint,
#[serde(rename = ":cgroup-skb")]
CgroupSkb,
#[serde(rename = ":lsm")]
Lsm,
#[serde(rename = ":perf-event")]
PerfEvent,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct BpfAttachPoint {
pub target: String,
#[serde(default)]
pub direction: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TataraDomain)]
#[tatara(keyword = "defbpf-program")]
pub struct BpfProgramSpec {
pub name: String,
pub kind: BpfProgramKind,
pub attach: BpfAttachPoint,
pub source: String,
#[serde(default = "default_license")]
pub license: String,
#[serde(default)]
pub pin_path: Option<String>,
#[serde(default)]
pub uses_maps: Vec<String>,
}
fn default_license() -> String {
"GPL".into()
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum BpfMapKind {
#[serde(rename = ":hash")]
Hash,
#[serde(rename = ":lru-hash")]
LruHash,
#[serde(rename = ":array")]
Array,
#[serde(rename = ":per-cpu-hash")]
PerCpuHash,
#[serde(rename = ":per-cpu-array")]
PerCpuArray,
#[serde(rename = ":ring-buf")]
RingBuf,
#[serde(rename = ":perf-event-array")]
PerfEventArray,
#[serde(rename = ":stack-trace")]
StackTrace,
#[serde(rename = ":lpm-trie")]
LpmTrie,
}
#[derive(Debug, Clone, Serialize, Deserialize, TataraDomain)]
#[tatara(keyword = "defbpf-map")]
pub struct BpfMapSpec {
pub name: String,
pub kind: BpfMapKind,
#[serde(default)]
pub key_size: u32,
pub value_size: u32,
pub max_entries: u32,
#[serde(default)]
pub pin_path: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TataraDomain)]
#[tatara(keyword = "defbpf-policy")]
pub struct BpfPolicySpec {
pub name: String,
pub description: String,
pub programs: Vec<String>,
#[serde(default)]
pub maps: Vec<String>,
}
impl BpfPolicySpec {
pub fn validate(
&self,
programs_by_name: &std::collections::HashMap<String, BpfProgramSpec>,
maps_by_name: &std::collections::HashMap<String, BpfMapSpec>,
) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
let known_maps: std::collections::HashSet<&str> =
self.maps.iter().map(String::as_str).collect();
for prog_name in &self.programs {
let Some(prog) = programs_by_name.get(prog_name) else {
errors.push(format!(
"policy `{}`: program `{}` not declared",
self.name, prog_name
));
continue;
};
for m in &prog.uses_maps {
if !known_maps.contains(m.as_str()) {
errors.push(format!(
"policy `{}`: program `{}` uses map `{}`, but the policy doesn't declare it",
self.name, prog_name, m
));
}
if !maps_by_name.contains_key(m) {
errors.push(format!(
"policy `{}`: program `{}` uses map `{}`, which has no BpfMapSpec",
self.name, prog_name, m
));
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}