etherparse/net/
ipv6_fragment_header.rs

1use super::super::*;
2
3/// IPv6 fragment header.
4#[derive(Clone, Debug, Eq, PartialEq)]
5pub 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
19impl 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).
26    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.
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")))]
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")))]
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")))]
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]
116    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]
163    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]
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)]
192mod 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}