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