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}