planarian_backend_netwatch/
ebpf.rs1pub const CAPTURE: usize = 512;
8pub const LEVELS: usize = 4;
9pub const NAME: usize = 96;
10pub const CG_ROOT: u32 = 1 << 31;
12const HEADER: usize = 8 + 4 * 6;
13pub const EVENT_SIZE: usize = HEADER + LEVELS * NAME + CAPTURE;
14
15pub const HOOK_RTNETLINK: u32 = 1;
16pub const HOOK_NFNETLINK: u32 = 2;
17pub const HOOK_GENL: u32 = 3;
18pub const HOOK_NFBATCH: u32 = 4;
20pub const HOOK_SOCKOPT: u32 = 5;
22pub const HOOK_NETDEV: u32 = 6;
25
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct Event {
28 pub cgroup: u64,
29 pub tgid: u32,
30 pub hook: u32,
31 pub len: u32,
33 pub netns: u32,
36 pub cgpath: Option<String>,
39 pub data: Vec<u8>,
41}
42
43impl Event {
44 pub fn parse(b: &[u8]) -> Option<Event> {
45 if b.len() < EVENT_SIZE {
46 return None;
47 }
48 let u32_at = |o: usize| u32::from_ne_bytes(b[o..o + 4].try_into().unwrap());
49 let captured = (u32_at(20) as usize).min(CAPTURE);
50 Some(Event {
51 cgroup: u64::from_ne_bytes(b[0..8].try_into().unwrap()),
52 tgid: u32_at(8),
53 hook: u32_at(12),
54 len: u32_at(16),
55 netns: u32_at(24),
56 cgpath: cgpath(u32_at(28), &b[HEADER..HEADER + LEVELS * NAME]),
57 data: b[HEADER + LEVELS * NAME..HEADER + LEVELS * NAME + captured].to_vec(),
58 })
59 }
60
61 pub fn truncated(&self) -> bool {
63 self.len as usize > self.data.len()
64 }
65
66 pub fn nlmsg_type(&self) -> Option<u16> {
68 self.data.get(4..6).map(|b| u16::from_ne_bytes(b.try_into().unwrap()))
69 }
70}
71
72fn cgpath(flags: u32, names: &[u8]) -> Option<String> {
75 let n = (flags & !CG_ROOT) as usize;
76 if flags & CG_ROOT == 0 || n == 0 || n > LEVELS {
77 return None;
78 }
79 let mut parts = Vec::with_capacity(n);
80 for slot in names.chunks(NAME).take(n) {
81 let end = slot.iter().position(|&c| c == 0)?;
82 if end == 0 {
83 return None;
84 }
85 parts.push(std::str::from_utf8(&slot[..end]).ok()?);
86 }
87 parts.reverse();
88 Some(parts.join("/"))
89}