1pub mod bpf_fn;
45pub mod codegen;
46pub mod runtime;
47pub mod spec;
48
49pub use spec::{
50 BpfAttachPoint, BpfMapKind, BpfMapSpec, BpfPolicySpec, BpfProgramKind, BpfProgramSpec,
51};
52
53impl tatara_lisp::DocumentedDomain for BpfProgramSpec {
60 const DOCSTRING: &'static str =
61 "One BPF program — kind (XDP/TC/kprobe/...), attach point, source, license. \
62 Loaded via aya at runtime; built hermetically through substrate's ebpf.nix.";
63 const FIELD_DOCS: &'static [(&'static str, &'static str)] = &[
64 ("name", "Program name — the symbol exported in the BPF object."),
65 ("kind", "BPF program kind. Drives the aya `#[xdp]` etc. attribute."),
66 ("attach", "Where the program attaches (interface, kernel symbol, cgroup, ...)"),
67 ("source", "Path to the program body — `*.rs`, `*.bpf.o`, or `*.tlisp:fn`."),
68 ("license", "SPDX license string. GPL required for most helpers."),
69 ("pin_path", "Optional bpffs pin path so the program survives the loader."),
70 ("uses_maps", "BPF maps this program reads or writes."),
71 ];
72}
73
74impl tatara_lisp::DocumentedDomain for BpfMapSpec {
75 const DOCSTRING: &'static str =
76 "One BPF map — hash / array / per-cpu / ring-buf / etc. \
77 The kernel-↔-userspace data plane for BPF programs.";
78 const FIELD_DOCS: &'static [(&'static str, &'static str)] = &[
79 ("name", "Map name."),
80 ("kind", "Map kind — drives access pattern (hash/array/perf-event/...)"),
81 ("key_size", "Key size in bytes (0 for keyless maps like RingBuf)."),
82 ("value_size", "Value size in bytes."),
83 ("max_entries", "Capacity. For RingBuf, total bytes (page-rounded)."),
84 ("pin_path", "Optional bpffs pin path."),
85 ];
86}
87
88impl tatara_lisp::DocumentedDomain for BpfPolicySpec {
89 const DOCSTRING: &'static str =
90 "Composition of programs + maps applied as one unit. The IaC-shape \
91 arch-synthesizer + FluxCD consume.";
92 const FIELD_DOCS: &'static [(&'static str, &'static str)] = &[
93 ("name", "Policy name."),
94 ("description", "Human-readable description."),
95 ("programs", "Names of `defbpf-program`s composed in this policy."),
96 ("maps", "Names of `defbpf-map`s composed in this policy."),
97 ];
98}
99
100impl tatara_lisp::DependentDomain for BpfMapSpec {
106 const DEPENDS_ON: &'static [&'static str] = &[];
107}
108impl tatara_lisp::DependentDomain for BpfProgramSpec {
109 const DEPENDS_ON: &'static [&'static str] = &["defbpf-map"];
110}
111impl tatara_lisp::DependentDomain for BpfPolicySpec {
112 const DEPENDS_ON: &'static [&'static str] = &["defbpf-program", "defbpf-map"];
113}
114
115impl tatara_lisp::AttestableDomain for BpfMapSpec {
120 const ATTESTATION_NAMESPACE: &'static str = "pleme.io/ebpf";
121}
122impl tatara_lisp::AttestableDomain for BpfProgramSpec {
123 const ATTESTATION_NAMESPACE: &'static str = "pleme.io/ebpf";
124}
125impl tatara_lisp::AttestableDomain for BpfPolicySpec {
126 const ATTESTATION_NAMESPACE: &'static str = "pleme.io/ebpf";
127}
128
129impl tatara_lisp::ValidatedDomain for BpfProgramSpec {
136 fn validate_value(value: &serde_json::Value) -> std::result::Result<(), String> {
137 let obj = value
138 .as_object()
139 .ok_or_else(|| "expected JSON object".to_string())?;
140 let license = obj
141 .get("license")
142 .and_then(|v| v.as_str())
143 .unwrap_or("");
144 let uses_maps = obj
145 .get("uses_maps")
146 .and_then(|v| v.as_array())
147 .map(|a| !a.is_empty())
148 .unwrap_or(false);
149 if uses_maps && !is_gpl_compatible(license) {
150 return Err(format!(
151 "BPF program declares `:uses-maps` but `:license` `{license}` \
152 is not GPL-compatible — kernel verifier will reject \
153 calls to bpf_map_lookup_elem etc."
154 ));
155 }
156 if let Some(kind) = obj.get("kind").and_then(|v| v.as_str()) {
158 let attach_target = obj
159 .get("attach")
160 .and_then(|a| a.get("target"))
161 .and_then(|t| t.as_str())
162 .unwrap_or("");
163 let needs_iface = matches!(kind, ":xdp" | ":tc");
164 if needs_iface && attach_target.is_empty() {
165 return Err(format!(
166 "BPF program kind `{kind}` requires `:attach (:target \"<iface>\")` — got empty target"
167 ));
168 }
169 }
170 Ok(())
171 }
172}
173
174fn is_gpl_compatible(license: &str) -> bool {
175 matches!(license, "GPL" | "GPL v2" | "Dual MIT/GPL" | "Dual BSD/GPL")
176}
177
178impl tatara_lisp::ValidatedDomain for BpfMapSpec {}
179impl tatara_lisp::ValidatedDomain for BpfPolicySpec {}
180
181impl tatara_lisp::CompliantDomain for BpfMapSpec {
187 const FRAMEWORKS: &'static [&'static str] = &[];
188 const CONTROLS: &'static [&'static str] = &[];
189}
190impl tatara_lisp::CompliantDomain for BpfProgramSpec {
191 const FRAMEWORKS: &'static [&'static str] = &[];
192 const CONTROLS: &'static [&'static str] = &[];
193}
194impl tatara_lisp::CompliantDomain for BpfPolicySpec {
195 const FRAMEWORKS: &'static [&'static str] = &["NIST 800-53", "CIS"];
196 const CONTROLS: &'static [&'static str] = &[
197 "NIST SC-7", "NIST SI-3", "CIS 5.1", ];
201}
202
203impl tatara_lisp::ObservableDomain for BpfMapSpec {
207 const METRIC_PREFIX: &'static str = "";
208 const LOG_LABELS: &'static [&'static str] = &[];
209}
210impl tatara_lisp::ObservableDomain for BpfProgramSpec {
211 const METRIC_PREFIX: &'static str = "tatara_ebpf_program";
212 const LOG_LABELS: &'static [&'static str] = &["program", "kind", "interface"];
213}
214impl tatara_lisp::ObservableDomain for BpfPolicySpec {
215 const METRIC_PREFIX: &'static str = "tatara_ebpf_policy";
216 const LOG_LABELS: &'static [&'static str] = &["policy"];
217}
218
219impl tatara_lisp::HelpDomain for BpfMapSpec {
221 const MNEMONIC: &'static str = "kernel-↔-userspace data plane";
222 const EXAMPLES: &'static [&'static str] = &[concat!(
223 "(defbpf-map\n",
224 " :name \"syn-counter\"\n",
225 " :kind :per-cpu-array\n",
226 " :key-size 4 :value-size 8 :max-entries 1)"
227 )];
228}
229impl tatara_lisp::HelpDomain for BpfProgramSpec {
230 const MNEMONIC: &'static str = "one BPF program (XDP/TC/kprobe/...)";
231 const EXAMPLES: &'static [&'static str] = &[concat!(
232 "(defbpf-program\n",
233 " :name \"drop-syn-flood\"\n",
234 " :kind :xdp\n",
235 " :attach (:target \"eth0\")\n",
236 " :source \"bpf/drop_syn.rs\"\n",
237 " :license \"GPL\")"
238 )];
239}
240impl tatara_lisp::HelpDomain for BpfPolicySpec {
241 const MNEMONIC: &'static str = "composition of programs + maps as one IaC unit";
242 const EXAMPLES: &'static [&'static str] = &[concat!(
243 "(defbpf-policy\n",
244 " :name \"edge-protection\"\n",
245 " :description \"L4 SYN-flood mitigation\"\n",
246 " :programs (\"drop_syn_flood\")\n",
247 " :maps (\"syn_counter\"))"
248 )];
249}
250
251impl tatara_lisp::StableDomain for BpfMapSpec {
254 const STABILITY: &'static str = "stable";
255 const SINCE_VERSION: &'static str = "0.1.0";
256}
257impl tatara_lisp::StableDomain for BpfProgramSpec {
258 const STABILITY: &'static str = "stable";
259 const SINCE_VERSION: &'static str = "0.2.0";
260}
261impl tatara_lisp::StableDomain for BpfPolicySpec {
262 const STABILITY: &'static str = "stable";
263 const SINCE_VERSION: &'static str = "0.2.0";
264}
265
266impl tatara_lisp::LifecycleProtocol for BpfProgramSpec {
273 const STRATEGY: tatara_lisp::RolloutStrategy = tatara_lisp::RolloutStrategy::BlueGreen;
274 const DRAIN_SECONDS: u32 = 5;
275}
276impl tatara_lisp::LifecycleProtocol for BpfMapSpec {
277 const STRATEGY: tatara_lisp::RolloutStrategy = tatara_lisp::RolloutStrategy::Recreate;
281 const DRAIN_SECONDS: u32 = 1;
282}
283impl tatara_lisp::LifecycleProtocol for BpfPolicySpec {
284 const STRATEGY: tatara_lisp::RolloutStrategy = tatara_lisp::RolloutStrategy::BlueGreen;
285 const DRAIN_SECONDS: u32 = 5;
286}
287
288pub fn register() {
292 tatara_lisp::domain::register::<BpfProgramSpec>();
293 tatara_lisp::domain::register::<BpfMapSpec>();
294 tatara_lisp::domain::register::<BpfPolicySpec>();
295 tatara_lisp::domain::register_doc::<BpfProgramSpec>();
297 tatara_lisp::domain::register_doc::<BpfMapSpec>();
298 tatara_lisp::domain::register_doc::<BpfPolicySpec>();
299 tatara_lisp::domain::register_deps::<BpfProgramSpec>();
301 tatara_lisp::domain::register_deps::<BpfMapSpec>();
302 tatara_lisp::domain::register_deps::<BpfPolicySpec>();
303 tatara_lisp::domain::register_attest::<BpfProgramSpec>();
305 tatara_lisp::domain::register_attest::<BpfMapSpec>();
306 tatara_lisp::domain::register_attest::<BpfPolicySpec>();
307 tatara_lisp::domain::register_validate::<BpfProgramSpec>();
309 tatara_lisp::domain::register_validate::<BpfMapSpec>();
310 tatara_lisp::domain::register_validate::<BpfPolicySpec>();
311 tatara_lisp::domain::register_lifecycle::<BpfProgramSpec>();
313 tatara_lisp::domain::register_lifecycle::<BpfMapSpec>();
314 tatara_lisp::domain::register_lifecycle::<BpfPolicySpec>();
315 tatara_lisp::domain::register_compliance::<BpfProgramSpec>();
317 tatara_lisp::domain::register_compliance::<BpfMapSpec>();
318 tatara_lisp::domain::register_compliance::<BpfPolicySpec>();
319 tatara_lisp::domain::register_observability::<BpfProgramSpec>();
321 tatara_lisp::domain::register_observability::<BpfMapSpec>();
322 tatara_lisp::domain::register_observability::<BpfPolicySpec>();
323 tatara_lisp::domain::register_help::<BpfProgramSpec>();
325 tatara_lisp::domain::register_help::<BpfMapSpec>();
326 tatara_lisp::domain::register_help::<BpfPolicySpec>();
327 tatara_lisp::domain::register_stability::<BpfProgramSpec>();
329 tatara_lisp::domain::register_stability::<BpfMapSpec>();
330 tatara_lisp::domain::register_stability::<BpfPolicySpec>();
331}