1use std::net::Ipv4Addr;
2
3use pnet::packet::Packet;
4use crate::packet::ethernet::ETHERNET_HEADER_LEN;
5use crate::packet::ipv4::IPV4_HEADER_LEN;
6
7pub const ICMPV4_HEADER_LEN: usize =
9 pnet::packet::icmp::echo_request::MutableEchoRequestPacket::minimum_packet_size();
10pub const ICMPV4_PACKET_LEN: usize = ETHERNET_HEADER_LEN + IPV4_HEADER_LEN + ICMPV4_HEADER_LEN;
12pub const ICMPV4_IP_PACKET_LEN: usize = IPV4_HEADER_LEN + ICMPV4_HEADER_LEN;
14
15#[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 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 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 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#[derive(Clone, Debug, PartialEq)]
172pub struct IcmpPacket {
173 pub icmp_type: IcmpType,
175 pub icmp_code: u8,
177 pub checksum: u16,
179 pub payload: Vec<u8>,
181}
182
183impl IcmpPacket {
184 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 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
200pub(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#[derive(Clone, Debug)]
213pub struct IcmpPacketBuilder {
214 pub src_ip: Ipv4Addr,
216 pub dst_ip: Ipv4Addr,
218 pub icmp_type: IcmpType,
220 pub sequence_number: Option<u16>,
222 pub identifier: Option<u16>,
224}
225
226impl IcmpPacketBuilder {
227 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 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}