• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::*;
2 
3 /// A slice containing an ICMPv6 network package.
4 ///
5 /// Struct allows the selective read of fields in the ICMPv6
6 /// packet.
7 #[derive(Clone, Debug, Eq, PartialEq)]
8 pub struct Icmpv6Slice<'a> {
9     pub(crate) slice: &'a [u8],
10 }
11 
12 impl<'a> Icmpv6Slice<'a> {
13     /// Creates a slice containing an ICMPv6 packet.
14     ///
15     /// # Errors
16     ///
17     /// The function will return an `Err` [`err::LenError`]
18     /// if the given slice is too small (smaller then [`Icmpv6Header::MIN_LEN`]) or
19     /// too large (bigger then [`icmpv6::MAX_ICMPV6_BYTE_LEN`]).
20     #[inline]
from_slice(slice: &'a [u8]) -> Result<Icmpv6Slice<'a>, err::LenError>21     pub fn from_slice(slice: &'a [u8]) -> Result<Icmpv6Slice<'a>, err::LenError> {
22         //check length
23         if slice.len() < Icmpv6Header::MIN_LEN {
24             return Err(err::LenError {
25                 required_len: Icmpv6Header::MIN_LEN,
26                 len: slice.len(),
27                 len_source: LenSource::Slice,
28                 layer: err::Layer::Icmpv6,
29                 layer_start_offset: 0,
30             });
31         }
32         if slice.len() > icmpv6::MAX_ICMPV6_BYTE_LEN {
33             return Err(err::LenError {
34                 required_len: icmpv6::MAX_ICMPV6_BYTE_LEN,
35                 len: slice.len(),
36                 len_source: LenSource::Slice,
37                 layer: err::Layer::Icmpv6,
38                 layer_start_offset: 0,
39             });
40         }
41 
42         //done
43         Ok(Icmpv6Slice { slice })
44     }
45 
46     /// Decode the header fields and copy the results to a [`Icmpv6Header`] struct.
47     #[inline]
header(&self) -> Icmpv6Header48     pub fn header(&self) -> Icmpv6Header {
49         Icmpv6Header {
50             icmp_type: self.icmp_type(),
51             checksum: self.checksum(),
52         }
53     }
54 
55     /// Number of bytes/octets that will be converted into a
56     /// [`Icmpv6Header`] when [`Icmpv6Slice::header`] gets called.
57     #[inline]
header_len(&self) -> usize58     pub fn header_len(&self) -> usize {
59         8
60     }
61 
62     /// Decode the header values (excluding the checksum) into an [`Icmpv6Type`] enum.
icmp_type(&self) -> Icmpv6Type63     pub fn icmp_type(&self) -> Icmpv6Type {
64         use crate::{icmpv6::*, Icmpv6Type::*};
65 
66         match self.type_u8() {
67             TYPE_DST_UNREACH => {
68                 if let Some(code) = DestUnreachableCode::from_u8(self.code_u8()) {
69                     return DestinationUnreachable(code);
70                 }
71             }
72             TYPE_PACKET_TOO_BIG => {
73                 if 0 == self.code_u8() {
74                     return PacketTooBig {
75                         mtu: u32::from_be_bytes(self.bytes5to8()),
76                     };
77                 }
78             }
79             TYPE_TIME_EXCEEDED => {
80                 if let Some(code) = TimeExceededCode::from_u8(self.code_u8()) {
81                     return TimeExceeded(code);
82                 }
83             }
84             TYPE_PARAMETER_PROBLEM => {
85                 if let Some(code) = ParameterProblemCode::from_u8(self.code_u8()) {
86                     return ParameterProblem(ParameterProblemHeader {
87                         code,
88                         pointer: u32::from_be_bytes(self.bytes5to8()),
89                     });
90                 }
91             }
92             TYPE_ECHO_REQUEST => {
93                 if 0 == self.code_u8() {
94                     return EchoRequest(IcmpEchoHeader::from_bytes(self.bytes5to8()));
95                 }
96             }
97             TYPE_ECHO_REPLY => {
98                 if 0 == self.code_u8() {
99                     return EchoReply(IcmpEchoHeader::from_bytes(self.bytes5to8()));
100                 }
101             }
102             _ => {}
103         }
104         Unknown {
105             type_u8: self.type_u8(),
106             code_u8: self.code_u8(),
107             bytes5to8: self.bytes5to8(),
108         }
109     }
110 
111     /// Returns "type" value in the ICMPv6 header.
112     #[inline]
type_u8(&self) -> u8113     pub fn type_u8(&self) -> u8 {
114         // SAFETY:
115         // Safe as the contructor checks that the slice has
116         // at least the length of Icmpv6Header::MIN_LEN (8).
117         unsafe { *self.slice.get_unchecked(0) }
118     }
119 
120     /// Returns "code" value in the ICMPv6 header.
121     #[inline]
code_u8(&self) -> u8122     pub fn code_u8(&self) -> u8 {
123         // SAFETY:
124         // Safe as the contructor checks that the slice has
125         // at least the length of Icmpv6Header::MIN_LEN (8).
126         unsafe { *self.slice.get_unchecked(1) }
127     }
128 
129     /// Returns "checksum" value in the ICMPv6 header.
130     #[inline]
checksum(&self) -> u16131     pub fn checksum(&self) -> u16 {
132         // SAFETY:
133         // Safe as the contructor checks that the slice has
134         // at least the length of Icmpv6Header::MIN_LEN  (8).
135         unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
136     }
137 
138     /// Returns if the checksum in the slice is correct.
is_checksum_valid(&self, source_ip: [u8; 16], destination_ip: [u8; 16]) -> bool139     pub fn is_checksum_valid(&self, source_ip: [u8; 16], destination_ip: [u8; 16]) -> bool {
140         // NOTE: rfc4443 section 2.3 - Icmp6 *does* use a pseudoheader,
141         // unlike Icmp4
142         checksum::Sum16BitWords::new()
143             .add_16bytes(source_ip)
144             .add_16bytes(destination_ip)
145             .add_4bytes((self.slice().len() as u32).to_be_bytes())
146             .add_2bytes([0, ip_number::IPV6_ICMP.0])
147             // NOTE: From RFC 1071
148             // To check a checksum, the 1's complement sum is computed over the
149             // same set of octets, including the checksum field.  If the result
150             // is all 1 bits (-0 in 1's complement arithmetic), the check
151             // succeeds.
152             .add_slice(self.slice)
153             .ones_complement()
154             == 0
155     }
156 
157     /// Returns the bytes from position 4 till and including the 8th position
158     /// in the ICMPv6 header.
159     ///
160     /// These bytes located at th 5th, 6th, 7th and 8th position of the ICMP
161     /// packet can depending on the ICMPv6 type and code contain additional data.
162     #[inline]
bytes5to8(&self) -> [u8; 4]163     pub fn bytes5to8(&self) -> [u8; 4] {
164         // SAFETY:
165         // Safe as the contructor checks that the slice has
166         // at least the length of Icmpv6Header::MIN_LEN  (8).
167         unsafe {
168             [
169                 *self.slice.get_unchecked(4),
170                 *self.slice.get_unchecked(5),
171                 *self.slice.get_unchecked(6),
172                 *self.slice.get_unchecked(7),
173             ]
174         }
175     }
176 
177     /// Returns the slice containing the ICMPv6 packet.
178     #[inline]
slice(&self) -> &'a [u8]179     pub fn slice(&self) -> &'a [u8] {
180         self.slice
181     }
182 
183     /// Returns a slice to the bytes not covered by `.header()`.
184     #[inline]
payload(&self) -> &'a [u8]185     pub fn payload(&self) -> &'a [u8] {
186         // SAFETY:
187         // Safe as the contructor checks that the slice has
188         // at least the length of Icmpv6Header::MIN_LEN(8).
189         unsafe { core::slice::from_raw_parts(self.slice.as_ptr().add(8), self.slice.len() - 8) }
190     }
191 }
192 
193 #[cfg(test)]
194 mod test {
195     use crate::{icmpv6::*, test_gens::*, Icmpv6Type::*, *};
196     use alloc::{format, vec::Vec};
197     use proptest::prelude::*;
198 
199     proptest! {
200         #[test]
201         fn from_slice(slice in proptest::collection::vec(any::<u8>(), 8..1024)) {
202             // ok case
203             assert_eq!(Icmpv6Slice::from_slice(&slice[..]).unwrap().slice(), &slice[..]);
204 
205             // too small size error case
206             for len in 0..8 {
207                 assert_eq!(
208                     Icmpv6Slice::from_slice(&slice[..len]).unwrap_err(),
209                     err::LenError{
210                         required_len: Icmpv6Header::MIN_LEN,
211                         len: len,
212                         len_source: LenSource::Slice,
213                         layer: err::Layer::Icmpv6,
214                         layer_start_offset: 0,
215                     }
216                 );
217             }
218         }
219     }
220 
221     proptest! {
222         /// This error can only occur on systems with a pointer size
223         /// bigger then 64 bits.
224         #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
225         #[test]
226         fn from_slice_too_big_error(
227             bad_len in ((core::u32::MAX as usize) + 1)..=(core::isize::MAX as usize),
228         ) {
229             // too large packet error case
230             {
231                 // SAFETY: In case the error is not triggered
232                 //         a segmentation fault will be triggered.
233                 let too_big_slice = unsafe {
234                     //NOTE: The pointer must be initialized with a non null value
235                     //      otherwise a key constraint of slices is not fulfilled
236                     //      which can lead to crashes in release mode.
237                     use core::ptr::NonNull;
238                     core::slice::from_raw_parts(
239                         NonNull::<u8>::dangling().as_ptr(),
240                         bad_len
241                     )
242                 };
243                 assert_eq!(
244                     Icmpv6Slice::from_slice(too_big_slice).unwrap_err(),
245                     err::LenError{
246                         required_len: u32::MAX as usize,
247                         len: bad_len,
248                         len_source: LenSource::Slice,
249                         layer: err::Layer::Icmpv6,
250                         layer_start_offset: 0
251                     }
252                 );
253             }
254         }
255     }
256 
257     proptest! {
258         #[test]
259         fn header(
260             icmp_type in icmpv6_type_any(),
261             checksum in any::<u16>()
262         ) {
263             let expected = Icmpv6Header {
264                 icmp_type,
265                 checksum
266             };
267             assert_eq!(
268                 Icmpv6Slice::from_slice(&expected.to_bytes()).unwrap().header(),
269                 expected
270             );
271         }
272     }
273 
274     proptest! {
275         #[test]
276         fn icmp_type(
277             checksum in any::<[u8;2]>(),
278             bytes5to8 in any::<[u8;4]>()
279         ) {
280             use Icmpv6Type::*;
281 
282             let gen_bytes = |type_u8: u8, code_u8: u8| -> [u8;8] {
283                 [
284                     type_u8, code_u8, checksum[0], checksum[1],
285                     bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3]
286                 ]
287             };
288 
289             let assert_unknown = |type_u8: u8, code_u8: u8| {
290                 assert_eq!(
291                     Icmpv6Slice::from_slice(&gen_bytes(type_u8, code_u8)).unwrap().icmp_type(),
292                     Unknown{
293                         type_u8,
294                         code_u8,
295                         bytes5to8,
296                     }
297                 );
298             };
299 
300             // destination unreachable
301             {
302                 // known codes
303                 for (code, code_u8) in dest_unreachable_code_test_consts::VALID_VALUES {
304                     assert_eq!(
305                         Icmpv6Slice::from_slice(&gen_bytes(TYPE_DST_UNREACH, code_u8)).unwrap().icmp_type(),
306                         DestinationUnreachable(code)
307                     );
308                 }
309 
310                 // unknown codes
311                 for code_u8 in 7..=u8::MAX {
312                     assert_unknown(TYPE_DST_UNREACH, code_u8);
313                 }
314             }
315 
316             // packet too big
317             {
318                 // known code
319                 assert_eq!(
320                     Icmpv6Slice::from_slice(&gen_bytes(TYPE_PACKET_TOO_BIG, 0)).unwrap().icmp_type(),
321                     PacketTooBig {
322                         mtu: u32::from_be_bytes(bytes5to8)
323                     }
324                 );
325 
326                 // unknown code
327                 for code_u8 in 1..=u8::MAX {
328                     assert_unknown(TYPE_PACKET_TOO_BIG, code_u8);
329                 }
330             }
331 
332             // time exceeded
333             {
334                 // known codes
335                 for (code, code_u8) in time_exceeded_code_test_consts::VALID_VALUES {
336                     assert_eq!(
337                         Icmpv6Slice::from_slice(&gen_bytes(TYPE_TIME_EXCEEDED, code_u8)).unwrap().icmp_type(),
338                         TimeExceeded(code)
339                     );
340                 }
341 
342                 // unknown codes
343                 for code_u8 in 2..=u8::MAX {
344                     assert_unknown(TYPE_TIME_EXCEEDED, code_u8);
345                 }
346             }
347 
348             // parameter problem
349             {
350                 // known codes
351                 for (code, code_u8) in parameter_problem_code_test_consts::VALID_VALUES {
352                     assert_eq!(
353                         Icmpv6Slice::from_slice(&gen_bytes(TYPE_PARAMETER_PROBLEM, code_u8)).unwrap().icmp_type(),
354                         ParameterProblem(ParameterProblemHeader{
355                             code,
356                             pointer: u32::from_be_bytes(bytes5to8),
357                         })
358                     );
359                 }
360 
361                 // unknown codes
362                 for code_u8 in 11..=u8::MAX {
363                     assert_unknown(TYPE_PARAMETER_PROBLEM, code_u8);
364                 }
365             }
366 
367             // echo request
368             {
369                 // known code
370                 assert_eq!(
371                     Icmpv6Slice::from_slice(&gen_bytes(TYPE_ECHO_REQUEST, 0)).unwrap().icmp_type(),
372                     EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))
373                 );
374 
375                 // unknown codes
376                 for code_u8 in 1..=u8::MAX {
377                     assert_unknown(TYPE_ECHO_REPLY, code_u8);
378                 }
379             }
380 
381             // echo reply
382             {
383                 // known code
384                 assert_eq!(
385                     Icmpv6Slice::from_slice(&gen_bytes(TYPE_ECHO_REPLY, 0)).unwrap().icmp_type(),
386                     EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))
387                 );
388 
389                 // unknown codes
390                 for code_u8 in 1..=u8::MAX {
391                     assert_unknown(TYPE_ECHO_REPLY, code_u8);
392                 }
393             }
394         }
395     }
396 
397     proptest! {
398         #[test]
399         fn header_len(
400             code_u8 in any::<u8>(),
401             bytes5to8 in any::<[u8;4]>(),
402         ) {
403             let len_8_types = [
404                 DestinationUnreachable(DestUnreachableCode::Prohibited),
405                 PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), },
406                 TimeExceeded(TimeExceededCode::HopLimitExceeded),
407                 ParameterProblem(
408                     ParameterProblemHeader{
409                         code: ParameterProblemCode::OptionTooBig,
410                         pointer: u32::from_be_bytes(bytes5to8),
411                     }
412                 ),
413                 EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)),
414                 EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)),
415             ];
416 
417             for t in len_8_types {
418                 assert_eq!(
419                     t.header_len(),
420                     Icmpv6Slice::from_slice(
421                         &Icmpv6Header::new(t).to_bytes()
422                     ).unwrap().header_len()
423                 );
424             }
425 
426             for t in 0..=u8::MAX {
427                 let header = Icmpv6Header::new(
428                     Unknown{
429                         type_u8: t,
430                         code_u8,
431                         bytes5to8,
432                     }
433                 );
434                 assert_eq!(
435                     8,
436                     Icmpv6Slice::from_slice(
437                         &header.to_bytes()
438                     ).unwrap().header_len()
439                 );
440             }
441         }
442     }
443 
444     proptest! {
445         #[test]
446         fn type_u8(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
447             assert_eq!(
448                 Icmpv6Slice::from_slice(&slice[..]).unwrap().type_u8(),
449                 slice[0]
450             );
451         }
452     }
453 
454     proptest! {
455         #[test]
456         fn code_u8(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
457             assert_eq!(
458                 Icmpv6Slice::from_slice(&slice[..]).unwrap().code_u8(),
459                 slice[1]
460             );
461         }
462     }
463 
464     proptest! {
465         #[test]
466         fn checksum(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
467             assert_eq!(
468                 Icmpv6Slice::from_slice(&slice[..]).unwrap().checksum(),
469                 u16::from_be_bytes([slice[2], slice[3]])
470             );
471         }
472     }
473 
474     proptest! {
475         #[test]
476         fn is_checksum_valid(
477             ip_header in ipv6_any(),
478             icmp_type in icmpv6_type_any(),
479             payload in proptest::collection::vec(any::<u8>(), 0..1024),
480             flip_byte in 0usize..1032,
481         ) {
482             // generate slice with a correct checksum
483             let header = Icmpv6Header::with_checksum(icmp_type, ip_header.source, ip_header.destination, &payload).unwrap();
484             let bytes = {
485                 let mut bytes = Vec::with_capacity(header.header_len() + payload.len());
486                 header.write(&mut bytes).unwrap();
487                 bytes.extend_from_slice(&payload);
488                 bytes
489             };
490 
491             // check that the checksum gets reported as ok
492             assert!(
493                 Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination)
494             );
495 
496             // corrupt icmp packet
497             {
498                 let mut corrupted_bytes = bytes.clone();
499                 let i = flip_byte % corrupted_bytes.len();
500                 corrupted_bytes[i] = !corrupted_bytes[i];
501 
502                 assert_eq!(
503                     false,
504                     Icmpv6Slice::from_slice(&corrupted_bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination)
505                 );
506             }
507 
508             // corrupt ip source
509             {
510                 let mut corrupted_source = ip_header.source;
511                 let i = flip_byte % corrupted_source.len();
512                 corrupted_source[i] = !corrupted_source[i];
513 
514                 assert_eq!(
515                     false,
516                     Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(corrupted_source, ip_header.destination)
517                 );
518             }
519 
520             // corrupt ip destination
521             {
522                 let mut corrupted_dest = ip_header.destination;
523                 let i = flip_byte % corrupted_dest.len();
524                 corrupted_dest[i] = !corrupted_dest[i];
525 
526                 assert_eq!(
527                     false,
528                     Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(ip_header.source, corrupted_dest)
529                 );
530             }
531 
532             // corrupt length
533             {
534                 let mut larger_bytes = bytes.clone();
535                 larger_bytes.push(0);
536                 larger_bytes.push(0);
537 
538                 assert_eq!(
539                     false,
540                     Icmpv6Slice::from_slice(&larger_bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination)
541                 );
542             }
543         }
544     }
545 
546     proptest! {
547         #[test]
548         fn bytes5to8(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
549             assert_eq!(
550                 Icmpv6Slice::from_slice(&slice[..]).unwrap().bytes5to8(),
551                 [slice[4], slice[5], slice[6], slice[7]]
552             );
553         }
554     }
555 
556     proptest! {
557         #[test]
558         fn slice(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
559             assert_eq!(
560                 Icmpv6Slice::from_slice(&slice[..]).unwrap().slice(),
561                 &slice[..]
562             );
563         }
564     }
565 
566     proptest! {
567         #[test]
568         fn payload(
569             type_u8 in any::<u8>(),
570             code_u8 in any::<u8>(),
571             bytes5to8 in any::<[u8;4]>(),
572             payload in proptest::collection::vec(any::<u8>(), 8..16)
573         ) {
574             let len_8_types = [
575                 Unknown{
576                     type_u8,
577                     code_u8,
578                     bytes5to8,
579                 },
580                 DestinationUnreachable(DestUnreachableCode::Prohibited),
581                 PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), },
582                 TimeExceeded(TimeExceededCode::HopLimitExceeded),
583                 ParameterProblem(
584                     ParameterProblemHeader{
585                         code: ParameterProblemCode::ExtensionHeaderChainTooLong,
586                         pointer: u32::from_be_bytes(bytes5to8),
587                     }
588                 ),
589                 EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)),
590                 EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)),
591             ];
592 
593             for t in len_8_types {
594                 let mut bytes = Vec::with_capacity(t.header_len() + payload.len());
595                 Icmpv6Header::new(t.clone()).write(&mut bytes).unwrap();
596                 bytes.extend_from_slice(&payload);
597 
598                 assert_eq!(
599                     Icmpv6Slice::from_slice(&bytes[..]).unwrap().payload(),
600                     &payload[..]
601                 );
602             }
603         }
604     }
605 
606     #[test]
debug()607     fn debug() {
608         let data = [0u8; 8];
609         assert_eq!(
610             format!("{:?}", Icmpv6Slice::from_slice(&data).unwrap()),
611             format!("Icmpv6Slice {{ slice: {:?} }}", &data)
612         );
613     }
614 
615     proptest! {
616         #[test]
617         fn clone_eq(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
618             assert_eq!(
619                 Icmpv6Slice::from_slice(&slice).unwrap().clone(),
620                 Icmpv6Slice::from_slice(&slice).unwrap()
621             );
622         }
623     }
624 }
625