network_types/
vxlan.rs

1use core::mem;
2
3/// VXLAN (Virtual eXtensible Local Area Network) header.
4///
5/// Encapsulates OSI layer 2 Ethernet frames within layer 4 UDP packets.
6/// Uses a 24-bit VXLAN Network Identifier (VNI) for traffic segregation.
7/// Header length: 8 bytes.
8/// Reference: RFC 7348.
9#[repr(C, packed)]
10#[derive(Debug, Copy, Clone)]
11#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
12pub struct VxlanHdr {
13    /// Flags (8 bits). Bit 3 (I flag) must be 1 if VNI is present. Other bits are reserved (R).
14    pub flags: u8,
15    /// Reserved field (24 bits). Must be zero on transmission.
16    pub _reserved1: [u8; 3],
17    /// Contains the 24-bit VNI (upper 3 bytes) and an 8-bit reserved field (the lowest byte).
18    /// The reserved field (the lowest byte) must be zero on transmission.
19    pub vni: [u8; 3],
20    pub _reserved2: u8,
21}
22
23/// Mask for the I-flag (VNI Present flag, bit 3) in the `flags` field.
24pub const VXLAN_I_FLAG_MASK: u8 = 0x08;
25
26impl VxlanHdr {
27    /// Length of the VXLAN header in bytes (8 bytes).
28    pub const LEN: usize = mem::size_of::<VxlanHdr>();
29
30    /// Creates a new `VxlanHdr`.
31    ///
32    /// Sets the I-flag, zeros reserved fields, and sets the VNI.
33    ///
34    /// # Parameters
35    /// - `vni`: The 24-bit VXLAN Network Identifier.
36    ///
37    /// # Returns
38    /// A new `VxlanHdr` instance.
39    pub fn new(vni: u32) -> Self {
40        let mut hdr = VxlanHdr {
41            flags: VXLAN_I_FLAG_MASK,
42            _reserved1: [0u8; 3],
43            vni: [0u8; 3],
44            _reserved2: 0u8,
45        };
46        hdr.set_vni(vni);
47        hdr
48    }
49
50    /// Creates a new `VxlanHdr`.
51    ///
52    /// Sets the I-flag, zeros reserved fields, and sets the VNI.
53    ///
54    /// # Parameters
55    /// - `flags`: The 8-bit value to set for the flag field.
56    /// - `vni`: The 24-bit VXLAN Network Identifier.
57    ///
58    /// # Returns
59    /// A new `VxlanHdr` instance.
60    pub fn with_flags(flags: u8, vni: [u8; 3]) -> Self {
61        Self {
62            flags,
63            _reserved1: [0; 3],
64            vni,
65            _reserved2: 0,
66        }
67    }
68
69    /// Returns the raw flags' byte.
70    ///
71    /// # Returns
72    /// The 8-bit flags field.
73    #[inline]
74    pub fn flags(&self) -> u8 {
75        self.flags
76    }
77
78    /// Sets the raw flags byte.
79    ///
80    /// # Parameters
81    /// - `flags`: The 8-bit value to set for the flags field.
82    #[inline]
83    pub fn set_flags(&mut self, flags: u8) {
84        self.flags = flags;
85    }
86
87    /// Checks if the I-flag (VNI Present) is set.
88    ///
89    /// # Returns
90    /// `true` if the I-flag is set, `false` otherwise.
91    #[inline]
92    pub fn vni_present(&self) -> bool {
93        (self.flags & VXLAN_I_FLAG_MASK) == VXLAN_I_FLAG_MASK
94    }
95
96    /// Sets or clears the I-flag (VNI Present).
97    ///
98    /// Preserves other flag bits.
99    ///
100    /// # Parameters
101    /// - `present`: If `true`, sets the I-flag; otherwise, clears it.
102    #[inline]
103    pub fn set_vni_present(&mut self, present: bool) {
104        if present {
105            self.flags |= VXLAN_I_FLAG_MASK;
106        } else {
107            self.flags &= !VXLAN_I_FLAG_MASK;
108        }
109    }
110
111    /// Returns the VXLAN Network Identifier (VNI).
112    ///
113    /// # Returns
114    /// The 24-bit VNI as a `u32`.
115    #[inline]
116    pub fn vni(&self) -> u32 {
117        u32::from_be_bytes([0, self.vni[0], self.vni[1], self.vni[2]])
118    }
119
120    /// Sets the VXLAN Network Identifier (VNI).
121    ///
122    /// Masks the input `vni` to 24 bits. Preserves the `reserved2` field.
123    ///
124    /// # Parameters
125    /// - `vni`: The 24-bit VNI value.
126    #[inline]
127    pub fn set_vni(&mut self, vni: u32) {
128        let vni_24bit = vni & 0x00FF_FFFF;
129        let vni_bytes = vni_24bit.to_be_bytes();
130        self.vni[0] = vni_bytes[1];
131        self.vni[1] = vni_bytes[2];
132        self.vni[2] = vni_bytes[3];
133    }
134}
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn test_vxlanhdr_len() {
141        assert_eq!(VxlanHdr::LEN, 8, "VXLAN header length should be 8 bytes");
142    }
143
144    #[test]
145    fn test_vxlanhdr_new() {
146        let vni_val: u32 = 0xABCDEF;
147        let hdr = VxlanHdr::new(vni_val);
148        assert_eq!(
149            hdr.flags(),
150            VXLAN_I_FLAG_MASK,
151            "Flags should have I-flag set and others zero"
152        );
153        assert!(hdr.vni_present(), "VNI present flag should be set by new()");
154        assert_eq!(hdr.vni(), vni_val, "VNI should be set correctly by new()");
155    }
156
157    #[test]
158    fn test_flags_management() {
159        let mut hdr = VxlanHdr::new(0x123);
160        hdr.set_flags(0xFF);
161        assert_eq!(hdr.flags(), 0xFF);
162        assert!(
163            hdr.vni_present(),
164            "I-flag should be set if flags byte is 0xFF"
165        );
166        hdr.set_flags(0x00);
167        assert!(
168            !hdr.vni_present(),
169            "I-flag should be clear if flags byte is 0x00"
170        );
171        hdr.set_vni_present(true);
172        assert_eq!(
173            hdr.flags(),
174            VXLAN_I_FLAG_MASK,
175            "set_vni_present(true) should set I-flag"
176        );
177        assert!(hdr.vni_present());
178        hdr.set_vni_present(false);
179        assert_eq!(
180            hdr.flags(),
181            0x00,
182            "set_vni_present(false) should clear I-flag"
183        );
184        assert!(!hdr.vni_present());
185        hdr.set_flags(0xF0);
186        assert!(!hdr.vni_present());
187        hdr.set_vni_present(true);
188        assert_eq!(
189            hdr.flags(),
190            0xF0 | VXLAN_I_FLAG_MASK,
191            "Setting I-flag should preserve other bits"
192        );
193        assert!(hdr.vni_present());
194        hdr.set_vni_present(false);
195        assert_eq!(
196            hdr.flags(),
197            0xF0 & !VXLAN_I_FLAG_MASK,
198            "Clearing I-flag should preserve other bits"
199        );
200        assert!(!hdr.vni_present());
201    }
202
203    #[test]
204    fn test_vni_management() {
205        let mut hdr = VxlanHdr::new(0);
206        let vni_val: u32 = 0xABCDEF;
207        hdr.set_vni(vni_val);
208        assert_eq!(hdr.vni(), vni_val, "VNI should be set correctly");
209        let large_vni: u32 = 0x12ABCDEF;
210        hdr.set_vni(large_vni);
211        assert_eq!(hdr.vni(), 0xABCDEF, "VNI should be masked to 24 bits");
212        hdr.set_vni(0);
213        assert_eq!(hdr.vni(), 0, "VNI should be settable to 0");
214        let max_vni: u32 = 0xFFFFFF;
215        hdr.set_vni(max_vni);
216        assert_eq!(hdr.vni(), max_vni, "Max 24-bit VNI should be settable");
217        hdr.set_vni(0x123456);
218        assert_eq!(hdr.vni(), 0x123456, "VNI should be updated");
219    }
220
221    #[test]
222    fn test_field_storage_and_retrieval_direct_manipulation() {
223        let mut hdr = VxlanHdr::with_flags(0x08, [0xAB, 0xCD, 0xEF]);
224        hdr.flags = 0x08;
225        hdr.vni = [0xAB, 0xCD, 0xEF];
226        assert_eq!(hdr.flags(), 0x08);
227        assert!(hdr.vni_present());
228        assert_eq!(hdr.vni(), 0xABCDEF);
229        hdr.set_vni_present(false);
230        assert_eq!(
231            hdr.flags, 0x00,
232            "Direct field check after set_vni_present(false)"
233        );
234        hdr.set_vni(0x654321);
235        assert_eq!(hdr.vni[0], 0x65, "Byte 0 of vni after set_vni");
236        assert_eq!(hdr.vni[1], 0x43, "Byte 1 of vni after set_vni");
237        assert_eq!(hdr.vni[2], 0x21, "Byte 2 of vni after set_vni");
238        assert_eq!(hdr.vni(), 0x654321);
239        assert_eq!(
240            hdr.vni[0], 0x65,
241            "Byte 0 of vni preserved after set_reserved2"
242        );
243        assert_eq!(hdr.vni(), 0x654321);
244    }
245}