Skip to main content

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