1use compact_str::CompactString;
10use smallvec::SmallVec;
11
12use super::ethernet::ethertype;
13use super::{FieldValue, ParseContext, ParseResult, Protocol, TunnelType};
14use crate::schema::{DataKind, FieldDescriptor};
15
16pub const ETHERTYPE_MPLS_UNICAST: u16 = 0x8847;
18
19pub const ETHERTYPE_MPLS_MULTICAST: u16 = 0x8848;
21
22const MAX_LABEL_STACK_DEPTH: u8 = 16;
24
25pub mod special_label {
27 pub const IPV4_EXPLICIT_NULL: u32 = 0;
29 pub const ROUTER_ALERT: u32 = 1;
31 pub const IPV6_EXPLICIT_NULL: u32 = 2;
33 pub const IMPLICIT_NULL: u32 = 3;
35 pub const ENTROPY_LABEL_INDICATOR: u32 = 7;
37 pub const GAL: u32 = 13;
39 pub const OAM_ALERT: u32 = 14;
41 pub const EXTENSION: u32 = 15;
43}
44
45fn special_label_name(label: u32) -> Option<&'static str> {
47 match label {
48 special_label::IPV4_EXPLICIT_NULL => Some("IPv4-Explicit-NULL"),
49 special_label::ROUTER_ALERT => Some("Router-Alert"),
50 special_label::IPV6_EXPLICIT_NULL => Some("IPv6-Explicit-NULL"),
51 special_label::IMPLICIT_NULL => Some("Implicit-NULL"),
52 special_label::ENTROPY_LABEL_INDICATOR => Some("ELI"),
53 special_label::GAL => Some("GAL"),
54 special_label::OAM_ALERT => Some("OAM-Alert"),
55 special_label::EXTENSION => Some("Extension"),
56 4..=6 | 8..=12 => Some("Reserved"),
57 _ => None,
58 }
59}
60
61#[derive(Debug, Clone, Copy)]
63pub struct MplsProtocol;
64
65impl Protocol for MplsProtocol {
66 fn name(&self) -> &'static str {
67 "mpls"
68 }
69
70 fn display_name(&self) -> &'static str {
71 "MPLS"
72 }
73
74 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
75 match context.hint("ethertype") {
77 Some(et) if et == ETHERTYPE_MPLS_UNICAST as u64 => Some(100),
78 Some(et) if et == ETHERTYPE_MPLS_MULTICAST as u64 => Some(100),
79 _ => None,
80 }
81 }
82
83 fn parse<'a>(&self, data: &'a [u8], _context: &ParseContext) -> ParseResult<'a> {
84 if data.len() < 4 {
86 return ParseResult::error("MPLS: label stack entry too short".to_string(), data);
87 }
88
89 let mut fields = SmallVec::new();
90 let mut offset = 0;
91 let mut stack_depth = 0u8;
92 let mut labels = Vec::with_capacity(3);
94 let mut bottom_of_stack = false;
95
96 let mut top_label = 0u32;
103 let mut top_tc = 0u8;
104 let mut top_ttl = 0u8;
105 let mut has_special_label = false;
106 let mut special_label_str: Option<&'static str> = None;
107
108 while !bottom_of_stack && offset + 4 <= data.len() {
109 if stack_depth >= MAX_LABEL_STACK_DEPTH {
111 return ParseResult::error(
112 format!("MPLS: label stack too deep (max {MAX_LABEL_STACK_DEPTH})"),
113 data,
114 );
115 }
116
117 let label_entry = u32::from_be_bytes([
118 data[offset],
119 data[offset + 1],
120 data[offset + 2],
121 data[offset + 3],
122 ]);
123
124 let label = (label_entry >> 12) & 0xFFFFF; let tc = ((label_entry >> 9) & 0x07) as u8; bottom_of_stack = (label_entry >> 8) & 0x01 == 1; let ttl = (label_entry & 0xFF) as u8; if stack_depth == 0 {
131 top_label = label;
132 top_tc = tc;
133 top_ttl = ttl;
134
135 if label <= 15 {
137 has_special_label = true;
138 special_label_str = special_label_name(label);
139 }
140 }
141
142 if label <= 15 && !has_special_label {
144 has_special_label = true;
145 }
146
147 labels.push(label.to_string());
148 stack_depth += 1;
149 offset += 4;
150 }
151
152 if stack_depth == 0 {
153 return ParseResult::error("MPLS: no labels found".to_string(), data);
154 }
155
156 fields.push(("label", FieldValue::UInt32(top_label)));
157 fields.push(("tc", FieldValue::UInt8(top_tc)));
158 fields.push(("bottom", FieldValue::Bool(bottom_of_stack)));
159 fields.push(("ttl", FieldValue::UInt8(top_ttl)));
160 fields.push(("stack_depth", FieldValue::UInt8(stack_depth)));
161 fields.push((
162 "labels",
163 FieldValue::OwnedString(CompactString::new(labels.join(","))),
164 ));
165
166 fields.push(("is_reserved_label", FieldValue::Bool(has_special_label)));
168 if let Some(name) = special_label_str {
169 fields.push(("special_label_name", FieldValue::Str(name)));
170 }
171
172 let mut child_hints = SmallVec::new();
174
175 if bottom_of_stack && offset < data.len() {
178 let first_byte = data[offset];
179 let version = (first_byte >> 4) & 0x0F;
180
181 match version {
182 4 => {
183 child_hints.push(("ethertype", ethertype::IPV4 as u64));
184 child_hints.push(("ip_version", 4u64));
185 }
186 6 => {
187 child_hints.push(("ethertype", ethertype::IPV6 as u64));
188 child_hints.push(("ip_version", 6u64));
189 }
190 _ => {
191 }
194 }
195 }
196
197 child_hints.push(("tunnel_type", TunnelType::Mpls as u64));
199 child_hints.push(("tunnel_id", top_label as u64)); ParseResult::success(fields, &data[offset..], child_hints)
202 }
203
204 fn schema_fields(&self) -> Vec<FieldDescriptor> {
205 vec![
206 FieldDescriptor::new("mpls.label", DataKind::UInt32).set_nullable(true),
207 FieldDescriptor::new("mpls.tc", DataKind::UInt8).set_nullable(true),
208 FieldDescriptor::new("mpls.bottom", DataKind::Bool).set_nullable(true),
209 FieldDescriptor::new("mpls.ttl", DataKind::UInt8).set_nullable(true),
210 FieldDescriptor::new("mpls.stack_depth", DataKind::UInt8).set_nullable(true),
211 FieldDescriptor::new("mpls.labels", DataKind::String).set_nullable(true),
212 FieldDescriptor::new("mpls.is_reserved_label", DataKind::Bool).set_nullable(true),
213 FieldDescriptor::new("mpls.special_label_name", DataKind::String).set_nullable(true),
214 ]
215 }
216
217 fn child_protocols(&self) -> &[&'static str] {
218 &["ipv4", "ipv6", "ethernet"]
219 }
220
221 fn dependencies(&self) -> &'static [&'static str] {
222 &["ethernet", "vlan"] }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 fn create_mpls_label(label: u32, tc: u8, bottom: bool, ttl: u8) -> [u8; 4] {
232 let entry = ((label & 0xFFFFF) << 12)
233 | ((tc as u32 & 0x07) << 9)
234 | ((bottom as u32) << 8)
235 | (ttl as u32 & 0xFF);
236 entry.to_be_bytes()
237 }
238
239 #[test]
241 fn test_can_parse_with_mpls_ethertype() {
242 let parser = MplsProtocol;
243
244 let ctx1 = ParseContext::new(1);
246 assert!(parser.can_parse(&ctx1).is_none());
247
248 let mut ctx2 = ParseContext::new(1);
250 ctx2.insert_hint("ethertype", ethertype::IPV4 as u64);
251 assert!(parser.can_parse(&ctx2).is_none());
252
253 let mut ctx3 = ParseContext::new(1);
255 ctx3.insert_hint("ethertype", 0x8847);
256 assert!(parser.can_parse(&ctx3).is_some());
257
258 let mut ctx4 = ParseContext::new(1);
260 ctx4.insert_hint("ethertype", 0x8848);
261 assert!(parser.can_parse(&ctx4).is_some());
262 }
263
264 #[test]
266 fn test_single_label_parsing() {
267 let mut data = Vec::new();
268 data.extend_from_slice(&create_mpls_label(1000, 3, true, 64));
270 data.extend_from_slice(&[0x45, 0x00, 0x00, 0x28]);
272
273 let parser = MplsProtocol;
274 let mut context = ParseContext::new(1);
275 context.insert_hint("ethertype", 0x8847);
276
277 let result = parser.parse(&data, &context);
278
279 assert!(result.is_ok());
280 assert_eq!(result.get("label"), Some(&FieldValue::UInt32(1000)));
281 assert_eq!(result.get("tc"), Some(&FieldValue::UInt8(3)));
282 assert_eq!(result.get("bottom"), Some(&FieldValue::Bool(true)));
283 assert_eq!(result.get("ttl"), Some(&FieldValue::UInt8(64)));
284 assert_eq!(result.get("stack_depth"), Some(&FieldValue::UInt8(1)));
285 assert_eq!(
286 result.get("labels"),
287 Some(&FieldValue::OwnedString(CompactString::new("1000")))
288 );
289 assert_eq!(result.remaining.len(), 4);
290 }
291
292 #[test]
294 fn test_label_stack_multiple_labels() {
295 let mut data = Vec::new();
296 data.extend_from_slice(&create_mpls_label(100, 0, false, 255));
298 data.extend_from_slice(&create_mpls_label(200, 1, false, 254));
300 data.extend_from_slice(&create_mpls_label(300, 2, true, 253));
302 data.extend_from_slice(&[0x45, 0x00]);
304
305 let parser = MplsProtocol;
306 let mut context = ParseContext::new(1);
307 context.insert_hint("ethertype", 0x8847);
308
309 let result = parser.parse(&data, &context);
310
311 assert!(result.is_ok());
312 assert_eq!(result.get("label"), Some(&FieldValue::UInt32(100)));
314 assert_eq!(result.get("tc"), Some(&FieldValue::UInt8(0)));
316 assert_eq!(result.get("ttl"), Some(&FieldValue::UInt8(255)));
318 assert_eq!(result.get("stack_depth"), Some(&FieldValue::UInt8(3)));
320 assert_eq!(
322 result.get("labels"),
323 Some(&FieldValue::OwnedString(CompactString::new("100,200,300")))
324 );
325 assert_eq!(result.get("bottom"), Some(&FieldValue::Bool(true)));
327 }
328
329 #[test]
331 fn test_tc_field_extraction() {
332 for tc in 0u8..=7 {
334 let data = create_mpls_label(500, tc, true, 128);
335
336 let parser = MplsProtocol;
337 let mut context = ParseContext::new(1);
338 context.insert_hint("ethertype", 0x8847);
339
340 let result = parser.parse(&data, &context);
341
342 assert!(result.is_ok());
343 assert_eq!(result.get("tc"), Some(&FieldValue::UInt8(tc)));
344 }
345 }
346
347 #[test]
349 fn test_bottom_of_stack_detection() {
350 let data1 = create_mpls_label(100, 0, true, 64);
352 let parser = MplsProtocol;
353 let mut context = ParseContext::new(1);
354 context.insert_hint("ethertype", 0x8847);
355
356 let result1 = parser.parse(&data1, &context);
357 assert!(result1.is_ok());
358 assert_eq!(result1.get("bottom"), Some(&FieldValue::Bool(true)));
359 assert_eq!(result1.get("stack_depth"), Some(&FieldValue::UInt8(1)));
360
361 let mut data2 = Vec::new();
363 data2.extend_from_slice(&create_mpls_label(100, 0, false, 64));
364 data2.extend_from_slice(&create_mpls_label(200, 0, true, 63));
365
366 let result2 = parser.parse(&data2, &context);
367 assert!(result2.is_ok());
368 assert_eq!(result2.get("stack_depth"), Some(&FieldValue::UInt8(2)));
369 }
370
371 #[test]
373 fn test_ttl_extraction() {
374 for ttl in [0u8, 1, 64, 128, 254, 255] {
376 let data = create_mpls_label(100, 0, true, ttl);
377
378 let parser = MplsProtocol;
379 let mut context = ParseContext::new(1);
380 context.insert_hint("ethertype", 0x8847);
381
382 let result = parser.parse(&data, &context);
383
384 assert!(result.is_ok());
385 assert_eq!(result.get("ttl"), Some(&FieldValue::UInt8(ttl)));
386 }
387 }
388
389 #[test]
391 fn test_child_protocol_detection() {
392 let parser = MplsProtocol;
393 let mut context = ParseContext::new(1);
394 context.insert_hint("ethertype", 0x8847);
395
396 let mut data_ipv4 = Vec::new();
398 data_ipv4.extend_from_slice(&create_mpls_label(100, 0, true, 64));
399 data_ipv4.extend_from_slice(&[0x45, 0x00, 0x00, 0x28]); let result_ipv4 = parser.parse(&data_ipv4, &context);
402 assert!(result_ipv4.is_ok());
403 assert_eq!(result_ipv4.hint("ethertype"), Some(ethertype::IPV4 as u64));
404 assert_eq!(result_ipv4.hint("ip_version"), Some(4u64));
405
406 let mut data_ipv6 = Vec::new();
408 data_ipv6.extend_from_slice(&create_mpls_label(100, 0, true, 64));
409 data_ipv6.extend_from_slice(&[0x60, 0x00, 0x00, 0x00]); let result_ipv6 = parser.parse(&data_ipv6, &context);
412 assert!(result_ipv6.is_ok());
413 assert_eq!(result_ipv6.hint("ethertype"), Some(ethertype::IPV6 as u64));
414 assert_eq!(result_ipv6.hint("ip_version"), Some(6u64));
415 }
416
417 #[test]
419 fn test_mpls_too_short() {
420 let short_data = [0x00, 0x01, 0x02]; let parser = MplsProtocol;
423 let mut context = ParseContext::new(1);
424 context.insert_hint("ethertype", 0x8847);
425
426 let result = parser.parse(&short_data, &context);
427 assert!(!result.is_ok());
428 assert!(result.error.is_some());
429 }
430
431 #[test]
433 fn test_max_label_value() {
434 let data = create_mpls_label(0xFFFFF, 7, true, 255);
435
436 let parser = MplsProtocol;
437 let mut context = ParseContext::new(1);
438 context.insert_hint("ethertype", 0x8847);
439
440 let result = parser.parse(&data, &context);
441
442 assert!(result.is_ok());
443 assert_eq!(result.get("label"), Some(&FieldValue::UInt32(0xFFFFF)));
444 assert_eq!(result.get("tc"), Some(&FieldValue::UInt8(7)));
445 assert_eq!(result.get("ttl"), Some(&FieldValue::UInt8(255)));
446 }
447
448 #[test]
450 fn test_mpls_schema_fields() {
451 let parser = MplsProtocol;
452 let fields = parser.schema_fields();
453
454 assert!(!fields.is_empty());
455 let field_names: Vec<&str> = fields.iter().map(|f| f.name).collect();
456 assert!(field_names.contains(&"mpls.label"));
457 assert!(field_names.contains(&"mpls.tc"));
458 assert!(field_names.contains(&"mpls.bottom"));
459 assert!(field_names.contains(&"mpls.ttl"));
460 assert!(field_names.contains(&"mpls.stack_depth"));
461 assert!(field_names.contains(&"mpls.labels"));
462 }
463
464 #[test]
466 fn test_special_label_ipv4_explicit_null() {
467 let parser = MplsProtocol;
468 let mut context = ParseContext::new(1);
469 context.insert_hint("ethertype", 0x8847);
470
471 let mut data = Vec::new();
473 data.extend_from_slice(&create_mpls_label(
474 special_label::IPV4_EXPLICIT_NULL,
475 0,
476 true,
477 64,
478 ));
479 data.extend_from_slice(&[0x45, 0x00]); let result = parser.parse(&data, &context);
482
483 assert!(result.is_ok());
484 assert_eq!(result.get("label"), Some(&FieldValue::UInt32(0)));
485 assert_eq!(
486 result.get("is_reserved_label"),
487 Some(&FieldValue::Bool(true))
488 );
489 assert_eq!(
490 result.get("special_label_name"),
491 Some(&FieldValue::Str("IPv4-Explicit-NULL"))
492 );
493 }
494
495 #[test]
497 fn test_special_label_router_alert() {
498 let parser = MplsProtocol;
499 let mut context = ParseContext::new(1);
500 context.insert_hint("ethertype", 0x8847);
501
502 let data = create_mpls_label(special_label::ROUTER_ALERT, 0, true, 64);
504
505 let result = parser.parse(&data, &context);
506
507 assert!(result.is_ok());
508 assert_eq!(result.get("label"), Some(&FieldValue::UInt32(1)));
509 assert_eq!(
510 result.get("is_reserved_label"),
511 Some(&FieldValue::Bool(true))
512 );
513 assert_eq!(
514 result.get("special_label_name"),
515 Some(&FieldValue::Str("Router-Alert"))
516 );
517 }
518
519 #[test]
521 fn test_special_label_ipv6_explicit_null() {
522 let parser = MplsProtocol;
523 let mut context = ParseContext::new(1);
524 context.insert_hint("ethertype", 0x8847);
525
526 let mut data = Vec::new();
528 data.extend_from_slice(&create_mpls_label(
529 special_label::IPV6_EXPLICIT_NULL,
530 0,
531 true,
532 64,
533 ));
534 data.extend_from_slice(&[0x60, 0x00]); let result = parser.parse(&data, &context);
537
538 assert!(result.is_ok());
539 assert_eq!(result.get("label"), Some(&FieldValue::UInt32(2)));
540 assert_eq!(
541 result.get("is_reserved_label"),
542 Some(&FieldValue::Bool(true))
543 );
544 assert_eq!(
545 result.get("special_label_name"),
546 Some(&FieldValue::Str("IPv6-Explicit-NULL"))
547 );
548 }
549
550 #[test]
552 fn test_all_special_labels() {
553 let parser = MplsProtocol;
554 let mut context = ParseContext::new(1);
555 context.insert_hint("ethertype", 0x8847);
556
557 let test_cases = [
558 (special_label::IPV4_EXPLICIT_NULL, "IPv4-Explicit-NULL"),
559 (special_label::ROUTER_ALERT, "Router-Alert"),
560 (special_label::IPV6_EXPLICIT_NULL, "IPv6-Explicit-NULL"),
561 (special_label::IMPLICIT_NULL, "Implicit-NULL"),
562 (special_label::ENTROPY_LABEL_INDICATOR, "ELI"),
563 (special_label::GAL, "GAL"),
564 (special_label::OAM_ALERT, "OAM-Alert"),
565 (special_label::EXTENSION, "Extension"),
566 ];
567
568 for (label, expected_name) in test_cases {
569 let data = create_mpls_label(label, 0, true, 64);
570 let result = parser.parse(&data, &context);
571
572 assert!(result.is_ok());
573 assert_eq!(
574 result.get("is_reserved_label"),
575 Some(&FieldValue::Bool(true))
576 );
577 assert_eq!(
578 result.get("special_label_name"),
579 Some(&FieldValue::Str(expected_name))
580 );
581 }
582 }
583
584 #[test]
586 fn test_reserved_labels() {
587 let parser = MplsProtocol;
588 let mut context = ParseContext::new(1);
589 context.insert_hint("ethertype", 0x8847);
590
591 for label in [4u32, 5, 6, 8, 9, 10, 11, 12] {
593 let data = create_mpls_label(label, 0, true, 64);
594 let result = parser.parse(&data, &context);
595
596 assert!(result.is_ok());
597 assert_eq!(
598 result.get("is_reserved_label"),
599 Some(&FieldValue::Bool(true))
600 );
601 assert_eq!(
602 result.get("special_label_name"),
603 Some(&FieldValue::Str("Reserved"))
604 );
605 }
606 }
607
608 #[test]
610 fn test_normal_label_not_reserved() {
611 let parser = MplsProtocol;
612 let mut context = ParseContext::new(1);
613 context.insert_hint("ethertype", 0x8847);
614
615 let data = create_mpls_label(16, 0, true, 64);
617 let result = parser.parse(&data, &context);
618
619 assert!(result.is_ok());
620 assert_eq!(
621 result.get("is_reserved_label"),
622 Some(&FieldValue::Bool(false))
623 );
624 assert!(result.get("special_label_name").is_none());
625
626 let data2 = create_mpls_label(1000, 0, true, 64);
628 let result2 = parser.parse(&data2, &context);
629
630 assert!(result2.is_ok());
631 assert_eq!(
632 result2.get("is_reserved_label"),
633 Some(&FieldValue::Bool(false))
634 );
635 assert!(result2.get("special_label_name").is_none());
636 }
637
638 #[test]
640 fn test_stack_depth_limit() {
641 let parser = MplsProtocol;
642 let mut context = ParseContext::new(1);
643 context.insert_hint("ethertype", 0x8847);
644
645 let mut data_16 = Vec::new();
647 for i in 0..15 {
648 data_16.extend_from_slice(&create_mpls_label(100 + i, 0, false, 64));
649 }
650 data_16.extend_from_slice(&create_mpls_label(115, 0, true, 64)); let result_16 = parser.parse(&data_16, &context);
653 assert!(result_16.is_ok());
654 assert_eq!(result_16.get("stack_depth"), Some(&FieldValue::UInt8(16)));
655
656 let mut data_17 = Vec::new();
658 for i in 0..16 {
659 data_17.extend_from_slice(&create_mpls_label(100 + i, 0, false, 64));
660 }
661 data_17.extend_from_slice(&create_mpls_label(116, 0, true, 64)); let result_17 = parser.parse(&data_17, &context);
664 assert!(!result_17.is_ok());
665 assert!(result_17.error.as_ref().unwrap().contains("too deep"));
666 }
667
668 #[test]
670 fn test_special_label_in_stack() {
671 let parser = MplsProtocol;
672 let mut context = ParseContext::new(1);
673 context.insert_hint("ethertype", 0x8847);
674
675 let mut data = Vec::new();
677 data.extend_from_slice(&create_mpls_label(1000, 0, false, 64));
678 data.extend_from_slice(&create_mpls_label(
679 special_label::ROUTER_ALERT,
680 0,
681 false,
682 63,
683 ));
684 data.extend_from_slice(&create_mpls_label(2000, 0, true, 62));
685
686 let result = parser.parse(&data, &context);
687
688 assert!(result.is_ok());
689 assert_eq!(result.get("label"), Some(&FieldValue::UInt32(1000)));
691 assert_eq!(
693 result.get("is_reserved_label"),
694 Some(&FieldValue::Bool(true))
695 );
696 assert!(result.get("special_label_name").is_none());
698 }
699
700 #[test]
702 fn test_schema_fields_complete() {
703 let parser = MplsProtocol;
704 let fields = parser.schema_fields();
705
706 let field_names: Vec<&str> = fields.iter().map(|f| f.name).collect();
707 assert!(field_names.contains(&"mpls.label"));
708 assert!(field_names.contains(&"mpls.tc"));
709 assert!(field_names.contains(&"mpls.bottom"));
710 assert!(field_names.contains(&"mpls.ttl"));
711 assert!(field_names.contains(&"mpls.stack_depth"));
712 assert!(field_names.contains(&"mpls.labels"));
713 assert!(field_names.contains(&"mpls.is_reserved_label"));
714 assert!(field_names.contains(&"mpls.special_label_name"));
715 }
716}