1use std::fmt::{self, Formatter};
73use std::mem;
74
75use zerocopy::byteorder::{BigEndian, U16, U32};
76use zerocopy::{FromBytes, IntoBytes, Unaligned};
77
78use crate::packet::HeaderParser;
79use crate::packet::PacketHeader;
80
81#[derive(
83 Debug,
84 Clone,
85 Copy,
86 PartialEq,
87 Eq,
88 Hash,
89 FromBytes,
90 IntoBytes,
91 Unaligned,
92 zerocopy::Immutable,
93 zerocopy::KnownLayout,
94)]
95#[repr(transparent)]
96pub struct Icmp6Type(pub u8);
97
98impl Icmp6Type {
99 pub const DST_UNREACH: Icmp6Type = Icmp6Type(1);
100 pub const PACKET_TOO_BIG: Icmp6Type = Icmp6Type(2);
101 pub const TIME_EXCEEDED: Icmp6Type = Icmp6Type(3);
102 pub const PARAM_PROB: Icmp6Type = Icmp6Type(4);
103 pub const ECHO_REQUEST: Icmp6Type = Icmp6Type(128);
104 pub const ECHO_REPLY: Icmp6Type = Icmp6Type(129);
105 pub const MLD_LISTENER_QUERY: Icmp6Type = Icmp6Type(130);
106 pub const MLD_LISTENER_REPORT: Icmp6Type = Icmp6Type(131);
107 pub const MLD_LISTENER_REDUCTION: Icmp6Type = Icmp6Type(132);
108
109 pub const ROUTER_SOLICITATION: Icmp6Type = Icmp6Type(133);
110 pub const ROUTER_ADVERTISEMENT: Icmp6Type = Icmp6Type(134);
111 pub const NEIGHBOR_SOLICITATION: Icmp6Type = Icmp6Type(135);
112 pub const NEIGHBOR_ADVERTISEMENT: Icmp6Type = Icmp6Type(136);
113 pub const REDIRECT_MESSAGE: Icmp6Type = Icmp6Type(137);
114 pub const ROUTER_RENUMBERING: Icmp6Type = Icmp6Type(138);
115 pub const NODE_INFORMATION_QUERY: Icmp6Type = Icmp6Type(139);
116 pub const NODE_INFORMATION_RESPONSE: Icmp6Type = Icmp6Type(140);
117
118 pub const INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION: Icmp6Type = Icmp6Type(141);
119 pub const INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT: Icmp6Type = Icmp6Type(142);
120 pub const MULTICAST_LISTENER_DISCOVERY_REPORTS: Icmp6Type = Icmp6Type(143);
121 pub const HOME_AGENT_ADDRESS_DISCOVERY_REQUEST: Icmp6Type = Icmp6Type(144);
122 pub const HOME_AGENT_ADDRESS_DISCOVERY_REPLY: Icmp6Type = Icmp6Type(145);
123 pub const MOBILE_PREFIX_SOLICITATION: Icmp6Type = Icmp6Type(146);
124 pub const MOBILE_PREFIX_ADVERTISEMENT: Icmp6Type = Icmp6Type(147);
125 pub const CERTIFICATION_PATH_SOLICITATION: Icmp6Type = Icmp6Type(148);
126 pub const CERTIFICATION_PATH_ADVERTISEMENT: Icmp6Type = Icmp6Type(149);
127 pub const EXPERIMENTAL_MOBILITY: Icmp6Type = Icmp6Type(150);
128 pub const MULTICAST_ROUTER_ADVERTISEMENT: Icmp6Type = Icmp6Type(151);
129 pub const MULTICAST_ROUTER_SOLICITATION: Icmp6Type = Icmp6Type(152);
130 pub const MULTICAST_ROUTER_TERMINATION: Icmp6Type = Icmp6Type(153);
131 pub const FMIPV6: Icmp6Type = Icmp6Type(154);
132 pub const RPL_CONTROL_MESSAGE: Icmp6Type = Icmp6Type(155);
133 pub const ILNPV6_LOCATOR_UPDATE: Icmp6Type = Icmp6Type(156);
134 pub const DUPLICATE_ADDRESS_REQUEST: Icmp6Type = Icmp6Type(157);
135 pub const DUPLICATE_ADDRESS_CONFIRM: Icmp6Type = Icmp6Type(158);
136 pub const MPL_CONTROL_MESSAGE: Icmp6Type = Icmp6Type(159);
137 pub const EXTENDED_ECHO_REQUEST: Icmp6Type = Icmp6Type(160);
138 pub const EXTENDED_ECHO_REPLY: Icmp6Type = Icmp6Type(161);
139}
140
141impl From<u8> for Icmp6Type {
142 fn from(value: u8) -> Self {
143 Icmp6Type(value)
144 }
145}
146
147impl From<Icmp6Type> for u8 {
148 fn from(value: Icmp6Type) -> Self {
149 value.0
150 }
151}
152
153impl fmt::Display for Icmp6Type {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 let s = match self.0 {
156 1 => "destination-unreachable",
157 2 => "packet-too-big",
158 3 => "time-exceeded",
159 4 => "parameter-problem",
160 128 => "echo-request",
161 129 => "echo-reply",
162 130 => "multicast-listener-query",
163 131 => "multicast-listener-report",
164 132 => "multicast-listener-reduction",
165 133 => "router-solicitation",
166 134 => "router-advertisement",
167 135 => "neighbor-solicitation",
168 136 => "neighbor-advertisement",
169 137 => "redirect-message",
170 138 => "router-renumbering",
171 139 => "node-information-query",
172 140 => "node-information-response",
173 141 => "inverse-neighbor-discovery-solicitation",
174 142 => "inverse-neighbor-discovery-advertisement",
175 143 => "multicast-listener-discovery-report",
176 144 => "home-agent-address-discovery-request",
177 145 => "home-agent-address-discovery-reply",
178 146 => "mobile-prefix-solicitation",
179 147 => "mobile-prefix-advertisement",
180 148 => "certification-path-solicitation",
181 149 => "certification-path-advertisement",
182 150 => "experimental-mobility",
183 151 => "multicast-router-advertisement",
184 152 => "multicast-router-solicitation",
185 153 => "multicast-router-termination",
186 154 => "fmipv6",
187 155 => "rpl-control-message",
188 156 => "ilnpv6-locator-update",
189 157 => "duplicate-address-request",
190 158 => "duplicate-address-confirmation",
191 159 => "mpl-control-message",
192 160 => "extended-echo-request",
193 161 => "extended-echo-reply",
194 _ => return write!(f, "unknown-{}", self.0),
195 };
196 write!(f, "{}", s)
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
202#[repr(transparent)]
203pub struct Icmp6CodeUnreachable(pub u8);
204
205impl Icmp6CodeUnreachable {
206 pub const NOROUTE: Icmp6CodeUnreachable = Icmp6CodeUnreachable(0); pub const ADMIN: Icmp6CodeUnreachable = Icmp6CodeUnreachable(1); pub const BEYONDSCOPE: Icmp6CodeUnreachable = Icmp6CodeUnreachable(2); pub const ADDR: Icmp6CodeUnreachable = Icmp6CodeUnreachable(3); pub const NOPORT: Icmp6CodeUnreachable = Icmp6CodeUnreachable(4); }
212
213impl From<u8> for Icmp6CodeUnreachable {
214 fn from(value: u8) -> Self {
215 Icmp6CodeUnreachable(value)
216 }
217}
218
219impl From<Icmp6CodeUnreachable> for u8 {
220 fn from(value: Icmp6CodeUnreachable) -> Self {
221 value.0
222 }
223}
224
225impl fmt::Display for Icmp6CodeUnreachable {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 let s = match self.0 {
228 0 => "no-route",
229 1 => "admin-prohibited",
230 2 => "beyond-scope",
231 3 => "address-unreachable",
232 4 => "port-unreachable",
233 _ => return write!(f, "unknown-{}", self.0),
234 };
235 write!(f, "{}", s)
236 }
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
241#[repr(transparent)]
242pub struct Icmp6CodeTimeExceed(pub u8);
243
244impl Icmp6CodeTimeExceed {
245 pub const TRANSIT: Icmp6CodeTimeExceed = Icmp6CodeTimeExceed(0); pub const REASSEMBLY: Icmp6CodeTimeExceed = Icmp6CodeTimeExceed(1); }
248
249impl From<u8> for Icmp6CodeTimeExceed {
250 fn from(value: u8) -> Self {
251 Icmp6CodeTimeExceed(value)
252 }
253}
254
255impl From<Icmp6CodeTimeExceed> for u8 {
256 fn from(value: Icmp6CodeTimeExceed) -> Self {
257 value.0
258 }
259}
260
261impl fmt::Display for Icmp6CodeTimeExceed {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 let s = match self.0 {
264 0 => "hop-limit-exceeded",
265 1 => "reassembly-timeout",
266 _ => return write!(f, "unknown-{}", self.0),
267 };
268 write!(f, "{}", s)
269 }
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
274#[repr(transparent)]
275pub struct Icmp6CodeParamProb(pub u8);
276
277impl Icmp6CodeParamProb {
278 pub const HEADER: Icmp6CodeParamProb = Icmp6CodeParamProb(0); pub const NEXTHEADER: Icmp6CodeParamProb = Icmp6CodeParamProb(1); pub const OPTION: Icmp6CodeParamProb = Icmp6CodeParamProb(2); }
282
283impl From<u8> for Icmp6CodeParamProb {
284 fn from(value: u8) -> Self {
285 Icmp6CodeParamProb(value)
286 }
287}
288
289impl From<Icmp6CodeParamProb> for u8 {
290 fn from(value: Icmp6CodeParamProb) -> Self {
291 value.0
292 }
293}
294
295impl fmt::Display for Icmp6CodeParamProb {
296 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297 let s = match self.0 {
298 0 => "erroneous-header",
299 1 => "unrecognized-next-header",
300 2 => "unrecognized-option",
301 _ => return write!(f, "unknown-{}", self.0),
302 };
303 write!(f, "{}", s)
304 }
305}
306
307#[repr(C, packed)]
309#[derive(
310 FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, zerocopy::KnownLayout, zerocopy::Immutable,
311)]
312pub struct Icmp6Header {
313 icmp6_type: Icmp6Type,
314 code: u8,
315 checksum: U16<BigEndian>,
316 un: U32<BigEndian>,
322}
323
324impl Icmp6Header {
325 pub const FIXED_LEN: usize = mem::size_of::<Icmp6Header>();
326
327 #[inline]
329 pub fn icmp6_type(&self) -> Icmp6Type {
330 self.icmp6_type
331 }
332
333 #[inline]
335 pub fn code(&self) -> u8 {
336 self.code
337 }
338
339 #[inline]
341 pub fn checksum(&self) -> u16 {
342 self.checksum.get()
343 }
344
345 #[inline]
347 pub fn un(&self) -> u32 {
348 self.un.get()
349 }
350
351 #[inline]
353 pub fn echo_id(&self) -> u16 {
354 (self.un.get() >> 16) as u16
355 }
356
357 #[inline]
359 pub fn echo_sequence(&self) -> u16 {
360 (self.un.get() & 0xFFFF) as u16
361 }
362
363 #[inline]
365 pub fn mtu(&self) -> u32 {
366 self.un.get()
367 }
368
369 #[inline]
371 pub fn pointer(&self) -> u32 {
372 self.un.get()
373 }
374
375 #[inline]
377 pub fn is_valid(&self) -> bool {
378 true
381 }
382
383 pub fn compute_checksum(src_ip: &[u8; 16], dst_ip: &[u8; 16], icmp6_data: &[u8]) -> u16 {
386 let mut sum: u32 = 0;
387
388 for i in (0..16).step_by(2) {
390 let word = u16::from_be_bytes([src_ip[i], src_ip[i + 1]]);
391 sum += word as u32;
392 }
393
394 for i in (0..16).step_by(2) {
396 let word = u16::from_be_bytes([dst_ip[i], dst_ip[i + 1]]);
397 sum += word as u32;
398 }
399
400 let icmp_len = icmp6_data.len() as u32;
402 sum += (icmp_len >> 16) & 0xFFFF;
403 sum += icmp_len & 0xFFFF;
404
405 sum += 58;
407
408 let mut i = 0;
410 while i < icmp6_data.len() {
411 if i + 1 < icmp6_data.len() {
412 let word = u16::from_be_bytes([icmp6_data[i], icmp6_data[i + 1]]);
413 sum += word as u32;
414 i += 2;
415 } else {
416 let word = u16::from_be_bytes([icmp6_data[i], 0]);
418 sum += word as u32;
419 i += 1;
420 }
421 }
422
423 while sum >> 16 != 0 {
425 sum = (sum & 0xFFFF) + (sum >> 16);
426 }
427
428 !sum as u16
430 }
431
432 pub fn verify_checksum(&self, src_ip: &[u8; 16], dst_ip: &[u8; 16], icmp6_data: &[u8]) -> bool {
434 let computed = Self::compute_checksum(src_ip, dst_ip, icmp6_data);
435 computed == 0 || computed == 0xFFFF
436 }
437}
438
439impl PacketHeader for Icmp6Header {
440 const NAME: &'static str = "Icmp6Header";
441 type InnerType = Icmp6Type;
442
443 #[inline]
444 fn inner_type(&self) -> Self::InnerType {
445 self.icmp6_type
446 }
447
448 #[inline]
449 fn total_len(&self, _buf: &[u8]) -> usize {
450 Self::FIXED_LEN
451 }
452
453 #[inline]
454 fn is_valid(&self) -> bool {
455 self.is_valid()
456 }
457}
458
459impl HeaderParser for Icmp6Header {
460 type Output<'a> = &'a Icmp6Header;
461
462 #[inline]
463 fn into_view<'a>(header: &'a Self, _: &'a [u8]) -> Self::Output<'a> {
464 header
465 }
466}
467
468impl fmt::Display for Icmp6Header {
469 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
470 write!(f, "ICMPv6 {}", self.icmp6_type())?;
471
472 match self.icmp6_type() {
474 Icmp6Type::ECHO_REQUEST | Icmp6Type::ECHO_REPLY => {
475 write!(f, " id={} seq={}", self.echo_id(), self.echo_sequence())?;
476 }
477 Icmp6Type::DST_UNREACH => {
478 let code = Icmp6CodeUnreachable::from(self.code());
479 write!(f, " code={}", code)?;
480 }
481 Icmp6Type::PACKET_TOO_BIG => {
482 write!(f, " mtu={}", self.mtu())?;
483 }
484 Icmp6Type::TIME_EXCEEDED => {
485 let code = Icmp6CodeTimeExceed::from(self.code());
486 write!(f, " code={}", code)?;
487 }
488 Icmp6Type::PARAM_PROB => {
489 let code = Icmp6CodeParamProb::from(self.code());
490 write!(f, " code={} ptr={}", code, self.pointer())?;
491 }
492 _ => {
493 if self.code() != 0 {
494 write!(f, " code={}", self.code())?;
495 }
496 }
497 }
498
499 Ok(())
500 }
501}
502
503#[cfg(test)]
504mod tests {
505 use super::*;
506
507 #[test]
508 fn test_icmp6_type_constants() {
509 assert_eq!(Icmp6Type::DST_UNREACH.0, 1);
510 assert_eq!(Icmp6Type::PACKET_TOO_BIG.0, 2);
511 assert_eq!(Icmp6Type::TIME_EXCEEDED.0, 3);
512 assert_eq!(Icmp6Type::ECHO_REQUEST.0, 128);
513 assert_eq!(Icmp6Type::ECHO_REPLY.0, 129);
514 assert_eq!(Icmp6Type::ROUTER_SOLICITATION.0, 133);
515 assert_eq!(Icmp6Type::NEIGHBOR_SOLICITATION.0, 135);
516 }
517
518 #[test]
519 fn test_icmp6_type_display() {
520 assert_eq!(format!("{}", Icmp6Type::ECHO_REQUEST), "echo-request");
521 assert_eq!(format!("{}", Icmp6Type::ECHO_REPLY), "echo-reply");
522 assert_eq!(
523 format!("{}", Icmp6Type::DST_UNREACH),
524 "destination-unreachable"
525 );
526 assert_eq!(
527 format!("{}", Icmp6Type::NEIGHBOR_SOLICITATION),
528 "neighbor-solicitation"
529 );
530 assert_eq!(
531 format!("{}", Icmp6Type::ROUTER_ADVERTISEMENT),
532 "router-advertisement"
533 );
534 assert_eq!(format!("{}", Icmp6Type::PACKET_TOO_BIG), "packet-too-big");
535 assert_eq!(format!("{}", Icmp6Type::TIME_EXCEEDED), "time-exceeded");
536 assert_eq!(format!("{}", Icmp6Type::PARAM_PROB), "parameter-problem");
537 assert_eq!(format!("{}", Icmp6Type::from(99)), "unknown-99");
538 }
539
540 #[test]
541 fn test_icmp6_code_constants() {
542 assert_eq!(Icmp6CodeUnreachable::NOROUTE.0, 0);
543 assert_eq!(Icmp6CodeUnreachable::NOPORT.0, 4);
544
545 assert_eq!(Icmp6CodeTimeExceed::TRANSIT.0, 0);
546 assert_eq!(Icmp6CodeTimeExceed::REASSEMBLY.0, 1);
547
548 assert_eq!(Icmp6CodeParamProb::HEADER.0, 0);
549 assert_eq!(Icmp6CodeParamProb::NEXTHEADER.0, 1);
550 }
551
552 #[test]
553 fn test_icmp6_code_display() {
554 assert_eq!(
555 format!("{}", Icmp6CodeUnreachable::NOPORT),
556 "port-unreachable"
557 );
558 assert_eq!(format!("{}", Icmp6CodeUnreachable::NOROUTE), "no-route");
559 assert_eq!(format!("{}", Icmp6CodeUnreachable::from(99)), "unknown-99");
560
561 assert_eq!(
562 format!("{}", Icmp6CodeTimeExceed::TRANSIT),
563 "hop-limit-exceeded"
564 );
565 assert_eq!(format!("{}", Icmp6CodeTimeExceed::from(99)), "unknown-99");
566
567 assert_eq!(
568 format!("{}", Icmp6CodeParamProb::HEADER),
569 "erroneous-header"
570 );
571 assert_eq!(format!("{}", Icmp6CodeParamProb::from(99)), "unknown-99");
572 }
573
574 #[test]
575 fn test_icmp6_header_size() {
576 assert_eq!(mem::size_of::<Icmp6Header>(), 8);
577 assert_eq!(Icmp6Header::FIXED_LEN, 8);
578 }
579
580 #[test]
581 fn test_icmp6_echo_request() {
582 let mut packet = Vec::new();
583
584 packet.push(128); packet.push(0); packet.extend_from_slice(&0u16.to_be_bytes()); packet.extend_from_slice(&1234u16.to_be_bytes()); packet.extend_from_slice(&5678u16.to_be_bytes()); let result = Icmp6Header::from_bytes(&packet);
592 assert!(result.is_ok());
593
594 let (header, _) = result.unwrap();
595 assert_eq!(header.icmp6_type(), Icmp6Type::ECHO_REQUEST);
596 assert_eq!(header.code(), 0);
597 assert_eq!(header.echo_id(), 1234);
598 assert_eq!(header.echo_sequence(), 5678);
599 assert!(header.is_valid());
600 }
601
602 #[test]
603 fn test_icmp6_echo_reply() {
604 let mut packet = Vec::new();
605
606 packet.push(129); packet.push(0); packet.extend_from_slice(&0u16.to_be_bytes()); packet.extend_from_slice(&1234u16.to_be_bytes()); packet.extend_from_slice(&5678u16.to_be_bytes()); let (header, _) = Icmp6Header::from_bytes(&packet).unwrap();
614
615 assert_eq!(header.icmp6_type(), Icmp6Type::ECHO_REPLY);
616 assert_eq!(header.code(), 0);
617 assert_eq!(header.echo_id(), 1234);
618 assert_eq!(header.echo_sequence(), 5678);
619 }
620
621 #[test]
622 fn test_icmp6_dest_unreachable() {
623 let mut packet = Vec::new();
624
625 packet.push(1); packet.push(Icmp6CodeUnreachable::NOPORT.0); packet.extend_from_slice(&0u16.to_be_bytes()); packet.extend_from_slice(&0u32.to_be_bytes()); let (header, _) = Icmp6Header::from_bytes(&packet).unwrap();
632
633 assert_eq!(header.icmp6_type(), Icmp6Type::DST_UNREACH);
634 assert_eq!(header.code(), Icmp6CodeUnreachable::NOPORT.0);
635 }
636
637 #[test]
638 fn test_icmp6_time_exceeded() {
639 let mut packet = Vec::new();
640
641 packet.push(3); packet.push(Icmp6CodeTimeExceed::TRANSIT.0); packet.extend_from_slice(&0u16.to_be_bytes()); packet.extend_from_slice(&0u32.to_be_bytes()); let (header, _) = Icmp6Header::from_bytes(&packet).unwrap();
648
649 assert_eq!(header.icmp6_type(), Icmp6Type::TIME_EXCEEDED);
650 assert_eq!(header.code(), Icmp6CodeTimeExceed::TRANSIT.0);
651 }
652
653 #[test]
654 fn test_icmp6_packet_too_big() {
655 let mut packet = Vec::new();
656
657 packet.push(2); packet.push(0); packet.extend_from_slice(&0u16.to_be_bytes()); let mtu = 1280u32;
664 packet.extend_from_slice(&mtu.to_be_bytes());
665
666 let (header, _) = Icmp6Header::from_bytes(&packet).unwrap();
667
668 assert_eq!(header.icmp6_type(), Icmp6Type::PACKET_TOO_BIG);
669 assert_eq!(header.code(), 0);
670 assert_eq!(header.mtu(), mtu);
671 }
672
673 #[test]
674 fn test_icmp6_param_problem() {
675 let mut packet = Vec::new();
676
677 packet.push(4); packet.push(Icmp6CodeParamProb::HEADER.0); packet.extend_from_slice(&0u16.to_be_bytes()); let pointer = 40u32;
684 packet.extend_from_slice(&pointer.to_be_bytes());
685
686 let (header, _) = Icmp6Header::from_bytes(&packet).unwrap();
687
688 assert_eq!(header.icmp6_type(), Icmp6Type::PARAM_PROB);
689 assert_eq!(header.code(), Icmp6CodeParamProb::HEADER.0);
690 assert_eq!(header.pointer(), pointer);
691 }
692
693 #[test]
694 fn test_icmp6_neighbor_solicitation() {
695 let mut packet = Vec::new();
696
697 packet.push(135); packet.push(0); packet.extend_from_slice(&0u16.to_be_bytes()); packet.extend_from_slice(&0u32.to_be_bytes()); let (header, _) = Icmp6Header::from_bytes(&packet).unwrap();
704
705 assert_eq!(header.icmp6_type(), Icmp6Type::NEIGHBOR_SOLICITATION);
706 assert_eq!(header.code(), 0);
707 }
708
709 #[test]
710 fn test_icmp6_router_advertisement() {
711 let mut packet = Vec::new();
712
713 packet.push(134); packet.push(0); packet.extend_from_slice(&0u16.to_be_bytes()); packet.extend_from_slice(&0u32.to_be_bytes()); let (header, _) = Icmp6Header::from_bytes(&packet).unwrap();
720
721 assert_eq!(header.icmp6_type(), Icmp6Type::ROUTER_ADVERTISEMENT);
722 assert_eq!(header.code(), 0);
723 }
724
725 #[test]
726 fn test_icmp6_parsing_too_small() {
727 let packet = vec![0u8; 7]; let result = Icmp6Header::from_bytes(&packet);
730 assert!(result.is_err());
731 }
732
733 #[test]
734 fn test_icmp6_total_len() {
735 let packet = create_test_echo_packet();
736 let (header, _) = Icmp6Header::from_bytes(&packet).unwrap();
737
738 assert_eq!(header.total_len(&packet), 8);
740 }
741
742 #[test]
743 fn test_icmp6_from_bytes_with_payload() {
744 let mut packet = Vec::new();
745
746 packet.push(128); packet.push(0); packet.extend_from_slice(&0u16.to_be_bytes()); packet.extend_from_slice(&1u16.to_be_bytes()); packet.extend_from_slice(&1u16.to_be_bytes()); let payload_data = b"Hello ICMPv6!";
755 packet.extend_from_slice(payload_data);
756
757 let result = Icmp6Header::from_bytes(&packet);
758 assert!(result.is_ok());
759
760 let (header, payload) = result.unwrap();
761
762 assert_eq!(header.icmp6_type(), Icmp6Type::ECHO_REQUEST);
764 assert_eq!(header.code(), 0);
765 assert_eq!(header.echo_id(), 1);
766 assert_eq!(header.echo_sequence(), 1);
767
768 assert_eq!(payload.len(), payload_data.len());
770 assert_eq!(payload, payload_data);
771 }
772
773 #[test]
774 fn test_icmp6_checksum_computation() {
775 let src_ip: [u8; 16] = [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
777 let dst_ip: [u8; 16] = [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
778
779 let mut packet = Vec::new();
780
781 packet.push(128); packet.push(0); packet.extend_from_slice(&0u16.to_be_bytes()); packet.extend_from_slice(&0x1234u16.to_be_bytes()); packet.extend_from_slice(&0x5678u16.to_be_bytes()); packet.extend_from_slice(b"test");
790
791 let checksum = Icmp6Header::compute_checksum(&src_ip, &dst_ip, &packet);
792
793 assert_ne!(checksum, 0);
795
796 packet[2..4].copy_from_slice(&checksum.to_be_bytes());
798
799 let (header, _) = Icmp6Header::from_bytes(&packet).unwrap();
801 assert!(header.verify_checksum(&src_ip, &dst_ip, &packet));
802 }
803
804 fn create_test_echo_packet() -> Vec<u8> {
806 let mut packet = Vec::new();
807
808 packet.push(128);
810
811 packet.push(0);
813
814 packet.extend_from_slice(&0u16.to_be_bytes());
816
817 packet.extend_from_slice(&1234u16.to_be_bytes());
819
820 packet.extend_from_slice(&1u16.to_be_bytes());
822
823 packet
824 }
825}