smoltcp/wire/
ipv6fragment.rs

1use super::{Error, Result};
2use core::fmt;
3
4use byteorder::{ByteOrder, NetworkEndian};
5
6/// A read/write wrapper around an IPv6 Fragment Header.
7#[derive(Debug, PartialEq, Eq)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9pub struct Header<T: AsRef<[u8]>> {
10    buffer: T,
11}
12
13// Format of the Fragment Header
14//
15// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16// |  Next Header  |   Reserved    |      Fragment Offset    |Res|M|
17// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
18// |                         Identification                        |
19// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20//
21// See https://tools.ietf.org/html/rfc8200#section-4.5 for details.
22//
23// **NOTE**: The fields start counting after the header length field.
24mod field {
25    use crate::wire::field::*;
26
27    // 16-bit field containing the fragment offset, reserved and more fragments values.
28    pub const FR_OF_M: Field = 0..2;
29    // 32-bit field identifying the fragmented packet
30    pub const IDENT: Field = 2..6;
31    /// 1 bit flag indicating if there are more fragments coming.
32    pub const M: usize = 1;
33}
34
35impl<T: AsRef<[u8]>> Header<T> {
36    /// Create a raw octet buffer with an IPv6 Fragment Header structure.
37    pub const fn new_unchecked(buffer: T) -> Header<T> {
38        Header { buffer }
39    }
40
41    /// Shorthand for a combination of [new_unchecked] and [check_len].
42    ///
43    /// [new_unchecked]: #method.new_unchecked
44    /// [check_len]: #method.check_len
45    pub fn new_checked(buffer: T) -> Result<Header<T>> {
46        let header = Self::new_unchecked(buffer);
47        header.check_len()?;
48        Ok(header)
49    }
50
51    /// Ensure that no accessor method will panic if called.
52    /// Returns `Err(Error)` if the buffer is too short.
53    pub fn check_len(&self) -> Result<()> {
54        let data = self.buffer.as_ref();
55        let len = data.len();
56
57        if len < field::IDENT.end {
58            Err(Error)
59        } else {
60            Ok(())
61        }
62    }
63
64    /// Consume the header, returning the underlying buffer.
65    pub fn into_inner(self) -> T {
66        self.buffer
67    }
68
69    /// Return the fragment offset field.
70    #[inline]
71    pub fn frag_offset(&self) -> u16 {
72        let data = self.buffer.as_ref();
73        NetworkEndian::read_u16(&data[field::FR_OF_M]) >> 3
74    }
75
76    /// Return more fragment flag field.
77    #[inline]
78    pub fn more_frags(&self) -> bool {
79        let data = self.buffer.as_ref();
80        (data[field::M] & 0x1) == 1
81    }
82
83    /// Return the fragment identification value field.
84    #[inline]
85    pub fn ident(&self) -> u32 {
86        let data = self.buffer.as_ref();
87        NetworkEndian::read_u32(&data[field::IDENT])
88    }
89}
90
91impl<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
92    /// Set reserved fields.
93    ///
94    /// Set 8-bit reserved field after the next header field.
95    /// Set 2-bit reserved field between fragment offset and more fragments.
96    #[inline]
97    pub fn clear_reserved(&mut self) {
98        let data = self.buffer.as_mut();
99        // Retain the higher order 5 bits and lower order 1 bit
100        data[field::M] &= 0xf9;
101    }
102
103    /// Set the fragment offset field.
104    #[inline]
105    pub fn set_frag_offset(&mut self, value: u16) {
106        let data = self.buffer.as_mut();
107        // Retain the lower order 3 bits
108        let raw = ((value & 0x1fff) << 3) | ((data[field::M] & 0x7) as u16);
109        NetworkEndian::write_u16(&mut data[field::FR_OF_M], raw);
110    }
111
112    /// Set the more fragments flag field.
113    #[inline]
114    pub fn set_more_frags(&mut self, value: bool) {
115        let data = self.buffer.as_mut();
116        // Retain the high order 7 bits
117        let raw = (data[field::M] & 0xfe) | (value as u8 & 0x1);
118        data[field::M] = raw;
119    }
120
121    /// Set the fragmentation identification field.
122    #[inline]
123    pub fn set_ident(&mut self, value: u32) {
124        let data = self.buffer.as_mut();
125        NetworkEndian::write_u32(&mut data[field::IDENT], value);
126    }
127}
128
129impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
130    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131        match Repr::parse(self) {
132            Ok(repr) => write!(f, "{repr}"),
133            Err(err) => {
134                write!(f, "IPv6 Fragment ({err})")?;
135                Ok(())
136            }
137        }
138    }
139}
140
141/// A high-level representation of an IPv6 Fragment header.
142#[derive(Debug, PartialEq, Eq, Clone, Copy)]
143#[cfg_attr(feature = "defmt", derive(defmt::Format))]
144pub struct Repr {
145    /// The offset of the data following this header, relative to the start of the Fragmentable
146    /// Part of the original packet.
147    pub frag_offset: u16,
148    /// When there are more fragments following this header
149    pub more_frags: bool,
150    /// The identification for every packet that is fragmented.
151    pub ident: u32,
152}
153
154impl Repr {
155    /// Parse an IPv6 Fragment Header and return a high-level representation.
156    pub fn parse<T>(header: &Header<&T>) -> Result<Repr>
157    where
158        T: AsRef<[u8]> + ?Sized,
159    {
160        header.check_len()?;
161        Ok(Repr {
162            frag_offset: header.frag_offset(),
163            more_frags: header.more_frags(),
164            ident: header.ident(),
165        })
166    }
167
168    /// Return the length, in bytes, of a header that will be emitted from this high-level
169    /// representation.
170    pub const fn buffer_len(&self) -> usize {
171        field::IDENT.end
172    }
173
174    /// Emit a high-level representation into an IPv6 Fragment Header.
175    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) {
176        header.clear_reserved();
177        header.set_frag_offset(self.frag_offset);
178        header.set_more_frags(self.more_frags);
179        header.set_ident(self.ident);
180    }
181}
182
183impl fmt::Display for Repr {
184    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185        write!(
186            f,
187            "IPv6 Fragment offset={} more={} ident={}",
188            self.frag_offset, self.more_frags, self.ident
189        )
190    }
191}
192
193#[cfg(test)]
194mod test {
195    use super::*;
196
197    // A Fragment Header with more fragments remaining
198    static BYTES_HEADER_MORE_FRAG: [u8; 6] = [0x0, 0x1, 0x0, 0x0, 0x30, 0x39];
199
200    // A Fragment Header with no more fragments remaining
201    static BYTES_HEADER_LAST_FRAG: [u8; 6] = [0xa, 0x0, 0x0, 0x1, 0x9, 0x32];
202
203    #[test]
204    fn test_check_len() {
205        // less than 6 bytes
206        assert_eq!(
207            Err(Error),
208            Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..5]).check_len()
209        );
210        // valid
211        assert_eq!(
212            Ok(()),
213            Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len()
214        );
215    }
216
217    #[test]
218    fn test_header_deconstruct() {
219        let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
220        assert_eq!(header.frag_offset(), 0);
221        assert!(header.more_frags());
222        assert_eq!(header.ident(), 12345);
223
224        let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG);
225        assert_eq!(header.frag_offset(), 320);
226        assert!(!header.more_frags());
227        assert_eq!(header.ident(), 67890);
228    }
229
230    #[test]
231    fn test_repr_parse_valid() {
232        let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
233        let repr = Repr::parse(&header).unwrap();
234        assert_eq!(
235            repr,
236            Repr {
237                frag_offset: 0,
238                more_frags: true,
239                ident: 12345
240            }
241        );
242
243        let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG);
244        let repr = Repr::parse(&header).unwrap();
245        assert_eq!(
246            repr,
247            Repr {
248                frag_offset: 320,
249                more_frags: false,
250                ident: 67890
251            }
252        );
253    }
254
255    #[test]
256    fn test_repr_emit() {
257        let repr = Repr {
258            frag_offset: 0,
259            more_frags: true,
260            ident: 12345,
261        };
262        let mut bytes = [0u8; 6];
263        let mut header = Header::new_unchecked(&mut bytes);
264        repr.emit(&mut header);
265        assert_eq!(header.into_inner(), &BYTES_HEADER_MORE_FRAG[0..6]);
266
267        let repr = Repr {
268            frag_offset: 320,
269            more_frags: false,
270            ident: 67890,
271        };
272        let mut bytes = [0u8; 6];
273        let mut header = Header::new_unchecked(&mut bytes);
274        repr.emit(&mut header);
275        assert_eq!(header.into_inner(), &BYTES_HEADER_LAST_FRAG[0..6]);
276    }
277
278    #[test]
279    fn test_buffer_len() {
280        let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
281        let repr = Repr::parse(&header).unwrap();
282        assert_eq!(repr.buffer_len(), BYTES_HEADER_MORE_FRAG.len());
283    }
284}