network_types/
mpls.rs

1use core::mem;
2
3/// Represents a Multiprotocol Label Switching (MPLS) header to RFC 3032
4/// https://www.rfc-editor.org/rfc/rfc3032.html.
5/// This header format applies to all MPLS messages.
6/// 20 bits for Label - 3 for TC - 1 for S - 8 for TTL
7#[repr(C, packed)]
8#[derive(Debug, Copy, Clone)]
9#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
10pub struct Mpls {
11    /// The first 3 bytes of the MPLS header containing Label (20 bits), Traffic Class (3 bits),
12    /// and Bottom of Stack (1 bit) fields in network byte order
13    pub lbl_tc_s: [u8; 3],
14    /// The Time to Live (TTL) field indicating maximum hop count
15    pub ttl: u8,
16}
17
18impl Mpls {
19    pub const LEN: usize = mem::size_of::<Mpls>();
20
21    /// Gets the 20-bit Label value.
22    #[inline]
23    pub fn label(&self) -> u32 {
24        let upper_bits = (self.lbl_tc_s[0] as u32) << 12;
25        let middle_bits = (self.lbl_tc_s[1] as u32) << 4;
26        let lower_bits = ((self.lbl_tc_s[2] & 0xF0) >> 4) as u32;
27        upper_bits | middle_bits | lower_bits
28    }
29
30    /// Sets the 20-bit Label value.
31    /// Input `label_value` should be a 20-bit integer (0 to 0xFFFFF).
32    #[inline]
33    pub fn set_label(&mut self, label: u32) {
34        self.lbl_tc_s[0] = ((label >> 12) & 0xFF) as u8;
35        self.lbl_tc_s[1] = ((label >> 4) & 0xFF) as u8;
36
37        // For the last byte, preserve TC and S bits
38        let preserved_bits = self.lbl_tc_s[2] & 0x0F;
39        self.lbl_tc_s[2] = ((label & 0x0F) << 4) as u8 | preserved_bits;
40    }
41
42    /// Gets the 3-bit Traffic Class value.
43    /// Assumes `self` is a valid reference to an MPLS header.
44    #[inline]
45    pub fn tc(&self) -> u8 {
46        (self.lbl_tc_s[2] & 0xE) >> 1
47    }
48
49    /// Sets the 3-bit Traffic Class value.
50    /// Input `tc_value` should be a 3-bit integer (0-7).
51    /// Assumes `self` is a valid, mutable reference to an MPLS header.
52    #[inline]
53    pub fn set_tc(&mut self, tc_value: u8) {
54        let preserved_bits = self.lbl_tc_s[2] & 0xF1;
55        self.lbl_tc_s[2] = preserved_bits | ((tc_value & 0x07) << 1);
56    }
57
58    /// Gets the 1-bit Bottom of Stack flag. Returns 0 or 1.
59    /// Assumes `self` is a valid reference to an MPLS header.
60    #[inline]
61    pub fn s(&self) -> u8 {
62        self.lbl_tc_s[2] & 0x01
63    }
64
65    /// Sets the 1-bit Bottom of Stack flag.
66    /// Input `s_value` should be 0 or 1.
67    /// Assumes `self` is a valid, mutable reference to an MPLS header.
68    #[inline]
69    pub fn set_s(&mut self, s_value: u8) {
70        let preserved_bits = self.lbl_tc_s[2] & 0xFE;
71        self.lbl_tc_s[2] = preserved_bits | (s_value & 0x01);
72    }
73
74    /// Gets the 8-bit Time to Live value.
75    #[inline]
76    pub fn ttl(&self) -> u8 {
77        self.ttl
78    }
79
80    /// Sets the 8-bit Time to Live value.
81    /// Input `ttl_value` is the new TTL value (0-255).
82    #[inline]
83    pub fn set_ttl(&mut self, ttl_value: u8) {
84        self.ttl = ttl_value;
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*; // Imports Mpls struct and its impl block
91
92    unsafe fn mpls_from_bytes(bytes: &[u8; Mpls::LEN]) -> &Mpls {
93        &*(bytes.as_ptr() as *const Mpls)
94    }
95
96    unsafe fn mpls_from_bytes_mut(bytes: &mut [u8; Mpls::LEN]) -> &mut Mpls {
97        &mut *(bytes.as_mut_ptr() as *mut Mpls)
98    }
99
100    #[test]
101    fn test_mpls_getters() {
102        // Label = 0xABCDE (A=10, B=11, C=12, D=13, E=14)
103        // lbl_tc_s[0] = 0xAB (bits 19-12 of label)
104        // lbl_tc_s[1] = 0xCD (bits 11-4 of label)
105        // lbl_tc_s[2] = 0xEB
106        let mpls_bytes: [u8; Mpls::LEN] = [0xAB, 0xCD, 0xEB, 0x40];
107        let mpls_header = unsafe { mpls_from_bytes(&mpls_bytes) };
108
109        assert_eq!(mpls_header.label(), 0xABCDE);
110        assert_eq!(mpls_header.tc(), 0x05); // 0b101
111        assert_eq!(mpls_header.s(), 0x01);
112        assert_eq!(mpls_header.ttl(), 0x40);
113    }
114
115    #[test]
116    fn test_mpls_set_label() {
117        let mut mpls_bytes: [u8; Mpls::LEN] = [0x00, 0x00, 0x00, 0x00];
118        let mpls_header = unsafe { mpls_from_bytes_mut(&mut mpls_bytes) };
119
120        mpls_header.set_label(0x12345);
121        // Expected:
122        // lbl_tc_s[0] = 0x12
123        // lbl_tc_s[1] = 0x34
124        // lbl_tc_s[2] = 0x50
125        assert_eq!(mpls_header.label(), 0x12345);
126        assert_eq!(mpls_bytes, [0x12, 0x34, 0x50, 0x00]);
127
128        // Set label again, ensure TC and S bits are preserved if they were set
129        mpls_bytes = [0xFF, 0xFF, 0x0F, 0xFF];
130        let mpls_header2 = unsafe { mpls_from_bytes_mut(&mut mpls_bytes) };
131        mpls_header2.set_label(0xABCDE);
132        // Expected:
133        // lbl_tc_s[0]=0xAB
134        // lbl_tc_s[1]=0xCD
135        // lbl_tc_s[2]=0xE0
136        assert_eq!(mpls_header2.label(), 0xABCDE);
137        assert_eq!(mpls_header2.tc(), 0x07); // Preserved
138        assert_eq!(mpls_header2.s(), 0x01); // Preserved
139        assert_eq!(mpls_bytes, [0xAB, 0xCD, 0xEF, 0xFF]);
140    }
141
142    #[test]
143    fn test_mpls_set_tc() {
144        let mut mpls_bytes: [u8; Mpls::LEN] = [0x00, 0x00, 0xA5, 0x00];
145        let mpls_header = unsafe { mpls_from_bytes_mut(&mut mpls_bytes) };
146
147        mpls_header.set_tc(0x06);
148        assert_eq!(mpls_header.tc(), 0x06);
149        assert_eq!(((mpls_bytes[2] & 0xF0) >> 4), 0x0A);
150        assert_eq!((mpls_bytes[2] & 0x01), 0x01);
151        assert_eq!(mpls_bytes, [0x00, 0x00, 0xAD, 0x00]);
152    }
153
154    #[test]
155    fn test_mpls_set_s() {
156        let mut mpls_bytes: [u8; Mpls::LEN] = [0x00, 0x00, 0xA6, 0x00];
157        let mpls_header = unsafe { mpls_from_bytes_mut(&mut mpls_bytes) };
158
159        mpls_header.set_s(0x01);
160        assert_eq!(mpls_header.s(), 0x01);
161        assert_eq!(((mpls_bytes[2] & 0xF0) >> 4), 0x0A); // Label part preserved
162        assert_eq!(((mpls_bytes[2] & 0x0E) >> 1), 0x03); // TC preserved
163        assert_eq!(mpls_bytes, [0x00, 0x00, 0xA7, 0x00]);
164    }
165
166    #[test]
167    fn test_mpls_set_ttl() {
168        let mut mpls_bytes: [u8; Mpls::LEN] = [0x12, 0x34, 0x56, 0x00];
169        let mpls_header = unsafe { mpls_from_bytes_mut(&mut mpls_bytes) };
170
171        mpls_header.set_ttl(0xFF);
172        assert_eq!(mpls_header.ttl(), 0xFF);
173        assert_eq!(mpls_bytes, [0x12, 0x34, 0x56, 0xFF]);
174    }
175}