1 use byteorder::{ByteOrder, NetworkEndian};
2 use core::{cmp, fmt};
3 
4 use super::{Error, Result};
5 use crate::phy::ChecksumCapabilities;
6 use crate::wire::ip::checksum;
7 use crate::wire::{Ipv4Packet, Ipv4Repr};
8 
9 enum_with_unknown! {
10     /// Internet protocol control message type.
11     pub enum Message(u8) {
12         /// Echo reply
13         EchoReply      =  0,
14         /// Destination unreachable
15         DstUnreachable =  3,
16         /// Message redirect
17         Redirect       =  5,
18         /// Echo request
19         EchoRequest    =  8,
20         /// Router advertisement
21         RouterAdvert   =  9,
22         /// Router solicitation
23         RouterSolicit  = 10,
24         /// Time exceeded
25         TimeExceeded   = 11,
26         /// Parameter problem
27         ParamProblem   = 12,
28         /// Timestamp
29         Timestamp      = 13,
30         /// Timestamp reply
31         TimestampReply = 14
32     }
33 }
34 
35 impl fmt::Display for Message {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result36     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 
53 enum_with_unknown! {
54     /// Internet protocol control message subtype for type "Destination Unreachable".
55     pub enum DstUnreachable(u8) {
56         /// Destination network unreachable
57         NetUnreachable   =  0,
58         /// Destination host unreachable
59         HostUnreachable  =  1,
60         /// Destination protocol unreachable
61         ProtoUnreachable =  2,
62         /// Destination port unreachable
63         PortUnreachable  =  3,
64         /// Fragmentation required, and DF flag set
65         FragRequired     =  4,
66         /// Source route failed
67         SrcRouteFailed   =  5,
68         /// Destination network unknown
69         DstNetUnknown    =  6,
70         /// Destination host unknown
71         DstHostUnknown   =  7,
72         /// Source host isolated
73         SrcHostIsolated  =  8,
74         /// Network administratively prohibited
75         NetProhibited    =  9,
76         /// Host administratively prohibited
77         HostProhibited   = 10,
78         /// Network unreachable for ToS
79         NetUnreachToS    = 11,
80         /// Host unreachable for ToS
81         HostUnreachToS   = 12,
82         /// Communication administratively prohibited
83         CommProhibited   = 13,
84         /// Host precedence violation
85         HostPrecedViol   = 14,
86         /// Precedence cutoff in effect
87         PrecedCutoff     = 15
88     }
89 }
90 
91 impl fmt::Display for DstUnreachable {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result92     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 
117 enum_with_unknown! {
118     /// Internet protocol control message subtype for type "Redirect Message".
119     pub enum Redirect(u8) {
120         /// Redirect Datagram for the Network
121         Net     = 0,
122         /// Redirect Datagram for the Host
123         Host    = 1,
124         /// Redirect Datagram for the ToS & network
125         NetToS  = 2,
126         /// Redirect Datagram for the ToS & host
127         HostToS = 3
128     }
129 }
130 
131 enum_with_unknown! {
132     /// Internet protocol control message subtype for type "Time Exceeded".
133     pub enum TimeExceeded(u8) {
134         /// TTL expired in transit
135         TtlExpired  = 0,
136         /// Fragment reassembly time exceeded
137         FragExpired = 1
138     }
139 }
140 
141 impl fmt::Display for TimeExceeded {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result142     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 
151 enum_with_unknown! {
152     /// Internet protocol control message subtype for type "Parameter Problem".
153     pub enum ParamProblem(u8) {
154         /// Pointer indicates the error
155         AtPointer     = 0,
156         /// Missing a required option
157         MissingOption = 1,
158         /// Bad length
159         BadLength     = 2
160     }
161 }
162 
163 /// A read/write wrapper around an Internet Control Message Protocol version 4 packet buffer.
164 #[derive(Debug, PartialEq, Eq, Clone)]
165 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
166 pub struct Packet<T: AsRef<[u8]>> {
167     buffer: T,
168 }
169 
170 mod 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 
185 impl<T: AsRef<[u8]>> Packet<T> {
186     /// Imbue a raw octet buffer with ICMPv4 packet structure.
new_unchecked(buffer: T) -> Packet<T>187     pub const fn new_unchecked(buffer: T) -> Packet<T> {
188         Packet { buffer }
189     }
190 
191     /// Shorthand for a combination of [new_unchecked] and [check_len].
192     ///
193     /// [new_unchecked]: #method.new_unchecked
194     /// [check_len]: #method.check_len
new_checked(buffer: T) -> Result<Packet<T>>195     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     /// Ensure that no accessor method will panic if called.
202     /// Returns `Err(Error)` if the buffer is too short.
203     ///
204     /// The result of this check is invalidated by calling [set_header_len].
205     ///
206     /// [set_header_len]: #method.set_header_len
check_len(&self) -> Result<()>207     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     /// Consume the packet, returning the underlying buffer.
into_inner(self) -> T217     pub fn into_inner(self) -> T {
218         self.buffer
219     }
220 
221     /// Return the message type field.
222     #[inline]
msg_type(&self) -> Message223     pub fn msg_type(&self) -> Message {
224         let data = self.buffer.as_ref();
225         Message::from(data[field::TYPE])
226     }
227 
228     /// Return the message code field.
229     #[inline]
msg_code(&self) -> u8230     pub fn msg_code(&self) -> u8 {
231         let data = self.buffer.as_ref();
232         data[field::CODE]
233     }
234 
235     /// Return the checksum field.
236     #[inline]
checksum(&self) -> u16237     pub fn checksum(&self) -> u16 {
238         let data = self.buffer.as_ref();
239         NetworkEndian::read_u16(&data[field::CHECKSUM])
240     }
241 
242     /// Return the identifier field (for echo request and reply packets).
243     ///
244     /// # Panics
245     /// This function may panic if this packet is not an echo request or reply packet.
246     #[inline]
echo_ident(&self) -> u16247     pub fn echo_ident(&self) -> u16 {
248         let data = self.buffer.as_ref();
249         NetworkEndian::read_u16(&data[field::ECHO_IDENT])
250     }
251 
252     /// Return the sequence number field (for echo request and reply packets).
253     ///
254     /// # Panics
255     /// This function may panic if this packet is not an echo request or reply packet.
256     #[inline]
echo_seq_no(&self) -> u16257     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     /// Return the header length.
263     /// The result depends on the value of the message type field.
header_len(&self) -> usize264     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, // make a conservative assumption
270         }
271     }
272 
273     /// Validate the header checksum.
274     ///
275     /// # Fuzzing
276     /// This function always returns `true` when fuzzing.
verify_checksum(&self) -> bool277     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 
287 impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
288     /// Return a pointer to the type-specific data.
289     #[inline]
data(&self) -> &'a [u8]290     pub fn data(&self) -> &'a [u8] {
291         let data = self.buffer.as_ref();
292         &data[self.header_len()..]
293     }
294 }
295 
296 impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
297     /// Set the message type field.
298     #[inline]
set_msg_type(&mut self, value: Message)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     /// Set the message code field.
305     #[inline]
set_msg_code(&mut self, value: u8)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     /// Set the checksum field.
312     #[inline]
set_checksum(&mut self, value: u16)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     /// Set the identifier field (for echo request and reply packets).
319     ///
320     /// # Panics
321     /// This function may panic if this packet is not an echo request or reply packet.
322     #[inline]
set_echo_ident(&mut self, value: u16)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     /// Set the sequence number field (for echo request and reply packets).
329     ///
330     /// # Panics
331     /// This function may panic if this packet is not an echo request or reply packet.
332     #[inline]
set_echo_seq_no(&mut self, value: u16)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     /// Compute and fill in the header checksum.
fill_checksum(&mut self)339     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 
349 impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
350     /// Return a mutable pointer to the type-specific data.
351     #[inline]
data_mut(&mut self) -> &mut [u8]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 
359 impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
as_ref(&self) -> &[u8]360     fn as_ref(&self) -> &[u8] {
361         self.buffer.as_ref()
362     }
363 }
364 
365 /// A high-level representation of an Internet Control Message Protocol version 4 packet header.
366 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
367 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
368 #[non_exhaustive]
369 pub 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 
392 impl<'a> Repr<'a> {
393     /// Parse an Internet Control Message Protocol version 4 packet and return
394     /// a high-level representation.
parse<T>( packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities, ) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized,395     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         // Valid checksum is expected.
403         if checksum_caps.icmpv4.rx() && !packet.verify_checksum() {
404             return Err(Error);
405         }
406 
407         match (packet.msg_type(), packet.msg_code()) {
408             (Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
409                 ident: packet.echo_ident(),
410                 seq_no: packet.echo_seq_no(),
411                 data: packet.data(),
412             }),
413 
414             (Message::EchoReply, 0) => Ok(Repr::EchoReply {
415                 ident: packet.echo_ident(),
416                 seq_no: packet.echo_seq_no(),
417                 data: packet.data(),
418             }),
419 
420             (Message::DstUnreachable, code) => {
421                 let ip_packet = Ipv4Packet::new_checked(packet.data())?;
422 
423                 let payload = &packet.data()[ip_packet.header_len() as usize..];
424                 // RFC 792 requires exactly eight bytes to be returned.
425                 // We allow more, since there isn't a reason not to, but require at least eight.
426                 if payload.len() < 8 {
427                     return Err(Error);
428                 }
429 
430                 Ok(Repr::DstUnreachable {
431                     reason: DstUnreachable::from(code),
432                     header: Ipv4Repr {
433                         src_addr: ip_packet.src_addr(),
434                         dst_addr: ip_packet.dst_addr(),
435                         next_header: ip_packet.next_header(),
436                         payload_len: payload.len(),
437                         hop_limit: ip_packet.hop_limit(),
438                     },
439                     data: payload,
440                 })
441             }
442 
443             (Message::TimeExceeded, code) => {
444                 let ip_packet = Ipv4Packet::new_checked(packet.data())?;
445 
446                 let payload = &packet.data()[ip_packet.header_len() as usize..];
447                 // RFC 792 requires exactly eight bytes to be returned.
448                 // We allow more, since there isn't a reason not to, but require at least eight.
449                 if payload.len() < 8 {
450                     return Err(Error);
451                 }
452 
453                 Ok(Repr::TimeExceeded {
454                     reason: TimeExceeded::from(code),
455                     header: Ipv4Repr {
456                         src_addr: ip_packet.src_addr(),
457                         dst_addr: ip_packet.dst_addr(),
458                         next_header: ip_packet.next_header(),
459                         payload_len: payload.len(),
460                         hop_limit: ip_packet.hop_limit(),
461                     },
462                     data: payload,
463                 })
464             }
465 
466             _ => Err(Error),
467         }
468     }
469 
470     /// Return the length of a packet that will be emitted from this high-level representation.
buffer_len(&self) -> usize471     pub const fn buffer_len(&self) -> usize {
472         match self {
473             &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
474                 field::ECHO_SEQNO.end + data.len()
475             }
476             &Repr::DstUnreachable { header, data, .. }
477             | &Repr::TimeExceeded { header, data, .. } => {
478                 field::UNUSED.end + header.buffer_len() + data.len()
479             }
480         }
481     }
482 
483     /// Emit a high-level representation into an Internet Control Message Protocol version 4
484     /// packet.
emit<T>(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,485     pub fn emit<T>(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
486     where
487         T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
488     {
489         packet.set_msg_code(0);
490         match *self {
491             Repr::EchoRequest {
492                 ident,
493                 seq_no,
494                 data,
495             } => {
496                 packet.set_msg_type(Message::EchoRequest);
497                 packet.set_msg_code(0);
498                 packet.set_echo_ident(ident);
499                 packet.set_echo_seq_no(seq_no);
500                 let data_len = cmp::min(packet.data_mut().len(), data.len());
501                 packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
502             }
503 
504             Repr::EchoReply {
505                 ident,
506                 seq_no,
507                 data,
508             } => {
509                 packet.set_msg_type(Message::EchoReply);
510                 packet.set_msg_code(0);
511                 packet.set_echo_ident(ident);
512                 packet.set_echo_seq_no(seq_no);
513                 let data_len = cmp::min(packet.data_mut().len(), data.len());
514                 packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
515             }
516 
517             Repr::DstUnreachable {
518                 reason,
519                 header,
520                 data,
521             } => {
522                 packet.set_msg_type(Message::DstUnreachable);
523                 packet.set_msg_code(reason.into());
524 
525                 let mut ip_packet = Ipv4Packet::new_unchecked(packet.data_mut());
526                 header.emit(&mut ip_packet, checksum_caps);
527                 let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
528                 payload.copy_from_slice(data)
529             }
530 
531             Repr::TimeExceeded {
532                 reason,
533                 header,
534                 data,
535             } => {
536                 packet.set_msg_type(Message::TimeExceeded);
537                 packet.set_msg_code(reason.into());
538 
539                 let mut ip_packet = Ipv4Packet::new_unchecked(packet.data_mut());
540                 header.emit(&mut ip_packet, checksum_caps);
541                 let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
542                 payload.copy_from_slice(data)
543             }
544         }
545 
546         if checksum_caps.icmpv4.tx() {
547             packet.fill_checksum()
548         } else {
549             // make sure we get a consistently zeroed checksum,
550             // since implementations might rely on it
551             packet.set_checksum(0);
552         }
553     }
554 }
555 
556 impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result557     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
558         match Repr::parse(self, &ChecksumCapabilities::default()) {
559             Ok(repr) => write!(f, "{repr}"),
560             Err(err) => {
561                 write!(f, "ICMPv4 ({err})")?;
562                 write!(f, " type={:?}", self.msg_type())?;
563                 match self.msg_type() {
564                     Message::DstUnreachable => {
565                         write!(f, " code={:?}", DstUnreachable::from(self.msg_code()))
566                     }
567                     Message::TimeExceeded => {
568                         write!(f, " code={:?}", TimeExceeded::from(self.msg_code()))
569                     }
570                     _ => write!(f, " code={}", self.msg_code()),
571                 }
572             }
573         }
574     }
575 }
576 
577 impl<'a> fmt::Display for Repr<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result578     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
579         match *self {
580             Repr::EchoRequest {
581                 ident,
582                 seq_no,
583                 data,
584             } => write!(
585                 f,
586                 "ICMPv4 echo request id={} seq={} len={}",
587                 ident,
588                 seq_no,
589                 data.len()
590             ),
591             Repr::EchoReply {
592                 ident,
593                 seq_no,
594                 data,
595             } => write!(
596                 f,
597                 "ICMPv4 echo reply id={} seq={} len={}",
598                 ident,
599                 seq_no,
600                 data.len()
601             ),
602             Repr::DstUnreachable { reason, .. } => {
603                 write!(f, "ICMPv4 destination unreachable ({reason})")
604             }
605             Repr::TimeExceeded { reason, .. } => {
606                 write!(f, "ICMPv4 time exceeded ({reason})")
607             }
608         }
609     }
610 }
611 
612 use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
613 
614 impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
pretty_print( buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, indent: &mut PrettyIndent, ) -> fmt::Result615     fn pretty_print(
616         buffer: &dyn AsRef<[u8]>,
617         f: &mut fmt::Formatter,
618         indent: &mut PrettyIndent,
619     ) -> fmt::Result {
620         let packet = match Packet::new_checked(buffer) {
621             Err(err) => return write!(f, "{indent}({err})"),
622             Ok(packet) => packet,
623         };
624         write!(f, "{indent}{packet}")?;
625 
626         match packet.msg_type() {
627             Message::DstUnreachable | Message::TimeExceeded => {
628                 indent.increase(f)?;
629                 super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent)
630             }
631             _ => Ok(()),
632         }
633     }
634 }
635 
636 #[cfg(test)]
637 mod test {
638     use super::*;
639 
640     static ECHO_PACKET_BYTES: [u8; 12] = [
641         0x08, 0x00, 0x8e, 0xfe, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
642     ];
643 
644     static ECHO_DATA_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
645 
646     #[test]
test_echo_deconstruct()647     fn test_echo_deconstruct() {
648         let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
649         assert_eq!(packet.msg_type(), Message::EchoRequest);
650         assert_eq!(packet.msg_code(), 0);
651         assert_eq!(packet.checksum(), 0x8efe);
652         assert_eq!(packet.echo_ident(), 0x1234);
653         assert_eq!(packet.echo_seq_no(), 0xabcd);
654         assert_eq!(packet.data(), &ECHO_DATA_BYTES[..]);
655         assert!(packet.verify_checksum());
656     }
657 
658     #[test]
test_echo_construct()659     fn test_echo_construct() {
660         let mut bytes = vec![0xa5; 12];
661         let mut packet = Packet::new_unchecked(&mut bytes);
662         packet.set_msg_type(Message::EchoRequest);
663         packet.set_msg_code(0);
664         packet.set_echo_ident(0x1234);
665         packet.set_echo_seq_no(0xabcd);
666         packet.data_mut().copy_from_slice(&ECHO_DATA_BYTES[..]);
667         packet.fill_checksum();
668         assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
669     }
670 
echo_packet_repr() -> Repr<'static>671     fn echo_packet_repr() -> Repr<'static> {
672         Repr::EchoRequest {
673             ident: 0x1234,
674             seq_no: 0xabcd,
675             data: &ECHO_DATA_BYTES,
676         }
677     }
678 
679     #[test]
test_echo_parse()680     fn test_echo_parse() {
681         let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
682         let repr = Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
683         assert_eq!(repr, echo_packet_repr());
684     }
685 
686     #[test]
test_echo_emit()687     fn test_echo_emit() {
688         let repr = echo_packet_repr();
689         let mut bytes = vec![0xa5; repr.buffer_len()];
690         let mut packet = Packet::new_unchecked(&mut bytes);
691         repr.emit(&mut packet, &ChecksumCapabilities::default());
692         assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
693     }
694 
695     #[test]
test_check_len()696     fn test_check_len() {
697         let bytes = [0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
698         assert_eq!(Packet::new_checked(&[]), Err(Error));
699         assert_eq!(Packet::new_checked(&bytes[..4]), Err(Error));
700         assert!(Packet::new_checked(&bytes[..]).is_ok());
701     }
702 }
703