pcapsql_core/protocol/
mpls.rs

1//! MPLS (Multi-Protocol Label Switching) protocol parser.
2//!
3//! MPLS provides a mechanism for label switching that can transport multiple
4//! types of traffic at high speed.
5//!
6//! RFC 3031: Multiprotocol Label Switching Architecture
7//! RFC 3032: MPLS Label Stack Encoding
8
9use compact_str::CompactString;
10use smallvec::SmallVec;
11
12use super::ethernet::ethertype;
13use super::{FieldValue, ParseContext, ParseResult, Protocol, TunnelType};
14use crate::schema::{DataKind, FieldDescriptor};
15
16/// EtherType for MPLS Unicast.
17pub const ETHERTYPE_MPLS_UNICAST: u16 = 0x8847;
18
19/// EtherType for MPLS Multicast.
20pub const ETHERTYPE_MPLS_MULTICAST: u16 = 0x8848;
21
22/// Maximum label stack depth (RFC compliance safety limit).
23const MAX_LABEL_STACK_DEPTH: u8 = 16;
24
25/// Special/reserved MPLS label values (RFC 3032).
26pub mod special_label {
27    /// IPv4 Explicit NULL Label - Used for PHP (Penultimate Hop Popping).
28    pub const IPV4_EXPLICIT_NULL: u32 = 0;
29    /// Router Alert Label - Delivers packet to router control plane.
30    pub const ROUTER_ALERT: u32 = 1;
31    /// IPv6 Explicit NULL Label.
32    pub const IPV6_EXPLICIT_NULL: u32 = 2;
33    /// Implicit NULL Label - Used in signaling, never appears on wire.
34    pub const IMPLICIT_NULL: u32 = 3;
35    /// Entropy Label Indicator (RFC 6790).
36    pub const ENTROPY_LABEL_INDICATOR: u32 = 7;
37    /// GAL (Generic Associated Channel Label) (RFC 5586).
38    pub const GAL: u32 = 13;
39    /// OAM Alert Label (RFC 3429).
40    pub const OAM_ALERT: u32 = 14;
41    /// Extension Label (RFC 7274).
42    pub const EXTENSION: u32 = 15;
43}
44
45/// Get the name of a special/reserved MPLS label.
46fn 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/// MPLS protocol parser.
62#[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 when ethertype hint equals MPLS unicast or multicast
76        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        // Each MPLS label stack entry is 4 bytes
85        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        // Typical MPLS stack has 1-3 labels
93        let mut labels = Vec::with_capacity(3);
94        let mut bottom_of_stack = false;
95
96        // Parse all labels in the stack
97        // Each label is 4 bytes:
98        // - Label: 20 bits
99        // - TC (Traffic Class): 3 bits
100        // - S (Bottom of Stack): 1 bit
101        // - TTL: 8 bits
102        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            // Check stack depth limit for safety
110            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; // Top 20 bits
125            let tc = ((label_entry >> 9) & 0x07) as u8; // Next 3 bits
126            bottom_of_stack = (label_entry >> 8) & 0x01 == 1; // Next 1 bit
127            let ttl = (label_entry & 0xFF) as u8; // Bottom 8 bits
128
129            // Store first (top) label values for the fields
130            if stack_depth == 0 {
131                top_label = label;
132                top_tc = tc;
133                top_ttl = ttl;
134
135                // Check if top label is a special/reserved label
136                if label <= 15 {
137                    has_special_label = true;
138                    special_label_str = special_label_name(label);
139                }
140            }
141
142            // Track if any label in stack is reserved
143            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        // Add special label fields
167        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        // Set up child hints
173        let mut child_hints = SmallVec::new();
174
175        // After the bottom of stack, we need to detect the inner protocol
176        // by looking at the first nibble of the payload
177        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                    // Unknown inner protocol, could be Ethernet or other
192                    // Check if it might be Ethernet (looking for valid MAC prefix)
193                }
194            }
195        }
196
197        // Signal tunnel boundary for encapsulation tracking
198        child_hints.push(("tunnel_type", TunnelType::Mpls as u64));
199        child_hints.push(("tunnel_id", top_label as u64)); // Top label as tunnel ID
200
201        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"] // MPLS follows Ethernet ethertype 0x8847/0x8848
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    use super::*;
229
230    /// Create an MPLS label stack entry.
231    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 1: can_parse with MPLS ethertype
240    #[test]
241    fn test_can_parse_with_mpls_ethertype() {
242        let parser = MplsProtocol;
243
244        // Without hint
245        let ctx1 = ParseContext::new(1);
246        assert!(parser.can_parse(&ctx1).is_none());
247
248        // With IPv4 ethertype
249        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        // With MPLS unicast ethertype
254        let mut ctx3 = ParseContext::new(1);
255        ctx3.insert_hint("ethertype", 0x8847);
256        assert!(parser.can_parse(&ctx3).is_some());
257
258        // With MPLS multicast ethertype
259        let mut ctx4 = ParseContext::new(1);
260        ctx4.insert_hint("ethertype", 0x8848);
261        assert!(parser.can_parse(&ctx4).is_some());
262    }
263
264    // Test 2: Single label parsing
265    #[test]
266    fn test_single_label_parsing() {
267        let mut data = Vec::new();
268        // Label 1000, TC 3, Bottom=true, TTL 64
269        data.extend_from_slice(&create_mpls_label(1000, 3, true, 64));
270        // Add IPv4 header start (version 4)
271        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 3: Label stack (multiple labels)
293    #[test]
294    fn test_label_stack_multiple_labels() {
295        let mut data = Vec::new();
296        // First label: 100, TC 0, Bottom=false, TTL 255
297        data.extend_from_slice(&create_mpls_label(100, 0, false, 255));
298        // Second label: 200, TC 1, Bottom=false, TTL 254
299        data.extend_from_slice(&create_mpls_label(200, 1, false, 254));
300        // Third label (bottom): 300, TC 2, Bottom=true, TTL 253
301        data.extend_from_slice(&create_mpls_label(300, 2, true, 253));
302        // Add IPv4 header
303        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        // Top label should be 100
313        assert_eq!(result.get("label"), Some(&FieldValue::UInt32(100)));
314        // TC of top label
315        assert_eq!(result.get("tc"), Some(&FieldValue::UInt8(0)));
316        // TTL of top label
317        assert_eq!(result.get("ttl"), Some(&FieldValue::UInt8(255)));
318        // Stack depth should be 3
319        assert_eq!(result.get("stack_depth"), Some(&FieldValue::UInt8(3)));
320        // Labels string
321        assert_eq!(
322            result.get("labels"),
323            Some(&FieldValue::OwnedString(CompactString::new("100,200,300")))
324        );
325        // Bottom flag reflects the last label
326        assert_eq!(result.get("bottom"), Some(&FieldValue::Bool(true)));
327    }
328
329    // Test 4: TC field extraction
330    #[test]
331    fn test_tc_field_extraction() {
332        // Test all TC values (0-7)
333        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 5: Bottom of stack detection
348    #[test]
349    fn test_bottom_of_stack_detection() {
350        // Single label with bottom=true
351        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        // Two labels, only second has bottom=true
362        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 6: TTL extraction
372    #[test]
373    fn test_ttl_extraction() {
374        // Test various TTL values
375        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 7: Child protocol detection (IPv4 vs IPv6)
390    #[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        // IPv4 inner (version nibble = 4)
397        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]); // IPv4 header start
400
401        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        // IPv6 inner (version nibble = 6)
407        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]); // IPv6 header start
410
411        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 8: Too short data
418    #[test]
419    fn test_mpls_too_short() {
420        let short_data = [0x00, 0x01, 0x02]; // Only 3 bytes
421
422        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 9: Maximum label value (20-bit: 0xFFFFF = 1048575)
432    #[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 10: Schema fields
449    #[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 11: Special label recognition - IPv4 Explicit NULL
465    #[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        // Label 0 = IPv4 Explicit NULL
472        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]); // IPv4 payload
480
481        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 12: Special label recognition - Router Alert
496    #[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        // Label 1 = Router Alert
503        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 13: Special label recognition - IPv6 Explicit NULL
520    #[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        // Label 2 = IPv6 Explicit NULL
527        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]); // IPv6 payload
535
536        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 14: All special/reserved labels
551    #[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 15: Reserved labels (4-6, 8-12)
585    #[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        // Labels 4-6 and 8-12 are reserved
592        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 16: Non-reserved label has no special name
609    #[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        // Labels >= 16 are normal labels
616        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        // Test a more typical label value
627        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 17: Stack depth limit (16 labels)
639    #[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        // Create exactly 16 labels (should pass)
646        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)); // Bottom
651
652        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        // Create 17 labels (should fail - exceeds MAX_LABEL_STACK_DEPTH)
657        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)); // Would be 17th
662
663        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 18: Label stack with special label in the middle
669    #[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        // Stack: [1000, Router Alert (1), 2000]
676        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        // Top label is not special
690        assert_eq!(result.get("label"), Some(&FieldValue::UInt32(1000)));
691        // But stack contains a reserved label
692        assert_eq!(
693            result.get("is_reserved_label"),
694            Some(&FieldValue::Bool(true))
695        );
696        // special_label_name is None because top label isn't special
697        assert!(result.get("special_label_name").is_none());
698    }
699
700    // Test 19: Schema fields include new reserved label fields
701    #[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}