1pub mod builder;
7pub mod checksum;
8pub mod error;
9pub mod extensions;
10pub mod types;
11
12pub use builder::IcmpBuilder;
14
15pub use checksum::{icmp_checksum, verify_icmp_checksum};
17pub use error::{ERROR_MIN_PAYLOAD, ERROR_TYPES, error_payload_offset, is_error_type};
18pub use extensions::{
19 InterfaceInformation, IpAddr, class_name as extension_class_name, has_extensions,
20 parse_extension_header, parse_extension_object, parse_interface_information,
21};
22pub use types::{ICMP_ANSWERS, code_name, type_name};
23
24use crate::layer::field::{FieldDesc, FieldError, FieldType, FieldValue};
25use crate::layer::{Layer, LayerIndex, LayerKind};
26use std::net::Ipv4Addr;
27
28pub const ICMP_MIN_HEADER_LEN: usize = 8;
30
31pub mod offsets {
33 pub const TYPE: usize = 0;
34 pub const CODE: usize = 1;
35 pub const CHECKSUM: usize = 2;
36
37 pub const ID: usize = 4;
41 pub const SEQ: usize = 6;
42
43 pub const GATEWAY: usize = 4;
45
46 pub const UNUSED_DU: usize = 4;
48 pub const LENGTH_DU: usize = 5;
49 pub const NEXT_HOP_MTU: usize = 6;
50
51 pub const UNUSED_TE: usize = 4;
53 pub const LENGTH_TE: usize = 6;
54
55 pub const PTR: usize = 4;
57 pub const UNUSED_PP: usize = 5;
58 pub const LENGTH_PP: usize = 6;
59
60 pub const TS_ORI: usize = 8;
62 pub const TS_RX: usize = 12;
63 pub const TS_TX: usize = 16;
64
65 pub const ADDR_MASK: usize = 4;
67}
68
69pub static FIELDS: &[FieldDesc] = &[
71 FieldDesc::new("type", offsets::TYPE, 1, FieldType::U8),
72 FieldDesc::new("code", offsets::CODE, 1, FieldType::U8),
73 FieldDesc::new("chksum", offsets::CHECKSUM, 2, FieldType::U16),
74 FieldDesc::new("id", offsets::ID, 2, FieldType::U16),
76 FieldDesc::new("seq", offsets::SEQ, 2, FieldType::U16),
77];
78
79#[derive(Debug, Clone)]
105pub struct IcmpLayer {
106 pub index: LayerIndex,
107}
108
109impl IcmpLayer {
110 #[must_use]
112 pub fn new(index: LayerIndex) -> Self {
113 Self { index }
114 }
115
116 pub fn icmp_type(&self, buf: &[u8]) -> Result<u8, FieldError> {
118 let slice = self.index.slice(buf);
119 if slice.is_empty() {
120 return Err(FieldError::BufferTooShort {
121 offset: self.index.start + offsets::TYPE,
122 need: 1,
123 have: 0,
124 });
125 }
126 Ok(slice[offsets::TYPE])
127 }
128
129 pub fn code(&self, buf: &[u8]) -> Result<u8, FieldError> {
131 let slice = self.index.slice(buf);
132 if slice.len() < offsets::CODE + 1 {
133 return Err(FieldError::BufferTooShort {
134 offset: self.index.start + offsets::CODE,
135 need: 1,
136 have: slice.len().saturating_sub(offsets::CODE),
137 });
138 }
139 Ok(slice[offsets::CODE])
140 }
141
142 pub fn checksum(&self, buf: &[u8]) -> Result<u16, FieldError> {
144 let slice = self.index.slice(buf);
145 if slice.len() < offsets::CHECKSUM + 2 {
146 return Err(FieldError::BufferTooShort {
147 offset: self.index.start + offsets::CHECKSUM,
148 need: 2,
149 have: slice.len().saturating_sub(offsets::CHECKSUM),
150 });
151 }
152 Ok(u16::from_be_bytes([
153 slice[offsets::CHECKSUM],
154 slice[offsets::CHECKSUM + 1],
155 ]))
156 }
157
158 pub fn id(&self, buf: &[u8]) -> Result<Option<u16>, FieldError> {
162 let icmp_type = self.icmp_type(buf)?;
163
164 if !matches!(
166 icmp_type,
167 types::types::ECHO_REQUEST
168 | types::types::ECHO_REPLY
169 | types::types::TIMESTAMP
170 | types::types::TIMESTAMP_REPLY
171 | types::types::INFO_REQUEST
172 | types::types::INFO_REPLY
173 | types::types::ADDRESS_MASK_REQUEST
174 | types::types::ADDRESS_MASK_REPLY
175 ) {
176 return Ok(None);
177 }
178
179 let slice = self.index.slice(buf);
180 if slice.len() < offsets::ID + 2 {
181 return Err(FieldError::BufferTooShort {
182 offset: self.index.start + offsets::ID,
183 need: 2,
184 have: slice.len().saturating_sub(offsets::ID),
185 });
186 }
187 Ok(Some(u16::from_be_bytes([
188 slice[offsets::ID],
189 slice[offsets::ID + 1],
190 ])))
191 }
192
193 pub fn seq(&self, buf: &[u8]) -> Result<Option<u16>, FieldError> {
197 let icmp_type = self.icmp_type(buf)?;
198
199 if !matches!(
201 icmp_type,
202 types::types::ECHO_REQUEST
203 | types::types::ECHO_REPLY
204 | types::types::TIMESTAMP
205 | types::types::TIMESTAMP_REPLY
206 | types::types::INFO_REQUEST
207 | types::types::INFO_REPLY
208 | types::types::ADDRESS_MASK_REQUEST
209 | types::types::ADDRESS_MASK_REPLY
210 ) {
211 return Ok(None);
212 }
213
214 let slice = self.index.slice(buf);
215 if slice.len() < offsets::SEQ + 2 {
216 return Err(FieldError::BufferTooShort {
217 offset: self.index.start + offsets::SEQ,
218 need: 2,
219 have: slice.len().saturating_sub(offsets::SEQ),
220 });
221 }
222 Ok(Some(u16::from_be_bytes([
223 slice[offsets::SEQ],
224 slice[offsets::SEQ + 1],
225 ])))
226 }
227
228 pub fn gateway(&self, buf: &[u8]) -> Result<Option<Ipv4Addr>, FieldError> {
232 let icmp_type = self.icmp_type(buf)?;
233
234 if icmp_type != types::types::REDIRECT {
235 return Ok(None);
236 }
237
238 let slice = self.index.slice(buf);
239 if slice.len() < offsets::GATEWAY + 4 {
240 return Err(FieldError::BufferTooShort {
241 offset: self.index.start + offsets::GATEWAY,
242 need: 4,
243 have: slice.len().saturating_sub(offsets::GATEWAY),
244 });
245 }
246
247 Ok(Some(Ipv4Addr::new(
248 slice[offsets::GATEWAY],
249 slice[offsets::GATEWAY + 1],
250 slice[offsets::GATEWAY + 2],
251 slice[offsets::GATEWAY + 3],
252 )))
253 }
254
255 pub fn next_hop_mtu(&self, buf: &[u8]) -> Result<Option<u16>, FieldError> {
259 let icmp_type = self.icmp_type(buf)?;
260 let code = self.code(buf)?;
261
262 if icmp_type != types::types::DEST_UNREACH || code != 4 {
264 return Ok(None);
265 }
266
267 let slice = self.index.slice(buf);
268 if slice.len() < offsets::NEXT_HOP_MTU + 2 {
269 return Err(FieldError::BufferTooShort {
270 offset: self.index.start + offsets::NEXT_HOP_MTU,
271 need: 2,
272 have: slice.len().saturating_sub(offsets::NEXT_HOP_MTU),
273 });
274 }
275
276 Ok(Some(u16::from_be_bytes([
277 slice[offsets::NEXT_HOP_MTU],
278 slice[offsets::NEXT_HOP_MTU + 1],
279 ])))
280 }
281
282 pub fn ptr(&self, buf: &[u8]) -> Result<Option<u8>, FieldError> {
286 let icmp_type = self.icmp_type(buf)?;
287
288 if icmp_type != types::types::PARAM_PROBLEM {
289 return Ok(None);
290 }
291
292 let slice = self.index.slice(buf);
293 if slice.len() < offsets::PTR + 1 {
294 return Err(FieldError::BufferTooShort {
295 offset: self.index.start + offsets::PTR,
296 need: 1,
297 have: slice.len().saturating_sub(offsets::PTR),
298 });
299 }
300
301 Ok(Some(slice[offsets::PTR]))
302 }
303
304 pub fn ts_ori(&self, buf: &[u8]) -> Result<Option<u32>, FieldError> {
309 let icmp_type = self.icmp_type(buf)?;
310
311 if !matches!(
312 icmp_type,
313 types::types::TIMESTAMP | types::types::TIMESTAMP_REPLY
314 ) {
315 return Ok(None);
316 }
317
318 let slice = self.index.slice(buf);
319 if slice.len() < offsets::TS_ORI + 4 {
320 return Err(FieldError::BufferTooShort {
321 offset: self.index.start + offsets::TS_ORI,
322 need: 4,
323 have: slice.len().saturating_sub(offsets::TS_ORI),
324 });
325 }
326
327 Ok(Some(u32::from_be_bytes([
328 slice[offsets::TS_ORI],
329 slice[offsets::TS_ORI + 1],
330 slice[offsets::TS_ORI + 2],
331 slice[offsets::TS_ORI + 3],
332 ])))
333 }
334
335 pub fn ts_rx(&self, buf: &[u8]) -> Result<Option<u32>, FieldError> {
340 let icmp_type = self.icmp_type(buf)?;
341
342 if !matches!(
343 icmp_type,
344 types::types::TIMESTAMP | types::types::TIMESTAMP_REPLY
345 ) {
346 return Ok(None);
347 }
348
349 let slice = self.index.slice(buf);
350 if slice.len() < offsets::TS_RX + 4 {
351 return Err(FieldError::BufferTooShort {
352 offset: self.index.start + offsets::TS_RX,
353 need: 4,
354 have: slice.len().saturating_sub(offsets::TS_RX),
355 });
356 }
357
358 Ok(Some(u32::from_be_bytes([
359 slice[offsets::TS_RX],
360 slice[offsets::TS_RX + 1],
361 slice[offsets::TS_RX + 2],
362 slice[offsets::TS_RX + 3],
363 ])))
364 }
365
366 pub fn ts_tx(&self, buf: &[u8]) -> Result<Option<u32>, FieldError> {
371 let icmp_type = self.icmp_type(buf)?;
372
373 if !matches!(
374 icmp_type,
375 types::types::TIMESTAMP | types::types::TIMESTAMP_REPLY
376 ) {
377 return Ok(None);
378 }
379
380 let slice = self.index.slice(buf);
381 if slice.len() < offsets::TS_TX + 4 {
382 return Err(FieldError::BufferTooShort {
383 offset: self.index.start + offsets::TS_TX,
384 need: 4,
385 have: slice.len().saturating_sub(offsets::TS_TX),
386 });
387 }
388
389 Ok(Some(u32::from_be_bytes([
390 slice[offsets::TS_TX],
391 slice[offsets::TS_TX + 1],
392 slice[offsets::TS_TX + 2],
393 slice[offsets::TS_TX + 3],
394 ])))
395 }
396
397 pub fn addr_mask(&self, buf: &[u8]) -> Result<Option<Ipv4Addr>, FieldError> {
401 let icmp_type = self.icmp_type(buf)?;
402
403 if !matches!(
404 icmp_type,
405 types::types::ADDRESS_MASK_REQUEST | types::types::ADDRESS_MASK_REPLY
406 ) {
407 return Ok(None);
408 }
409
410 let slice = self.index.slice(buf);
411 if slice.len() < offsets::ADDR_MASK + 4 {
412 return Err(FieldError::BufferTooShort {
413 offset: self.index.start + offsets::ADDR_MASK,
414 need: 4,
415 have: slice.len().saturating_sub(offsets::ADDR_MASK),
416 });
417 }
418
419 Ok(Some(Ipv4Addr::new(
420 slice[offsets::ADDR_MASK],
421 slice[offsets::ADDR_MASK + 1],
422 slice[offsets::ADDR_MASK + 2],
423 slice[offsets::ADDR_MASK + 3],
424 )))
425 }
426
427 pub fn set_type(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
429 let start = self.index.start + offsets::TYPE;
430 if buf.len() < start + 1 {
431 return Err(FieldError::BufferTooShort {
432 offset: start,
433 need: 1,
434 have: buf.len().saturating_sub(start),
435 });
436 }
437 buf[start] = value;
438 Ok(())
439 }
440
441 pub fn set_code(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
443 let start = self.index.start + offsets::CODE;
444 if buf.len() < start + 1 {
445 return Err(FieldError::BufferTooShort {
446 offset: start,
447 need: 1,
448 have: buf.len().saturating_sub(start),
449 });
450 }
451 buf[start] = value;
452 Ok(())
453 }
454
455 pub fn set_checksum(&self, buf: &mut [u8], value: u16) -> Result<(), FieldError> {
457 let start = self.index.start + offsets::CHECKSUM;
458 if buf.len() < start + 2 {
459 return Err(FieldError::BufferTooShort {
460 offset: start,
461 need: 2,
462 have: buf.len().saturating_sub(start),
463 });
464 }
465 buf[start..start + 2].copy_from_slice(&value.to_be_bytes());
466 Ok(())
467 }
468
469 pub fn set_id(&self, buf: &mut [u8], value: u16) -> Result<(), FieldError> {
471 let start = self.index.start + offsets::ID;
472 if buf.len() < start + 2 {
473 return Err(FieldError::BufferTooShort {
474 offset: start,
475 need: 2,
476 have: buf.len().saturating_sub(start),
477 });
478 }
479 buf[start..start + 2].copy_from_slice(&value.to_be_bytes());
480 Ok(())
481 }
482
483 pub fn set_seq(&self, buf: &mut [u8], value: u16) -> Result<(), FieldError> {
485 let start = self.index.start + offsets::SEQ;
486 if buf.len() < start + 2 {
487 return Err(FieldError::BufferTooShort {
488 offset: start,
489 need: 2,
490 have: buf.len().saturating_sub(start),
491 });
492 }
493 buf[start..start + 2].copy_from_slice(&value.to_be_bytes());
494 Ok(())
495 }
496
497 pub fn set_gateway(&self, buf: &mut [u8], value: Ipv4Addr) -> Result<(), FieldError> {
499 let start = self.index.start + offsets::GATEWAY;
500 if buf.len() < start + 4 {
501 return Err(FieldError::BufferTooShort {
502 offset: start,
503 need: 4,
504 have: buf.len().saturating_sub(start),
505 });
506 }
507 buf[start..start + 4].copy_from_slice(&value.octets());
508 Ok(())
509 }
510
511 pub fn set_next_hop_mtu(&self, buf: &mut [u8], value: u16) -> Result<(), FieldError> {
513 let start = self.index.start + offsets::NEXT_HOP_MTU;
514 if buf.len() < start + 2 {
515 return Err(FieldError::BufferTooShort {
516 offset: start,
517 need: 2,
518 have: buf.len().saturating_sub(start),
519 });
520 }
521 buf[start..start + 2].copy_from_slice(&value.to_be_bytes());
522 Ok(())
523 }
524
525 pub fn set_ptr(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
527 let start = self.index.start + offsets::PTR;
528 if buf.len() < start + 1 {
529 return Err(FieldError::BufferTooShort {
530 offset: start,
531 need: 1,
532 have: buf.len().saturating_sub(start),
533 });
534 }
535 buf[start] = value;
536 Ok(())
537 }
538
539 pub fn set_ts_ori(&self, buf: &mut [u8], value: u32) -> Result<(), FieldError> {
541 let start = self.index.start + offsets::TS_ORI;
542 if buf.len() < start + 4 {
543 return Err(FieldError::BufferTooShort {
544 offset: start,
545 need: 4,
546 have: buf.len().saturating_sub(start),
547 });
548 }
549 buf[start..start + 4].copy_from_slice(&value.to_be_bytes());
550 Ok(())
551 }
552
553 pub fn set_ts_rx(&self, buf: &mut [u8], value: u32) -> Result<(), FieldError> {
555 let start = self.index.start + offsets::TS_RX;
556 if buf.len() < start + 4 {
557 return Err(FieldError::BufferTooShort {
558 offset: start,
559 need: 4,
560 have: buf.len().saturating_sub(start),
561 });
562 }
563 buf[start..start + 4].copy_from_slice(&value.to_be_bytes());
564 Ok(())
565 }
566
567 pub fn set_ts_tx(&self, buf: &mut [u8], value: u32) -> Result<(), FieldError> {
569 let start = self.index.start + offsets::TS_TX;
570 if buf.len() < start + 4 {
571 return Err(FieldError::BufferTooShort {
572 offset: start,
573 need: 4,
574 have: buf.len().saturating_sub(start),
575 });
576 }
577 buf[start..start + 4].copy_from_slice(&value.to_be_bytes());
578 Ok(())
579 }
580
581 pub fn set_addr_mask(&self, buf: &mut [u8], value: Ipv4Addr) -> Result<(), FieldError> {
583 let start = self.index.start + offsets::ADDR_MASK;
584 if buf.len() < start + 4 {
585 return Err(FieldError::BufferTooShort {
586 offset: start,
587 need: 4,
588 have: buf.len().saturating_sub(start),
589 });
590 }
591 buf[start..start + 4].copy_from_slice(&value.octets());
592 Ok(())
593 }
594
595 #[must_use]
597 pub fn summary(&self, buf: &[u8]) -> String {
598 if let (Ok(icmp_type), Ok(code)) = (self.icmp_type(buf), self.code(buf)) {
599 let type_str = type_name(icmp_type);
600 let code_str = code_name(icmp_type, code);
601
602 let details = match icmp_type {
604 types::types::REDIRECT => {
605 if let Ok(Some(gw)) = self.gateway(buf) {
606 format!(" gw={gw}")
607 } else {
608 String::new()
609 }
610 },
611 types::types::DEST_UNREACH if code == 4 => {
612 if let Ok(Some(mtu)) = self.next_hop_mtu(buf) {
614 format!(" mtu={mtu}")
615 } else {
616 String::new()
617 }
618 },
619 types::types::PARAM_PROBLEM => {
620 if let Ok(Some(ptr)) = self.ptr(buf) {
621 format!(" ptr={ptr}")
622 } else {
623 String::new()
624 }
625 },
626 types::types::ECHO_REQUEST | types::types::ECHO_REPLY => {
627 let id_str = self
628 .id(buf)
629 .ok()
630 .flatten()
631 .map(|id| format!(" id={id}"))
632 .unwrap_or_default();
633 let seq_str = self
634 .seq(buf)
635 .ok()
636 .flatten()
637 .map(|seq| format!(" seq={seq}"))
638 .unwrap_or_default();
639 format!("{id_str}{seq_str}")
640 },
641 _ => String::new(),
642 };
643
644 format!("ICMP {type_str} {code_str}{details}")
645 } else {
646 "ICMP".to_string()
647 }
648 }
649
650 #[must_use]
652 pub fn header_len(&self, buf: &[u8]) -> usize {
653 if let Ok(icmp_type) = self.icmp_type(buf) {
656 match icmp_type {
657 types::types::TIMESTAMP | types::types::TIMESTAMP_REPLY => 20,
658 _ => ICMP_MIN_HEADER_LEN,
659 }
660 } else {
661 ICMP_MIN_HEADER_LEN
662 }
663 }
664
665 #[must_use]
667 pub fn field_names(&self) -> &'static [&'static str] {
668 &[
669 "type",
670 "code",
671 "chksum",
672 "id",
673 "seq",
674 "gw",
675 "ptr",
676 "mtu",
677 "ts_ori",
678 "ts_rx",
679 "ts_tx",
680 "addr_mask",
681 ]
682 }
683
684 pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
686 match name {
687 "type" => Some(self.icmp_type(buf).map(FieldValue::U8)),
688 "code" => Some(self.code(buf).map(FieldValue::U8)),
689 "chksum" => Some(self.checksum(buf).map(FieldValue::U16)),
690 "id" => Some(
691 self.id(buf)
692 .and_then(|opt| {
693 opt.ok_or(FieldError::InvalidValue(
694 "id field not available for this ICMP type".into(),
695 ))
696 })
697 .map(FieldValue::U16),
698 ),
699 "seq" => Some(
700 self.seq(buf)
701 .and_then(|opt| {
702 opt.ok_or(FieldError::InvalidValue(
703 "seq field not available for this ICMP type".into(),
704 ))
705 })
706 .map(FieldValue::U16),
707 ),
708 "gw" => Some(
709 self.gateway(buf)
710 .and_then(|opt| {
711 opt.ok_or(FieldError::InvalidValue(
712 "gw field not available for this ICMP type".into(),
713 ))
714 })
715 .map(FieldValue::Ipv4),
716 ),
717 "ptr" => Some(
718 self.ptr(buf)
719 .and_then(|opt| {
720 opt.ok_or(FieldError::InvalidValue(
721 "ptr field not available for this ICMP type".into(),
722 ))
723 })
724 .map(FieldValue::U8),
725 ),
726 "mtu" => Some(
727 self.next_hop_mtu(buf)
728 .and_then(|opt| {
729 opt.ok_or(FieldError::InvalidValue(
730 "mtu field not available for this ICMP type".into(),
731 ))
732 })
733 .map(FieldValue::U16),
734 ),
735 "ts_ori" => Some(
736 self.ts_ori(buf)
737 .and_then(|opt| {
738 opt.ok_or(FieldError::InvalidValue(
739 "ts_ori field not available for this ICMP type".into(),
740 ))
741 })
742 .map(FieldValue::U32),
743 ),
744 "ts_rx" => Some(
745 self.ts_rx(buf)
746 .and_then(|opt| {
747 opt.ok_or(FieldError::InvalidValue(
748 "ts_rx field not available for this ICMP type".into(),
749 ))
750 })
751 .map(FieldValue::U32),
752 ),
753 "ts_tx" => Some(
754 self.ts_tx(buf)
755 .and_then(|opt| {
756 opt.ok_or(FieldError::InvalidValue(
757 "ts_tx field not available for this ICMP type".into(),
758 ))
759 })
760 .map(FieldValue::U32),
761 ),
762 "addr_mask" => Some(
763 self.addr_mask(buf)
764 .and_then(|opt| {
765 opt.ok_or(FieldError::InvalidValue(
766 "addr_mask field not available for this ICMP type".into(),
767 ))
768 })
769 .map(FieldValue::Ipv4),
770 ),
771 _ => None,
772 }
773 }
774
775 pub fn set_field(
777 &self,
778 buf: &mut [u8],
779 name: &str,
780 value: FieldValue,
781 ) -> Option<Result<(), FieldError>> {
782 match name {
783 "type" => {
784 if let FieldValue::U8(v) = value {
785 Some(self.set_type(buf, v))
786 } else {
787 Some(Err(FieldError::InvalidValue(format!(
788 "type: expected U8, got {value:?}"
789 ))))
790 }
791 },
792 "code" => {
793 if let FieldValue::U8(v) = value {
794 Some(self.set_code(buf, v))
795 } else {
796 Some(Err(FieldError::InvalidValue(format!(
797 "code: expected U8, got {value:?}"
798 ))))
799 }
800 },
801 "chksum" => {
802 if let FieldValue::U16(v) = value {
803 Some(self.set_checksum(buf, v))
804 } else {
805 Some(Err(FieldError::InvalidValue(format!(
806 "chksum: expected U16, got {value:?}"
807 ))))
808 }
809 },
810 "id" => {
811 if let FieldValue::U16(v) = value {
812 Some(self.set_id(buf, v))
813 } else {
814 Some(Err(FieldError::InvalidValue(format!(
815 "id: expected U16, got {value:?}"
816 ))))
817 }
818 },
819 "seq" => {
820 if let FieldValue::U16(v) = value {
821 Some(self.set_seq(buf, v))
822 } else {
823 Some(Err(FieldError::InvalidValue(format!(
824 "seq: expected U16, got {value:?}"
825 ))))
826 }
827 },
828 "gw" => {
829 if let FieldValue::Ipv4(v) = value {
830 Some(self.set_gateway(buf, v))
831 } else {
832 Some(Err(FieldError::InvalidValue(format!(
833 "gw: expected Ipv4, got {value:?}"
834 ))))
835 }
836 },
837 "ptr" => {
838 if let FieldValue::U8(v) = value {
839 Some(self.set_ptr(buf, v))
840 } else {
841 Some(Err(FieldError::InvalidValue(format!(
842 "ptr: expected U8, got {value:?}"
843 ))))
844 }
845 },
846 "mtu" => {
847 if let FieldValue::U16(v) = value {
848 Some(self.set_next_hop_mtu(buf, v))
849 } else {
850 Some(Err(FieldError::InvalidValue(format!(
851 "mtu: expected U16, got {value:?}"
852 ))))
853 }
854 },
855 "ts_ori" => {
856 if let FieldValue::U32(v) = value {
857 Some(self.set_ts_ori(buf, v))
858 } else {
859 Some(Err(FieldError::InvalidValue(format!(
860 "ts_ori: expected U32, got {value:?}"
861 ))))
862 }
863 },
864 "ts_rx" => {
865 if let FieldValue::U32(v) = value {
866 Some(self.set_ts_rx(buf, v))
867 } else {
868 Some(Err(FieldError::InvalidValue(format!(
869 "ts_rx: expected U32, got {value:?}"
870 ))))
871 }
872 },
873 "ts_tx" => {
874 if let FieldValue::U32(v) = value {
875 Some(self.set_ts_tx(buf, v))
876 } else {
877 Some(Err(FieldError::InvalidValue(format!(
878 "ts_tx: expected U32, got {value:?}"
879 ))))
880 }
881 },
882 "addr_mask" => {
883 if let FieldValue::Ipv4(v) = value {
884 Some(self.set_addr_mask(buf, v))
885 } else {
886 Some(Err(FieldError::InvalidValue(format!(
887 "addr_mask: expected Ipv4, got {value:?}"
888 ))))
889 }
890 },
891 _ => None,
892 }
893 }
894}
895
896impl Layer for IcmpLayer {
897 fn kind(&self) -> LayerKind {
898 LayerKind::Icmp
899 }
900
901 fn summary(&self, data: &[u8]) -> String {
902 self.summary(data)
903 }
904
905 fn header_len(&self, data: &[u8]) -> usize {
906 self.header_len(data)
907 }
908
909 fn hashret(&self, data: &[u8]) -> Vec<u8> {
910 if let (Ok(Some(id)), Ok(Some(seq))) = (self.id(data), self.seq(data)) {
912 let mut hash = Vec::with_capacity(4);
913 hash.extend_from_slice(&id.to_be_bytes());
914 hash.extend_from_slice(&seq.to_be_bytes());
915 hash
916 } else {
917 vec![]
919 }
920 }
921
922 fn answers(&self, data: &[u8], other: &Self, other_data: &[u8]) -> bool {
923 if let (Ok(self_type), Ok(other_type)) = (self.icmp_type(data), other.icmp_type(other_data))
925 {
926 for &(req_type, reply_type) in ICMP_ANSWERS {
928 if self_type == reply_type && other_type == req_type {
929 if let (
931 Ok(Some(self_id)),
932 Ok(Some(other_id)),
933 Ok(Some(self_seq)),
934 Ok(Some(other_seq)),
935 ) = (
936 self.id(data),
937 other.id(other_data),
938 self.seq(data),
939 other.seq(other_data),
940 ) {
941 return self_id == other_id && self_seq == other_seq;
942 }
943 return true;
944 }
945 }
946 }
947 false
948 }
949
950 fn extract_padding<'a>(&self, data: &'a [u8]) -> (&'a [u8], &'a [u8]) {
951 let header_len = self.header_len(data);
954 let start = self.index.start;
955 let end = data.len();
956
957 if start + header_len <= end {
958 (&data[start..end], &[])
959 } else {
960 (&data[start..end], &[])
961 }
962 }
963
964 fn field_names(&self) -> &'static [&'static str] {
965 self.field_names()
966 }
967}
968
969#[cfg(test)]
970mod tests {
971 use super::*;
972
973 #[test]
974 fn test_icmp_echo_parse() {
975 let data = [
977 0x08, 0x00, 0x12, 0x34, 0x56, 0x78, 0x00, 0x01, 0x48, 0x65, 0x6c, 0x6c, 0x6f, ];
985
986 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
987 let icmp = IcmpLayer::new(index);
988
989 assert_eq!(icmp.icmp_type(&data).unwrap(), 8);
990 assert_eq!(icmp.code(&data).unwrap(), 0);
991 assert_eq!(icmp.checksum(&data).unwrap(), 0x1234);
992 assert_eq!(icmp.id(&data).unwrap(), Some(0x5678));
993 assert_eq!(icmp.seq(&data).unwrap(), Some(1));
994 }
995
996 #[test]
997 fn test_icmp_summary() {
998 let data = [
999 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, ];
1005
1006 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1007 let icmp = IcmpLayer::new(index);
1008
1009 let summary = icmp.summary(&data);
1010 assert!(summary.contains("echo-request"));
1011 }
1012
1013 #[test]
1014 fn test_icmp_set_fields() {
1015 let mut data = vec![0u8; 8];
1016 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1017 let icmp = IcmpLayer::new(index);
1018
1019 icmp.set_type(&mut data, types::types::ECHO_REPLY).unwrap();
1020 icmp.set_code(&mut data, 0).unwrap();
1021 icmp.set_checksum(&mut data, 0xABCD).unwrap();
1022 icmp.set_id(&mut data, 0x1234).unwrap();
1023 icmp.set_seq(&mut data, 42).unwrap();
1024
1025 assert_eq!(icmp.icmp_type(&data).unwrap(), types::types::ECHO_REPLY);
1026 assert_eq!(icmp.code(&data).unwrap(), 0);
1027 assert_eq!(icmp.checksum(&data).unwrap(), 0xABCD);
1028 assert_eq!(icmp.id(&data).unwrap(), Some(0x1234));
1029 assert_eq!(icmp.seq(&data).unwrap(), Some(42));
1030 }
1031
1032 #[test]
1033 fn test_icmp_answers() {
1034 let request_data = [
1036 0x08, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x05, ];
1041
1042 let reply_data = [
1044 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x05, ];
1049
1050 let request_index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1051 let reply_index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1052
1053 let request = IcmpLayer::new(request_index);
1054 let reply = IcmpLayer::new(reply_index);
1055
1056 assert!(reply.answers(&reply_data, &request, &request_data));
1057 assert!(!request.answers(&request_data, &reply, &reply_data));
1058 }
1059
1060 #[test]
1061 fn test_icmp_dest_unreach_no_id() {
1062 let data = [
1064 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1071
1072 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1073 let icmp = IcmpLayer::new(index);
1074
1075 assert_eq!(icmp.icmp_type(&data).unwrap(), 3);
1076 assert_eq!(icmp.code(&data).unwrap(), 3);
1077 assert_eq!(icmp.id(&data).unwrap(), None);
1078 assert_eq!(icmp.seq(&data).unwrap(), None);
1079 }
1080
1081 #[test]
1082 fn test_icmp_redirect_gateway() {
1083 let data = [
1085 0x05, 0x01, 0x00, 0x00, 192, 168, 1, 1, ];
1090
1091 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1092 let icmp = IcmpLayer::new(index);
1093
1094 assert_eq!(icmp.icmp_type(&data).unwrap(), 5);
1095 assert_eq!(
1096 icmp.gateway(&data).unwrap(),
1097 Some(Ipv4Addr::new(192, 168, 1, 1))
1098 );
1099 }
1100
1101 #[test]
1102 fn test_icmp_dest_unreach_mtu() {
1103 let mut data = vec![
1105 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc, ];
1112
1113 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1114 let icmp = IcmpLayer::new(index);
1115
1116 assert_eq!(icmp.icmp_type(&data).unwrap(), 3);
1117 assert_eq!(icmp.code(&data).unwrap(), 4);
1118 assert_eq!(icmp.next_hop_mtu(&data).unwrap(), Some(1500));
1119
1120 icmp.set_next_hop_mtu(&mut data, 1400).unwrap();
1122 assert_eq!(icmp.next_hop_mtu(&data).unwrap(), Some(1400));
1123 }
1124
1125 #[test]
1126 fn test_icmp_param_problem_ptr() {
1127 let mut data = vec![
1129 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, ];
1136
1137 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1138 let icmp = IcmpLayer::new(index);
1139
1140 assert_eq!(icmp.icmp_type(&data).unwrap(), 12);
1141 assert_eq!(icmp.ptr(&data).unwrap(), Some(20));
1142
1143 icmp.set_ptr(&mut data, 30).unwrap();
1145 assert_eq!(icmp.ptr(&data).unwrap(), Some(30));
1146 }
1147
1148 #[test]
1149 fn test_icmp_timestamp_fields() {
1150 let mut data = vec![
1152 0x0d, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, ];
1164
1165 let index = LayerIndex::new(LayerKind::Icmp, 0, 20); let icmp = IcmpLayer::new(index);
1167
1168 assert_eq!(icmp.icmp_type(&data).unwrap(), 13);
1169 assert_eq!(icmp.code(&data).unwrap(), 0);
1170 assert_eq!(icmp.id(&data).unwrap(), Some(0x1234));
1171 assert_eq!(icmp.seq(&data).unwrap(), Some(1));
1172 assert_eq!(icmp.ts_ori(&data).unwrap(), Some(4096));
1173 assert_eq!(icmp.ts_rx(&data).unwrap(), Some(8192));
1174 assert_eq!(icmp.ts_tx(&data).unwrap(), Some(12288));
1175
1176 icmp.set_ts_ori(&mut data, 5000).unwrap();
1178 icmp.set_ts_rx(&mut data, 6000).unwrap();
1179 icmp.set_ts_tx(&mut data, 7000).unwrap();
1180
1181 assert_eq!(icmp.ts_ori(&data).unwrap(), Some(5000));
1182 assert_eq!(icmp.ts_rx(&data).unwrap(), Some(6000));
1183 assert_eq!(icmp.ts_tx(&data).unwrap(), Some(7000));
1184 }
1185
1186 #[test]
1187 fn test_icmp_timestamp_header_len() {
1188 let data = vec![
1190 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1192 0x00, 0x00, 0x00, 0x00, 0x00,
1193 ];
1194
1195 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1196 let icmp = IcmpLayer::new(index);
1197
1198 assert_eq!(icmp.header_len(&data), 20);
1199 }
1200
1201 #[test]
1202 fn test_icmp_address_mask() {
1203 let mut data = vec![
1205 0x11, 0x00, 0x00, 0x00, 255, 255, 255, 0, ];
1210
1211 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1212 let icmp = IcmpLayer::new(index);
1213
1214 assert_eq!(icmp.icmp_type(&data).unwrap(), 17);
1215 assert_eq!(
1216 icmp.addr_mask(&data).unwrap(),
1217 Some(Ipv4Addr::new(255, 255, 255, 0))
1218 );
1219
1220 icmp.set_addr_mask(&mut data, Ipv4Addr::new(255, 255, 0, 0))
1222 .unwrap();
1223 assert_eq!(
1224 icmp.addr_mask(&data).unwrap(),
1225 Some(Ipv4Addr::new(255, 255, 0, 0))
1226 );
1227 }
1228
1229 #[test]
1230 fn test_icmp_conditional_fields_none() {
1231 let data = vec![
1233 0x08, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x01,
1235 ];
1236
1237 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1238 let icmp = IcmpLayer::new(index);
1239
1240 assert_eq!(icmp.gateway(&data).unwrap(), None);
1241 assert_eq!(icmp.ptr(&data).unwrap(), None);
1242 assert_eq!(icmp.next_hop_mtu(&data).unwrap(), None);
1243 assert_eq!(icmp.addr_mask(&data).unwrap(), None);
1244 assert_eq!(icmp.ts_ori(&data).unwrap(), None);
1245 assert_eq!(icmp.ts_rx(&data).unwrap(), None);
1246 assert_eq!(icmp.ts_tx(&data).unwrap(), None);
1247 }
1248
1249 #[test]
1250 fn test_icmp_summary_with_details() {
1251 let echo_data = vec![
1255 0x08, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x05, ];
1258 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1259 let icmp = IcmpLayer::new(index);
1260 let summary = icmp.summary(&echo_data);
1261 assert!(summary.contains("id="));
1262 assert!(summary.contains("seq="));
1263
1264 let redirect_data = vec![
1266 0x05, 0x01, 0x00, 0x00, 192, 168, 1, 1, ];
1268 let icmp = IcmpLayer::new(index);
1269 let summary = icmp.summary(&redirect_data);
1270 assert!(summary.contains("gw="));
1271
1272 let pp_data = vec![
1274 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
1276 ];
1277 let icmp = IcmpLayer::new(index);
1278 let summary = icmp.summary(&pp_data);
1279 assert!(summary.contains("ptr="));
1280
1281 let du_data = vec![
1283 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc, ];
1286 let icmp = IcmpLayer::new(index);
1287 let summary = icmp.summary(&du_data);
1288 assert!(summary.contains("mtu="));
1289 }
1290
1291 #[test]
1292 fn test_icmp_set_gateway() {
1293 let mut data = vec![
1295 0x05, 0x01, 0x00, 0x00, 0, 0, 0, 0, ];
1297
1298 let index = LayerIndex::new(LayerKind::Icmp, 0, ICMP_MIN_HEADER_LEN);
1299 let icmp = IcmpLayer::new(index);
1300
1301 icmp.set_gateway(&mut data, Ipv4Addr::new(10, 0, 0, 1))
1302 .unwrap();
1303 assert_eq!(
1304 icmp.gateway(&data).unwrap(),
1305 Some(Ipv4Addr::new(10, 0, 0, 1))
1306 );
1307 }
1308
1309 #[test]
1310 fn test_icmp_error_type_detection() {
1311 assert!(error::is_error_type(types::types::DEST_UNREACH));
1313 assert!(error::is_error_type(types::types::TIME_EXCEEDED));
1314 assert!(error::is_error_type(types::types::PARAM_PROBLEM));
1315 assert!(error::is_error_type(types::types::SOURCE_QUENCH));
1316 assert!(error::is_error_type(types::types::REDIRECT));
1317
1318 assert!(!error::is_error_type(types::types::ECHO_REQUEST));
1319 assert!(!error::is_error_type(types::types::ECHO_REPLY));
1320 }
1321
1322 #[test]
1323 fn test_icmp_error_payload_offset() {
1324 assert_eq!(
1326 error::error_payload_offset(types::types::DEST_UNREACH),
1327 Some(8)
1328 );
1329 assert_eq!(
1330 error::error_payload_offset(types::types::TIME_EXCEEDED),
1331 Some(8)
1332 );
1333
1334 assert_eq!(
1336 error::error_payload_offset(types::types::ECHO_REQUEST),
1337 None
1338 );
1339 }
1340}