Skip to main content

network_types/
udp.rs

1use core::mem;
2
3use crate::{getter_be, setter_be};
4
5/// UDP header, which is present after the IP header.
6///
7/// This struct represents the User Datagram Protocol (UDP) header as defined in RFC 768.
8/// The UDP header is 8 bytes long and contains source and destination ports, length, and checksum fields.
9/// All fields are stored in network byte order (big-endian).
10///
11/// # Example
12/// ```
13/// use network_types::udp::UdpHdr;
14///
15/// let mut udp_header = UdpHdr {
16///     src: [0, 0],
17///     dst: [0, 0],
18///     len: [0, 0],
19///     check: [0, 0],
20/// };
21///
22/// udp_header.set_src_port(12345);
23/// udp_header.set_dst_port(80);
24/// udp_header.set_len(28); // 8 bytes header + 20 bytes payload
25/// udp_header.set_checksum(0); // Checksum calculation would be done separately
26/// ```
27#[repr(C)]
28#[derive(Debug, Copy, Clone)]
29#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
30#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
31pub struct UdpHdr {
32    /// Source port in network byte order (big-endian)
33    pub src: [u8; 2],
34    /// Destination port in network byte order (big-endian)
35    pub dst: [u8; 2],
36    /// Length of UDP header and data in bytes, in network byte order (big-endian)
37    pub len: [u8; 2],
38    /// Checksum of UDP header and data, in network byte order (big-endian)
39    pub check: [u8; 2],
40}
41
42impl UdpHdr {
43    /// The size of the UDP header in bytes (8 bytes).
44    pub const LEN: usize = mem::size_of::<UdpHdr>();
45
46    /// Returns the source port number.
47    ///
48    /// This method converts the source port from network byte order (big-endian)
49    /// to host byte order.
50    ///
51    /// # Returns
52    /// The source port as a u16 value.
53    #[inline]
54    pub fn src_port(&self) -> u16 {
55        // SAFETY: Pointer arithmetic in bounds of the struct.
56        unsafe { getter_be!(self, src, u16) }
57    }
58
59    /// Sets the source port number.
60    ///
61    /// This method converts the source port from host byte order
62    /// to network byte order (big-endian).
63    ///
64    /// # Parameters
65    /// * `source` - The source port number to set.
66    #[inline]
67    pub fn set_src_port(&mut self, src: u16) {
68        // SAFETY: Pointer arithmetic in bounds of the struct.
69        unsafe { setter_be!(self, src, src) }
70    }
71
72    /// Returns the destination port number.
73    ///
74    /// This method converts the destination port from network byte order (big-endian)
75    /// to host byte order.
76    ///
77    /// # Returns
78    /// The destination port as a u16 value.
79    #[inline]
80    pub fn dst_port(&self) -> u16 {
81        // SAFETY: Pointer arithmetic in bounds of the struct.
82        unsafe { getter_be!(self, dst, u16) }
83    }
84
85    /// Sets the destination port number.
86    ///
87    /// This method converts the destination port from host byte order
88    /// to network byte order (big-endian).
89    ///
90    /// # Parameters
91    /// * `dest` - The destination port number to set.
92    /// ```
93    #[inline]
94    pub fn set_dst_port(&mut self, dst: u16) {
95        // SAFETY: Pointer arithmetic in bounds of the struct.
96        unsafe { setter_be!(self, dst, dst) }
97    }
98
99    /// Returns the length of the UDP datagram in bytes.
100    ///
101    /// The length includes both the UDP header (8 bytes) and the UDP payload.
102    /// This method converts the length from network byte order (big-endian)
103    /// to host byte order.
104    ///
105    /// # Returns
106    /// The length as a u16 value.
107    #[inline]
108    pub fn len(&self) -> u16 {
109        // SAFETY: Pointer arithmetic in bounds of the struct.
110        unsafe { getter_be!(self, len, u16) }
111    }
112
113    /// Returns true if the UDP length field is zero.
114    ///
115    /// A zero length indicates an invalid or empty UDP datagram, as the minimum valid length
116    /// is 8 bytes (the size of the UDP header).
117    ///
118    /// # Returns
119    /// `true` if length is zero, `false` otherwise.
120    pub fn is_empty(&self) -> bool {
121        self.len == [0, 0]
122    }
123
124    /// Sets the length of the UDP datagram in bytes.
125    ///
126    /// The length should include both the UDP header (8 bytes) and the UDP payload.
127    /// This method converts the length from host byte order to network byte order (big-endian).
128    ///
129    /// # Parameters
130    /// * `len` - The length to set in bytes.
131    #[inline]
132    pub fn set_len(&mut self, len: u16) {
133        // SAFETY: Pointer arithmetic in bounds of the struct.
134        unsafe { setter_be!(self, len, len) }
135    }
136
137    /// Returns the UDP checksum.
138    ///
139    /// The checksum is calculated over the UDP header, the UDP payload, and a pseudo-header
140    /// derived from the IP header. This method converts the checksum from network byte order
141    /// (big-endian) to host byte order.
142    ///
143    /// # Returns
144    /// The checksum as a u16 value.
145    #[inline]
146    pub fn checksum(&self) -> u16 {
147        // SAFETY: Pointer arithmetic in bounds of the struct.
148        unsafe { getter_be!(self, check, u16) }
149    }
150
151    /// Sets the UDP checksum.
152    ///
153    /// The checksum should be calculated over the UDP header, the UDP payload, and a pseudo-header
154    /// derived from the IP header. This method converts the checksum from host byte order to
155    /// network byte order (big-endian).
156    ///
157    /// A value of 0 indicates that the checksum is not used (IPv4 only).
158    ///
159    /// # Parameters
160    /// * `check` - The checksum value to set.
161    #[inline]
162    pub fn set_checksum(&mut self, check: u16) {
163        // SAFETY: Pointer arithmetic in bounds of the struct.
164        unsafe { setter_be!(self, check, check) }
165    }
166}
167
168#[cfg(test)]
169mod test {
170    use super::UdpHdr;
171    use core::mem;
172
173    #[test]
174    fn test_udp_hdr_size() {
175        // UdpHdr should be exactly 8 bytes
176        assert_eq!(UdpHdr::LEN, 8);
177        assert_eq!(UdpHdr::LEN, mem::size_of::<UdpHdr>());
178    }
179
180    #[test]
181    fn test_source_port() {
182        let mut udp_hdr = UdpHdr {
183            src: [0, 0],
184            dst: [0, 0],
185            len: [0, 0],
186            check: [0, 0],
187        };
188
189        // Test with a standard value
190        let test_port: u16 = 12345;
191        udp_hdr.set_src_port(test_port);
192        assert_eq!(udp_hdr.src_port(), test_port);
193
194        // Verify byte order in raw storage (big-endian/network byte order)
195        assert_eq!(udp_hdr.src, [0x30, 0x39]); // 12345 in big-endian
196
197        // Test with zero
198        udp_hdr.set_src_port(0);
199        assert_eq!(udp_hdr.src_port(), 0);
200        assert_eq!(udp_hdr.src, [0, 0]);
201
202        // Test with max value
203        udp_hdr.set_src_port(u16::MAX);
204        assert_eq!(udp_hdr.src_port(), u16::MAX);
205        assert_eq!(udp_hdr.src, [0xFF, 0xFF]);
206    }
207
208    #[test]
209    fn test_dest_port() {
210        let mut udp_hdr = UdpHdr {
211            src: [0, 0],
212            dst: [0, 0],
213            len: [0, 0],
214            check: [0, 0],
215        };
216
217        // Test with a standard value
218        let test_port: u16 = 80;
219        udp_hdr.set_dst_port(test_port);
220        assert_eq!(udp_hdr.dst_port(), test_port);
221
222        // Verify byte order in raw storage (big-endian/network byte order)
223        assert_eq!(udp_hdr.dst, [0x00, 0x50]); // 80 in big-endian
224
225        // Test with zero
226        udp_hdr.set_dst_port(0);
227        assert_eq!(udp_hdr.dst_port(), 0);
228        assert_eq!(udp_hdr.dst, [0, 0]);
229
230        // Test with max value
231        udp_hdr.set_dst_port(u16::MAX);
232        assert_eq!(udp_hdr.dst_port(), u16::MAX);
233        assert_eq!(udp_hdr.dst, [0xFF, 0xFF]);
234    }
235
236    #[test]
237    fn test_length() {
238        let mut udp_hdr = UdpHdr {
239            src: [0, 0],
240            dst: [0, 0],
241            len: [0, 0],
242            check: [0, 0],
243        };
244
245        // Test with a standard value (8 bytes header + 20 bytes payload)
246        let test_len: u16 = 28;
247        udp_hdr.set_len(test_len);
248        assert_eq!(udp_hdr.len(), test_len);
249
250        // Verify byte order in raw storage (big-endian/network byte order)
251        assert_eq!(udp_hdr.len, [0x00, 0x1C]); // 28 in big-endian
252
253        // Test with minimum valid value (just the header)
254        udp_hdr.set_len(8);
255        assert_eq!(udp_hdr.len(), 8);
256        assert_eq!(udp_hdr.len, [0x00, 0x08]);
257
258        // Test with max value
259        udp_hdr.set_len(u16::MAX);
260        assert_eq!(udp_hdr.len(), u16::MAX);
261        assert_eq!(udp_hdr.len, [0xFF, 0xFF]);
262    }
263
264    #[test]
265    fn test_empty() {
266        let mut udp_hdr = UdpHdr {
267            src: [0, 0],
268            dst: [0, 0],
269            len: [0, 0],
270            check: [0, 0],
271        };
272        assert!(udp_hdr.is_empty());
273        udp_hdr.set_len(8);
274        assert!(!udp_hdr.is_empty());
275        udp_hdr.set_len(0);
276        assert!(udp_hdr.is_empty());
277    }
278
279    #[test]
280    fn test_checksum() {
281        let mut udp_hdr = UdpHdr {
282            src: [0, 0],
283            dst: [0, 0],
284            len: [0, 0],
285            check: [0, 0],
286        };
287
288        // Test with a standard value
289        let test_checksum: u16 = 0x1234;
290        udp_hdr.set_checksum(test_checksum);
291        assert_eq!(udp_hdr.checksum(), test_checksum);
292
293        // Verify byte order in raw storage (big-endian/network byte order)
294        assert_eq!(udp_hdr.check, [0x12, 0x34]);
295
296        // Test with zero (indicating checksum not used in IPv4)
297        udp_hdr.set_checksum(0);
298        assert_eq!(udp_hdr.checksum(), 0);
299        assert_eq!(udp_hdr.check, [0, 0]);
300
301        // Test with max value
302        udp_hdr.set_checksum(u16::MAX);
303        assert_eq!(udp_hdr.checksum(), u16::MAX);
304        assert_eq!(udp_hdr.check, [0xFF, 0xFF]);
305    }
306
307    #[test]
308    fn test_complete_udp_header() {
309        // Test creating a complete UDP header
310        let mut udp_hdr = UdpHdr {
311            src: [0, 0],
312            dst: [0, 0],
313            len: [0, 0],
314            check: [0, 0],
315        };
316
317        // Set all fields
318        udp_hdr.set_src_port(12345);
319        udp_hdr.set_dst_port(80);
320        udp_hdr.set_len(28); // 8 bytes header + 20 bytes payload
321        udp_hdr.set_checksum(0x1234);
322
323        // Verify all values are correctly set and retrieved
324        assert_eq!(udp_hdr.src_port(), 12345);
325        assert_eq!(udp_hdr.dst_port(), 80);
326        assert_eq!(udp_hdr.len(), 28);
327        assert_eq!(udp_hdr.checksum(), 0x1234);
328
329        // Verify raw byte storage
330        assert_eq!(udp_hdr.src, [0x30, 0x39]); // 12345 in big-endian
331        assert_eq!(udp_hdr.dst, [0x00, 0x50]); // 80 in big-endian
332        assert_eq!(udp_hdr.len, [0x00, 0x1C]); // 28 in big-endian
333        assert_eq!(udp_hdr.check, [0x12, 0x34]); // 0x1234 in big-endian
334    }
335
336    #[test]
337    #[cfg(feature = "wincode")]
338    fn test_serialize() {
339        use core::mem;
340
341        let udp = UdpHdr {
342            src: 4242_u16.to_be_bytes(),
343            dst: 4789_u16.to_be_bytes(),
344            len: 42_u16.to_be_bytes(),
345            check: 0_u16.to_be_bytes(),
346        };
347
348        let mut bytes = [0u8; mem::size_of::<UdpHdr>()];
349        wincode::serialize_into(bytes.as_mut_slice(), &udp).unwrap();
350    }
351}