1use pnet_packet::{
2 icmpv6::{echo_reply::EchoReplyPacket, Icmpv6Code, Icmpv6Packet, Icmpv6Type, Icmpv6Types},
3 Packet,
4};
5
6use crate::{
7 echo_reply::PayloadLengthDelimitedEchoReply, types::Payload, LenWithPayloadLengthDelimited,
8 ICMP_HEADER_SIZE,
9};
10
11#[derive(Debug, Clone)]
13pub enum Icmpv6 {
14 EchoReply(PayloadLengthDelimitedEchoReply),
15 Other(Icmpv6Type, Icmpv6Code, Payload),
16}
17
18impl Icmpv6 {
19 pub fn parse_from_packet_bytes(bytes: &[u8]) -> Result<Option<Self>, ParseError> {
20 if bytes.len() < ICMP_HEADER_SIZE {
21 return Ok(None);
22 }
23
24 let icmp_packet = if let Some(x) = Icmpv6Packet::new(bytes) {
25 x
26 } else {
27 return Err(ParseError::NotIcmpPacket);
28 };
29
30 match icmp_packet.get_icmpv6_type() {
31 Icmpv6Types::EchoReply => {
32 let echo_reply_packet =
33 EchoReplyPacket::owned(bytes[..ICMP_HEADER_SIZE].to_owned())
34 .ok_or(ParseError::NotEchoReplyPacket)?;
35
36 let char_a = if let Some(x) = bytes.get(ICMP_HEADER_SIZE) {
37 x
38 } else {
39 return Ok(None);
40 };
41 let char_b = if let Some(x) = bytes.get(ICMP_HEADER_SIZE + 1) {
42 x
43 } else {
44 return Ok(None);
45 };
46 let len = LenWithPayloadLengthDelimited::from_bytes([*char_a, *char_b]);
47
48 if bytes.len()
49 < ICMP_HEADER_SIZE
50 + LenWithPayloadLengthDelimited::size()
51 + (*len.inner()) as usize
52 {
53 return Ok(None);
54 }
55
56 return Ok(Some(Icmpv6::EchoReply(
57 PayloadLengthDelimitedEchoReply::new(
58 echo_reply_packet.get_identifier().into(),
59 echo_reply_packet.get_sequence_number().into(),
60 len,
61 bytes[ICMP_HEADER_SIZE + LenWithPayloadLengthDelimited::size()
62 ..ICMP_HEADER_SIZE
63 + LenWithPayloadLengthDelimited::size()
64 + (*len.inner()) as usize]
65 .to_vec()
66 .into(),
67 ),
68 )));
69 }
70 icmp_type => Ok(Some(Icmpv6::Other(
71 icmp_type,
72 icmp_packet.get_icmpv6_code(),
73 icmp_packet.payload().to_vec().into(),
74 ))),
75 }
76 }
77}
78
79#[derive(Debug)]
81pub enum ParseError {
82 NotIcmpPacket,
83 NotEchoReplyPacket,
84}
85impl core::fmt::Display for ParseError {
86 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87 write!(f, "{self:?}")
88 }
89}
90impl std::error::Error for ParseError {}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 use crate::echo_request::PayloadLengthDelimitedEchoRequest;
97
98 #[test]
99 fn test_parse_from_packet_bytes() {
100 let echo_request =
101 PayloadLengthDelimitedEchoRequest::new(Some(1.into()), Some(2.into()), b"1234");
102 let mut bytes = echo_request.render_v6_packet_bytes();
103 bytes[0] = Icmpv6Types::EchoReply.0;
104
105 match Icmpv6::parse_from_packet_bytes(&bytes) {
106 Ok(Some(Icmpv6::EchoReply(PayloadLengthDelimitedEchoReply {
107 identifier,
108 sequence_number,
109 len,
110 payload,
111 }))) => {
112 assert_eq!(identifier, echo_request.identifier);
113 assert_eq!(sequence_number, echo_request.sequence_number);
114 assert_eq!(len, echo_request.len());
115 assert_eq!(payload.inner(), echo_request.payload());
116 }
117 x => panic!("{x:?}"),
118 }
119 }
120}