1use smallvec::SmallVec;
4
5use super::FieldValue;
6
7pub type FieldEntry<'data> = (&'static str, FieldValue<'data>);
11
12pub type HintEntry = (&'static str, u64);
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
17#[repr(u8)]
18pub enum TunnelType {
19 #[default]
21 None = 0,
22 Vxlan = 1,
24 Gre = 2,
26 Gtp = 3,
28 Mpls = 4,
30 IpInIp = 5,
32 Ip6InIp = 6,
34 Ipsec = 7,
36 Ip4InIp6 = 8,
38 Ip6InIp6 = 9,
40}
41
42impl TunnelType {
43 pub fn from_u64(value: u64) -> Self {
45 match value {
46 0 => TunnelType::None,
47 1 => TunnelType::Vxlan,
48 2 => TunnelType::Gre,
49 3 => TunnelType::Gtp,
50 4 => TunnelType::Mpls,
51 5 => TunnelType::IpInIp,
52 6 => TunnelType::Ip6InIp,
53 7 => TunnelType::Ipsec,
54 8 => TunnelType::Ip4InIp6,
55 9 => TunnelType::Ip6InIp6,
56 _ => TunnelType::None,
57 }
58 }
59
60 pub fn as_str(&self) -> Option<&'static str> {
62 match self {
63 TunnelType::None => None,
64 TunnelType::Vxlan => Some("vxlan"),
65 TunnelType::Gre => Some("gre"),
66 TunnelType::Gtp => Some("gtp"),
67 TunnelType::Mpls => Some("mpls"),
68 TunnelType::IpInIp => Some("ipinip"),
69 TunnelType::Ip6InIp => Some("ip6inip"),
70 TunnelType::Ipsec => Some("ipsec"),
71 TunnelType::Ip4InIp6 => Some("ip4inip6"),
72 TunnelType::Ip6InIp6 => Some("ip6inip6"),
73 }
74 }
75}
76
77#[derive(Debug, Clone, Copy, Default)]
79pub struct TunnelLayer {
80 pub tunnel_type: TunnelType,
82 pub tunnel_id: Option<u64>,
85 pub offset: usize,
87}
88
89#[derive(Debug, Clone)]
91pub struct ParseContext {
92 pub link_type: u16,
94
95 pub parent_protocol: Option<&'static str>,
97
98 pub hints: SmallVec<[HintEntry; 4]>,
101
102 pub offset: usize,
104
105 pub encap_depth: u8,
107
108 pub tunnel_stack: SmallVec<[TunnelLayer; 4]>,
111}
112
113impl ParseContext {
114 pub fn new(link_type: u16) -> Self {
116 Self {
117 link_type,
118 parent_protocol: None,
119 hints: SmallVec::new(),
120 offset: 0,
121 encap_depth: 0,
122 tunnel_stack: SmallVec::new(),
123 }
124 }
125
126 pub fn push_tunnel(&mut self, tunnel_type: TunnelType, tunnel_id: Option<u64>) {
128 self.tunnel_stack.push(TunnelLayer {
129 tunnel_type,
130 tunnel_id,
131 offset: self.offset,
132 });
133 self.encap_depth = self.encap_depth.saturating_add(1);
134 }
135
136 pub fn current_tunnel(&self) -> Option<&TunnelLayer> {
138 self.tunnel_stack.last()
139 }
140
141 pub fn current_tunnel_type(&self) -> TunnelType {
143 self.tunnel_stack
144 .last()
145 .map(|t| t.tunnel_type)
146 .unwrap_or(TunnelType::None)
147 }
148
149 pub fn current_tunnel_id(&self) -> Option<u64> {
151 self.tunnel_stack.last().and_then(|t| t.tunnel_id)
152 }
153
154 #[inline]
156 pub fn hint(&self, key: &str) -> Option<u64> {
157 self.hints.iter().find(|(k, _)| *k == key).map(|(_, v)| *v)
158 }
159
160 #[inline]
163 pub fn insert_hint(&mut self, key: &'static str, value: u64) {
164 self.hints.push((key, value));
165 }
166
167 #[inline]
169 pub fn set_hint(&mut self, key: &'static str, value: u64) {
170 if let Some(entry) = self.hints.iter_mut().find(|(k, _)| *k == key) {
171 entry.1 = value;
172 } else {
173 self.hints.push((key, value));
174 }
175 }
176
177 #[inline]
179 pub fn clear_hints(&mut self) {
180 self.hints.clear();
181 }
182
183 pub fn is_root(&self) -> bool {
185 self.parent_protocol.is_none()
186 }
187}
188
189#[derive(Debug, Clone)]
197pub struct ParseResult<'data> {
198 pub fields: SmallVec<[FieldEntry<'data>; 16]>,
201
202 pub remaining: &'data [u8],
204
205 pub child_hints: SmallVec<[HintEntry; 4]>,
207
208 pub error: Option<String>,
210
211 pub encap_depth: u8,
213
214 pub tunnel_type: TunnelType,
216
217 pub tunnel_id: Option<u64>,
219}
220
221impl<'data> ParseResult<'data> {
222 pub fn success(
227 fields: SmallVec<[FieldEntry<'data>; 16]>,
228 remaining: &'data [u8],
229 child_hints: SmallVec<[HintEntry; 4]>,
230 ) -> Self {
231 Self {
232 fields,
233 remaining,
234 child_hints,
235 error: None,
236 encap_depth: 0,
237 tunnel_type: TunnelType::None,
238 tunnel_id: None,
239 }
240 }
241
242 pub fn error(error: String, remaining: &'data [u8]) -> Self {
244 Self {
245 fields: SmallVec::new(),
246 remaining,
247 child_hints: SmallVec::new(),
248 error: Some(error),
249 encap_depth: 0,
250 tunnel_type: TunnelType::None,
251 tunnel_id: None,
252 }
253 }
254
255 pub fn partial(
257 fields: SmallVec<[FieldEntry<'data>; 16]>,
258 remaining: &'data [u8],
259 error: String,
260 ) -> Self {
261 Self {
262 fields,
263 remaining,
264 child_hints: SmallVec::new(),
265 error: Some(error),
266 encap_depth: 0,
267 tunnel_type: TunnelType::None,
268 tunnel_id: None,
269 }
270 }
271
272 pub fn set_encap_context(&mut self, ctx: &ParseContext) {
274 self.encap_depth = ctx.encap_depth;
275 self.tunnel_type = ctx.current_tunnel_type();
276 self.tunnel_id = ctx.current_tunnel_id();
277 }
278
279 pub fn get(&self, name: &str) -> Option<&FieldValue<'data>> {
281 self.fields.iter().find(|(k, _)| *k == name).map(|(_, v)| v)
282 }
283
284 pub fn hint(&self, name: &str) -> Option<u64> {
286 self.child_hints
287 .iter()
288 .find(|(k, _)| *k == name)
289 .map(|(_, v)| *v)
290 }
291
292 pub fn is_ok(&self) -> bool {
294 self.error.is_none()
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301 use crate::protocol::ethernet::ethertype;
302
303 #[test]
304 fn test_tunnel_type_conversions() {
305 assert_eq!(TunnelType::from_u64(0), TunnelType::None);
306 assert_eq!(TunnelType::from_u64(1), TunnelType::Vxlan);
307 assert_eq!(TunnelType::from_u64(2), TunnelType::Gre);
308 assert_eq!(TunnelType::from_u64(3), TunnelType::Gtp);
309 assert_eq!(TunnelType::from_u64(4), TunnelType::Mpls);
310 assert_eq!(TunnelType::from_u64(5), TunnelType::IpInIp);
311 assert_eq!(TunnelType::from_u64(6), TunnelType::Ip6InIp);
312 assert_eq!(TunnelType::from_u64(7), TunnelType::Ipsec);
313 assert_eq!(TunnelType::from_u64(99), TunnelType::None); assert_eq!(TunnelType::None.as_str(), None);
316 assert_eq!(TunnelType::Vxlan.as_str(), Some("vxlan"));
317 assert_eq!(TunnelType::Gre.as_str(), Some("gre"));
318 assert_eq!(TunnelType::Gtp.as_str(), Some("gtp"));
319 assert_eq!(TunnelType::IpInIp.as_str(), Some("ipinip"));
320 }
321
322 #[test]
323 fn test_context_encap_tracking() {
324 let mut ctx = ParseContext::new(1);
325 assert_eq!(ctx.encap_depth, 0);
326 assert_eq!(ctx.current_tunnel_type(), TunnelType::None);
327 assert_eq!(ctx.current_tunnel_id(), None);
328
329 ctx.push_tunnel(TunnelType::Vxlan, Some(100));
331 assert_eq!(ctx.encap_depth, 1);
332 assert_eq!(ctx.current_tunnel_type(), TunnelType::Vxlan);
333 assert_eq!(ctx.current_tunnel_id(), Some(100));
334
335 ctx.push_tunnel(TunnelType::IpInIp, None);
337 assert_eq!(ctx.encap_depth, 2);
338 assert_eq!(ctx.current_tunnel_type(), TunnelType::IpInIp);
339 assert_eq!(ctx.current_tunnel_id(), None);
340
341 assert_eq!(ctx.tunnel_stack.len(), 2);
343 assert_eq!(ctx.tunnel_stack[0].tunnel_type, TunnelType::Vxlan);
344 assert_eq!(ctx.tunnel_stack[1].tunnel_type, TunnelType::IpInIp);
345 }
346
347 #[test]
348 fn test_parse_result_encap_context() {
349 let mut ctx = ParseContext::new(1);
350 ctx.push_tunnel(TunnelType::Gtp, Some(0x12345678));
351
352 let mut result = ParseResult::success(SmallVec::new(), &[], SmallVec::new());
353 assert_eq!(result.encap_depth, 0);
354 assert_eq!(result.tunnel_type, TunnelType::None);
355 assert_eq!(result.tunnel_id, None);
356
357 result.set_encap_context(&ctx);
358 assert_eq!(result.encap_depth, 1);
359 assert_eq!(result.tunnel_type, TunnelType::Gtp);
360 assert_eq!(result.tunnel_id, Some(0x12345678));
361 }
362
363 #[test]
364 fn test_context_hint_access() {
365 let mut ctx = ParseContext::new(1);
366 ctx.insert_hint("ip_protocol", 6);
367 ctx.insert_hint("dst_port", 80);
368
369 assert_eq!(ctx.hint("ip_protocol"), Some(6));
370 assert_eq!(ctx.hint("dst_port"), Some(80));
371 assert_eq!(ctx.hint("nonexistent"), None);
372 }
373
374 #[test]
375 fn test_context_set_hint_update() {
376 let mut ctx = ParseContext::new(1);
377 ctx.set_hint("ip_protocol", 6);
378 ctx.set_hint("ip_protocol", 17); assert_eq!(ctx.hint("ip_protocol"), Some(17));
381 assert_eq!(ctx.hints.len(), 1); }
383
384 #[test]
385 fn test_context_set_hint_insert() {
386 let mut ctx = ParseContext::new(1);
387 ctx.set_hint("ip_protocol", 6);
388 ctx.set_hint("dst_port", 80); assert_eq!(ctx.hint("ip_protocol"), Some(6));
391 assert_eq!(ctx.hint("dst_port"), Some(80));
392 assert_eq!(ctx.hints.len(), 2);
393 }
394
395 #[test]
396 fn test_context_clear_hints() {
397 let mut ctx = ParseContext::new(1);
398 ctx.insert_hint("ip_protocol", 6);
399 ctx.insert_hint("dst_port", 80);
400
401 ctx.clear_hints();
402
403 assert_eq!(ctx.hints.len(), 0);
404 assert_eq!(ctx.hint("ip_protocol"), None);
405 }
406
407 #[test]
408 fn test_hint_count_stays_inline() {
409 let mut ctx = ParseContext::new(1);
410 ctx.insert_hint("ethertype", ethertype::IPV4 as u64);
411 ctx.insert_hint("ip_protocol", 6);
412 ctx.insert_hint("src_port", 12345);
413 ctx.insert_hint("dst_port", 80);
414
415 assert!(!ctx.hints.spilled());
417 }
418
419 #[test]
420 fn test_parse_result_success() {
421 let mut fields = SmallVec::new();
422 fields.push(("src_port", FieldValue::UInt16(80)));
423
424 let mut hints = SmallVec::new();
425 hints.push(("transport", 6u64));
426
427 let result = ParseResult::success(fields, &[], hints);
428
429 assert!(result.is_ok());
430 assert_eq!(result.get("src_port"), Some(&FieldValue::UInt16(80)));
431 assert_eq!(result.hint("transport"), Some(6));
432 }
433
434 #[test]
435 fn test_parse_result_error() {
436 let result = ParseResult::error("test error".to_string(), &[1, 2, 3]);
437
438 assert!(!result.is_ok());
439 assert_eq!(result.error, Some("test error".to_string()));
440 assert_eq!(result.remaining, &[1, 2, 3]);
441 }
442}