1use byteorder::{ByteOrder, NetworkEndian};
2use core::{cmp, fmt};
3
4use crate::phy::ChecksumCapabilities;
5use crate::wire::ip::checksum;
6use crate::wire::{Ipv4Packet, Ipv4Repr};
7use crate::{Error, Result};
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
141enum_with_unknown! {
142 pub enum ParamProblem(u8) {
144 AtPointer = 0,
146 MissingOption = 1,
148 BadLength = 2
150 }
151}
152
153#[derive(Debug, PartialEq, Clone)]
155#[cfg_attr(feature = "defmt", derive(defmt::Format))]
156pub struct Packet<T: AsRef<[u8]>> {
157 buffer: T,
158}
159
160mod field {
161 use crate::wire::field::*;
162
163 pub const TYPE: usize = 0;
164 pub const CODE: usize = 1;
165 pub const CHECKSUM: Field = 2..4;
166
167 pub const UNUSED: Field = 4..8;
168
169 pub const ECHO_IDENT: Field = 4..6;
170 pub const ECHO_SEQNO: Field = 6..8;
171
172 pub const HEADER_END: usize = 8;
173}
174
175impl<T: AsRef<[u8]>> Packet<T> {
176 pub fn new_unchecked(buffer: T) -> Packet<T> {
178 Packet { buffer }
179 }
180
181 pub fn new_checked(buffer: T) -> Result<Packet<T>> {
186 let packet = Self::new_unchecked(buffer);
187 packet.check_len()?;
188 Ok(packet)
189 }
190
191 pub fn check_len(&self) -> Result<()> {
198 let len = self.buffer.as_ref().len();
199 if len < field::HEADER_END {
200 Err(Error::Truncated)
201 } else {
202 Ok(())
203 }
204 }
205
206 pub fn into_inner(self) -> T {
208 self.buffer
209 }
210
211 #[inline]
213 pub fn msg_type(&self) -> Message {
214 let data = self.buffer.as_ref();
215 Message::from(data[field::TYPE])
216 }
217
218 #[inline]
220 pub fn msg_code(&self) -> u8 {
221 let data = self.buffer.as_ref();
222 data[field::CODE]
223 }
224
225 #[inline]
227 pub fn checksum(&self) -> u16 {
228 let data = self.buffer.as_ref();
229 NetworkEndian::read_u16(&data[field::CHECKSUM])
230 }
231
232 #[inline]
237 pub fn echo_ident(&self) -> u16 {
238 let data = self.buffer.as_ref();
239 NetworkEndian::read_u16(&data[field::ECHO_IDENT])
240 }
241
242 #[inline]
247 pub fn echo_seq_no(&self) -> u16 {
248 let data = self.buffer.as_ref();
249 NetworkEndian::read_u16(&data[field::ECHO_SEQNO])
250 }
251
252 pub fn header_len(&self) -> usize {
255 match self.msg_type() {
256 Message::EchoRequest => field::ECHO_SEQNO.end,
257 Message::EchoReply => field::ECHO_SEQNO.end,
258 Message::DstUnreachable => field::UNUSED.end,
259 _ => field::UNUSED.end, }
261 }
262
263 pub fn verify_checksum(&self) -> bool {
268 if cfg!(fuzzing) {
269 return true;
270 }
271
272 let data = self.buffer.as_ref();
273 checksum::data(data) == !0
274 }
275}
276
277impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
278 #[inline]
280 pub fn data(&self) -> &'a [u8] {
281 let data = self.buffer.as_ref();
282 &data[self.header_len()..]
283 }
284}
285
286impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
287 #[inline]
289 pub fn set_msg_type(&mut self, value: Message) {
290 let data = self.buffer.as_mut();
291 data[field::TYPE] = value.into()
292 }
293
294 #[inline]
296 pub fn set_msg_code(&mut self, value: u8) {
297 let data = self.buffer.as_mut();
298 data[field::CODE] = value
299 }
300
301 #[inline]
303 pub fn set_checksum(&mut self, value: u16) {
304 let data = self.buffer.as_mut();
305 NetworkEndian::write_u16(&mut data[field::CHECKSUM], value)
306 }
307
308 #[inline]
313 pub fn set_echo_ident(&mut self, value: u16) {
314 let data = self.buffer.as_mut();
315 NetworkEndian::write_u16(&mut data[field::ECHO_IDENT], value)
316 }
317
318 #[inline]
323 pub fn set_echo_seq_no(&mut self, value: u16) {
324 let data = self.buffer.as_mut();
325 NetworkEndian::write_u16(&mut data[field::ECHO_SEQNO], value)
326 }
327
328 pub fn fill_checksum(&mut self) {
330 self.set_checksum(0);
331 let checksum = {
332 let data = self.buffer.as_ref();
333 !checksum::data(data)
334 };
335 self.set_checksum(checksum)
336 }
337}
338
339impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
340 #[inline]
342 pub fn data_mut(&mut self) -> &mut [u8] {
343 let range = self.header_len()..;
344 let data = self.buffer.as_mut();
345 &mut data[range]
346 }
347}
348
349impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
350 fn as_ref(&self) -> &[u8] {
351 self.buffer.as_ref()
352 }
353}
354
355#[derive(Debug, PartialEq, Eq, Clone, Copy)]
357#[cfg_attr(feature = "defmt", derive(defmt::Format))]
358#[non_exhaustive]
359pub enum Repr<'a> {
360 EchoRequest {
361 ident: u16,
362 seq_no: u16,
363 data: &'a [u8],
364 },
365 EchoReply {
366 ident: u16,
367 seq_no: u16,
368 data: &'a [u8],
369 },
370 DstUnreachable {
371 reason: DstUnreachable,
372 header: Ipv4Repr,
373 data: &'a [u8],
374 },
375}
376
377impl<'a> Repr<'a> {
378 pub fn parse<T>(
381 packet: &Packet<&'a T>,
382 checksum_caps: &ChecksumCapabilities,
383 ) -> Result<Repr<'a>>
384 where
385 T: AsRef<[u8]> + ?Sized,
386 {
387 if checksum_caps.icmpv4.rx() && !packet.verify_checksum() {
389 return Err(Error::Checksum);
390 }
391
392 match (packet.msg_type(), packet.msg_code()) {
393 (Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
394 ident: packet.echo_ident(),
395 seq_no: packet.echo_seq_no(),
396 data: packet.data(),
397 }),
398
399 (Message::EchoReply, 0) => Ok(Repr::EchoReply {
400 ident: packet.echo_ident(),
401 seq_no: packet.echo_seq_no(),
402 data: packet.data(),
403 }),
404
405 (Message::DstUnreachable, code) => {
406 let ip_packet = Ipv4Packet::new_checked(packet.data())?;
407
408 let payload = &packet.data()[ip_packet.header_len() as usize..];
409 if payload.len() < 8 {
412 return Err(Error::Truncated);
413 }
414
415 Ok(Repr::DstUnreachable {
416 reason: DstUnreachable::from(code),
417 header: Ipv4Repr {
418 src_addr: ip_packet.src_addr(),
419 dst_addr: ip_packet.dst_addr(),
420 protocol: ip_packet.protocol(),
421 payload_len: payload.len(),
422 hop_limit: ip_packet.hop_limit(),
423 },
424 data: payload,
425 })
426 }
427 _ => Err(Error::Unrecognized),
428 }
429 }
430
431 pub fn buffer_len(&self) -> usize {
433 match self {
434 &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
435 field::ECHO_SEQNO.end + data.len()
436 }
437 &Repr::DstUnreachable { header, data, .. } => {
438 field::UNUSED.end + header.buffer_len() + data.len()
439 }
440 }
441 }
442
443 pub fn emit<T>(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
446 where
447 T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
448 {
449 packet.set_msg_code(0);
450 match *self {
451 Repr::EchoRequest {
452 ident,
453 seq_no,
454 data,
455 } => {
456 packet.set_msg_type(Message::EchoRequest);
457 packet.set_msg_code(0);
458 packet.set_echo_ident(ident);
459 packet.set_echo_seq_no(seq_no);
460 let data_len = cmp::min(packet.data_mut().len(), data.len());
461 packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
462 }
463
464 Repr::EchoReply {
465 ident,
466 seq_no,
467 data,
468 } => {
469 packet.set_msg_type(Message::EchoReply);
470 packet.set_msg_code(0);
471 packet.set_echo_ident(ident);
472 packet.set_echo_seq_no(seq_no);
473 let data_len = cmp::min(packet.data_mut().len(), data.len());
474 packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
475 }
476
477 Repr::DstUnreachable {
478 reason,
479 header,
480 data,
481 } => {
482 packet.set_msg_type(Message::DstUnreachable);
483 packet.set_msg_code(reason.into());
484
485 let mut ip_packet = Ipv4Packet::new_unchecked(packet.data_mut());
486 header.emit(&mut ip_packet, checksum_caps);
487 let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
488 payload.copy_from_slice(data)
489 }
490 }
491
492 if checksum_caps.icmpv4.tx() {
493 packet.fill_checksum()
494 } else {
495 packet.set_checksum(0);
498 }
499 }
500}
501
502impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
503 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
504 match Repr::parse(self, &ChecksumCapabilities::default()) {
505 Ok(repr) => write!(f, "{}", repr),
506 Err(err) => {
507 write!(f, "ICMPv4 ({})", err)?;
508 write!(f, " type={:?}", self.msg_type())?;
509 match self.msg_type() {
510 Message::DstUnreachable => {
511 write!(f, " code={:?}", DstUnreachable::from(self.msg_code()))
512 }
513 _ => write!(f, " code={}", self.msg_code()),
514 }
515 }
516 }
517 }
518}
519
520impl<'a> fmt::Display for Repr<'a> {
521 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
522 match *self {
523 Repr::EchoRequest {
524 ident,
525 seq_no,
526 data,
527 } => write!(
528 f,
529 "ICMPv4 echo request id={} seq={} len={}",
530 ident,
531 seq_no,
532 data.len()
533 ),
534 Repr::EchoReply {
535 ident,
536 seq_no,
537 data,
538 } => write!(
539 f,
540 "ICMPv4 echo reply id={} seq={} len={}",
541 ident,
542 seq_no,
543 data.len()
544 ),
545 Repr::DstUnreachable { reason, .. } => {
546 write!(f, "ICMPv4 destination unreachable ({})", reason)
547 }
548 }
549 }
550}
551
552use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
553
554impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
555 fn pretty_print(
556 buffer: &dyn AsRef<[u8]>,
557 f: &mut fmt::Formatter,
558 indent: &mut PrettyIndent,
559 ) -> fmt::Result {
560 let packet = match Packet::new_checked(buffer) {
561 Err(err) => return write!(f, "{}({})", indent, err),
562 Ok(packet) => packet,
563 };
564 write!(f, "{}{}", indent, packet)?;
565
566 match packet.msg_type() {
567 Message::DstUnreachable => {
568 indent.increase(f)?;
569 super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent)
570 }
571 _ => Ok(()),
572 }
573 }
574}
575
576#[cfg(test)]
577mod test {
578 use super::*;
579
580 static ECHO_PACKET_BYTES: [u8; 12] = [
581 0x08, 0x00, 0x8e, 0xfe, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
582 ];
583
584 static ECHO_DATA_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
585
586 #[test]
587 fn test_echo_deconstruct() {
588 let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
589 assert_eq!(packet.msg_type(), Message::EchoRequest);
590 assert_eq!(packet.msg_code(), 0);
591 assert_eq!(packet.checksum(), 0x8efe);
592 assert_eq!(packet.echo_ident(), 0x1234);
593 assert_eq!(packet.echo_seq_no(), 0xabcd);
594 assert_eq!(packet.data(), &ECHO_DATA_BYTES[..]);
595 assert!(packet.verify_checksum());
596 }
597
598 #[test]
599 fn test_echo_construct() {
600 let mut bytes = vec![0xa5; 12];
601 let mut packet = Packet::new_unchecked(&mut bytes);
602 packet.set_msg_type(Message::EchoRequest);
603 packet.set_msg_code(0);
604 packet.set_echo_ident(0x1234);
605 packet.set_echo_seq_no(0xabcd);
606 packet.data_mut().copy_from_slice(&ECHO_DATA_BYTES[..]);
607 packet.fill_checksum();
608 assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
609 }
610
611 fn echo_packet_repr() -> Repr<'static> {
612 Repr::EchoRequest {
613 ident: 0x1234,
614 seq_no: 0xabcd,
615 data: &ECHO_DATA_BYTES,
616 }
617 }
618
619 #[test]
620 fn test_echo_parse() {
621 let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
622 let repr = Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
623 assert_eq!(repr, echo_packet_repr());
624 }
625
626 #[test]
627 fn test_echo_emit() {
628 let repr = echo_packet_repr();
629 let mut bytes = vec![0xa5; repr.buffer_len()];
630 let mut packet = Packet::new_unchecked(&mut bytes);
631 repr.emit(&mut packet, &ChecksumCapabilities::default());
632 assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
633 }
634
635 #[test]
636 fn test_check_len() {
637 let bytes = [0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
638 assert_eq!(Packet::new_checked(&[]), Err(Error::Truncated));
639 assert_eq!(Packet::new_checked(&bytes[..4]), Err(Error::Truncated));
640 assert!(Packet::new_checked(&bytes[..]).is_ok());
641 }
642}