1use byteorder::{ByteOrder, NetworkEndian};
2use core::{cmp, fmt};
3
4use super::{Error, Result};
5use crate::phy::ChecksumCapabilities;
6use crate::wire::ip::checksum;
7use crate::wire::{Ipv4Packet, Ipv4Repr};
8
9enum_with_unknown! {
10 pub enum Message(u8) {
12 EchoReply = 0,
14 DstUnreachable = 3,
16 Redirect = 5,
18 EchoRequest = 8,
20 RouterAdvert = 9,
22 RouterSolicit = 10,
24 TimeExceeded = 11,
26 ParamProblem = 12,
28 Timestamp = 13,
30 TimestampReply = 14
32 }
33}
34
35impl fmt::Display for Message {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 match *self {
38 Message::EchoReply => write!(f, "echo reply"),
39 Message::DstUnreachable => write!(f, "destination unreachable"),
40 Message::Redirect => write!(f, "message redirect"),
41 Message::EchoRequest => write!(f, "echo request"),
42 Message::RouterAdvert => write!(f, "router advertisement"),
43 Message::RouterSolicit => write!(f, "router solicitation"),
44 Message::TimeExceeded => write!(f, "time exceeded"),
45 Message::ParamProblem => write!(f, "parameter problem"),
46 Message::Timestamp => write!(f, "timestamp"),
47 Message::TimestampReply => write!(f, "timestamp reply"),
48 Message::Unknown(id) => write!(f, "{id}"),
49 }
50 }
51}
52
53enum_with_unknown! {
54 pub enum DstUnreachable(u8) {
56 NetUnreachable = 0,
58 HostUnreachable = 1,
60 ProtoUnreachable = 2,
62 PortUnreachable = 3,
64 FragRequired = 4,
66 SrcRouteFailed = 5,
68 DstNetUnknown = 6,
70 DstHostUnknown = 7,
72 SrcHostIsolated = 8,
74 NetProhibited = 9,
76 HostProhibited = 10,
78 NetUnreachToS = 11,
80 HostUnreachToS = 12,
82 CommProhibited = 13,
84 HostPrecedViol = 14,
86 PrecedCutoff = 15
88 }
89}
90
91impl fmt::Display for DstUnreachable {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 match *self {
94 DstUnreachable::NetUnreachable => write!(f, "destination network unreachable"),
95 DstUnreachable::HostUnreachable => write!(f, "destination host unreachable"),
96 DstUnreachable::ProtoUnreachable => write!(f, "destination protocol unreachable"),
97 DstUnreachable::PortUnreachable => write!(f, "destination port unreachable"),
98 DstUnreachable::FragRequired => write!(f, "fragmentation required, and DF flag set"),
99 DstUnreachable::SrcRouteFailed => write!(f, "source route failed"),
100 DstUnreachable::DstNetUnknown => write!(f, "destination network unknown"),
101 DstUnreachable::DstHostUnknown => write!(f, "destination host unknown"),
102 DstUnreachable::SrcHostIsolated => write!(f, "source host isolated"),
103 DstUnreachable::NetProhibited => write!(f, "network administratively prohibited"),
104 DstUnreachable::HostProhibited => write!(f, "host administratively prohibited"),
105 DstUnreachable::NetUnreachToS => write!(f, "network unreachable for ToS"),
106 DstUnreachable::HostUnreachToS => write!(f, "host unreachable for ToS"),
107 DstUnreachable::CommProhibited => {
108 write!(f, "communication administratively prohibited")
109 }
110 DstUnreachable::HostPrecedViol => write!(f, "host precedence violation"),
111 DstUnreachable::PrecedCutoff => write!(f, "precedence cutoff in effect"),
112 DstUnreachable::Unknown(id) => write!(f, "{id}"),
113 }
114 }
115}
116
117enum_with_unknown! {
118 pub enum Redirect(u8) {
120 Net = 0,
122 Host = 1,
124 NetToS = 2,
126 HostToS = 3
128 }
129}
130
131enum_with_unknown! {
132 pub enum TimeExceeded(u8) {
134 TtlExpired = 0,
136 FragExpired = 1
138 }
139}
140
141impl fmt::Display for TimeExceeded {
142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143 match *self {
144 TimeExceeded::TtlExpired => write!(f, "time-to-live exceeded in transit"),
145 TimeExceeded::FragExpired => write!(f, "fragment reassembly time exceeded"),
146 TimeExceeded::Unknown(id) => write!(f, "{id}"),
147 }
148 }
149}
150
151enum_with_unknown! {
152 pub enum ParamProblem(u8) {
154 AtPointer = 0,
156 MissingOption = 1,
158 BadLength = 2
160 }
161}
162
163#[derive(Debug, PartialEq, Eq, Clone)]
165#[cfg_attr(feature = "defmt", derive(defmt::Format))]
166pub struct Packet<T: AsRef<[u8]>> {
167 buffer: T,
168}
169
170mod field {
171 use crate::wire::field::*;
172
173 pub const TYPE: usize = 0;
174 pub const CODE: usize = 1;
175 pub const CHECKSUM: Field = 2..4;
176
177 pub const UNUSED: Field = 4..8;
178
179 pub const ECHO_IDENT: Field = 4..6;
180 pub const ECHO_SEQNO: Field = 6..8;
181
182 pub const HEADER_END: usize = 8;
183}
184
185impl<T: AsRef<[u8]>> Packet<T> {
186 pub const fn new_unchecked(buffer: T) -> Packet<T> {
188 Packet { buffer }
189 }
190
191 pub fn new_checked(buffer: T) -> Result<Packet<T>> {
196 let packet = Self::new_unchecked(buffer);
197 packet.check_len()?;
198 Ok(packet)
199 }
200
201 pub fn check_len(&self) -> Result<()> {
208 let len = self.buffer.as_ref().len();
209 if len < field::HEADER_END {
210 Err(Error)
211 } else {
212 Ok(())
213 }
214 }
215
216 pub fn into_inner(self) -> T {
218 self.buffer
219 }
220
221 #[inline]
223 pub fn msg_type(&self) -> Message {
224 let data = self.buffer.as_ref();
225 Message::from(data[field::TYPE])
226 }
227
228 #[inline]
230 pub fn msg_code(&self) -> u8 {
231 let data = self.buffer.as_ref();
232 data[field::CODE]
233 }
234
235 #[inline]
237 pub fn checksum(&self) -> u16 {
238 let data = self.buffer.as_ref();
239 NetworkEndian::read_u16(&data[field::CHECKSUM])
240 }
241
242 #[inline]
247 pub fn echo_ident(&self) -> u16 {
248 let data = self.buffer.as_ref();
249 NetworkEndian::read_u16(&data[field::ECHO_IDENT])
250 }
251
252 #[inline]
257 pub fn echo_seq_no(&self) -> u16 {
258 let data = self.buffer.as_ref();
259 NetworkEndian::read_u16(&data[field::ECHO_SEQNO])
260 }
261
262 pub fn header_len(&self) -> usize {
265 match self.msg_type() {
266 Message::EchoRequest => field::ECHO_SEQNO.end,
267 Message::EchoReply => field::ECHO_SEQNO.end,
268 Message::DstUnreachable => field::UNUSED.end,
269 _ => field::UNUSED.end, }
271 }
272
273 pub fn verify_checksum(&self) -> bool {
278 if cfg!(fuzzing) {
279 return true;
280 }
281
282 let data = self.buffer.as_ref();
283 checksum::data(data) == !0
284 }
285}
286
287impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
288 #[inline]
290 pub fn data(&self) -> &'a [u8] {
291 let data = self.buffer.as_ref();
292 &data[self.header_len()..]
293 }
294}
295
296impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
297 #[inline]
299 pub fn set_msg_type(&mut self, value: Message) {
300 let data = self.buffer.as_mut();
301 data[field::TYPE] = value.into()
302 }
303
304 #[inline]
306 pub fn set_msg_code(&mut self, value: u8) {
307 let data = self.buffer.as_mut();
308 data[field::CODE] = value
309 }
310
311 #[inline]
313 pub fn set_checksum(&mut self, value: u16) {
314 let data = self.buffer.as_mut();
315 NetworkEndian::write_u16(&mut data[field::CHECKSUM], value)
316 }
317
318 #[inline]
323 pub fn set_echo_ident(&mut self, value: u16) {
324 let data = self.buffer.as_mut();
325 NetworkEndian::write_u16(&mut data[field::ECHO_IDENT], value)
326 }
327
328 #[inline]
333 pub fn set_echo_seq_no(&mut self, value: u16) {
334 let data = self.buffer.as_mut();
335 NetworkEndian::write_u16(&mut data[field::ECHO_SEQNO], value)
336 }
337
338 pub fn fill_checksum(&mut self) {
340 self.set_checksum(0);
341 let checksum = {
342 let data = self.buffer.as_ref();
343 !checksum::data(data)
344 };
345 self.set_checksum(checksum)
346 }
347}
348
349impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
350 #[inline]
352 pub fn data_mut(&mut self) -> &mut [u8] {
353 let range = self.header_len()..;
354 let data = self.buffer.as_mut();
355 &mut data[range]
356 }
357}
358
359impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
360 fn as_ref(&self) -> &[u8] {
361 self.buffer.as_ref()
362 }
363}
364
365#[derive(Debug, PartialEq, Eq, Clone, Copy)]
367#[cfg_attr(feature = "defmt", derive(defmt::Format))]
368#[non_exhaustive]
369pub enum Repr<'a> {
370 EchoRequest {
371 ident: u16,
372 seq_no: u16,
373 data: &'a [u8],
374 },
375 EchoReply {
376 ident: u16,
377 seq_no: u16,
378 data: &'a [u8],
379 },
380 DstUnreachable {
381 reason: DstUnreachable,
382 header: Ipv4Repr,
383 data: &'a [u8],
384 },
385 TimeExceeded {
386 reason: TimeExceeded,
387 header: Ipv4Repr,
388 data: &'a [u8],
389 },
390}
391
392impl<'a> Repr<'a> {
393 pub fn parse<T>(
396 packet: &Packet<&'a T>,
397 checksum_caps: &ChecksumCapabilities,
398 ) -> Result<Repr<'a>>
399 where
400 T: AsRef<[u8]> + ?Sized,
401 {
402 packet.check_len()?;
403
404 if checksum_caps.icmpv4.rx() && !packet.verify_checksum() {
406 return Err(Error);
407 }
408
409 match (packet.msg_type(), packet.msg_code()) {
410 (Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
411 ident: packet.echo_ident(),
412 seq_no: packet.echo_seq_no(),
413 data: packet.data(),
414 }),
415
416 (Message::EchoReply, 0) => Ok(Repr::EchoReply {
417 ident: packet.echo_ident(),
418 seq_no: packet.echo_seq_no(),
419 data: packet.data(),
420 }),
421
422 (Message::DstUnreachable, code) => {
423 let ip_packet = Ipv4Packet::new_checked(packet.data())?;
424
425 let payload = &packet.data()[ip_packet.header_len() as usize..];
426 if payload.len() < 8 {
429 return Err(Error);
430 }
431
432 Ok(Repr::DstUnreachable {
433 reason: DstUnreachable::from(code),
434 header: Ipv4Repr {
435 src_addr: ip_packet.src_addr(),
436 dst_addr: ip_packet.dst_addr(),
437 next_header: ip_packet.next_header(),
438 payload_len: payload.len(),
439 hop_limit: ip_packet.hop_limit(),
440 },
441 data: payload,
442 })
443 }
444
445 (Message::TimeExceeded, code) => {
446 let ip_packet = Ipv4Packet::new_checked(packet.data())?;
447
448 let payload = &packet.data()[ip_packet.header_len() as usize..];
449 if payload.len() < 8 {
452 return Err(Error);
453 }
454
455 Ok(Repr::TimeExceeded {
456 reason: TimeExceeded::from(code),
457 header: Ipv4Repr {
458 src_addr: ip_packet.src_addr(),
459 dst_addr: ip_packet.dst_addr(),
460 next_header: ip_packet.next_header(),
461 payload_len: payload.len(),
462 hop_limit: ip_packet.hop_limit(),
463 },
464 data: payload,
465 })
466 }
467
468 _ => Err(Error),
469 }
470 }
471
472 pub const fn buffer_len(&self) -> usize {
474 match self {
475 &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
476 field::ECHO_SEQNO.end + data.len()
477 }
478 &Repr::DstUnreachable { header, data, .. }
479 | &Repr::TimeExceeded { header, data, .. } => {
480 field::UNUSED.end + header.buffer_len() + data.len()
481 }
482 }
483 }
484
485 pub fn emit<T>(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
488 where
489 T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
490 {
491 packet.set_msg_code(0);
492 match *self {
493 Repr::EchoRequest {
494 ident,
495 seq_no,
496 data,
497 } => {
498 packet.set_msg_type(Message::EchoRequest);
499 packet.set_msg_code(0);
500 packet.set_echo_ident(ident);
501 packet.set_echo_seq_no(seq_no);
502 let data_len = cmp::min(packet.data_mut().len(), data.len());
503 packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
504 }
505
506 Repr::EchoReply {
507 ident,
508 seq_no,
509 data,
510 } => {
511 packet.set_msg_type(Message::EchoReply);
512 packet.set_msg_code(0);
513 packet.set_echo_ident(ident);
514 packet.set_echo_seq_no(seq_no);
515 let data_len = cmp::min(packet.data_mut().len(), data.len());
516 packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
517 }
518
519 Repr::DstUnreachable {
520 reason,
521 header,
522 data,
523 } => {
524 packet.set_msg_type(Message::DstUnreachable);
525 packet.set_msg_code(reason.into());
526
527 let mut ip_packet = Ipv4Packet::new_unchecked(packet.data_mut());
528 header.emit(&mut ip_packet, checksum_caps);
529 let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
530 payload.copy_from_slice(data)
531 }
532
533 Repr::TimeExceeded {
534 reason,
535 header,
536 data,
537 } => {
538 packet.set_msg_type(Message::TimeExceeded);
539 packet.set_msg_code(reason.into());
540
541 let mut ip_packet = Ipv4Packet::new_unchecked(packet.data_mut());
542 header.emit(&mut ip_packet, checksum_caps);
543 let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
544 payload.copy_from_slice(data)
545 }
546 }
547
548 if checksum_caps.icmpv4.tx() {
549 packet.fill_checksum()
550 } else {
551 packet.set_checksum(0);
554 }
555 }
556}
557
558impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
559 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
560 match Repr::parse(self, &ChecksumCapabilities::default()) {
561 Ok(repr) => write!(f, "{repr}"),
562 Err(err) => {
563 write!(f, "ICMPv4 ({err})")?;
564 write!(f, " type={:?}", self.msg_type())?;
565 match self.msg_type() {
566 Message::DstUnreachable => {
567 write!(f, " code={:?}", DstUnreachable::from(self.msg_code()))
568 }
569 Message::TimeExceeded => {
570 write!(f, " code={:?}", TimeExceeded::from(self.msg_code()))
571 }
572 _ => write!(f, " code={}", self.msg_code()),
573 }
574 }
575 }
576 }
577}
578
579impl<'a> fmt::Display for Repr<'a> {
580 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
581 match *self {
582 Repr::EchoRequest {
583 ident,
584 seq_no,
585 data,
586 } => write!(
587 f,
588 "ICMPv4 echo request id={} seq={} len={}",
589 ident,
590 seq_no,
591 data.len()
592 ),
593 Repr::EchoReply {
594 ident,
595 seq_no,
596 data,
597 } => write!(
598 f,
599 "ICMPv4 echo reply id={} seq={} len={}",
600 ident,
601 seq_no,
602 data.len()
603 ),
604 Repr::DstUnreachable { reason, .. } => {
605 write!(f, "ICMPv4 destination unreachable ({reason})")
606 }
607 Repr::TimeExceeded { reason, .. } => {
608 write!(f, "ICMPv4 time exceeded ({reason})")
609 }
610 }
611 }
612}
613
614use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
615
616impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
617 fn pretty_print(
618 buffer: &dyn AsRef<[u8]>,
619 f: &mut fmt::Formatter,
620 indent: &mut PrettyIndent,
621 ) -> fmt::Result {
622 let packet = match Packet::new_checked(buffer) {
623 Err(err) => return write!(f, "{indent}({err})"),
624 Ok(packet) => packet,
625 };
626 write!(f, "{indent}{packet}")?;
627
628 match packet.msg_type() {
629 Message::DstUnreachable | Message::TimeExceeded => {
630 indent.increase(f)?;
631 super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent)
632 }
633 _ => Ok(()),
634 }
635 }
636}
637
638#[cfg(test)]
639mod test {
640 use super::*;
641
642 static ECHO_PACKET_BYTES: [u8; 12] = [
643 0x08, 0x00, 0x8e, 0xfe, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
644 ];
645
646 static ECHO_DATA_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
647
648 #[test]
649 fn test_echo_deconstruct() {
650 let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
651 assert_eq!(packet.msg_type(), Message::EchoRequest);
652 assert_eq!(packet.msg_code(), 0);
653 assert_eq!(packet.checksum(), 0x8efe);
654 assert_eq!(packet.echo_ident(), 0x1234);
655 assert_eq!(packet.echo_seq_no(), 0xabcd);
656 assert_eq!(packet.data(), &ECHO_DATA_BYTES[..]);
657 assert!(packet.verify_checksum());
658 }
659
660 #[test]
661 fn test_echo_construct() {
662 let mut bytes = vec![0xa5; 12];
663 let mut packet = Packet::new_unchecked(&mut bytes);
664 packet.set_msg_type(Message::EchoRequest);
665 packet.set_msg_code(0);
666 packet.set_echo_ident(0x1234);
667 packet.set_echo_seq_no(0xabcd);
668 packet.data_mut().copy_from_slice(&ECHO_DATA_BYTES[..]);
669 packet.fill_checksum();
670 assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
671 }
672
673 fn echo_packet_repr() -> Repr<'static> {
674 Repr::EchoRequest {
675 ident: 0x1234,
676 seq_no: 0xabcd,
677 data: &ECHO_DATA_BYTES,
678 }
679 }
680
681 #[test]
682 fn test_echo_parse() {
683 let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
684 let repr = Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
685 assert_eq!(repr, echo_packet_repr());
686 }
687
688 #[test]
689 fn test_echo_emit() {
690 let repr = echo_packet_repr();
691 let mut bytes = vec![0xa5; repr.buffer_len()];
692 let mut packet = Packet::new_unchecked(&mut bytes);
693 repr.emit(&mut packet, &ChecksumCapabilities::default());
694 assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
695 }
696
697 #[test]
698 fn test_check_len() {
699 let bytes = [0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
700 assert_eq!(Packet::new_checked(&[]), Err(Error));
701 assert_eq!(Packet::new_checked(&bytes[..4]), Err(Error));
702 assert!(Packet::new_checked(&bytes[..]).is_ok());
703 }
704}