• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::super::*;
2 use crate::err::ipv6_exts::ExtPayloadLenError;
3 use arrayvec::ArrayVec;
4 use core::fmt::{Debug, Formatter};
5 
6 /// Deprecated. Use [Ipv6RawExtHeader] instead.
7 #[deprecated(
8     since = "0.14.0",
9     note = "Please use the type Ipv6RawExtHeader instead"
10 )]
11 pub type Ipv6RawExtensionHeader = Ipv6RawExtHeader;
12 
13 /// Raw IPv6 extension header (undecoded payload).
14 ///
15 /// IPv6 extension header with only minimal data interpretation. NOTE only ipv6 header
16 /// extensions with the first two bytes representing the next header and the header length
17 /// in 8-octets (- 8 octets) can be represented with this struct. This excludes the "Authentication
18 /// Header" (AH) and "Encapsulating Security Payload" (ESP).
19 ///
20 /// The following headers can be represented in a [`Ipv6RawExtHeader`]:
21 /// * Hop by Hop
22 /// * Destination Options
23 /// * Routing
24 /// * Mobility
25 /// * Host Identity Protocol
26 /// * Shim6 Protocol
27 #[derive(Clone)]
28 pub struct Ipv6RawExtHeader {
29     /// IP protocol number specifying the next header or transport layer protocol.
30     ///
31     /// See [IpNumber] or [ip_number] for a definition of the known values.
32     pub next_header: IpNumber,
33     /// Length of the extension header in 8 octets (minus the first 8 octets).
34     header_length: u8,
35     //// The data contained in the extension header (excluding next_header & hdr length).
36     payload_buffer: [u8; 0xff * 8 + 6],
37 }
38 
39 impl Debug for Ipv6RawExtHeader {
fmt(&self, f: &mut Formatter) -> Result<(), core::fmt::Error>40     fn fmt(&self, f: &mut Formatter) -> Result<(), core::fmt::Error> {
41         let mut s = f.debug_struct("Ipv6RawExtHeader");
42         s.field("next_header", &self.next_header);
43         s.field("payload", &self.payload());
44         s.finish()
45     }
46 }
47 
48 impl PartialEq for Ipv6RawExtHeader {
eq(&self, other: &Self) -> bool49     fn eq(&self, other: &Self) -> bool {
50         self.next_header == other.next_header && self.payload() == other.payload()
51     }
52 }
53 
54 impl Eq for Ipv6RawExtHeader {}
55 
56 impl Default for Ipv6RawExtHeader {
default() -> Self57     fn default() -> Self {
58         Ipv6RawExtHeader {
59             next_header: IpNumber(255),
60             header_length: 0,
61             payload_buffer: [0; 0xff * 8 + 6],
62         }
63     }
64 }
65 
66 impl Ipv6RawExtHeader {
67     /// Minimum length of an raw IPv6 extension header in bytes/octets.
68     pub const MIN_LEN: usize = 8;
69 
70     /// Maximum length of an raw IPv6 extension header in bytes/octets.
71     ///
72     /// This number is calculated by multiplying the maximum "hdr ext len"
73     /// (0xff) with 8 and adding 8. As RFC8200 states that "hdr ext len" is
74     /// defined as "8-bit unsigned integer. Length of the Hop-by-Hop Options
75     /// header in 8-octet units, not including the first 8 octets."
76     pub const MAX_LEN: usize = 8 + (8 * 0xff);
77 
78     /// Minimum length of a [Ipv6RawExtHeader] payload
79     pub const MIN_PAYLOAD_LEN: usize = 6;
80 
81     /// Maximum length of a [Ipv6RawExtHeader] the payload
82     pub const MAX_PAYLOAD_LEN: usize = 0xff * 8 + 6;
83 
84     /// Returns true if the given header type ip number can be represented in an `Ipv6ExtensionHeader`.
header_type_supported(next_header: IpNumber) -> bool85     pub fn header_type_supported(next_header: IpNumber) -> bool {
86         use crate::ip_number::*;
87         matches!(
88             next_header,
89             IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6
90         )
91     }
92 
93     /// Creates an generic IPv6 extension header with the given data.
94     ///
95     /// # Arguments
96     ///
97     /// * `next_header` - type of content after this header (protocol number)
98     /// * `payload` - slice containing the data of the header. This must NOT contain the `next header` and `extended header length` fields of the header.
99     ///
100     /// Note that `payload` must have at least the length of 6 bytes and only supports
101     /// length increases in steps of 8. This measn that the following expression must be true `(payload.len() + 2) % 8 == 0`.
102     /// The maximum length of the payload is `2046` bytes ([`Ipv6RawExtHeader::MAX_PAYLOAD_LEN`]).
103     ///
104     /// If a payload with a non supported length is provided a [`crate::err::ipv6_exts::ExtPayloadLenError`] is returned.
new_raw( next_header: IpNumber, payload: &[u8], ) -> Result<Ipv6RawExtHeader, ExtPayloadLenError>105     pub fn new_raw(
106         next_header: IpNumber,
107         payload: &[u8],
108     ) -> Result<Ipv6RawExtHeader, ExtPayloadLenError> {
109         use ExtPayloadLenError::*;
110         if payload.len() < Self::MIN_PAYLOAD_LEN {
111             Err(TooSmall(payload.len()))
112         } else if payload.len() > Self::MAX_PAYLOAD_LEN {
113             Err(TooBig(payload.len()))
114         } else if 0 != (payload.len() + 2) % 8 {
115             Err(Unaligned(payload.len()))
116         } else {
117             let mut result = Ipv6RawExtHeader {
118                 next_header,
119                 header_length: ((payload.len() - 6) / 8) as u8,
120                 payload_buffer: [0; Self::MAX_PAYLOAD_LEN],
121             };
122             result.payload_buffer[..payload.len()].copy_from_slice(payload);
123             Ok(result)
124         }
125     }
126 
127     /// Read an Ipv6ExtensionHeader from a slice and return the header & unused parts of the slice.
from_slice(slice: &[u8]) -> Result<(Ipv6RawExtHeader, &[u8]), err::LenError>128     pub fn from_slice(slice: &[u8]) -> Result<(Ipv6RawExtHeader, &[u8]), err::LenError> {
129         let s = Ipv6RawExtHeaderSlice::from_slice(slice)?;
130         let rest = &slice[s.slice().len()..];
131         let header = s.to_header();
132         Ok((header, rest))
133     }
134 
135     /// Return a slice containing the current payload. This does NOT contain
136     /// the `next_header` and `header_length` fields. But everything after these
137     /// two fields.
payload(&self) -> &[u8]138     pub fn payload(&self) -> &[u8] {
139         &self.payload_buffer[..(6 + usize::from(self.header_length) * 8)]
140     }
141 
142     /// Sets the payload (content of the header after the `next_header` & `header_length` fields).
143     ///
144     /// Note that `payload` must have at least the length of 6 bytes and only supports
145     /// length increases in steps of 8. This measn that the following expression must be true `(payload.len() + 2) % 8 == 0`.
146     /// The maximum length of the payload is `2046` bytes ([`crate::Ipv6RawExtHeader::MAX_PAYLOAD_LEN`]).
147     ///
148     /// If a payload with a non supported length is provided a [`crate::err::ipv6_exts::ExtPayloadLenError`] is returned and the payload of the header is not changed.
set_payload(&mut self, payload: &[u8]) -> Result<(), ExtPayloadLenError>149     pub fn set_payload(&mut self, payload: &[u8]) -> Result<(), ExtPayloadLenError> {
150         use ExtPayloadLenError::*;
151         if payload.len() < Self::MIN_PAYLOAD_LEN {
152             Err(TooSmall(payload.len()))
153         } else if payload.len() > Self::MAX_PAYLOAD_LEN {
154             Err(TooBig(payload.len()))
155         } else if 0 != (payload.len() + 2) % 8 {
156             Err(Unaligned(payload.len()))
157         } else {
158             self.payload_buffer[..payload.len()].copy_from_slice(payload);
159             self.header_length = ((payload.len() - 6) / 8) as u8;
160             Ok(())
161         }
162     }
163 
164     /// Read an fragment header from the current reader position.
165     #[cfg(feature = "std")]
166     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
read<T: std::io::Read + std::io::Seek + Sized>( reader: &mut T, ) -> Result<Ipv6RawExtHeader, std::io::Error>167     pub fn read<T: std::io::Read + std::io::Seek + Sized>(
168         reader: &mut T,
169     ) -> Result<Ipv6RawExtHeader, std::io::Error> {
170         let (next_header, header_length) = {
171             let mut d: [u8; 2] = [0; 2];
172             reader.read_exact(&mut d)?;
173             (IpNumber(d[0]), d[1])
174         };
175 
176         Ok(Ipv6RawExtHeader {
177             next_header,
178             header_length,
179             payload_buffer: {
180                 let mut buffer = [0; 0xff * 8 + 6];
181                 reader.read_exact(&mut buffer[..usize::from(header_length) * 8 + 6])?;
182                 buffer
183             },
184         })
185     }
186 
187     /// Read an fragment header from the current limited reader position.
188     #[cfg(feature = "std")]
189     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
read_limited<T: std::io::Read + std::io::Seek + Sized>( reader: &mut crate::io::LimitedReader<T>, ) -> Result<Ipv6RawExtHeader, err::io::LimitedReadError>190     pub fn read_limited<T: std::io::Read + std::io::Seek + Sized>(
191         reader: &mut crate::io::LimitedReader<T>,
192     ) -> Result<Ipv6RawExtHeader, err::io::LimitedReadError> {
193         // set layer start
194         reader.start_layer(err::Layer::Ipv6ExtHeader);
195 
196         // read next & len
197         let (next_header, header_length) = {
198             let mut d: [u8; 2] = [0; 2];
199             reader.read_exact(&mut d)?;
200             (IpNumber(d[0]), d[1])
201         };
202 
203         Ok(Ipv6RawExtHeader {
204             next_header,
205             header_length,
206             payload_buffer: {
207                 let mut buffer = [0; 0xff * 8 + 6];
208                 reader.read_exact(&mut buffer[..usize::from(header_length) * 8 + 6])?;
209                 buffer
210             },
211         })
212     }
213 
214     /// Writes a given IPv6 extension header to the current position.
215     #[cfg(feature = "std")]
216     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
write<W: std::io::Write + Sized>(&self, writer: &mut W) -> Result<(), std::io::Error>217     pub fn write<W: std::io::Write + Sized>(&self, writer: &mut W) -> Result<(), std::io::Error> {
218         writer.write_all(&[self.next_header.0, self.header_length])?;
219         writer.write_all(self.payload())?;
220         Ok(())
221     }
222 
223     /// Returns the serialized header.
to_bytes(&self) -> ArrayVec<u8,224     pub fn to_bytes(&self) -> ArrayVec<u8, { Ipv6RawExtHeader::MAX_LEN }> {
225         let mut result = ArrayVec::new();
226         result.extend([self.next_header.0, self.header_length]);
227         // Unwrap Panic Safety:
228         // The following unwrap should never panic, as
229         // the payload length can at most have the size max
230         // header length - 2 and as the internal buffer used to
231         // store the payload data has exactly this size.
232         result.try_extend_from_slice(self.payload()).unwrap();
233         result
234     }
235 
236     /// Length of the header in bytes.
header_len(&self) -> usize237     pub fn header_len(&self) -> usize {
238         2 + (6 + usize::from(self.header_length) * 8)
239     }
240 }
241 
242 #[cfg(test)]
243 mod test {
244     use super::*;
245     use crate::test_gens::*;
246     use alloc::{format, vec::Vec};
247     use proptest::prelude::*;
248     use std::io::Cursor;
249 
250     #[test]
default()251     fn default() {
252         let default_header = Ipv6RawExtHeader {
253             ..Default::default()
254         };
255 
256         assert_eq!(default_header.next_header, IpNumber(255));
257         assert_eq!(default_header.header_length, 0);
258         assert_eq!(default_header.payload_buffer, [0; 0xff * 8 + 6])
259     }
260 
261     proptest! {
262         #[test]
263         fn debug(header in ipv6_raw_ext_any()) {
264             assert_eq!(
265                 format!("{:?}", header),
266                 format!(
267                     "Ipv6RawExtHeader {{ next_header: {:?}, payload: {:?} }}",
268                     header.next_header,
269                     header.payload()
270                 )
271             );
272         }
273     }
274 
275     proptest! {
276         #[test]
277         fn clone_eq(header in ipv6_raw_ext_any()) {
278             assert_eq!(header.clone(), header);
279         }
280     }
281 
282     #[test]
header_type_supported()283     fn header_type_supported() {
284         use ip_number::*;
285         for value in 0..=u8::MAX {
286             let expected_supported = match IpNumber(value) {
287                 IPV6_HOP_BY_HOP | IPV6_DEST_OPTIONS | IPV6_ROUTE | MOBILITY | HIP | SHIM6 => true,
288                 _ => false,
289             };
290             assert_eq!(
291                 expected_supported,
292                 Ipv6RawExtHeader::header_type_supported(IpNumber(value))
293             );
294         }
295     }
296 
297     proptest! {
298         #[test]
299         fn new_raw(header in ipv6_raw_ext_any()) {
300             use ExtPayloadLenError::*;
301 
302             // ok
303             {
304                 let actual = Ipv6RawExtHeader::new_raw(header.next_header, header.payload()).unwrap();
305                 assert_eq!(actual.next_header, header.next_header);
306                 assert_eq!(actual.payload(), header.payload());
307             }
308 
309             // smaller then minimum
310             for len in 0..Ipv6RawExtHeader::MIN_PAYLOAD_LEN {
311                 assert_eq!(
312                     Ipv6RawExtHeader::new_raw(header.next_header, &header.payload()[..len]).unwrap_err(),
313                     TooSmall(len)
314                 );
315             }
316 
317             // bigger then maximum
318             {
319                 let bytes = [0u8;Ipv6RawExtHeader::MAX_PAYLOAD_LEN + 1];
320                 assert_eq!(
321                     Ipv6RawExtHeader::new_raw(header.next_header, &bytes).unwrap_err(),
322                     TooBig(bytes.len())
323                 );
324             }
325 
326             // non aligned payload
327             {
328                 let mut bytes = header.to_bytes();
329                 bytes.pop().unwrap();
330                 bytes.pop().unwrap();
331 
332                 for offset in 1..8 {
333                     if offset + header.header_len() < Ipv6RawExtHeader::MAX_LEN {
334                         bytes.push(0);
335                         assert_eq!(
336                             Ipv6RawExtHeader::new_raw(header.next_header, &bytes).unwrap_err(),
337                             Unaligned(bytes.len())
338                         );
339                     }
340                 }
341             }
342         }
343     }
344 
345     proptest! {
346         #[test]
347         fn from_slice(header in ipv6_raw_ext_any()) {
348             // ok
349             {
350                 let mut bytes = Vec::with_capacity(header.header_len() + 2);
351                 bytes.extend_from_slice(&header.to_bytes());
352                 bytes.push(1);
353                 bytes.push(2);
354 
355                 let (actual_header, actual_rest) = Ipv6RawExtHeader::from_slice(&bytes).unwrap();
356                 assert_eq!(actual_header, header);
357                 assert_eq!(actual_rest, &[1, 2]);
358             }
359 
360             // length error
361             {
362                 let bytes = header.to_bytes();
363                 for len in 0..bytes.len() {
364                     assert_eq!(
365                         Ipv6RawExtHeader::from_slice(&bytes[..len]).unwrap_err(),
366                         err::LenError{
367                             required_len: if len < Ipv6RawExtHeader::MIN_LEN {
368                                 Ipv6RawExtHeader::MIN_LEN
369                             } else {
370                                 header.header_len()
371                             },
372                             len: len,
373                             len_source: LenSource::Slice,
374                             layer: err::Layer::Ipv6ExtHeader,
375                             layer_start_offset: 0,
376                         }
377                     );
378                 }
379             }
380         }
381     }
382 
383     proptest! {
384         #[test]
385         fn set_payload(
386             header_a in ipv6_raw_ext_any(),
387             header_b in ipv6_raw_ext_any()
388         ) {
389             use ExtPayloadLenError::*;
390             // ok
391             {
392                 let mut actual = header_a.clone();
393                 actual.set_payload(header_b.payload()).unwrap();
394                 assert_eq!(actual.payload(), header_b.payload());
395             }
396 
397             // smaller then minimum
398             for len in 0..Ipv6RawExtHeader::MIN_PAYLOAD_LEN {
399                 let mut actual = header_a.clone();
400                 assert_eq!(
401                     actual.set_payload(&header_b.payload()[..len]).unwrap_err(),
402                     TooSmall(len)
403                 );
404                 assert_eq!(actual.payload(), header_a.payload());
405             }
406 
407             // bigger then maximum
408             {
409                 let bytes = [0u8;Ipv6RawExtHeader::MAX_PAYLOAD_LEN + 1];
410                 let mut actual = header_a.clone();
411                 assert_eq!(
412                     actual.set_payload(&bytes).unwrap_err(),
413                     TooBig(bytes.len())
414                 );
415             }
416 
417             // non aligned payload
418             {
419                 let mut bytes = header_b.to_bytes();
420                 bytes.pop().unwrap();
421                 bytes.pop().unwrap();
422 
423                 for offset in 1..8 {
424                     if offset + header_b.header_len() < Ipv6RawExtHeader::MAX_LEN {
425                         bytes.push(0);
426                         let mut actual = header_a.clone();
427                         assert_eq!(
428                             actual.set_payload(&bytes).unwrap_err(),
429                             Unaligned(bytes.len())
430                         );
431                     }
432                 }
433             }
434         }
435     }
436 
437     proptest! {
438         #[test]
439         fn read(header in ipv6_raw_ext_any()) {
440             // ok
441             {
442                 let bytes = header.to_bytes();
443                 let mut cursor = Cursor::new(&bytes[..]);
444                 let actual = Ipv6RawExtHeader::read(&mut cursor).unwrap();
445                 assert_eq!(actual, header);
446             }
447 
448             // length error
449             {
450                 let bytes = header.to_bytes();
451                 for len in 0..bytes.len() {
452                     let mut cursor = Cursor::new(&bytes[..len]);
453                     assert!(Ipv6RawExtHeader::read(&mut cursor).is_err());
454                 }
455             }
456         }
457     }
458 
459     proptest! {
460         #[test]
461         fn write(header in ipv6_raw_ext_any()) {
462             // ok case
463             {
464                 let mut buffer = [0u8;Ipv6RawExtHeader::MAX_LEN];
465                 let len = {
466                     let mut cursor = Cursor::new(&mut buffer[..]);
467                     header.write(&mut cursor).unwrap();
468                     cursor.position() as usize
469                 };
470                 let (dec_header, dec_rest) = Ipv6RawExtHeader::from_slice(&buffer[..len]).unwrap();
471                 assert_eq!(header, dec_header);
472                 assert_eq!(dec_rest, &[]);
473             }
474 
475             // length error
476             for len in 0..header.header_len() {
477                 let mut buffer = [0u8;Ipv6RawExtHeader::MAX_LEN];
478                 let mut cursor = Cursor::new(&mut buffer[..len]);
479                 assert!(header.write(&mut cursor).is_err());
480             }
481         }
482     }
483 
484     proptest! {
485         #[test]
486         fn to_bytes(header in ipv6_raw_ext_any()) {
487             let bytes = header.to_bytes();
488             assert_eq!(bytes[0], header.next_header.0);
489             assert_eq!(bytes[1], header.header_length);
490             assert_eq!(&bytes[2..], header.payload());
491         }
492     }
493 
494     proptest! {
495         #[test]
496         fn header_len(header in ipv6_raw_ext_any()) {
497             assert_eq!(header.header_len(), header.to_bytes().len());
498         }
499     }
500 }
501