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}