etherparse/net/ipv6_fragment_header_slice.rs
1use crate::*;
2use core::slice::from_raw_parts;
3
4/// Slice containing an IPv6 fragment header.
5#[derive(Clone, Debug, Eq, PartialEq)]
6pub struct Ipv6FragmentHeaderSlice<'a> {
7 /// Slice containing the packet data.
8 slice: &'a [u8],
9}
10
11impl<'a> Ipv6FragmentHeaderSlice<'a> {
12 /// Creates a hop by hop header slice from a slice.
13 pub fn from_slice(slice: &'a [u8]) -> Result<Ipv6FragmentHeaderSlice<'a>, err::LenError> {
14 // the fragmentation header has the exact size of 8 bytes
15 if slice.len() < 8 {
16 Err(err::LenError {
17 required_len: 8,
18 len: slice.len(),
19 len_source: LenSource::Slice,
20 layer: err::Layer::Ipv6FragHeader,
21 layer_start_offset: 0,
22 })
23 } else {
24 Ok(Ipv6FragmentHeaderSlice {
25 // SAFETY:
26 // Safe as slice length is checked to be at least 8 before this
27 // code can be reached.
28 slice: unsafe { from_raw_parts(slice.as_ptr(), 8) },
29 })
30 }
31 }
32
33 /// Creates a hop by hop header slice from a slice (assumes slice size & content was validated before).
34 ///
35 /// # Safety
36 ///
37 /// This function assumes that the passed slice has at least the length
38 /// of 8. If a slice with length less then 8 is passed to this function
39 /// the behavior will be undefined.
40 pub unsafe fn from_slice_unchecked(slice: &'a [u8]) -> Ipv6FragmentHeaderSlice<'a> {
41 debug_assert!(slice.len() >= Ipv6FragmentHeader::LEN);
42 // the fragmentation header has the exact size of 8 bytes
43 Ipv6FragmentHeaderSlice {
44 slice: from_raw_parts(slice.as_ptr(), Ipv6FragmentHeader::LEN),
45 }
46 }
47
48 /// Returns the slice containing the ipv6 fragment header.
49 #[inline]
50 pub fn slice(&self) -> &'a [u8] {
51 self.slice
52 }
53
54 /// Returns the IP protocol number of the next header.
55 ///
56 /// See [IpNumber] or [ip_number] for a definition of the known values.
57 #[inline]
58 pub fn next_header(&self) -> IpNumber {
59 // SAFETY:
60 // Slice size checked to be at least 8 bytes in constructor.
61 IpNumber(unsafe { *self.slice.get_unchecked(0) })
62 }
63
64 /// Fragment offset
65 #[inline]
66 pub fn fragment_offset(&self) -> IpFragOffset {
67 unsafe {
68 // SAFETY: Safe as the resulting number is guaranteed to be only
69 // 13 bit long.
70 IpFragOffset::new_unchecked(u16::from_be_bytes([
71 // SAFETY:
72 // Slice size checked to be at least 8 bytes in constructor.
73 (*self.slice.get_unchecked(2) >> 3) & 0b0001_1111u8,
74 ((*self.slice.get_unchecked(2) << 5) & 0b1110_0000u8)
75 | (*self.slice.get_unchecked(3) & 0b0001_1111u8),
76 ]))
77 }
78 }
79
80 /// True if more fragment packets will follow. False if this is the last packet.
81 #[inline]
82 pub fn more_fragments(&self) -> bool {
83 // SAFETY:
84 // Slice size checked to be at least 8 bytes in constructor.
85 unsafe { 0 != *self.slice.get_unchecked(3) & 0b1000_0000u8 }
86 }
87
88 /// Identifcation value generated by the source
89 pub fn identification(&self) -> u32 {
90 // SAFETY:
91 // Slice size checked to be at least 8 bytes in constructor.
92 unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(4)) }
93 }
94
95 /// Checks if the fragment header actually fragments the packet.
96 ///
97 /// Returns false if the fragment offset is 0 and the more flag
98 /// is not set. Otherwise returns true.
99 ///
100 /// [RFC8200](https://datatracker.ietf.org/doc/html/rfc8200) explicitly
101 /// states that fragment headers that don't fragment the packet payload are
102 /// allowed. See the following quote from
103 /// RFC8200 page 32:
104 ///
105 /// > Revised the text to handle the case of fragments that are whole
106 /// > datagrams (i.e., both the Fragment Offset field and the M flag
107 /// > are zero). If received, they should be processed as a
108 /// > reassembled packet. Any other fragments that match should be
109 /// > processed independently. The Fragment creation process was
110 /// > modified to not create whole datagram fragments (Fragment
111 /// > Offset field and the M flag are zero). See
112 /// > [RFC6946](https://datatracker.ietf.org/doc/html/6946) and
113 /// > [RFC8021](https://datatracker.ietf.org/doc/html/rfc8021) for more
114 /// > information."
115 ///
116 /// ```
117 /// use etherparse::Ipv6FragmentHeaderSlice;
118 ///
119 /// {
120 /// let slice = Ipv6FragmentHeaderSlice::from_slice(&[
121 /// 0, 0, 0, 0, // offset 0 & more_fragments not set
122 /// 1, 2, 3, 4,
123 /// ]).unwrap();
124 /// assert!(false == slice.is_fragmenting_payload());
125 /// }
126 ///
127 /// {
128 /// let slice = Ipv6FragmentHeaderSlice::from_slice(&[
129 /// 0, 0, 0, 0b1000_0000u8, // more_fragments set
130 /// 1, 2, 3, 4,
131 /// ]).unwrap();
132 /// assert!(slice.is_fragmenting_payload());
133 /// }
134 ///
135 /// {
136 /// let slice = Ipv6FragmentHeaderSlice::from_slice(&[
137 /// 0, 0, 1, 0, // non zero offset
138 /// 1, 2, 3, 4,
139 /// ]).unwrap();
140 /// assert!(slice.is_fragmenting_payload());
141 /// }
142 /// ```
143 #[inline]
144 pub fn is_fragmenting_payload(&self) -> bool {
145 // SAFETY:
146 // Slice size checked to be at least 8 bytes in constructor.
147 unsafe {
148 0 != *self.slice.get_unchecked(2) || 0 != (*self.slice.get_unchecked(3) & 0b1001_1111u8)
149 // exclude the reserved bytes
150 }
151 }
152
153 /// Decode some of the fields and copy the results to a
154 /// Ipv6FragmentHeader struct.
155 pub fn to_header(&self) -> Ipv6FragmentHeader {
156 Ipv6FragmentHeader {
157 next_header: self.next_header(),
158 fragment_offset: self.fragment_offset(),
159 more_fragments: self.more_fragments(),
160 identification: self.identification(),
161 }
162 }
163}
164
165#[cfg(test)]
166mod test {
167 use crate::{test_gens::*, *};
168 use alloc::{format, vec::Vec};
169 use proptest::prelude::*;
170
171 proptest! {
172 #[test]
173 fn debug(input in ipv6_fragment_any()) {
174 let bytes = input.to_bytes();
175 let slice = Ipv6FragmentHeaderSlice::from_slice(
176 &bytes
177 ).unwrap();
178 assert_eq!(
179 &format!(
180 "Ipv6FragmentHeaderSlice {{ slice: {:?} }}",
181 slice.slice()
182 ),
183 &format!("{:?}", slice)
184 );
185 }
186 }
187
188 proptest! {
189 #[test]
190 fn clone_eq(input in ipv6_fragment_any()) {
191 let bytes = input.to_bytes();
192 let slice = Ipv6FragmentHeaderSlice::from_slice(
193 &bytes
194 ).unwrap();
195 assert_eq!(slice, slice.clone());
196 }
197 }
198
199 proptest! {
200 #[test]
201 fn from_slice(
202 input in ipv6_fragment_any(),
203 dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
204 ) {
205 // serialize
206 let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
207 input.write(&mut buffer).unwrap();
208 buffer.extend(&dummy_data[..]);
209
210 // calls with a valid result
211 {
212 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer[..]).unwrap();
213 assert_eq!(slice.slice(), &buffer[..8]);
214 }
215
216 // call with not enough data in the slice
217 for len in 0..Ipv6FragmentHeader::LEN {
218 assert_eq!(
219 Ipv6FragmentHeaderSlice::from_slice(&buffer[0..len]).unwrap_err(),
220 err::LenError{
221 required_len: 8,
222 len: len,
223 len_source: LenSource::Slice,
224 layer: err::Layer::Ipv6FragHeader,
225 layer_start_offset: 0,
226 }
227 );
228 }
229 }
230 }
231
232 proptest! {
233 #[test]
234 fn from_slice_unchecked(
235 input in ipv6_fragment_any(),
236 dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
237 ) {
238 // serialize
239 let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
240 input.write(&mut buffer).unwrap();
241 buffer.extend(&dummy_data[..]);
242
243 // calls with a valid result
244 unsafe {
245 let slice = Ipv6FragmentHeaderSlice::from_slice_unchecked(&buffer[..]);
246 assert_eq!(slice.slice(), &buffer[..8]);
247 }
248 }
249 }
250
251 proptest! {
252 #[test]
253 fn getters(input in ipv6_fragment_any()) {
254 let buffer = input.to_bytes();
255 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer[..]).unwrap();
256
257 assert_eq!(input.next_header, slice.next_header());
258 assert_eq!(input.fragment_offset, slice.fragment_offset());
259 assert_eq!(input.more_fragments, slice.more_fragments());
260 assert_eq!(input.identification, slice.identification());
261 }
262 }
263
264 proptest! {
265 #[test]
266 fn is_fragmenting_payload(
267 non_zero_offset in 1u16..0b0001_1111_1111_1111u16,
268 identification in any::<u32>(),
269 next_header in ip_number_any(),
270 ) {
271 // negative case
272 {
273 let header = Ipv6FragmentHeader {
274 next_header,
275 fragment_offset: 0.try_into().unwrap(),
276 more_fragments: false,
277 identification
278 };
279 // slice
280 let buffer = header.to_bytes();
281 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
282 assert!(false == slice.is_fragmenting_payload());
283 }
284 // positive case (non zero offset)
285 {
286 let header = Ipv6FragmentHeader {
287 next_header,
288 fragment_offset: non_zero_offset.try_into().unwrap(),
289 more_fragments: false,
290 identification
291 };
292 // slice
293 let buffer = header.to_bytes();
294 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
295 assert!(slice.is_fragmenting_payload());
296 }
297
298 // positive case (more fragments)
299 {
300 let header = Ipv6FragmentHeader {
301 next_header,
302 fragment_offset: 0.try_into().unwrap(),
303 more_fragments: true,
304 identification
305 };
306 // slice
307 let buffer = header.to_bytes();
308 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
309 assert!(slice.is_fragmenting_payload());
310 }
311
312 // positive case (non zero offset & more fragments)
313 {
314 let header = Ipv6FragmentHeader {
315 next_header,
316 fragment_offset: non_zero_offset.try_into().unwrap(),
317 more_fragments: true,
318 identification
319 };
320 // slice
321 let buffer = header.to_bytes();
322 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
323 assert!(slice.is_fragmenting_payload());
324 }
325 }
326 }
327
328 proptest! {
329 #[test]
330 fn to_header(input in ipv6_fragment_any()) {
331 let buffer = input.to_bytes();
332 let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
333 assert_eq!(input, slice.to_header());
334 }
335 }
336}