icmp_packet/
icmpv6.rs

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//
12#[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//
80#[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}