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