1use crate::{err::ValueTooBigError, *};
2use arrayvec::ArrayVec;
3
4#[derive(Clone, Debug, PartialEq, Eq)]
6pub struct Icmpv6Header {
7 pub icmp_type: Icmpv6Type,
9 pub checksum: u16,
11}
12
13impl Icmpv6Header {
14 pub const MIN_LEN: usize = 8;
19
20 #[deprecated(since = "0.14.0", note = "Please use Icmpv6Header::MIN_LEN instead")]
22 pub const MIN_SERIALIZED_SIZE: usize = Icmpv6Header::MIN_LEN;
23
24 pub const MAX_LEN: usize = 8 + 16 + 16;
31
32 #[deprecated(since = "0.14.0", note = "Please use Icmpv6Header::MAX_LEN instead")]
34 pub const MAX_SERIALIZED_SIZE: usize = Icmpv6Header::MAX_LEN;
35
36 #[inline]
38 pub fn new(icmp_type: Icmpv6Type) -> Icmpv6Header {
39 Icmpv6Header {
40 icmp_type,
41 checksum: 0, }
43 }
44
45 pub fn with_checksum(
48 icmp_type: Icmpv6Type,
49 source_ip: [u8; 16],
50 destination_ip: [u8; 16],
51 payload: &[u8],
52 ) -> Result<Icmpv6Header, ValueTooBigError<usize>> {
53 let checksum = icmp_type.calc_checksum(source_ip, destination_ip, payload)?;
54 Ok(Icmpv6Header {
55 icmp_type,
56 checksum,
57 })
58 }
59
60 #[inline]
63 pub fn from_slice(slice: &[u8]) -> Result<(Icmpv6Header, &[u8]), err::LenError> {
64 let header = Icmpv6Slice::from_slice(slice)?.header();
65 let len = header.header_len();
66 Ok((header, &slice[len..]))
67 }
68
69 #[cfg(feature = "std")]
71 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
72 pub fn read<T: std::io::Read + Sized>(reader: &mut T) -> Result<Icmpv6Header, std::io::Error> {
73 let mut start = [0u8; 8];
75 reader.read_exact(&mut start)?;
76 Ok(Icmpv6Slice { slice: &start }.header())
77 }
78
79 #[cfg(feature = "std")]
81 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
82 pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
83 writer.write_all(&self.to_bytes())
84 }
85
86 #[inline]
91 pub fn header_len(&self) -> usize {
92 self.icmp_type.header_len()
93 }
94
95 #[inline]
98 pub fn fixed_payload_size(&self) -> Option<usize> {
99 self.icmp_type.fixed_payload_size()
100 }
101
102 pub fn update_checksum(
104 &mut self,
105 source_ip: [u8; 16],
106 destination_ip: [u8; 16],
107 payload: &[u8],
108 ) -> Result<(), ValueTooBigError<usize>> {
109 self.checksum = self
110 .icmp_type
111 .calc_checksum(source_ip, destination_ip, payload)?;
112 Ok(())
113 }
114
115 #[inline]
117 pub fn to_bytes(&self) -> ArrayVec<u8, { Icmpv6Header::MAX_LEN }> {
118 let checksum_be = self.checksum.to_be_bytes();
119
120 let return_trivial =
121 |type_u8: u8, code_u8: u8| -> ArrayVec<u8, { Icmpv6Header::MAX_LEN }> {
122 #[rustfmt::skip]
123 let mut re = ArrayVec::from([
124 type_u8, code_u8, checksum_be[0], checksum_be[1],
125 0, 0, 0, 0,
126
127 0, 0, 0, 0,
128 0, 0, 0, 0,
129 0, 0, 0, 0,
130 0, 0, 0, 0,
131
132 0, 0, 0, 0,
133 0, 0, 0, 0,
134 0, 0, 0, 0,
135 0, 0, 0, 0,
136 ]);
137 unsafe {
139 re.set_len(8);
140 }
141 re
142 };
143
144 let return_4u8 = |type_u8: u8,
145 code_u8: u8,
146 bytes5to8: [u8; 4]|
147 -> ArrayVec<u8, { Icmpv6Header::MAX_LEN }> {
148 #[rustfmt::skip]
149 let mut re = ArrayVec::from([
150 type_u8, code_u8, checksum_be[0], checksum_be[1],
151 bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3],
152
153 0, 0, 0, 0,
154 0, 0, 0, 0,
155 0, 0, 0, 0,
156 0, 0, 0, 0,
157
158 0, 0, 0, 0,
159 0, 0, 0, 0,
160 0, 0, 0, 0,
161 0, 0, 0, 0,
162 ]);
163 unsafe {
165 re.set_len(8);
166 }
167 re
168 };
169
170 use crate::{icmpv6::*, Icmpv6Type::*};
171 match self.icmp_type {
172 Unknown {
173 type_u8,
174 code_u8,
175 bytes5to8,
176 } => return_4u8(type_u8, code_u8, bytes5to8),
177 DestinationUnreachable(header) => return_trivial(TYPE_DST_UNREACH, header.code_u8()),
178 PacketTooBig { mtu } => return_4u8(TYPE_PACKET_TOO_BIG, 0, mtu.to_be_bytes()),
179 TimeExceeded(code) => return_trivial(TYPE_TIME_EXCEEDED, code.code_u8()),
180 ParameterProblem(header) => return_4u8(
181 TYPE_PARAMETER_PROBLEM,
182 header.code.code_u8(),
183 header.pointer.to_be_bytes(),
184 ),
185 EchoRequest(echo) => return_4u8(TYPE_ECHO_REQUEST, 0, echo.to_bytes()),
186 EchoReply(echo) => return_4u8(TYPE_ECHO_REPLY, 0, echo.to_bytes()),
187 }
188 }
189}
190
191#[cfg(test)]
192mod test {
193 use crate::{
194 err::{ValueTooBigError, ValueType},
195 icmpv6::*,
196 test_gens::*,
197 *,
198 };
199 use alloc::{format, vec::Vec};
200 use arrayvec::ArrayVec;
201 use proptest::prelude::*;
202
203 proptest! {
204 #[test]
205 fn new(icmp_type in icmpv6_type_any()) {
206 assert_eq!(
207 Icmpv6Header::new(icmp_type.clone()),
208 Icmpv6Header {
209 icmp_type,
210 checksum: 0,
211 }
212 );
213 }
214 }
215
216 proptest! {
217 #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
218 #[test]
219 fn with_checksum(
220 ip_header in ipv6_any(),
221 icmp_type in icmpv6_type_any(),
222 bad_len in (core::u32::MAX - 7) as usize..=(core::isize::MAX as usize),
224 payload in proptest::collection::vec(any::<u8>(), 0..1024)
225 ) {
226
227 {
229 let too_big_slice = unsafe {
232 use core::ptr::NonNull;
236 core::slice::from_raw_parts(
237 NonNull::<u8>::dangling().as_ptr(),
238 bad_len
239 )
240 };
241 assert_eq!(
242 Icmpv6Header::with_checksum(icmp_type.clone(), ip_header.source, ip_header.destination, too_big_slice),
243 Err(ValueTooBigError{
244 actual: bad_len,
245 max_allowed: (core::u32::MAX - 8) as usize,
246 value_type: ValueType::Icmpv6PayloadLength,
247 })
248 );
249 }
250
251 assert_eq!(
253 Icmpv6Header::with_checksum(icmp_type.clone(), ip_header.source, ip_header.destination, &payload).unwrap(),
254 Icmpv6Header {
255 icmp_type,
256 checksum: icmp_type.calc_checksum(ip_header.source, ip_header.destination, &payload).unwrap(),
257 }
258 );
259 }
260 }
261
262 proptest! {
263 #[test]
264 fn from_slice(
265 icmp_type in icmpv6_type_any(),
266 checksum in any::<u16>(),
267 ) {
268 let bytes = {
269 Icmpv6Header {
270 icmp_type: icmp_type.clone(),
271 checksum,
272 }.to_bytes()
273 };
274
275 {
277 let result = Icmpv6Header::from_slice(&bytes).unwrap();
278 assert_eq!(
279 Icmpv6Header{
280 icmp_type,
281 checksum,
282 },
283 result.0,
284 );
285 assert_eq!(&bytes[8..], result.1);
286 }
287
288
289 for length in 0..8 {
291 assert_eq!(
292 Icmpv6Header::from_slice(&bytes[..length]).unwrap_err(),
293 err::LenError{
294 required_len: bytes.len(),
295 len: length,
296 len_source: LenSource::Slice,
297 layer: err::Layer::Icmpv6,
298 layer_start_offset: 0
299 }
300 );
301 }
302 }
303 }
304
305 proptest! {
306 #[test]
307 fn read(
308 icmp_type in icmpv6_type_any(),
309 checksum in any::<u16>(),
310 ) {
311 let header = Icmpv6Header {
312 icmp_type: icmp_type.clone(),
313 checksum,
314 };
315 let bytes = header.to_bytes();
316
317 {
319 let mut cursor = std::io::Cursor::new(&bytes);
320 let result = Icmpv6Header::read(&mut cursor).unwrap();
321 assert_eq!(header, result,);
322 assert_eq!(header.header_len() as u64, cursor.position());
323 }
324
325 for length in 0..header.header_len() {
327 let mut cursor = std::io::Cursor::new(&bytes[..length]);
328 assert!(Icmpv6Header::read(&mut cursor).is_err());
329 }
330 }
331 }
332
333 proptest! {
334 #[test]
335 fn write(
336 icmp_type in icmpv6_type_any(),
337 checksum in any::<u16>(),
338 bad_len in 0..8usize
339 ) {
340 {
342 let mut buffer = Vec::with_capacity(icmp_type.header_len());
343 let header = Icmpv6Header {
344 icmp_type,
345 checksum,
346 };
347 header.write(&mut buffer).unwrap();
348 assert_eq!(
349 &header.to_bytes(),
350 &buffer[..]
351 );
352 }
353
354 {
356 let mut buffer = [0u8;Icmpv6Header::MAX_LEN];
357 let mut writer = std::io::Cursor::new(&mut buffer[..bad_len]);
358 Icmpv6Header {
359 icmp_type,
360 checksum,
361 }.write(&mut writer).unwrap_err();
362 }
363 }
364 }
365
366 proptest! {
367 #[test]
368 fn header_len(icmp_type in icmpv6_type_any(), checksum in any::<u16>()) {
369 assert_eq!(
370 icmp_type.header_len(),
371 Icmpv6Header{
372 icmp_type,
373 checksum
374 }.header_len()
375 );
376 }
377 }
378
379 proptest! {
380 #[test]
381 fn fixed_payload_size(icmp_type in icmpv6_type_any(), checksum in any::<u16>()) {
382 assert_eq!(
383 icmp_type.fixed_payload_size(),
384 Icmpv6Header{
385 icmp_type,
386 checksum
387 }.fixed_payload_size()
388 );
389 }
390 }
391
392 proptest! {
393 #[test]
394 #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
395 fn update_checksum(
396 ip_header in ipv6_any(),
397 icmp_type in icmpv6_type_any(),
398 start_checksum in any::<u16>(),
399 bad_len in (core::u32::MAX - 7) as usize..=(core::isize::MAX as usize),
401 payload in proptest::collection::vec(any::<u8>(), 0..1024)
402 ) {
403
404 {
406 let too_big_slice = unsafe {
409 use core::ptr::NonNull;
413 core::slice::from_raw_parts(
414 NonNull::<u8>::dangling().as_ptr(),
415 bad_len
416 )
417 };
418 assert_eq!(
419 Icmpv6Header{
420 icmp_type,
421 checksum: 0
422 }.update_checksum(ip_header.source, ip_header.destination, too_big_slice),
423 Err(ValueTooBigError{
424 actual: bad_len,
425 max_allowed: (u32::MAX - 8) as usize,
426 value_type: ValueType::Icmpv6PayloadLength
427 })
428 );
429 }
430
431 assert_eq!(
433 {
434 let mut header = Icmpv6Header{
435 icmp_type,
436 checksum: start_checksum,
437 };
438 header.update_checksum(ip_header.source, ip_header.destination, &payload).unwrap();
439 header
440 },
441 Icmpv6Header{
442 icmp_type,
443 checksum: icmp_type.calc_checksum(ip_header.source, ip_header.destination, &payload).unwrap(),
444 }
445 );
446 }
447 }
448
449 proptest! {
450 #[test]
451 fn to_bytes(
452 checksum in any::<u16>(),
453 rand_u32 in any::<u32>(),
454 rand_4bytes in any::<[u8;4]>(),
455 ) {
456 use Icmpv6Type::*;
457
458 let with_5to8_bytes = |type_u8: u8, code_u8: u8, bytes5to8: [u8;4]| -> ArrayVec<u8, { Icmpv6Header::MAX_LEN }> {
459 let mut bytes = ArrayVec::<u8, { Icmpv6Header::MAX_LEN }>::new();
460 bytes.push(type_u8);
461 bytes.push(code_u8);
462 bytes.try_extend_from_slice(&checksum.to_be_bytes()).unwrap();
463 bytes.try_extend_from_slice(&bytes5to8).unwrap();
464 bytes
465 };
466
467 let simple_bytes = |type_u8: u8, code_u8: u8| -> ArrayVec<u8, { Icmpv6Header::MAX_LEN }> {
468 with_5to8_bytes(type_u8, code_u8, [0;4])
469 };
470
471 for (code, code_u8) in dest_unreachable_code_test_consts::VALID_VALUES {
473 assert_eq!(
474 Icmpv6Header{
475 icmp_type: DestinationUnreachable(code),
476 checksum
477 }.to_bytes(),
478 simple_bytes(TYPE_DST_UNREACH, code_u8)
479 );
480 }
481
482 assert_eq!(
484 Icmpv6Header{
485 icmp_type: PacketTooBig{ mtu: rand_u32 },
486 checksum
487 }.to_bytes(),
488 with_5to8_bytes(TYPE_PACKET_TOO_BIG, 0, rand_u32.to_be_bytes())
489 );
490
491 for (code, code_u8) in time_exceeded_code_test_consts::VALID_VALUES {
493 assert_eq!(
494 Icmpv6Header{
495 icmp_type: TimeExceeded(code),
496 checksum
497 }.to_bytes(),
498 simple_bytes(TYPE_TIME_EXCEEDED, code_u8)
499 );
500 }
501
502 for (code, code_u8) in parameter_problem_code_test_consts::VALID_VALUES {
504 assert_eq!(
505 Icmpv6Header{
506 icmp_type: ParameterProblem(
507 ParameterProblemHeader{
508 code,
509 pointer: rand_u32,
510 }
511 ),
512 checksum
513 }.to_bytes(),
514 with_5to8_bytes(TYPE_PARAMETER_PROBLEM, code_u8, rand_u32.to_be_bytes())
515 );
516 }
517
518 assert_eq!(
520 Icmpv6Header{
521 icmp_type: EchoRequest(IcmpEchoHeader {
522 id: u16::from_be_bytes([rand_4bytes[0], rand_4bytes[1]]),
523 seq: u16::from_be_bytes([rand_4bytes[2], rand_4bytes[3]]),
524 }),
525 checksum
526 }.to_bytes(),
527 with_5to8_bytes(TYPE_ECHO_REQUEST, 0, rand_4bytes)
528 );
529
530 assert_eq!(
532 Icmpv6Header{
533 icmp_type: EchoReply(IcmpEchoHeader {
534 id: u16::from_be_bytes([rand_4bytes[0], rand_4bytes[1]]),
535 seq: u16::from_be_bytes([rand_4bytes[2], rand_4bytes[3]]),
536 }),
537 checksum
538 }.to_bytes(),
539 with_5to8_bytes(TYPE_ECHO_REPLY, 0, rand_4bytes)
540 );
541
542 for type_u8 in 0..=u8::MAX {
544 for code_u8 in 0..=u8::MAX {
545 assert_eq!(
546 Icmpv6Header{
547 icmp_type: Unknown {
548 type_u8,
549 code_u8,
550 bytes5to8: rand_4bytes,
551 },
552 checksum
553 }.to_bytes(),
554 with_5to8_bytes(type_u8, code_u8, rand_4bytes)
555 );
556 }
557 }
558 }
559 }
560
561 #[test]
562 fn debug() {
563 let t = Icmpv6Type::Unknown {
564 type_u8: 0,
565 code_u8: 1,
566 bytes5to8: [2, 3, 4, 5],
567 };
568 assert_eq!(
569 format!(
570 "{:?}",
571 Icmpv6Header {
572 icmp_type: t.clone(),
573 checksum: 7
574 }
575 ),
576 format!("Icmpv6Header {{ icmp_type: {:?}, checksum: {:?} }}", t, 7)
577 );
578 }
579
580 proptest! {
581 #[test]
582 fn clone_eq(icmp_type in icmpv6_type_any(), checksum in any::<u16>()) {
583 let header = Icmpv6Header{ icmp_type, checksum };
584 assert_eq!(header, header.clone());
585 }
586 }
587}