etherparse/net/
ipv6_raw_ext_header_slice.rs

1use crate::*;
2use core::slice::from_raw_parts;
3
4/// Deprecated. Use [Ipv6RawExtHeaderSlice] instead.
5#[deprecated(
6    since = "0.14.0",
7    note = "Please use the type Ipv6RawExtHeaderSlice instead"
8)]
9pub type Ipv6RawExtensionHeaderSlice<'a> = Ipv6RawExtHeaderSlice<'a>;
10
11/// Slice containing an IPv6 extension header without specific decoding methods (fallback in case no specific implementation is available).
12///
13/// Slice containing an IPv6 extension header with only minimal data interpretation. NOTE only ipv6 header
14/// extensions with the first two bytes representing the next header and the header length
15/// in 8-octets (- 8 octets) can be represented with this struct. This excludes the "Authentication
16/// Header" (AH) and "Encapsulating Security Payload" (ESP).
17///
18/// The following headers can be represented in a Ipv6ExtensionHeaderSlice:
19/// * HopbyHop
20/// * Destination Options
21/// * Routing
22/// * Mobility
23/// * Host Identity Protocol
24/// * Shim6 Protocol
25#[derive(Clone, Debug, Eq, PartialEq)]
26pub struct Ipv6RawExtHeaderSlice<'a> {
27    /// Slice containing the packet data.
28    slice: &'a [u8],
29}
30
31impl<'a> Ipv6RawExtHeaderSlice<'a> {
32    /// Returns true if the given header type ip number can be represented in an `Ipv6ExtensionHeaderSlice`.
33    pub fn header_type_supported(next_header: IpNumber) -> bool {
34        Ipv6RawExtHeader::header_type_supported(next_header)
35    }
36
37    /// Creates a generic ipv6 extension header slice from a slice.
38    pub fn from_slice(slice: &'a [u8]) -> Result<Ipv6RawExtHeaderSlice<'a>, err::LenError> {
39        //check length
40        if slice.len() < 8 {
41            return Err(err::LenError {
42                required_len: 8,
43                len: slice.len(),
44                len_source: LenSource::Slice,
45                layer: err::Layer::Ipv6ExtHeader,
46                layer_start_offset: 0,
47            });
48        }
49
50        //check length
51        let len = ((slice[1] as usize) + 1) * 8;
52
53        //check the length again now that the expected length is known
54        if slice.len() < len {
55            return Err(err::LenError {
56                required_len: len,
57                len: slice.len(),
58                len_source: LenSource::Slice,
59                layer: err::Layer::Ipv6ExtHeader,
60                layer_start_offset: 0,
61            });
62        }
63
64        //all good
65        Ok(Ipv6RawExtHeaderSlice {
66            // SAFETY:
67            // Safe as the slice has been checked in the previous if
68            // to have at least the the length of the variable len.
69            slice: unsafe { from_raw_parts(slice.as_ptr(), len) },
70        })
71    }
72
73    /// Creates a raw ipv6 extension header slice from a slice (assumes slice
74    /// size & content was validated before).
75    ///
76    /// # Safety
77    ///
78    /// This method assumes that the slice was previously validated to contain
79    /// a valid & supported raw ipv6 extension header. This means the slice length
80    /// must at least be at least 8 and `(slice[1] + 1)*8`. The data that the
81    /// slice points must also be valid (meaning no nullptr or alike allowed).
82    ///
83    /// If these preconditions are not fulfilled the behavior of this function
84    /// and the methods of the return [`IpAuthHeaderSlice`] will be undefined.
85    pub unsafe fn from_slice_unchecked(slice: &'a [u8]) -> Ipv6RawExtHeaderSlice<'a> {
86        Ipv6RawExtHeaderSlice {
87            slice: from_raw_parts(slice.as_ptr(), ((*slice.get_unchecked(1) as usize) + 1) * 8),
88        }
89    }
90
91    /// Returns the slice containing the ipv6 extension header
92    #[inline]
93    pub fn slice(&self) -> &'a [u8] {
94        self.slice
95    }
96
97    /// Returns the IP protocol number of the next header or transport layer protocol.
98    ///
99    /// See [IpNumber] or [ip_number] for a definition of the known values.
100    #[inline]
101    pub fn next_header(&self) -> IpNumber {
102        IpNumber(unsafe { *self.slice.get_unchecked(0) })
103    }
104
105    /// Returns a slice containing the payload data of the header.
106    ///
107    /// This contains all the data after the header length field
108    /// until the end of the header (length specified by the
109    /// hdr ext length field).
110    #[inline]
111    pub fn payload(&self) -> &'a [u8] {
112        unsafe { from_raw_parts(self.slice.as_ptr().add(2), self.slice.len() - 2) }
113    }
114
115    /// Convert the slice to an [Ipv6RawExtHeader].
116    ///
117    /// Decode some of the fields and copy the results to a
118    /// [Ipv6RawExtHeader] struct together with a slice pointing
119    /// to the non decoded parts.
120    pub fn to_header(&self) -> Ipv6RawExtHeader {
121        Ipv6RawExtHeader::new_raw(self.next_header(), self.payload()).unwrap()
122    }
123}
124
125#[cfg(test)]
126mod test {
127    use crate::{test_gens::*, *};
128    use alloc::{format, vec::Vec};
129    use proptest::prelude::*;
130
131    proptest! {
132        #[test]
133        fn debug(header in ipv6_raw_ext_any()) {
134            let bytes = header.to_bytes();
135            let slice = Ipv6RawExtHeaderSlice::from_slice(&bytes).unwrap();
136            assert_eq!(
137                format!("{:?}", slice),
138                format!("Ipv6RawExtHeaderSlice {{ slice: {:?} }}", slice.slice())
139            );
140        }
141    }
142
143    proptest! {
144        #[test]
145        fn clone_eq(header in ipv6_raw_ext_any()) {
146            let bytes = header.to_bytes();
147            let slice = Ipv6RawExtHeaderSlice::from_slice(&bytes).unwrap();
148            assert_eq!(slice.clone(), slice);
149        }
150    }
151
152    #[test]
153    fn header_type_supported() {
154        use ip_number::*;
155        for value in 0..=u8::MAX {
156            let expected_supported = match IpNumber(value) {
157                IPV6_HOP_BY_HOP | IPV6_DEST_OPTIONS | IPV6_ROUTE | MOBILITY | HIP | SHIM6 => true,
158                _ => false,
159            };
160            assert_eq!(
161                expected_supported,
162                Ipv6RawExtHeaderSlice::header_type_supported(IpNumber(value))
163            );
164        }
165    }
166
167    proptest! {
168        #[test]
169        fn from_slice(header in ipv6_raw_ext_any()) {
170            // ok
171            {
172                let mut bytes = Vec::with_capacity(header.header_len() + 2);
173                bytes.extend_from_slice(&header.to_bytes());
174                bytes.push(1);
175                bytes.push(2);
176
177                let (actual_header, actual_rest) = Ipv6RawExtHeader::from_slice(&bytes).unwrap();
178                assert_eq!(actual_header, header);
179                assert_eq!(actual_rest, &[1, 2]);
180            }
181
182            // length error
183            {
184                let bytes = header.to_bytes();
185                for len in 0..bytes.len() {
186                    assert_eq!(
187                        Ipv6RawExtHeader::from_slice(&bytes[..len]).unwrap_err(),
188                        err::LenError{
189                            required_len: if len < Ipv6RawExtHeader::MIN_LEN {
190                                Ipv6RawExtHeader::MIN_LEN
191                            } else {
192                                header.header_len()
193                            },
194                            len: len,
195                            len_source: LenSource::Slice,
196                            layer: err::Layer::Ipv6ExtHeader,
197                            layer_start_offset: 0,
198                        }
199                    );
200                }
201            }
202        }
203    }
204
205    proptest! {
206        #[test]
207        fn from_slice_unchecked(header in ipv6_raw_ext_any()) {
208            let bytes = header.to_bytes();
209            let slice = unsafe {
210                Ipv6RawExtHeaderSlice::from_slice_unchecked(&bytes)
211            };
212            assert_eq!(&bytes[..], slice.slice());
213        }
214    }
215
216    proptest! {
217        #[test]
218        fn getters(header in ipv6_raw_ext_any()) {
219            let bytes = header.to_bytes();
220            let slice = Ipv6RawExtHeaderSlice::from_slice(&bytes).unwrap();
221            assert_eq!(slice.next_header(), header.next_header);
222            assert_eq!(slice.payload(), header.payload());
223        }
224    }
225
226    proptest! {
227        #[test]
228        fn to_header(header in ipv6_raw_ext_any()) {
229            let bytes = header.to_bytes();
230            let slice = Ipv6RawExtHeaderSlice::from_slice(&bytes).unwrap();
231            assert_eq!(header, slice.to_header());
232        }
233    }
234}