Skip to main content

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