cross_socket/packet/
icmp.rs

1use std::net::Ipv4Addr;
2
3use pnet::packet::Packet;
4use crate::packet::ethernet::ETHERNET_HEADER_LEN;
5use crate::packet::ipv4::IPV4_HEADER_LEN;
6
7/// ICMPv4 Header Length
8pub const ICMPV4_HEADER_LEN: usize =
9    pnet::packet::icmp::echo_request::MutableEchoRequestPacket::minimum_packet_size();
10/// ICMPv4 Minimum Packet Length
11pub const ICMPV4_PACKET_LEN: usize = ETHERNET_HEADER_LEN + IPV4_HEADER_LEN + ICMPV4_HEADER_LEN;
12/// ICMPv4 IP Packet Length
13pub const ICMPV4_IP_PACKET_LEN: usize = IPV4_HEADER_LEN + ICMPV4_HEADER_LEN;
14
15/// ICMP Types
16/// <https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml>
17#[derive(Clone, Debug, PartialEq)]
18pub enum IcmpType {
19    EchoReply,
20    DestinationUnreachable,
21    SourceQuench,
22    RedirectMessage,
23    EchoRequest,
24    RouterAdvertisement,
25    RouterSolicitation,
26    TimeExceeded,
27    ParameterProblem,
28    TimestampRequest,
29    TimestampReply,
30    InformationRequest,
31    InformationReply,
32    AddressMaskRequest,
33    AddressMaskReply,
34    Traceroute,
35    DatagramConversionError,
36    MobileHostRedirect,
37    IPv6WhereAreYou,
38    IPv6IAmHere,
39    MobileRegistrationRequest,
40    MobileRegistrationReply,
41    DomainNameRequest,
42    DomainNameReply,
43    SKIP,
44    Photuris,
45    Unknown(u8),
46}
47
48impl IcmpType {
49    /// Get the number of the ICMP type
50    pub fn number(&self) -> u8 {
51        match *self {
52            IcmpType::EchoReply => 0,
53            IcmpType::DestinationUnreachable => 3,
54            IcmpType::SourceQuench => 4,
55            IcmpType::RedirectMessage => 5,
56            IcmpType::EchoRequest => 8,
57            IcmpType::RouterAdvertisement => 9,
58            IcmpType::RouterSolicitation => 10,
59            IcmpType::TimeExceeded => 11,
60            IcmpType::ParameterProblem => 12,
61            IcmpType::TimestampRequest => 13,
62            IcmpType::TimestampReply => 14,
63            IcmpType::InformationRequest => 15,
64            IcmpType::InformationReply => 16,
65            IcmpType::AddressMaskRequest => 17,
66            IcmpType::AddressMaskReply => 18,
67            IcmpType::Traceroute => 30,
68            IcmpType::DatagramConversionError => 31,
69            IcmpType::MobileHostRedirect => 32,
70            IcmpType::IPv6WhereAreYou => 33,
71            IcmpType::IPv6IAmHere => 34,
72            IcmpType::MobileRegistrationRequest => 35,
73            IcmpType::MobileRegistrationReply => 36,
74            IcmpType::DomainNameRequest => 37,
75            IcmpType::DomainNameReply => 38,
76            IcmpType::SKIP => 39,
77            IcmpType::Photuris => 40,
78            IcmpType::Unknown(n) => n,
79        }
80    }
81    /// Get the ID of the ICMP type
82    pub fn id(&self) -> String {
83        match *self {
84            IcmpType::EchoReply => String::from("echo_reply"),
85            IcmpType::DestinationUnreachable => String::from("destination_unreachable"),
86            IcmpType::SourceQuench => String::from("source_quench"),
87            IcmpType::RedirectMessage => String::from("redirect_message"),
88            IcmpType::EchoRequest => String::from("echo_request"),
89            IcmpType::RouterAdvertisement => String::from("router_advertisement"),
90            IcmpType::RouterSolicitation => String::from("router_solicitation"),
91            IcmpType::TimeExceeded => String::from("time_exceeded"),
92            IcmpType::ParameterProblem => String::from("parameter_problem"),
93            IcmpType::TimestampRequest => String::from("timestamp_request"),
94            IcmpType::TimestampReply => String::from("timestamp_reply"),
95            IcmpType::InformationRequest => String::from("information_request"),
96            IcmpType::InformationReply => String::from("information_reply"),
97            IcmpType::AddressMaskRequest => String::from("address_mask_request"),
98            IcmpType::AddressMaskReply => String::from("address_mask_reply"),
99            IcmpType::Traceroute => String::from("traceroute"),
100            IcmpType::DatagramConversionError => String::from("datagram_conversion_error"),
101            IcmpType::MobileHostRedirect => String::from("mobile_host_redirect"),
102            IcmpType::IPv6WhereAreYou => String::from("ipv6_where_are_you"),
103            IcmpType::IPv6IAmHere => String::from("ipv6_i_am_here"),
104            IcmpType::MobileRegistrationRequest => String::from("mobile_registration_request"),
105            IcmpType::MobileRegistrationReply => String::from("mobile_registration_reply"),
106            IcmpType::DomainNameRequest => String::from("domain_name_request"),
107            IcmpType::DomainNameReply => String::from("domain_name_reply"),
108            IcmpType::SKIP => String::from("skip"),
109            IcmpType::Photuris => String::from("photuris"),
110            IcmpType::Unknown(n) => format!("unknown_{}", n),
111        }
112    }
113    /// Get the name of the ICMP type
114    pub fn name(&self) -> String {
115        match *self {
116            IcmpType::EchoReply => String::from("Echo Reply"),
117            IcmpType::DestinationUnreachable => String::from("Destination Unreachable"),
118            IcmpType::SourceQuench => String::from("Source Quench"),
119            IcmpType::RedirectMessage => String::from("Redirect Message"),
120            IcmpType::EchoRequest => String::from("Echo Request"),
121            IcmpType::RouterAdvertisement => String::from("Router Advertisement"),
122            IcmpType::RouterSolicitation => String::from("Router Solicitation"),
123            IcmpType::TimeExceeded => String::from("Time Exceeded"),
124            IcmpType::ParameterProblem => String::from("Parameter Problem"),
125            IcmpType::TimestampRequest => String::from("Timestamp Request"),
126            IcmpType::TimestampReply => String::from("Timestamp Reply"),
127            IcmpType::InformationRequest => String::from("Information Request"),
128            IcmpType::InformationReply => String::from("Information Reply"),
129            IcmpType::AddressMaskRequest => String::from("Address Mask Request"),
130            IcmpType::AddressMaskReply => String::from("Address Mask Reply"),
131            IcmpType::Traceroute => String::from("Traceroute"),
132            IcmpType::DatagramConversionError => String::from("Datagram Conversion Error"),
133            IcmpType::MobileHostRedirect => String::from("Mobile Host Redirect"),
134            IcmpType::IPv6WhereAreYou => String::from("IPv6 Where Are You"),
135            IcmpType::IPv6IAmHere => String::from("IPv6 I Am Here"),
136            IcmpType::MobileRegistrationRequest => String::from("Mobile Registration Request"),
137            IcmpType::MobileRegistrationReply => String::from("Mobile Registration Reply"),
138            IcmpType::DomainNameRequest => String::from("Domain Name Request"),
139            IcmpType::DomainNameReply => String::from("Domain Name Reply"),
140            IcmpType::SKIP => String::from("SKIP"),
141            IcmpType::Photuris => String::from("Photuris"),
142            IcmpType::Unknown(n) => format!("Unknown ({})", n),
143        }
144    }
145    pub(crate) fn from_pnet_type(t: pnet::packet::icmp::IcmpType) -> IcmpType {
146        match t {
147            pnet::packet::icmp::IcmpTypes::EchoReply => IcmpType::EchoReply,
148            pnet::packet::icmp::IcmpTypes::DestinationUnreachable => {
149                IcmpType::DestinationUnreachable
150            }
151            pnet::packet::icmp::IcmpTypes::SourceQuench => IcmpType::SourceQuench,
152            pnet::packet::icmp::IcmpTypes::RedirectMessage => IcmpType::RedirectMessage,
153            pnet::packet::icmp::IcmpTypes::EchoRequest => IcmpType::EchoRequest,
154            pnet::packet::icmp::IcmpTypes::RouterAdvertisement => IcmpType::RouterAdvertisement,
155            pnet::packet::icmp::IcmpTypes::RouterSolicitation => IcmpType::RouterSolicitation,
156            pnet::packet::icmp::IcmpTypes::TimeExceeded => IcmpType::TimeExceeded,
157            pnet::packet::icmp::IcmpTypes::ParameterProblem => IcmpType::ParameterProblem,
158            pnet::packet::icmp::IcmpTypes::Timestamp => IcmpType::TimestampRequest,
159            pnet::packet::icmp::IcmpTypes::TimestampReply => IcmpType::TimestampReply,
160            pnet::packet::icmp::IcmpTypes::InformationRequest => IcmpType::InformationRequest,
161            pnet::packet::icmp::IcmpTypes::InformationReply => IcmpType::InformationReply,
162            pnet::packet::icmp::IcmpTypes::AddressMaskRequest => IcmpType::AddressMaskRequest,
163            pnet::packet::icmp::IcmpTypes::AddressMaskReply => IcmpType::AddressMaskReply,
164            pnet::packet::icmp::IcmpTypes::Traceroute => IcmpType::Traceroute,
165            _ => IcmpType::Unknown(t.0),
166        }
167    }
168}
169
170/// Represents an ICMP packet.
171#[derive(Clone, Debug, PartialEq)]
172pub struct IcmpPacket {
173    /// ICMP type
174    pub icmp_type: IcmpType,
175    /// ICMP code
176    pub icmp_code: u8,
177    /// ICMP checksum
178    pub checksum: u16,
179    /// Payload.
180    pub payload: Vec<u8>,
181}
182
183impl IcmpPacket {
184    /// Constructs a new IcmpPacket from pnet::packet::icmp::IcmpPacket.
185    pub(crate) fn from_pnet_packet(packet: &pnet::packet::icmp::IcmpPacket) -> IcmpPacket {
186        IcmpPacket {
187            icmp_type: IcmpType::from_pnet_type(packet.get_icmp_type()),
188            icmp_code: packet.get_icmp_code().0,
189            checksum: packet.get_checksum(),
190            payload: packet.payload().to_vec(),
191        }
192    }
193    /// Constructs a new IcmpPacket from bytes
194    pub fn from_bytes(packet: &[u8]) -> IcmpPacket {
195        let icmp_packet = pnet::packet::icmp::IcmpPacket::new(packet).unwrap();
196        IcmpPacket::from_pnet_packet(&icmp_packet)
197    }
198}
199
200/// Build ICMP packet
201pub(crate) fn build_icmp_echo_packet(
202    icmp_packet: &mut pnet::packet::icmp::echo_request::MutableEchoRequestPacket,
203) {
204    icmp_packet.set_icmp_type(pnet::packet::icmp::IcmpTypes::EchoRequest);
205    icmp_packet.set_sequence_number(rand::random::<u16>());
206    icmp_packet.set_identifier(rand::random::<u16>());
207    let icmp_check_sum = pnet::packet::util::checksum(&icmp_packet.packet(), 1);
208    icmp_packet.set_checksum(icmp_check_sum);
209}
210
211/// ICMP Packet Builder
212#[derive(Clone, Debug)]
213pub struct IcmpPacketBuilder {
214    /// Source IPv4 address
215    pub src_ip: Ipv4Addr,
216    /// Destination IPv4 address
217    pub dst_ip: Ipv4Addr,
218    /// ICMP type
219    pub icmp_type: IcmpType,
220    /// ICMP sequence number
221    pub sequence_number: Option<u16>,
222    /// ICMP identifier
223    pub identifier: Option<u16>,
224}
225
226impl IcmpPacketBuilder {
227    /// Constructs a new IcmpPacketBuilder
228    pub fn new(src_ip: Ipv4Addr, dst_ip: Ipv4Addr) -> IcmpPacketBuilder {
229        IcmpPacketBuilder {
230            src_ip: src_ip,
231            dst_ip: dst_ip,
232            icmp_type: IcmpType::EchoRequest,
233            sequence_number: None,
234            identifier: None,
235        }
236    }
237    /// Build ICMP packet and return bytes
238    pub fn build(&self) -> Vec<u8> {
239        let buffer: &mut [u8] = &mut [0u8; ICMPV4_HEADER_LEN];
240        let mut icmp_packet =
241            pnet::packet::icmp::echo_request::MutableEchoRequestPacket::new(buffer).unwrap();
242        icmp_packet.set_icmp_type(pnet::packet::icmp::IcmpType(self.icmp_type.number()));
243        if let Some(sequence_number) = self.sequence_number {
244            icmp_packet.set_sequence_number(sequence_number);
245        } else {
246            icmp_packet.set_sequence_number(rand::random::<u16>());
247        }
248        if let Some(identifier) = self.identifier {
249            icmp_packet.set_identifier(identifier);
250        } else {
251            icmp_packet.set_identifier(rand::random::<u16>());
252        }
253        let icmp_check_sum = pnet::packet::util::checksum(&icmp_packet.packet(), 1);
254        icmp_packet.set_checksum(icmp_check_sum);
255        icmp_packet.packet().to_vec()
256    }
257}