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}