1use crate::program::LoadedProgram;
2use crate::result::HookResult;
3
4#[repr(usize)]
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum HookPoint {
8 PreIngress = 0,
10 PreDispatch = 1,
11
12 AnnounceReceived = 2,
14 PathUpdated = 3,
15 AnnounceRetransmit = 4,
16
17 LinkRequestReceived = 5,
19 LinkEstablished = 6,
20 LinkClosed = 7,
21
22 InterfaceUp = 8,
24 InterfaceDown = 9,
25 InterfaceConfigChanged = 10,
26
27 SendOnInterface = 11,
29 BroadcastOnAllInterfaces = 12,
30 DeliverLocal = 13,
31 TunnelSynthesize = 14,
32
33 Tick = 15,
35}
36
37impl HookPoint {
38 pub const COUNT: usize = 16;
40}
41
42pub enum HookContext<'a> {
47 Packet { ctx: &'a crate::context::PacketContext, raw: &'a [u8] },
48 Interface { interface_id: u64 },
49 Tick,
50 Announce {
51 destination_hash: [u8; 16],
52 hops: u8,
53 interface_id: u64,
54 },
55 Link {
56 link_id: [u8; 16],
57 interface_id: u64,
58 },
59}
60
61pub type HookFn = fn(&HookSlot, &HookContext) -> Option<HookResult>;
63
64pub fn hook_noop(_slot: &HookSlot, _ctx: &HookContext) -> Option<HookResult> {
68 None
69}
70
71fn hook_has_programs(_slot: &HookSlot, _ctx: &HookContext) -> Option<HookResult> {
76 None
79}
80
81pub struct HookSlot {
83 pub programs: Vec<LoadedProgram>,
84 pub runner: HookFn,
87}
88
89impl HookSlot {
90 pub fn update_runner(&mut self) {
92 if self.programs.is_empty() {
93 self.runner = hook_noop;
94 } else {
95 self.runner = hook_has_programs;
96 }
97 }
98
99 pub fn attach(&mut self, program: LoadedProgram) {
101 self.programs.push(program);
102 self.programs.sort_by(|a, b| b.priority.cmp(&a.priority));
103 self.update_runner();
104 }
105
106 pub fn detach(&mut self, name: &str) -> Option<LoadedProgram> {
108 let pos = self.programs.iter().position(|p| p.name == name)?;
109 let prog = self.programs.remove(pos);
110 self.update_runner();
111 Some(prog)
112 }
113
114 pub fn has_programs(&self) -> bool {
116 self.runner as *const () as usize != hook_noop as *const () as usize
117 }
118}
119
120pub fn create_hook_slots() -> [HookSlot; HookPoint::COUNT] {
123 std::array::from_fn(|_| HookSlot {
124 programs: Vec::new(),
125 runner: hook_noop,
126 })
127}
128
129#[macro_export]
138macro_rules! run_hook {
139 ($driver:expr, $point:expr, $ctx:expr) => {{
140 ($driver.hook_slots[$point as usize].runner)(
141 &$driver.hook_slots[$point as usize],
142 &$ctx,
143 )
144 }};
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use crate::result::Verdict;
151
152 #[test]
153 fn test_hook_point_count() {
154 assert_eq!(HookPoint::COUNT, 16);
155 assert_eq!(HookPoint::Tick as usize, 15);
157 }
158
159 #[test]
160 fn test_hook_noop_returns_none() {
161 let slot = HookSlot {
162 programs: Vec::new(),
163 runner: hook_noop,
164 };
165 let ctx = HookContext::Tick;
166 assert!(hook_noop(&slot, &ctx).is_none());
167 }
168
169 #[test]
170 fn test_create_hook_slots() {
171 let slots = create_hook_slots();
172 assert_eq!(slots.len(), HookPoint::COUNT);
173 for slot in &slots {
174 assert!(slot.programs.is_empty());
175 let ctx = HookContext::Tick;
177 assert!((slot.runner)(slot, &ctx).is_none());
178 }
179 }
180
181 #[test]
182 fn test_run_hook_macro() {
183 struct FakeDriver {
184 hook_slots: [HookSlot; HookPoint::COUNT],
185 }
186 let driver = FakeDriver {
187 hook_slots: create_hook_slots(),
188 };
189 let ctx = HookContext::Tick;
190 let result = run_hook!(driver, HookPoint::Tick, ctx);
191 assert!(result.is_none());
192
193 let ctx2 = HookContext::Interface { interface_id: 42 };
194 let result2 = run_hook!(driver, HookPoint::InterfaceUp, ctx2);
195 assert!(result2.is_none());
196 }
197
198 #[test]
199 fn test_verdict_values() {
200 assert_eq!(Verdict::Continue as u32, 0);
201 assert_eq!(Verdict::Drop as u32, 1);
202 assert_eq!(Verdict::Modify as u32, 2);
203 assert_eq!(Verdict::Halt as u32, 3);
204 }
205
206 #[test]
207 fn test_hook_result_helpers() {
208 let drop_r = HookResult::drop_result();
209 assert!(drop_r.is_drop());
210 assert_eq!(drop_r.verdict, Verdict::Drop as u32);
211
212 let cont_r = HookResult::continue_result();
213 assert!(!cont_r.is_drop());
214 assert_eq!(cont_r.verdict, Verdict::Continue as u32);
215 assert_eq!(cont_r.modified_data_len, 0);
216 assert_eq!(cont_r.inject_actions_count, 0);
217 assert_eq!(cont_r.log_len, 0);
218 }
219}