1use smallvec::SmallVec;
9
10use super::{FieldValue, ParseContext, ParseResult, Protocol, TunnelType};
11use crate::schema::{DataKind, FieldDescriptor};
12
13pub const VXLAN_PORT: u16 = 4789;
15
16#[derive(Debug, Clone, Copy)]
18pub struct VxlanProtocol;
19
20impl Protocol for VxlanProtocol {
21 fn name(&self) -> &'static str {
22 "vxlan"
23 }
24
25 fn display_name(&self) -> &'static str {
26 "VXLAN"
27 }
28
29 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
30 match context.hint("dst_port") {
32 Some(port) if port == VXLAN_PORT as u64 => Some(100),
33 _ => None,
34 }
35 }
36
37 fn parse<'a>(&self, data: &'a [u8], _context: &ParseContext) -> ParseResult<'a> {
38 if data.len() < 8 {
40 return ParseResult::error("VXLAN header too short".to_string(), data);
41 }
42
43 let mut fields = SmallVec::new();
44
45 let flags = data[0];
49 let i_flag = (flags & 0x08) != 0;
50
51 fields.push(("flags", FieldValue::UInt8(flags)));
52
53 fields.push(("i_flag_valid", FieldValue::Bool(i_flag)));
56
57 let reserved_flags_zero = (flags & 0xF7) == 0;
59 fields.push((
60 "flags_valid",
61 FieldValue::Bool(reserved_flags_zero && i_flag),
62 ));
63
64 let vni = ((data[4] as u32) << 16) | ((data[5] as u32) << 8) | (data[6] as u32);
69 fields.push(("vni", FieldValue::UInt32(vni)));
70
71 let inner_frame_len = data.len() - 8;
75 if inner_frame_len > 0 {
76 fields.push((
77 "inner_frame_length",
78 FieldValue::UInt32(inner_frame_len as u32),
79 ));
80 }
81
82 let mut child_hints = SmallVec::new();
84
85 child_hints.push(("link_type", 1u64)); child_hints.push(("tunnel_type", TunnelType::Vxlan as u64));
90 child_hints.push(("tunnel_id", vni as u64));
91
92 ParseResult::success(fields, &data[8..], child_hints)
93 }
94
95 fn schema_fields(&self) -> Vec<FieldDescriptor> {
96 vec![
97 FieldDescriptor::new("vxlan.flags", DataKind::UInt8).set_nullable(true),
98 FieldDescriptor::new("vxlan.vni", DataKind::UInt32).set_nullable(true),
99 FieldDescriptor::new("vxlan.i_flag_valid", DataKind::Bool).set_nullable(true),
100 FieldDescriptor::new("vxlan.flags_valid", DataKind::Bool).set_nullable(true),
101 FieldDescriptor::new("vxlan.inner_frame_length", DataKind::UInt32).set_nullable(true),
102 ]
103 }
104
105 fn child_protocols(&self) -> &[&'static str] {
106 &["ethernet"]
108 }
109
110 fn dependencies(&self) -> &'static [&'static str] {
111 &["udp"] }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 fn create_vxlan_header(vni: u32, i_flag: bool) -> [u8; 8] {
121 let mut header = [0u8; 8];
122
123 if i_flag {
125 header[0] = 0x08;
126 }
127
128 header[4] = ((vni >> 16) & 0xFF) as u8;
130 header[5] = ((vni >> 8) & 0xFF) as u8;
131 header[6] = (vni & 0xFF) as u8;
132
133 header
134 }
135
136 #[test]
138 fn test_can_parse_with_udp_port_4789() {
139 let parser = VxlanProtocol;
140
141 let ctx1 = ParseContext::new(1);
143 assert!(parser.can_parse(&ctx1).is_none());
144
145 let mut ctx2 = ParseContext::new(1);
147 ctx2.insert_hint("dst_port", 80);
148 assert!(parser.can_parse(&ctx2).is_none());
149
150 let mut ctx3 = ParseContext::new(1);
152 ctx3.insert_hint("dst_port", 4789);
153 assert!(parser.can_parse(&ctx3).is_some());
154 assert_eq!(parser.can_parse(&ctx3), Some(100));
155 }
156
157 #[test]
159 fn test_vni_extraction() {
160 let parser = VxlanProtocol;
161 let mut context = ParseContext::new(1);
162 context.insert_hint("dst_port", 4789);
163
164 let test_vnis = [0u32, 1, 100, 1000, 0xFFFFFF]; for vni in test_vnis {
168 let header = create_vxlan_header(vni, true);
169 let result = parser.parse(&header, &context);
170
171 assert!(result.is_ok());
172 assert_eq!(result.get("vni"), Some(&FieldValue::UInt32(vni)));
173 }
174 }
175
176 #[test]
178 fn test_flags_parsing() {
179 let parser = VxlanProtocol;
180 let mut context = ParseContext::new(1);
181 context.insert_hint("dst_port", 4789);
182
183 let header_with_i = create_vxlan_header(100, true);
185 let result = parser.parse(&header_with_i, &context);
186 assert!(result.is_ok());
187 assert_eq!(result.get("flags"), Some(&FieldValue::UInt8(0x08)));
188
189 let header_without_i = create_vxlan_header(100, false);
191 let result = parser.parse(&header_without_i, &context);
192 assert!(result.is_ok());
193 assert_eq!(result.get("flags"), Some(&FieldValue::UInt8(0x00)));
194 }
195
196 #[test]
198 fn test_i_flag_validation() {
199 let parser = VxlanProtocol;
200 let mut context = ParseContext::new(1);
201 context.insert_hint("dst_port", 4789);
202
203 let valid_header = create_vxlan_header(12345, true);
205 let result = parser.parse(&valid_header, &context);
206 assert!(result.is_ok());
207
208 let no_i_header = create_vxlan_header(12345, false);
210 let result = parser.parse(&no_i_header, &context);
211 assert!(result.is_ok());
212 }
213
214 #[test]
216 fn test_inner_ethernet_frame_detection() {
217 let parser = VxlanProtocol;
218 let mut context = ParseContext::new(1);
219 context.insert_hint("dst_port", 4789);
220
221 let mut data = Vec::new();
222 data.extend_from_slice(&create_vxlan_header(100, true));
223 data.extend_from_slice(&[
225 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x08, 0x00, ]);
229
230 let result = parser.parse(&data, &context);
231
232 assert!(result.is_ok());
233 assert_eq!(result.remaining.len(), 14); assert_eq!(result.hint("link_type"), Some(1u64));
235 }
236
237 #[test]
239 fn test_child_protocol_hint() {
240 let parser = VxlanProtocol;
241 let mut context = ParseContext::new(1);
242 context.insert_hint("dst_port", 4789);
243
244 let header = create_vxlan_header(42, true);
245 let result = parser.parse(&header, &context);
246
247 assert!(result.is_ok());
248 assert_eq!(result.hint("link_type"), Some(1u64));
250 }
251
252 #[test]
254 fn test_vxlan_too_short() {
255 let parser = VxlanProtocol;
256 let mut context = ParseContext::new(1);
257 context.insert_hint("dst_port", 4789);
258
259 let short_header = [0x08, 0x00, 0x00, 0x00]; let result = parser.parse(&short_header, &context);
261
262 assert!(!result.is_ok());
263 assert!(result.error.is_some());
264 }
265
266 #[test]
268 fn test_vni_with_payload() {
269 let parser = VxlanProtocol;
270 let mut context = ParseContext::new(1);
271 context.insert_hint("dst_port", 4789);
272
273 let mut data = Vec::new();
274 data.extend_from_slice(&create_vxlan_header(999999, true));
275 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]);
277
278 let result = parser.parse(&data, &context);
279
280 assert!(result.is_ok());
281 assert_eq!(result.get("vni"), Some(&FieldValue::UInt32(999999)));
282 assert_eq!(result.remaining.len(), 4);
283 }
284
285 #[test]
287 fn test_vxlan_schema_fields() {
288 let parser = VxlanProtocol;
289 let fields = parser.schema_fields();
290
291 assert!(!fields.is_empty());
292 let field_names: Vec<&str> = fields.iter().map(|f| f.name).collect();
293 assert!(field_names.contains(&"vxlan.flags"));
294 assert!(field_names.contains(&"vxlan.vni"));
295 }
296
297 #[test]
299 fn test_specific_vni_values() {
300 let parser = VxlanProtocol;
301 let mut context = ParseContext::new(1);
302 context.insert_hint("dst_port", 4789);
303
304 let header1 = create_vxlan_header(1, true);
306 let result = parser.parse(&header1, &context);
307 assert!(result.is_ok());
308 assert_eq!(result.get("vni"), Some(&FieldValue::UInt32(1)));
309
310 let header2 = create_vxlan_header(0xFFFFFF, true);
312 let result = parser.parse(&header2, &context);
313 assert!(result.is_ok());
314 assert_eq!(result.get("vni"), Some(&FieldValue::UInt32(0xFFFFFF)));
315 }
316
317 #[test]
319 fn test_i_flag_valid_field() {
320 let parser = VxlanProtocol;
321 let mut context = ParseContext::new(1);
322 context.insert_hint("dst_port", 4789);
323
324 let valid_header = create_vxlan_header(100, true);
326 let result = parser.parse(&valid_header, &context);
327 assert!(result.is_ok());
328 assert_eq!(result.get("i_flag_valid"), Some(&FieldValue::Bool(true)));
329
330 let invalid_header = create_vxlan_header(100, false);
332 let result = parser.parse(&invalid_header, &context);
333 assert!(result.is_ok()); assert_eq!(result.get("i_flag_valid"), Some(&FieldValue::Bool(false)));
335 }
336
337 #[test]
339 fn test_flags_valid_field() {
340 let parser = VxlanProtocol;
341 let mut context = ParseContext::new(1);
342 context.insert_hint("dst_port", 4789);
343
344 let valid_header = create_vxlan_header(100, true); let result = parser.parse(&valid_header, &context);
347 assert!(result.is_ok());
348 assert_eq!(result.get("flags_valid"), Some(&FieldValue::Bool(true)));
349
350 let no_i_header = create_vxlan_header(100, false); let result = parser.parse(&no_i_header, &context);
353 assert!(result.is_ok());
354 assert_eq!(result.get("flags_valid"), Some(&FieldValue::Bool(false)));
355 }
356
357 #[test]
359 fn test_flags_valid_reserved_bits() {
360 let parser = VxlanProtocol;
361 let mut context = ParseContext::new(1);
362 context.insert_hint("dst_port", 4789);
363
364 let mut header = [0u8; 8];
366 header[0] = 0x09; header[4] = 0x00;
368 header[5] = 0x00;
369 header[6] = 0x64; let result = parser.parse(&header, &context);
372 assert!(result.is_ok());
373 assert_eq!(result.get("flags_valid"), Some(&FieldValue::Bool(false)));
375 assert_eq!(result.get("i_flag_valid"), Some(&FieldValue::Bool(true)));
377 }
378
379 #[test]
381 fn test_inner_frame_length_field() {
382 let parser = VxlanProtocol;
383 let mut context = ParseContext::new(1);
384 context.insert_hint("dst_port", 4789);
385
386 let header_only = create_vxlan_header(100, true);
388 let result = parser.parse(&header_only, &context);
389 assert!(result.is_ok());
390 assert!(result.get("inner_frame_length").is_none());
392
393 let mut with_frame = Vec::new();
395 with_frame.extend_from_slice(&create_vxlan_header(100, true));
396 with_frame.extend_from_slice(&[0u8; 14]); let result = parser.parse(&with_frame, &context);
398 assert!(result.is_ok());
399 assert_eq!(
400 result.get("inner_frame_length"),
401 Some(&FieldValue::UInt32(14))
402 );
403
404 let mut with_payload = Vec::new();
406 with_payload.extend_from_slice(&create_vxlan_header(100, true));
407 with_payload.extend_from_slice(&[0u8; 1500]); let result = parser.parse(&with_payload, &context);
409 assert!(result.is_ok());
410 assert_eq!(
411 result.get("inner_frame_length"),
412 Some(&FieldValue::UInt32(1500))
413 );
414 }
415
416 #[test]
418 fn test_vni_range() {
419 let parser = VxlanProtocol;
420 let mut context = ParseContext::new(1);
421 context.insert_hint("dst_port", 4789);
422
423 let test_cases = [
424 (0, "zero VNI"),
425 (1, "minimum VNI"),
426 (4096, "common tenant VNI"),
427 (100000, "large VNI"),
428 (0xFFFFFF, "maximum VNI (24-bit)"),
429 ];
430
431 for (vni, _desc) in test_cases {
432 let header = create_vxlan_header(vni, true);
433 let result = parser.parse(&header, &context);
434 assert!(result.is_ok());
435 assert_eq!(result.get("vni"), Some(&FieldValue::UInt32(vni)));
436 }
437 }
438
439 #[test]
441 fn test_schema_fields_complete() {
442 let parser = VxlanProtocol;
443 let fields = parser.schema_fields();
444
445 let field_names: Vec<&str> = fields.iter().map(|f| f.name).collect();
446 assert!(field_names.contains(&"vxlan.flags"));
447 assert!(field_names.contains(&"vxlan.vni"));
448 assert!(field_names.contains(&"vxlan.i_flag_valid"));
449 assert!(field_names.contains(&"vxlan.flags_valid"));
450 assert!(field_names.contains(&"vxlan.inner_frame_length"));
451 }
452}