1use crate::PrimitiveValues;
4
5use alloc::vec::Vec;
6
7use crate::ethernet::ETHERNET_HEADER_LEN;
8use crate::ipv4::IPV4_HEADER_LEN;
9use xenet_macro::packet;
10use xenet_macro_helper::types::*;
11
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15pub const ICMPV4_HEADER_LEN: usize = echo_request::MutableEchoRequestPacket::minimum_packet_size();
17pub const ICMPV4_PACKET_LEN: usize = ETHERNET_HEADER_LEN + IPV4_HEADER_LEN + ICMPV4_HEADER_LEN;
19pub const ICMPV4_IP_PACKET_LEN: usize = IPV4_HEADER_LEN + ICMPV4_HEADER_LEN;
21
22#[derive(Clone, Debug, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25pub struct IcmpHeader {
26 pub icmp_type: IcmpType,
27 pub icmp_code: IcmpCode,
28 pub checksum: u16be,
29}
30
31impl IcmpHeader {
32 pub fn from_bytes(packet: &[u8]) -> Result<IcmpHeader, String> {
34 if packet.len() < ICMPV4_HEADER_LEN {
35 return Err("Packet is too small for ICMPv4 header".to_string());
36 }
37 match IcmpPacket::new(packet) {
38 Some(icmp_packet) => Ok(IcmpHeader {
39 icmp_type: icmp_packet.get_icmp_type(),
40 icmp_code: icmp_packet.get_icmp_code(),
41 checksum: icmp_packet.get_checksum(),
42 }),
43 None => Err("Failed to parse ICMPv4 packet".to_string()),
44 }
45 }
46 pub(crate) fn from_packet(icmp_packet: &IcmpPacket) -> IcmpHeader {
48 IcmpHeader {
49 icmp_type: icmp_packet.get_icmp_type(),
50 icmp_code: icmp_packet.get_icmp_code(),
51 checksum: icmp_packet.get_checksum(),
52 }
53 }
54}
55
56#[repr(u8)]
58#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
59#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
60pub enum IcmpType {
61 EchoReply,
62 DestinationUnreachable,
63 SourceQuench,
64 RedirectMessage,
65 EchoRequest,
66 RouterAdvertisement,
67 RouterSolicitation,
68 TimeExceeded,
69 ParameterProblem,
70 TimestampRequest,
71 TimestampReply,
72 InformationRequest,
73 InformationReply,
74 AddressMaskRequest,
75 AddressMaskReply,
76 Traceroute,
77 DatagramConversionError,
78 MobileHostRedirect,
79 IPv6WhereAreYou,
80 IPv6IAmHere,
81 MobileRegistrationRequest,
82 MobileRegistrationReply,
83 DomainNameRequest,
84 DomainNameReply,
85 SKIP,
86 Photuris,
87 Unknown(u8),
88}
89
90impl IcmpType {
91 pub fn new(val: u8) -> IcmpType {
93 match val {
94 0 => IcmpType::EchoReply,
95 3 => IcmpType::DestinationUnreachable,
96 4 => IcmpType::SourceQuench,
97 5 => IcmpType::RedirectMessage,
98 8 => IcmpType::EchoRequest,
99 9 => IcmpType::RouterAdvertisement,
100 10 => IcmpType::RouterSolicitation,
101 11 => IcmpType::TimeExceeded,
102 12 => IcmpType::ParameterProblem,
103 13 => IcmpType::TimestampRequest,
104 14 => IcmpType::TimestampReply,
105 15 => IcmpType::InformationRequest,
106 16 => IcmpType::InformationReply,
107 17 => IcmpType::AddressMaskRequest,
108 18 => IcmpType::AddressMaskReply,
109 30 => IcmpType::Traceroute,
110 31 => IcmpType::DatagramConversionError,
111 32 => IcmpType::MobileHostRedirect,
112 33 => IcmpType::IPv6WhereAreYou,
113 34 => IcmpType::IPv6IAmHere,
114 35 => IcmpType::MobileRegistrationRequest,
115 36 => IcmpType::MobileRegistrationReply,
116 37 => IcmpType::DomainNameRequest,
117 38 => IcmpType::DomainNameReply,
118 39 => IcmpType::SKIP,
119 40 => IcmpType::Photuris,
120 n => IcmpType::Unknown(n),
121 }
122 }
123 pub fn name(&self) -> String {
125 match *self {
126 IcmpType::EchoReply => String::from("Echo Reply"),
127 IcmpType::DestinationUnreachable => String::from("Destination Unreachable"),
128 IcmpType::SourceQuench => String::from("Source Quench"),
129 IcmpType::RedirectMessage => String::from("Redirect Message"),
130 IcmpType::EchoRequest => String::from("Echo Request"),
131 IcmpType::RouterAdvertisement => String::from("Router Advertisement"),
132 IcmpType::RouterSolicitation => String::from("Router Solicitation"),
133 IcmpType::TimeExceeded => String::from("Time Exceeded"),
134 IcmpType::ParameterProblem => String::from("Parameter Problem"),
135 IcmpType::TimestampRequest => String::from("Timestamp Request"),
136 IcmpType::TimestampReply => String::from("Timestamp Reply"),
137 IcmpType::InformationRequest => String::from("Information Request"),
138 IcmpType::InformationReply => String::from("Information Reply"),
139 IcmpType::AddressMaskRequest => String::from("Address Mask Request"),
140 IcmpType::AddressMaskReply => String::from("Address Mask Reply"),
141 IcmpType::Traceroute => String::from("Traceroute"),
142 IcmpType::DatagramConversionError => String::from("Datagram Conversion Error"),
143 IcmpType::MobileHostRedirect => String::from("Mobile Host Redirect"),
144 IcmpType::IPv6WhereAreYou => String::from("IPv6 Where Are You"),
145 IcmpType::IPv6IAmHere => String::from("IPv6 I Am Here"),
146 IcmpType::MobileRegistrationRequest => String::from("Mobile Registration Request"),
147 IcmpType::MobileRegistrationReply => String::from("Mobile Registration Reply"),
148 IcmpType::DomainNameRequest => String::from("Domain Name Request"),
149 IcmpType::DomainNameReply => String::from("Domain Name Reply"),
150 IcmpType::SKIP => String::from("SKIP"),
151 IcmpType::Photuris => String::from("Photuris"),
152 IcmpType::Unknown(n) => format!("Unknown ({})", n),
153 }
154 }
155}
156
157impl PrimitiveValues for IcmpType {
158 type T = (u8,);
159 fn to_primitive_values(&self) -> (u8,) {
160 match *self {
161 IcmpType::EchoReply => (0,),
162 IcmpType::DestinationUnreachable => (3,),
163 IcmpType::SourceQuench => (4,),
164 IcmpType::RedirectMessage => (5,),
165 IcmpType::EchoRequest => (8,),
166 IcmpType::RouterAdvertisement => (9,),
167 IcmpType::RouterSolicitation => (10,),
168 IcmpType::TimeExceeded => (11,),
169 IcmpType::ParameterProblem => (12,),
170 IcmpType::TimestampRequest => (13,),
171 IcmpType::TimestampReply => (14,),
172 IcmpType::InformationRequest => (15,),
173 IcmpType::InformationReply => (16,),
174 IcmpType::AddressMaskRequest => (17,),
175 IcmpType::AddressMaskReply => (18,),
176 IcmpType::Traceroute => (30,),
177 IcmpType::DatagramConversionError => (31,),
178 IcmpType::MobileHostRedirect => (32,),
179 IcmpType::IPv6WhereAreYou => (33,),
180 IcmpType::IPv6IAmHere => (34,),
181 IcmpType::MobileRegistrationRequest => (35,),
182 IcmpType::MobileRegistrationReply => (36,),
183 IcmpType::DomainNameRequest => (37,),
184 IcmpType::DomainNameReply => (38,),
185 IcmpType::SKIP => (39,),
186 IcmpType::Photuris => (40,),
187 IcmpType::Unknown(n) => (n,),
188 }
189 }
190}
191
192#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
194#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
195pub struct IcmpCode(pub u8);
196
197impl IcmpCode {
198 pub fn new(val: u8) -> IcmpCode {
200 IcmpCode(val)
201 }
202}
203
204impl PrimitiveValues for IcmpCode {
205 type T = (u8,);
206 fn to_primitive_values(&self) -> (u8,) {
207 (self.0,)
208 }
209}
210
211#[packet]
213pub struct Icmp {
214 #[construct_with(u8)]
215 pub icmp_type: IcmpType,
216 #[construct_with(u8)]
217 pub icmp_code: IcmpCode,
218 pub checksum: u16be,
219 #[payload]
223 pub payload: Vec<u8>,
224}
225
226pub fn checksum(packet: &IcmpPacket) -> u16be {
228 use crate::util;
229 use crate::Packet;
230
231 util::checksum(packet.packet(), 1)
232}
233
234#[cfg(test)]
235mod checksum_tests {
236 use super::*;
237 use alloc::vec;
238
239 #[test]
240 fn checksum_zeros() {
241 let mut data = vec![0u8; 8];
242 let expected = 65535;
243 let mut pkg = MutableIcmpPacket::new(&mut data[..]).unwrap();
244 assert_eq!(checksum(&pkg.to_immutable()), expected);
245 pkg.set_checksum(123);
246 assert_eq!(checksum(&pkg.to_immutable()), expected);
247 }
248
249 #[test]
250 fn checksum_nonzero() {
251 let mut data = vec![255u8; 8];
252 let expected = 0;
253 let mut pkg = MutableIcmpPacket::new(&mut data[..]).unwrap();
254 assert_eq!(checksum(&pkg.to_immutable()), expected);
255 pkg.set_checksum(0);
256 assert_eq!(checksum(&pkg.to_immutable()), expected);
257 }
258
259 #[test]
260 fn checksum_odd_bytes() {
261 let mut data = vec![191u8; 7];
262 let expected = 49535;
263 let pkg = IcmpPacket::new(&mut data[..]).unwrap();
264 assert_eq!(checksum(&pkg), expected);
265 }
266}
267
268pub mod echo_reply {
269 use crate::icmp::{IcmpCode, IcmpType};
282 use crate::PrimitiveValues;
283
284 use alloc::vec::Vec;
285
286 use xenet_macro::packet;
287 use xenet_macro_helper::types::*;
288
289 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
291 pub struct Identifier(pub u16);
292
293 impl Identifier {
294 pub fn new(val: u16) -> Identifier {
296 Identifier(val)
297 }
298 }
299
300 impl PrimitiveValues for Identifier {
301 type T = (u16,);
302 fn to_primitive_values(&self) -> (u16,) {
303 (self.0,)
304 }
305 }
306
307 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
309 pub struct SequenceNumber(pub u16);
310
311 impl SequenceNumber {
312 pub fn new(val: u16) -> SequenceNumber {
314 SequenceNumber(val)
315 }
316 }
317
318 impl PrimitiveValues for SequenceNumber {
319 type T = (u16,);
320 fn to_primitive_values(&self) -> (u16,) {
321 (self.0,)
322 }
323 }
324
325 #[allow(non_snake_case)]
328 #[allow(non_upper_case_globals)]
329 pub mod IcmpCodes {
330 use crate::icmp::IcmpCode;
331 pub const NoCode: IcmpCode = IcmpCode(0);
333 }
334
335 #[packet]
337 pub struct EchoReply {
338 #[construct_with(u8)]
339 pub icmp_type: IcmpType,
340 #[construct_with(u8)]
341 pub icmp_code: IcmpCode,
342 pub checksum: u16be,
343 pub identifier: u16be,
344 pub sequence_number: u16be,
345 #[payload]
346 pub payload: Vec<u8>,
347 }
348}
349
350pub mod echo_request {
351 use crate::icmp::{IcmpCode, IcmpType};
364 use crate::PrimitiveValues;
365
366 use alloc::vec::Vec;
367
368 use xenet_macro::packet;
369 use xenet_macro_helper::types::*;
370
371 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
373 pub struct Identifier(pub u16);
374
375 impl Identifier {
376 pub fn new(val: u16) -> Identifier {
378 Identifier(val)
379 }
380 }
381
382 impl PrimitiveValues for Identifier {
383 type T = (u16,);
384 fn to_primitive_values(&self) -> (u16,) {
385 (self.0,)
386 }
387 }
388
389 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
391 pub struct SequenceNumber(pub u16);
392
393 impl SequenceNumber {
394 pub fn new(val: u16) -> SequenceNumber {
396 SequenceNumber(val)
397 }
398 }
399
400 impl PrimitiveValues for SequenceNumber {
401 type T = (u16,);
402 fn to_primitive_values(&self) -> (u16,) {
403 (self.0,)
404 }
405 }
406
407 #[allow(non_snake_case)]
410 #[allow(non_upper_case_globals)]
411 pub mod IcmpCodes {
412 use crate::icmp::IcmpCode;
413 pub const NoCode: IcmpCode = IcmpCode(0);
415 }
416
417 #[packet]
419 pub struct EchoRequest {
420 #[construct_with(u8)]
421 pub icmp_type: IcmpType,
422 #[construct_with(u8)]
423 pub icmp_code: IcmpCode,
424 pub checksum: u16be,
425 pub identifier: u16be,
426 pub sequence_number: u16be,
427 #[payload]
428 pub payload: Vec<u8>,
429 }
430}
431
432pub mod destination_unreachable {
433 use crate::icmp::{IcmpCode, IcmpType};
446
447 use alloc::vec::Vec;
448
449 use xenet_macro::packet;
450 use xenet_macro_helper::types::*;
451
452 #[allow(non_snake_case)]
454 #[allow(non_upper_case_globals)]
455 pub mod IcmpCodes {
456 use crate::icmp::IcmpCode;
457 pub const DestinationNetworkUnreachable: IcmpCode = IcmpCode(0);
459 pub const DestinationHostUnreachable: IcmpCode = IcmpCode(1);
461 pub const DestinationProtocolUnreachable: IcmpCode = IcmpCode(2);
463 pub const DestinationPortUnreachable: IcmpCode = IcmpCode(3);
465 pub const FragmentationRequiredAndDFFlagSet: IcmpCode = IcmpCode(4);
467 pub const SourceRouteFailed: IcmpCode = IcmpCode(5);
469 pub const DestinationNetworkUnknown: IcmpCode = IcmpCode(6);
471 pub const DestinationHostUnknown: IcmpCode = IcmpCode(7);
473 pub const SourceHostIsolated: IcmpCode = IcmpCode(8);
475 pub const NetworkAdministrativelyProhibited: IcmpCode = IcmpCode(9);
477 pub const HostAdministrativelyProhibited: IcmpCode = IcmpCode(10);
479 pub const NetworkUnreachableForTOS: IcmpCode = IcmpCode(11);
481 pub const HostUnreachableForTOS: IcmpCode = IcmpCode(12);
483 pub const CommunicationAdministrativelyProhibited: IcmpCode = IcmpCode(13);
485 pub const HostPrecedenceViolation: IcmpCode = IcmpCode(14);
487 pub const PrecedenceCutoffInEffect: IcmpCode = IcmpCode(15);
489 }
490
491 #[packet]
493 pub struct DestinationUnreachable {
494 #[construct_with(u8)]
495 pub icmp_type: IcmpType,
496 #[construct_with(u8)]
497 pub icmp_code: IcmpCode,
498 pub checksum: u16be,
499 pub unused: u16be,
500 pub next_hop_mtu: u16be,
501 #[payload]
502 pub payload: Vec<u8>,
503 }
504}
505
506pub mod time_exceeded {
507 use crate::icmp::{IcmpCode, IcmpType};
520
521 use alloc::vec::Vec;
522
523 use xenet_macro::packet;
524 use xenet_macro_helper::types::*;
525
526 #[allow(non_snake_case)]
528 #[allow(non_upper_case_globals)]
529 pub mod IcmpCodes {
530 use crate::icmp::IcmpCode;
531 pub const TimeToLiveExceededInTransit: IcmpCode = IcmpCode(0);
533 pub const FragmentReasemblyTimeExceeded: IcmpCode = IcmpCode(1);
535 }
536
537 #[packet]
539 pub struct TimeExceeded {
540 #[construct_with(u8)]
541 pub icmp_type: IcmpType,
542 #[construct_with(u8)]
543 pub icmp_code: IcmpCode,
544 pub checksum: u16be,
545 pub unused: u32be,
546 #[payload]
547 pub payload: Vec<u8>,
548 }
549}