• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::*;
2 use core::slice::from_raw_parts;
3 
4 /// Slice containing an IPv6 fragment header.
5 #[derive(Clone, Debug, Eq, PartialEq)]
6 pub struct Ipv6FragmentHeaderSlice<'a> {
7     /// Slice containing the packet data.
8     slice: &'a [u8],
9 }
10 
11 impl<'a> Ipv6FragmentHeaderSlice<'a> {
12     /// Creates a hop by hop header slice from a slice.
from_slice(slice: &'a [u8]) -> Result<Ipv6FragmentHeaderSlice<'a>, err::LenError>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.
from_slice_unchecked(slice: &'a [u8]) -> Ipv6FragmentHeaderSlice<'a>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]
slice(&self) -> &'a [u8]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]
next_header(&self) -> IpNumber58     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]
fragment_offset(&self) -> IpFragOffset66     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]
more_fragments(&self) -> bool82     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
identification(&self) -> u3289     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]
is_fragmenting_payload(&self) -> bool144     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.
to_header(&self) -> Ipv6FragmentHeader155     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)]
166 mod 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 }
337