Skip to main content

packet_strata/packet/tunnel/
mpls.rs

1//! MPLS (Multiprotocol Label Switching) protocol parser
2//!
3//! This module implements parsing for MPLS as defined in RFC 3032.
4//! MPLS uses a label stack where each label is 4 bytes.
5//!
6//! # MPLS Label Format
7//!
8//! ```text
9//!  0                   1                   2                   3
10//!  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
11//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12//! |                Label                  | TC  |S|       TTL     |
13//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
14//! ```
15//!
16//! - Label: 20 bits - Label value
17//! - TC: 3 bits - Traffic Class (formerly EXP)
18//! - S: 1 bit - Bottom of Stack flag (1 = last label)
19//! - TTL: 8 bits - Time to Live
20//!
21//! # EtherTypes
22//!
23//! - MPLS Unicast: 0x8847
24//! - MPLS Multicast: 0x8848
25//!
26//! # Examples
27//!
28//! ## Basic MPLS parsing (single label)
29//!
30//! ```
31//! use packet_strata::packet::tunnel::mpls::MplsLabel;
32//! use packet_strata::packet::HeaderParser;
33//!
34//! // Single MPLS label with bottom-of-stack set
35//! let packet = vec![
36//!     0x00, 0x01, 0x01, 0x40,  // Label=16, TC=0, S=1, TTL=64
37//!     // Inner IP packet follows...
38//!     0x45, 0x00, 0x00, 0x00,
39//! ];
40//!
41//! let (label, payload) = MplsLabel::from_bytes(&packet).unwrap();
42//! assert_eq!(label.label(), 16);
43//! assert!(label.is_bottom_of_stack());
44//! assert_eq!(label.ttl(), 64);
45//! ```
46//!
47//! ## MPLS label stack iteration
48//!
49//! ```
50//! use packet_strata::packet::tunnel::mpls::MplsLabelStackIter;
51//!
52//! // Two MPLS labels stacked
53//! let packet = vec![
54//!     0x00, 0x01, 0x00, 0x40,  // Label=16, TC=0, S=0, TTL=64
55//!     0x00, 0x02, 0x01, 0x3F,  // Label=32, TC=0, S=1, TTL=63
56//!     // Inner payload...
57//!     0x45, 0x00, 0x00, 0x00,
58//! ];
59//!
60//! let mut iter = MplsLabelStackIter::new(&packet);
61//! let first = iter.next().unwrap();
62//! assert_eq!(first.label(), 16);
63//! assert!(!first.is_bottom_of_stack());
64//!
65//! let second = iter.next().unwrap();
66//! assert_eq!(second.label(), 32);
67//! assert!(second.is_bottom_of_stack());
68//!
69//! assert!(iter.next().is_none());
70//! ```
71
72use std::fmt::{self, Formatter};
73
74use zerocopy::byteorder::{BigEndian, U32};
75use zerocopy::{FromBytes, IntoBytes, Unaligned};
76
77use crate::packet::{HeaderParser, PacketHeader};
78
79/// MPLS Unicast EtherType
80pub const MPLS_ETHERTYPE_UNICAST: u16 = 0x8847;
81
82/// MPLS Multicast EtherType
83pub const MPLS_ETHERTYPE_MULTICAST: u16 = 0x8848;
84
85/// Check if EtherType is MPLS
86#[inline]
87pub fn is_mpls_ethertype(ethertype: u16) -> bool {
88    ethertype == MPLS_ETHERTYPE_UNICAST || ethertype == MPLS_ETHERTYPE_MULTICAST
89}
90
91/// Reserved MPLS label values (0-15)
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93#[repr(u32)]
94pub enum MplsReservedLabel {
95    /// IPv4 Explicit NULL Label
96    Ipv4ExplicitNull = 0,
97    /// Router Alert Label
98    RouterAlert = 1,
99    /// IPv6 Explicit NULL Label
100    Ipv6ExplicitNull = 2,
101    /// Implicit NULL Label
102    ImplicitNull = 3,
103    /// Entropy Label Indicator
104    EntropyLabelIndicator = 7,
105    /// GAL (Generic Associated Channel Label)
106    Gal = 13,
107    /// OAM Alert Label
108    OamAlert = 14,
109    /// Extension Label
110    Extension = 15,
111}
112
113impl MplsReservedLabel {
114    /// Check if a label value is reserved (0-15)
115    #[inline]
116    pub fn is_reserved(label: u32) -> bool {
117        label <= 15
118    }
119
120    /// Try to convert a label value to a reserved label
121    pub fn from_label(label: u32) -> Option<Self> {
122        match label {
123            0 => Some(MplsReservedLabel::Ipv4ExplicitNull),
124            1 => Some(MplsReservedLabel::RouterAlert),
125            2 => Some(MplsReservedLabel::Ipv6ExplicitNull),
126            3 => Some(MplsReservedLabel::ImplicitNull),
127            7 => Some(MplsReservedLabel::EntropyLabelIndicator),
128            13 => Some(MplsReservedLabel::Gal),
129            14 => Some(MplsReservedLabel::OamAlert),
130            15 => Some(MplsReservedLabel::Extension),
131            _ => None,
132        }
133    }
134}
135
136impl fmt::Display for MplsReservedLabel {
137    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
138        match self {
139            MplsReservedLabel::Ipv4ExplicitNull => write!(f, "IPv4 Explicit NULL"),
140            MplsReservedLabel::RouterAlert => write!(f, "Router Alert"),
141            MplsReservedLabel::Ipv6ExplicitNull => write!(f, "IPv6 Explicit NULL"),
142            MplsReservedLabel::ImplicitNull => write!(f, "Implicit NULL"),
143            MplsReservedLabel::EntropyLabelIndicator => write!(f, "Entropy Label Indicator"),
144            MplsReservedLabel::Gal => write!(f, "GAL"),
145            MplsReservedLabel::OamAlert => write!(f, "OAM Alert"),
146            MplsReservedLabel::Extension => write!(f, "Extension"),
147        }
148    }
149}
150
151/// MPLS Label structure (4 bytes)
152///
153/// ```text
154///  0                   1                   2                   3
155///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
156/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
157/// |                Label                  | TC  |S|       TTL     |
158/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
159/// ```
160#[repr(C, packed)]
161#[derive(
162    FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, zerocopy::KnownLayout, zerocopy::Immutable,
163)]
164pub struct MplsLabel {
165    label_tc_s_ttl: U32<BigEndian>,
166}
167
168impl MplsLabel {
169    /// Label field mask (bits 0-19)
170    pub const LABEL_MASK: u32 = 0xFFFFF000;
171    pub const LABEL_SHIFT: u32 = 12;
172
173    /// Traffic Class field mask (bits 20-22)
174    pub const TC_MASK: u32 = 0x00000E00;
175    pub const TC_SHIFT: u32 = 9;
176
177    /// Bottom of Stack flag (bit 23)
178    pub const BOS_MASK: u32 = 0x00000100;
179    pub const BOS_SHIFT: u32 = 8;
180
181    /// TTL field mask (bits 24-31)
182    pub const TTL_MASK: u32 = 0x000000FF;
183
184    /// Maximum label value (20 bits)
185    pub const MAX_LABEL: u32 = 0xFFFFF;
186
187    /// Maximum TC value (3 bits)
188    pub const MAX_TC: u8 = 7;
189
190    #[allow(unused)]
191    const NAME: &'static str = "MplsLabel";
192
193    /// Returns the raw 32-bit value
194    #[inline]
195    pub fn raw(&self) -> u32 {
196        self.label_tc_s_ttl.get()
197    }
198
199    /// Returns the 20-bit label value
200    #[inline]
201    pub fn label(&self) -> u32 {
202        (self.raw() & Self::LABEL_MASK) >> Self::LABEL_SHIFT
203    }
204
205    /// Returns the 3-bit Traffic Class (TC) value
206    ///
207    /// This was formerly called EXP (Experimental) bits.
208    /// Used for QoS and ECN purposes.
209    #[inline]
210    pub fn traffic_class(&self) -> u8 {
211        ((self.raw() & Self::TC_MASK) >> Self::TC_SHIFT) as u8
212    }
213
214    /// Alias for traffic_class() - returns the EXP bits
215    #[inline]
216    pub fn exp(&self) -> u8 {
217        self.traffic_class()
218    }
219
220    /// Returns true if this is the bottom of the label stack (S=1)
221    #[inline]
222    pub fn is_bottom_of_stack(&self) -> bool {
223        self.raw() & Self::BOS_MASK != 0
224    }
225
226    /// Returns the 8-bit TTL value
227    #[inline]
228    pub fn ttl(&self) -> u8 {
229        (self.raw() & Self::TTL_MASK) as u8
230    }
231
232    /// Returns true if the label is a reserved label (0-15)
233    #[inline]
234    pub fn is_reserved(&self) -> bool {
235        MplsReservedLabel::is_reserved(self.label())
236    }
237
238    /// Returns the reserved label type if this is a reserved label
239    #[inline]
240    pub fn reserved_label(&self) -> Option<MplsReservedLabel> {
241        MplsReservedLabel::from_label(self.label())
242    }
243
244    /// Returns true if this is the IPv4 Explicit NULL label
245    #[inline]
246    pub fn is_ipv4_explicit_null(&self) -> bool {
247        self.label() == 0
248    }
249
250    /// Returns true if this is the IPv6 Explicit NULL label
251    #[inline]
252    pub fn is_ipv6_explicit_null(&self) -> bool {
253        self.label() == 2
254    }
255
256    /// Returns true if this is the Implicit NULL label
257    #[inline]
258    pub fn is_implicit_null(&self) -> bool {
259        self.label() == 3
260    }
261
262    /// Returns true if this is the Router Alert label
263    #[inline]
264    pub fn is_router_alert(&self) -> bool {
265        self.label() == 1
266    }
267
268    /// Validates the MPLS label
269    ///
270    /// MPLS labels are always valid structurally, but we check for
271    /// reasonable TTL values.
272    #[inline]
273    fn is_valid(&self) -> bool {
274        // All MPLS labels are structurally valid
275        // TTL of 0 might indicate an expired packet, but it's still parseable
276        true
277    }
278}
279
280impl PacketHeader for MplsLabel {
281    const NAME: &'static str = "MplsLabel";
282    /// Inner type - we return the label value
283    type InnerType = u32;
284
285    #[inline]
286    fn inner_type(&self) -> Self::InnerType {
287        self.label()
288    }
289
290    /// Returns the total header length (always 4 bytes per label)
291    #[inline]
292    fn total_len(&self, _buf: &[u8]) -> usize {
293        Self::FIXED_LEN // 4 bytes
294    }
295
296    /// Validates the MPLS label
297    #[inline]
298    fn is_valid(&self) -> bool {
299        self.is_valid()
300    }
301}
302
303impl HeaderParser for MplsLabel {
304    type Output<'a> = &'a MplsLabel;
305
306    #[inline]
307    fn into_view<'a>(header: &'a Self, _options: &'a [u8]) -> Self::Output<'a> {
308        header
309    }
310}
311
312impl fmt::Display for MplsLabel {
313    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
314        write!(
315            f,
316            "MPLS label={} tc={} s={} ttl={}",
317            self.label(),
318            self.traffic_class(),
319            if self.is_bottom_of_stack() { 1 } else { 0 },
320            self.ttl()
321        )?;
322
323        if let Some(reserved) = self.reserved_label() {
324            write!(f, " ({})", reserved)?;
325        }
326
327        Ok(())
328    }
329}
330
331/// Iterator over MPLS label stack
332///
333/// Iterates through the MPLS label stack until it finds a label with
334/// the Bottom of Stack (S) bit set.
335pub struct MplsLabelStackIter<'a> {
336    data: &'a [u8],
337    finished: bool,
338}
339
340impl<'a> MplsLabelStackIter<'a> {
341    /// Create a new label stack iterator
342    pub fn new(data: &'a [u8]) -> Self {
343        Self {
344            data,
345            finished: false,
346        }
347    }
348
349    /// Returns the remaining unparsed data
350    ///
351    /// This is useful for getting the payload after the label stack.
352    pub fn remaining(&self) -> &'a [u8] {
353        self.data
354    }
355
356    /// Parse the entire label stack and return the payload
357    ///
358    /// Consumes all labels until bottom of stack is found and returns
359    /// the remaining data as payload.
360    pub fn skip_to_payload(mut self) -> &'a [u8] {
361        while self.next().is_some() {}
362        self.data
363    }
364
365    /// Collect all labels into a Vec
366    pub fn collect_labels(self) -> Vec<MplsLabel> {
367        self.copied().collect()
368    }
369}
370
371impl<'a> Iterator for MplsLabelStackIter<'a> {
372    type Item = &'a MplsLabel;
373
374    fn next(&mut self) -> Option<Self::Item> {
375        if self.finished || self.data.len() < 4 {
376            return None;
377        }
378
379        // Parse the label using zerocopy
380        let (label_ref, rest) = zerocopy::Ref::<_, MplsLabel>::from_prefix(self.data).ok()?;
381
382        let label = zerocopy::Ref::into_ref(label_ref);
383
384        // Check if this is the bottom of stack
385        if label.is_bottom_of_stack() {
386            self.finished = true;
387        }
388
389        self.data = rest;
390
391        Some(label)
392    }
393}
394
395/// MPLS Label Stack wrapper for full stack parsing
396#[derive(Debug, Clone)]
397pub struct MplsLabelStack<'a> {
398    /// The raw label stack data
399    pub data: &'a [u8],
400    /// Number of labels in the stack
401    pub count: usize,
402    /// Total size of the label stack in bytes
403    pub total_size: usize,
404}
405
406impl<'a> MplsLabelStack<'a> {
407    /// Parse an MPLS label stack from a buffer
408    ///
409    /// Returns the label stack and the remaining payload.
410    pub fn parse(data: &'a [u8]) -> Option<(Self, &'a [u8])> {
411        let mut offset = 0;
412        let mut count = 0;
413
414        loop {
415            if data.len() < offset + 4 {
416                return None; // Not enough data
417            }
418
419            let label_data = &data[offset..offset + 4];
420            let raw =
421                u32::from_be_bytes([label_data[0], label_data[1], label_data[2], label_data[3]]);
422
423            count += 1;
424            offset += 4;
425
426            // Check bottom of stack
427            if raw & MplsLabel::BOS_MASK != 0 {
428                break;
429            }
430
431            // Safety limit to prevent infinite loops
432            if count > 16 {
433                return None;
434            }
435        }
436
437        Some((
438            MplsLabelStack {
439                data: &data[..offset],
440                count,
441                total_size: offset,
442            },
443            &data[offset..],
444        ))
445    }
446
447    /// Get an iterator over the labels in the stack
448    pub fn iter(&self) -> MplsLabelStackIter<'a> {
449        MplsLabelStackIter::new(self.data)
450    }
451
452    /// Get the first (outermost) label
453    pub fn first(&self) -> Option<&'a MplsLabel> {
454        self.iter().next()
455    }
456
457    /// Get the last (innermost) label
458    pub fn last(&self) -> Option<&'a MplsLabel> {
459        self.iter().last()
460    }
461}
462
463impl<'a> IntoIterator for &'a MplsLabelStack<'a> {
464    type Item = &'a MplsLabel;
465    type IntoIter = MplsLabelStackIter<'a>;
466
467    fn into_iter(self) -> Self::IntoIter {
468        self.iter()
469    }
470}
471
472impl fmt::Display for MplsLabelStack<'_> {
473    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
474        write!(f, "MPLS[")?;
475        let mut first = true;
476        for label in self.iter() {
477            if !first {
478                write!(f, " -> ")?;
479            }
480            write!(f, "{}", label.label())?;
481            first = false;
482        }
483        write!(f, "]")
484    }
485}
486
487#[cfg(test)]
488mod tests {
489    use super::*;
490
491    #[test]
492    fn test_mpls_label_size() {
493        assert_eq!(std::mem::size_of::<MplsLabel>(), 4);
494        assert_eq!(MplsLabel::FIXED_LEN, 4);
495    }
496
497    #[test]
498    fn test_mpls_single_label() {
499        // Label=16, TC=0, S=1, TTL=64
500        let packet = vec![0x00, 0x01, 0x01, 0x40, 0x45, 0x00, 0x00, 0x00];
501
502        let (label, payload) = MplsLabel::from_bytes(&packet).unwrap();
503        assert_eq!(label.label(), 16);
504        assert_eq!(label.traffic_class(), 0);
505        assert!(label.is_bottom_of_stack());
506        assert_eq!(label.ttl(), 64);
507        assert_eq!(payload.len(), 4);
508    }
509
510    #[test]
511    fn test_mpls_label_max_value() {
512        // Label=0xFFFFF (max), TC=7, S=1, TTL=255
513        let packet = vec![0xFF, 0xFF, 0xFF, 0xFF];
514
515        let (label, _) = MplsLabel::from_bytes(&packet).unwrap();
516        assert_eq!(label.label(), 0xFFFFF);
517        assert_eq!(label.traffic_class(), 7);
518        assert!(label.is_bottom_of_stack());
519        assert_eq!(label.ttl(), 255);
520    }
521
522    #[test]
523    fn test_mpls_label_zero() {
524        // Label=0 (IPv4 Explicit NULL), TC=0, S=1, TTL=1
525        let packet = vec![0x00, 0x00, 0x01, 0x01];
526
527        let (label, _) = MplsLabel::from_bytes(&packet).unwrap();
528        assert_eq!(label.label(), 0);
529        assert!(label.is_ipv4_explicit_null());
530        assert!(label.is_reserved());
531        assert_eq!(
532            label.reserved_label(),
533            Some(MplsReservedLabel::Ipv4ExplicitNull)
534        );
535    }
536
537    #[test]
538    fn test_mpls_label_ipv6_explicit_null() {
539        // Label=2 (IPv6 Explicit NULL), TC=0, S=1, TTL=64
540        let packet = vec![0x00, 0x00, 0x21, 0x40];
541
542        let (label, _) = MplsLabel::from_bytes(&packet).unwrap();
543        assert_eq!(label.label(), 2);
544        assert!(label.is_ipv6_explicit_null());
545        assert!(label.is_reserved());
546    }
547
548    #[test]
549    fn test_mpls_label_router_alert() {
550        // Label=1 (Router Alert), TC=0, S=1, TTL=64
551        let packet = vec![0x00, 0x00, 0x11, 0x40];
552
553        let (label, _) = MplsLabel::from_bytes(&packet).unwrap();
554        assert_eq!(label.label(), 1);
555        assert!(label.is_router_alert());
556    }
557
558    #[test]
559    fn test_mpls_label_implicit_null() {
560        // Label=3 (Implicit NULL), TC=0, S=1, TTL=64
561        let packet = vec![0x00, 0x00, 0x31, 0x40];
562
563        let (label, _) = MplsLabel::from_bytes(&packet).unwrap();
564        assert_eq!(label.label(), 3);
565        assert!(label.is_implicit_null());
566    }
567
568    #[test]
569    fn test_mpls_label_not_bos() {
570        // Label=100, TC=3, S=0, TTL=128
571        let packet = vec![0x00, 0x06, 0x46, 0x80];
572
573        let (label, _) = MplsLabel::from_bytes(&packet).unwrap();
574        assert_eq!(label.label(), 100);
575        assert_eq!(label.traffic_class(), 3);
576        assert!(!label.is_bottom_of_stack());
577        assert_eq!(label.ttl(), 128);
578    }
579
580    #[test]
581    fn test_mpls_label_stack_iterator() {
582        // Two MPLS labels
583        let packet = vec![
584            0x00, 0x01, 0x00, 0x40, // Label=16, S=0
585            0x00, 0x02, 0x01, 0x3F, // Label=32, S=1
586            0x45, 0x00, 0x00, 0x00, // Payload
587        ];
588
589        let mut iter = MplsLabelStackIter::new(&packet);
590
591        let first = iter.next().unwrap();
592        assert_eq!(first.label(), 16);
593        assert!(!first.is_bottom_of_stack());
594
595        let second = iter.next().unwrap();
596        assert_eq!(second.label(), 32);
597        assert!(second.is_bottom_of_stack());
598
599        assert!(iter.next().is_none());
600        assert_eq!(iter.remaining().len(), 4);
601    }
602
603    #[test]
604    fn test_mpls_label_stack_three_labels() {
605        // Three MPLS labels
606        let packet = vec![
607            0x00, 0x00, 0xC0, 0x40, // Label=12, S=0
608            0x00, 0x01, 0x80, 0x3F, // Label=24, S=0
609            0x00, 0x02, 0x41, 0x3E, // Label=36, S=1
610            0x45, 0x00, // Payload
611        ];
612
613        let mut iter = MplsLabelStackIter::new(&packet);
614
615        let first = iter.next().unwrap();
616        assert_eq!(first.label(), 12);
617        assert!(!first.is_bottom_of_stack());
618
619        let second = iter.next().unwrap();
620        assert_eq!(second.label(), 24);
621        assert!(!second.is_bottom_of_stack());
622
623        let third = iter.next().unwrap();
624        assert_eq!(third.label(), 36);
625        assert!(third.is_bottom_of_stack());
626
627        assert!(iter.next().is_none());
628    }
629
630    #[test]
631    fn test_mpls_label_stack_skip_to_payload() {
632        let packet = vec![
633            0x00, 0x01, 0x00, 0x40, // Label=16, S=0
634            0x00, 0x02, 0x01, 0x3F, // Label=32, S=1
635            0x45, 0x00, 0x00, 0x14, // IPv4 header start
636        ];
637
638        let iter = MplsLabelStackIter::new(&packet);
639        let payload = iter.skip_to_payload();
640        assert_eq!(payload.len(), 4);
641        assert_eq!(payload[0], 0x45); // IPv4 version
642    }
643
644    #[test]
645    fn test_mpls_label_stack_collect() {
646        let packet = vec![
647            0x00, 0x01, 0x00, 0x40, // Label=16, S=0
648            0x00, 0x02, 0x01, 0x3F, // Label=32, S=1
649        ];
650
651        let iter = MplsLabelStackIter::new(&packet);
652        let labels: Vec<MplsLabel> = iter.collect_labels();
653
654        assert_eq!(labels.len(), 2);
655        assert_eq!(labels[0].label(), 16);
656        assert_eq!(labels[1].label(), 32);
657    }
658
659    #[test]
660    fn test_mpls_label_stack_parse() {
661        let packet = vec![
662            0x00, 0x01, 0x00, 0x40, // Label=16, S=0
663            0x00, 0x02, 0x01, 0x3F, // Label=32, S=1
664            0x45, 0x00, 0x00, 0x14,
665        ];
666
667        let (stack, payload) = MplsLabelStack::parse(&packet).unwrap();
668        assert_eq!(stack.count, 2);
669        assert_eq!(stack.total_size, 8);
670        assert_eq!(payload.len(), 4);
671
672        let first = stack.first().unwrap();
673        assert_eq!(first.label(), 16);
674
675        let last = stack.last().unwrap();
676        assert_eq!(last.label(), 32);
677    }
678
679    #[test]
680    fn test_mpls_label_stack_single() {
681        let packet = vec![
682            0x00, 0x01, 0x01, 0x40, // Label=16, S=1
683            0x45, 0x00,
684        ];
685
686        let (stack, payload) = MplsLabelStack::parse(&packet).unwrap();
687        assert_eq!(stack.count, 1);
688        assert_eq!(stack.total_size, 4);
689        assert_eq!(payload.len(), 2);
690    }
691
692    #[test]
693    fn test_mpls_label_too_small() {
694        let packet = vec![0x00, 0x01, 0x01]; // Only 3 bytes
695        let result = MplsLabel::from_bytes(&packet);
696        assert!(result.is_err());
697    }
698
699    #[test]
700    fn test_mpls_label_stack_empty() {
701        let packet: Vec<u8> = vec![];
702        let result = MplsLabelStack::parse(&packet);
703        assert!(result.is_none());
704    }
705
706    #[test]
707    fn test_mpls_display() {
708        let packet = vec![0x00, 0x01, 0x01, 0x40];
709        let (label, _) = MplsLabel::from_bytes(&packet).unwrap();
710        let display = format!("{}", label);
711        assert!(display.contains("label=16"));
712        assert!(display.contains("ttl=64"));
713    }
714
715    #[test]
716    fn test_mpls_display_reserved() {
717        let packet = vec![0x00, 0x00, 0x01, 0x40]; // Label=0
718        let (label, _) = MplsLabel::from_bytes(&packet).unwrap();
719        let display = format!("{}", label);
720        assert!(display.contains("IPv4 Explicit NULL"));
721    }
722
723    #[test]
724    fn test_mpls_label_stack_display() {
725        let packet = vec![
726            0x00, 0x01, 0x00, 0x40, // Label=16
727            0x00, 0x02, 0x01, 0x3F, // Label=32
728        ];
729
730        let (stack, _) = MplsLabelStack::parse(&packet).unwrap();
731        let display = format!("{}", stack);
732        assert!(display.contains("MPLS["));
733        assert!(display.contains("16"));
734        assert!(display.contains("32"));
735    }
736
737    #[test]
738    fn test_mpls_reserved_labels() {
739        assert!(MplsReservedLabel::is_reserved(0));
740        assert!(MplsReservedLabel::is_reserved(15));
741        assert!(!MplsReservedLabel::is_reserved(16));
742
743        assert_eq!(
744            MplsReservedLabel::from_label(0),
745            Some(MplsReservedLabel::Ipv4ExplicitNull)
746        );
747        assert_eq!(
748            MplsReservedLabel::from_label(1),
749            Some(MplsReservedLabel::RouterAlert)
750        );
751        assert_eq!(
752            MplsReservedLabel::from_label(13),
753            Some(MplsReservedLabel::Gal)
754        );
755        assert_eq!(MplsReservedLabel::from_label(16), None);
756    }
757
758    #[test]
759    fn test_mpls_exp_alias() {
760        let packet = vec![0x00, 0x06, 0x47, 0x40]; // TC=3
761        let (label, _) = MplsLabel::from_bytes(&packet).unwrap();
762        assert_eq!(label.exp(), label.traffic_class());
763        assert_eq!(label.exp(), 3);
764    }
765
766    #[test]
767    fn test_mpls_ethertype() {
768        assert!(is_mpls_ethertype(0x8847));
769        assert!(is_mpls_ethertype(0x8848));
770        assert!(!is_mpls_ethertype(0x0800));
771    }
772
773    #[test]
774    fn test_mpls_inner_type() {
775        let packet = vec![0x00, 0x10, 0x01, 0x40]; // Label=256
776        let (label, _) = MplsLabel::from_bytes(&packet).unwrap();
777        assert_eq!(label.inner_type(), 256);
778    }
779
780    #[test]
781    fn test_mpls_label_into_iter() {
782        let packet = vec![0x00, 0x01, 0x00, 0x40, 0x00, 0x02, 0x01, 0x3F];
783
784        let (stack, _) = MplsLabelStack::parse(&packet).unwrap();
785        let labels: Vec<u32> = (&stack).into_iter().map(|l| l.label()).collect();
786        assert_eq!(labels, vec![16, 32]);
787    }
788}