• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::super::*;
2 
3 /// IPv6 fragment header.
4 #[derive(Clone, Debug, Eq, PartialEq)]
5 pub struct Ipv6FragmentHeader {
6     /// IP protocol number specifying the next header or transport layer protocol.
7     ///
8     /// See [IpNumber] or [ip_number] for a definition of the known values.
9     pub next_header: IpNumber,
10     /// Offset of the current IP payload relative to the start of the fragmented
11     /// packet payload.
12     pub fragment_offset: IpFragOffset,
13     /// True if more fragment packets will follow. False if this is the last packet.
14     pub more_fragments: bool,
15     /// Identifcation value generated by the source.
16     pub identification: u32,
17 }
18 
19 impl Ipv6FragmentHeader {
20     /// Length of the serialized header.
21     pub const LEN: usize = 8;
22 
23     /// Create a new fragmentation header with the given parameters.
24     ///
25     /// Note that the `fragment_offset` can only support values between 0 and 0x1fff (inclusive).
new( next_header: IpNumber, fragment_offset: IpFragOffset, more_fragments: bool, identification: u32, ) -> Ipv6FragmentHeader26     pub const fn new(
27         next_header: IpNumber,
28         fragment_offset: IpFragOffset,
29         more_fragments: bool,
30         identification: u32,
31     ) -> Ipv6FragmentHeader {
32         Ipv6FragmentHeader {
33             next_header,
34             fragment_offset,
35             more_fragments,
36             identification,
37         }
38     }
39 
40     /// Read an Ipv6FragmentHeader from a slice and return the header & unused parts of the slice.
from_slice(slice: &[u8]) -> Result<(Ipv6FragmentHeader, &[u8]), err::LenError>41     pub fn from_slice(slice: &[u8]) -> Result<(Ipv6FragmentHeader, &[u8]), err::LenError> {
42         let s = Ipv6FragmentHeaderSlice::from_slice(slice)?;
43         let rest = &slice[8..];
44         let header = s.to_header();
45         Ok((header, rest))
46     }
47 
48     /// Read an fragment header from the current reader position.
49     #[cfg(feature = "std")]
50     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
read<T: std::io::Read + std::io::Seek + Sized>( reader: &mut T, ) -> Result<Ipv6FragmentHeader, std::io::Error>51     pub fn read<T: std::io::Read + std::io::Seek + Sized>(
52         reader: &mut T,
53     ) -> Result<Ipv6FragmentHeader, std::io::Error> {
54         let buffer = {
55             let mut buffer: [u8; 8] = [0; 8];
56             reader.read_exact(&mut buffer)?;
57             buffer
58         };
59 
60         Ok(Ipv6FragmentHeader {
61             next_header: IpNumber(buffer[0]),
62             fragment_offset: unsafe {
63                 // SAFE as the resulting number is guaranteed to have at most
64                 // 13 bits.
65                 IpFragOffset::new_unchecked(u16::from_be_bytes([
66                     (buffer[2] >> 3) & 0b0001_1111u8,
67                     ((buffer[2] << 5) & 0b1110_0000u8) | (buffer[3] & 0b0001_1111u8),
68                 ]))
69             },
70             more_fragments: 0 != buffer[3] & 0b1000_0000u8,
71             identification: u32::from_be_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
72         })
73     }
74 
75     /// Read an fragment header from the current reader position.
76     #[cfg(feature = "std")]
77     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
read_limited<T: std::io::Read + std::io::Seek + Sized>( reader: &mut crate::io::LimitedReader<T>, ) -> Result<Ipv6FragmentHeader, crate::err::io::LimitedReadError>78     pub fn read_limited<T: std::io::Read + std::io::Seek + Sized>(
79         reader: &mut crate::io::LimitedReader<T>,
80     ) -> Result<Ipv6FragmentHeader, crate::err::io::LimitedReadError> {
81         use err::Layer;
82 
83         // set layer so errors contain the correct layer & offset
84         reader.start_layer(Layer::Ipv6FragHeader);
85 
86         let buffer = {
87             let mut buffer: [u8; 8] = [0; 8];
88             reader.read_exact(&mut buffer)?;
89             buffer
90         };
91 
92         Ok(Ipv6FragmentHeader {
93             next_header: IpNumber(buffer[0]),
94             fragment_offset: unsafe {
95                 // SAFE as the resulting number is guaranteed to have at most
96                 // 13 bits.
97                 IpFragOffset::new_unchecked(u16::from_be_bytes([
98                     (buffer[2] >> 3) & 0b0001_1111u8,
99                     ((buffer[2] << 5) & 0b1110_0000u8) | (buffer[3] & 0b0001_1111u8),
100                 ]))
101             },
102             more_fragments: 0 != buffer[3] & 0b1000_0000u8,
103             identification: u32::from_be_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
104         })
105     }
106 
107     /// Writes a given IPv6 fragment header to the current position.
108     #[cfg(feature = "std")]
109     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error>110     pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
111         writer.write_all(&self.to_bytes())
112     }
113 
114     /// Length of the header in bytes.
115     #[inline]
header_len(&self) -> usize116     pub fn header_len(&self) -> usize {
117         Ipv6FragmentHeader::LEN
118     }
119 
120     /// Checks if the fragment header actually fragments the packet.
121     ///
122     /// Returns false if the fragment offset is 0 and the more flag
123     /// is not set. Otherwise returns true.
124     ///
125     /// [RFC8200](https://datatracker.ietf.org/doc/html/rfc8200) explicitly
126     /// states that fragment headers that don't fragment the packet payload are
127     /// allowed. See the following quote from
128     /// RFC8200 page 32:
129     ///
130     /// > Revised the text to handle the case of fragments that are whole
131     /// > datagrams (i.e., both the Fragment Offset field and the M flag
132     /// > are zero).  If received, they should be processed as a
133     /// > reassembled packet.  Any other fragments that match should be
134     /// > processed independently.  The Fragment creation process was
135     /// > modified to not create whole datagram fragments (Fragment
136     /// > Offset field and the M flag are zero).  See
137     /// > [RFC6946](https://datatracker.ietf.org/doc/html/6946) and
138     /// > [RFC8021](https://datatracker.ietf.org/doc/html/rfc8021) for more
139     /// > information."
140     ///
141     /// ```
142     /// use etherparse::{Ipv6FragmentHeader, ip_number::UDP};
143     ///
144     /// // offset 0 & no more fragments result in an unfragmented payload
145     /// {
146     ///     let header = Ipv6FragmentHeader::new(UDP, 0.try_into().unwrap(), false, 123);
147     ///     assert!(false == header.is_fragmenting_payload());
148     /// }
149     ///
150     /// // offset 0 & but more fragments will come -> fragmented
151     /// {
152     ///     let header = Ipv6FragmentHeader::new(UDP, 0.try_into().unwrap(), true, 123);
153     ///     assert!(header.is_fragmenting_payload());
154     /// }
155     ///
156     /// // offset non zero & no more fragments will come -> fragmented
157     /// {
158     ///     let header = Ipv6FragmentHeader::new(UDP, 1.try_into().unwrap(), false, 123);
159     ///     assert!(header.is_fragmenting_payload());
160     /// }
161     /// ```
162     #[inline]
is_fragmenting_payload(&self) -> bool163     pub fn is_fragmenting_payload(&self) -> bool {
164         self.more_fragments || (0 != self.fragment_offset.value())
165     }
166 
167     /// Returns the serialized form of the header as a statically
168     /// sized byte array.
169     #[inline]
to_bytes(&self) -> [u8; 8]170     pub fn to_bytes(&self) -> [u8; 8] {
171         let fo_be: [u8; 2] = self.fragment_offset.value().to_be_bytes();
172         let id_be = self.identification.to_be_bytes();
173         [
174             self.next_header.0,
175             0,
176             (((fo_be[0] << 3) & 0b1111_1000u8) | ((fo_be[1] >> 5) & 0b0000_0111u8)),
177             ((fo_be[1] & 0b0001_1111u8)
178                 | if self.more_fragments {
179                     0b1000_0000u8
180                 } else {
181                     0
182                 }),
183             id_be[0],
184             id_be[1],
185             id_be[2],
186             id_be[3],
187         ]
188     }
189 }
190 
191 #[cfg(test)]
192 mod test {
193     use crate::{test_gens::*, *};
194     use alloc::{format, vec::Vec};
195     use proptest::prelude::*;
196     use std::io::Cursor;
197 
198     proptest! {
199         #[test]
200         fn debug(input in ipv6_fragment_any()) {
201             assert_eq!(
202                 &format!(
203                     "Ipv6FragmentHeader {{ next_header: {:?}, fragment_offset: {:?}, more_fragments: {}, identification: {} }}",
204                     input.next_header,
205                     input.fragment_offset,
206                     input.more_fragments,
207                     input.identification
208                 ),
209                 &format!("{:?}", input)
210             );
211         }
212     }
213 
214     proptest! {
215         #[test]
216         fn clone_eq(input in ipv6_fragment_any()) {
217             assert_eq!(input, input.clone());
218         }
219     }
220 
221     proptest! {
222         #[test]
223         fn new(
224             next_header in ip_number_any(),
225             fragment_offset in 0..IpFragOffset::MAX_U16,
226             more_fragments in any::<bool>(),
227             identification in any::<u32>(),
228         ) {
229             let a = Ipv6FragmentHeader::new(
230                 next_header,
231                 fragment_offset.try_into().unwrap(),
232                 more_fragments,
233                 identification
234             );
235             assert_eq!(next_header, a.next_header);
236             assert_eq!(fragment_offset, a.fragment_offset.value());
237             assert_eq!(more_fragments, a.more_fragments);
238             assert_eq!(identification, a.identification);
239         }
240     }
241 
242     proptest! {
243         #[test]
244         fn from_slice(
245             input in ipv6_fragment_any(),
246             dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
247         ) {
248             // serialize
249             let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
250             input.write(&mut buffer).unwrap();
251             buffer.extend(&dummy_data[..]);
252 
253             // calls with a valid result
254             {
255                 let (result, rest) = Ipv6FragmentHeader::from_slice(&buffer[..]).unwrap();
256                 assert_eq!(input, result);
257                 assert_eq!(&buffer[8..], rest);
258             }
259             // call with not enough data in the slice
260             for len in 0..Ipv6FragmentHeader::LEN {
261                 assert_eq!(
262                     Ipv6FragmentHeader::from_slice(&buffer[0..len]).unwrap_err(),
263                     err::LenError{
264                         required_len: Ipv6FragmentHeader::LEN,
265                         len: len,
266                         len_source: LenSource::Slice,
267                         layer: err::Layer::Ipv6FragHeader,
268                         layer_start_offset: 0,
269                     }
270                 );
271             }
272         }
273     }
274 
275     proptest! {
276         #[test]
277         fn read(
278             input in ipv6_fragment_any(),
279             dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
280         ) {
281             use std::io::ErrorKind;
282 
283             // serialize
284             let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
285             input.write(&mut buffer).unwrap();
286             buffer.extend(&dummy_data[..]);
287 
288             // calls with a valid result
289             {
290                 let mut cursor = Cursor::new(&buffer);
291                 let result = Ipv6FragmentHeader::read(&mut cursor).unwrap();
292                 assert_eq!(input, result);
293                 assert_eq!(cursor.position(), 8);
294             }
295 
296             // call with not enough data in the slice
297             for len in 0..Ipv6FragmentHeader::LEN {
298                 let mut cursor = Cursor::new(&buffer[0..len]);
299                 assert_eq!(
300                     Ipv6FragmentHeader::read(&mut cursor)
301                     .unwrap_err()
302                     .kind(),
303                     ErrorKind::UnexpectedEof
304                 );
305             }
306         }
307     }
308 
309     proptest! {
310         #[test]
311         fn write(input in ipv6_fragment_any()) {
312 
313             // normal write
314             {
315                 let mut buffer = Vec::with_capacity(8);
316                 input.write(&mut buffer).unwrap();
317                 assert_eq!(
318                     &buffer,
319                     &input.to_bytes()
320                 );
321             }
322 
323             // not enough memory for write
324             for len in 0..Ipv6FragmentHeader::LEN {
325                 let mut buffer = [0u8;Ipv6FragmentHeader::LEN];
326                 let mut cursor = Cursor::new(&mut buffer[..len]);
327                 assert!(
328                     input.write(&mut cursor).is_err()
329                 );
330             }
331         }
332     }
333 
334     proptest! {
335         #[test]
336         fn header_len(input in ipv6_fragment_any()) {
337             assert_eq!(8, input.header_len());
338         }
339     }
340 
341     proptest! {
342         #[test]
343         fn is_fragmenting_payload(
344             non_zero_offset in 1u16..0b0001_1111_1111_1111u16,
345             identification in any::<u32>(),
346             next_header in ip_number_any(),
347 
348         ) {
349             // negative case
350             {
351                 let header = Ipv6FragmentHeader {
352                     next_header,
353                     fragment_offset: 0.try_into().unwrap(),
354                     more_fragments: false,
355                     identification
356                 };
357                 assert!(false == header.is_fragmenting_payload());
358             }
359             // positive case (non zero offset)
360             {
361                 let header = Ipv6FragmentHeader {
362                     next_header,
363                     fragment_offset: non_zero_offset.try_into().unwrap(),
364                     more_fragments: false,
365                     identification
366                 };
367                 assert!(header.is_fragmenting_payload());
368             }
369 
370             // positive case (more fragments)
371             {
372                 let header = Ipv6FragmentHeader {
373                     next_header,
374                     fragment_offset: 0.try_into().unwrap(),
375                     more_fragments: true,
376                     identification
377                 };
378                 assert!(header.is_fragmenting_payload());
379             }
380 
381             // positive case (non zero offset & more fragments)
382             {
383                 let header = Ipv6FragmentHeader {
384                     next_header,
385                     fragment_offset: non_zero_offset.try_into().unwrap(),
386                     more_fragments: true,
387                     identification
388                 };
389                 assert!(header.is_fragmenting_payload());
390             }
391         }
392     }
393 
394     proptest! {
395         #[test]
396         fn to_bytes(input in ipv6_fragment_any()) {
397 
398             // normal write
399             {
400                 let fragment_offset_be = input.fragment_offset.value().to_be_bytes();
401                 let id_be = input.identification.to_be_bytes();
402                 assert_eq!(
403                     &input.to_bytes(),
404                     &[
405                         input.next_header.0,
406                         0,
407                         (
408                             (fragment_offset_be[0] << 3 & 0b1111_1000u8) |
409                             (fragment_offset_be[1] >> 5 & 0b0000_0111u8)
410                         ),
411                         (
412                             (fragment_offset_be[1] & 0b0001_1111u8) |
413                             if input.more_fragments {
414                                 0b1000_0000u8
415                             } else {
416                                 0u8
417                             }
418                         ),
419                         id_be[0],
420                         id_be[1],
421                         id_be[2],
422                         id_be[3],
423                     ]
424                 );
425             }
426         }
427     }
428 }
429