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::MldRepr; 8 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 9 use crate::wire::NdiscRepr; 10 use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr}; 11 12 enum_with_unknown! { 13 /// Internet protocol control message type. 14 pub enum Message(u8) { 15 /// Destination Unreachable. 16 DstUnreachable = 0x01, 17 /// Packet Too Big. 18 PktTooBig = 0x02, 19 /// Time Exceeded. 20 TimeExceeded = 0x03, 21 /// Parameter Problem. 22 ParamProblem = 0x04, 23 /// Echo Request 24 EchoRequest = 0x80, 25 /// Echo Reply 26 EchoReply = 0x81, 27 /// Multicast Listener Query 28 MldQuery = 0x82, 29 /// Router Solicitation 30 RouterSolicit = 0x85, 31 /// Router Advertisement 32 RouterAdvert = 0x86, 33 /// Neighbor Solicitation 34 NeighborSolicit = 0x87, 35 /// Neighbor Advertisement 36 NeighborAdvert = 0x88, 37 /// Redirect 38 Redirect = 0x89, 39 /// Multicast Listener Report 40 MldReport = 0x8f 41 } 42 } 43 44 impl Message { 45 /// Per [RFC 4443 § 2.1] ICMPv6 message types with the highest order 46 /// bit set are informational messages while message types without 47 /// the highest order bit set are error messages. 48 /// 49 /// [RFC 4443 § 2.1]: https://tools.ietf.org/html/rfc4443#section-2.1 is_error(&self) -> bool50 pub fn is_error(&self) -> bool { 51 (u8::from(*self) & 0x80) != 0x80 52 } 53 54 /// Return a boolean value indicating if the given message type 55 /// is an [NDISC] message type. 56 /// 57 /// [NDISC]: https://tools.ietf.org/html/rfc4861 is_ndisc(&self) -> bool58 pub const fn is_ndisc(&self) -> bool { 59 match *self { 60 Message::RouterSolicit 61 | Message::RouterAdvert 62 | Message::NeighborSolicit 63 | Message::NeighborAdvert 64 | Message::Redirect => true, 65 _ => false, 66 } 67 } 68 69 /// Return a boolean value indicating if the given message type 70 /// is an [MLD] message type. 71 /// 72 /// [MLD]: https://tools.ietf.org/html/rfc3810 is_mld(&self) -> bool73 pub const fn is_mld(&self) -> bool { 74 match *self { 75 Message::MldQuery | Message::MldReport => true, 76 _ => false, 77 } 78 } 79 } 80 81 impl fmt::Display for Message { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 83 match *self { 84 Message::DstUnreachable => write!(f, "destination unreachable"), 85 Message::PktTooBig => write!(f, "packet too big"), 86 Message::TimeExceeded => write!(f, "time exceeded"), 87 Message::ParamProblem => write!(f, "parameter problem"), 88 Message::EchoReply => write!(f, "echo reply"), 89 Message::EchoRequest => write!(f, "echo request"), 90 Message::RouterSolicit => write!(f, "router solicitation"), 91 Message::RouterAdvert => write!(f, "router advertisement"), 92 Message::NeighborSolicit => write!(f, "neighbor solicitation"), 93 Message::NeighborAdvert => write!(f, "neighbor advert"), 94 Message::Redirect => write!(f, "redirect"), 95 Message::MldQuery => write!(f, "multicast listener query"), 96 Message::MldReport => write!(f, "multicast listener report"), 97 Message::Unknown(id) => write!(f, "{id}"), 98 } 99 } 100 } 101 102 enum_with_unknown! { 103 /// Internet protocol control message subtype for type "Destination Unreachable". 104 pub enum DstUnreachable(u8) { 105 /// No Route to destination. 106 NoRoute = 0, 107 /// Communication with destination administratively prohibited. 108 AdminProhibit = 1, 109 /// Beyond scope of source address. 110 BeyondScope = 2, 111 /// Address unreachable. 112 AddrUnreachable = 3, 113 /// Port unreachable. 114 PortUnreachable = 4, 115 /// Source address failed ingress/egress policy. 116 FailedPolicy = 5, 117 /// Reject route to destination. 118 RejectRoute = 6 119 } 120 } 121 122 impl fmt::Display for DstUnreachable { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result123 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 124 match *self { 125 DstUnreachable::NoRoute => write!(f, "no route to destination"), 126 DstUnreachable::AdminProhibit => write!( 127 f, 128 "communication with destination administratively prohibited" 129 ), 130 DstUnreachable::BeyondScope => write!(f, "beyond scope of source address"), 131 DstUnreachable::AddrUnreachable => write!(f, "address unreachable"), 132 DstUnreachable::PortUnreachable => write!(f, "port unreachable"), 133 DstUnreachable::FailedPolicy => { 134 write!(f, "source address failed ingress/egress policy") 135 } 136 DstUnreachable::RejectRoute => write!(f, "reject route to destination"), 137 DstUnreachable::Unknown(id) => write!(f, "{id}"), 138 } 139 } 140 } 141 142 enum_with_unknown! { 143 /// Internet protocol control message subtype for the type "Parameter Problem". 144 pub enum ParamProblem(u8) { 145 /// Erroneous header field encountered. 146 ErroneousHdrField = 0, 147 /// Unrecognized Next Header type encountered. 148 UnrecognizedNxtHdr = 1, 149 /// Unrecognized IPv6 option encountered. 150 UnrecognizedOption = 2 151 } 152 } 153 154 impl fmt::Display for ParamProblem { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 156 match *self { 157 ParamProblem::ErroneousHdrField => write!(f, "erroneous header field."), 158 ParamProblem::UnrecognizedNxtHdr => write!(f, "unrecognized next header type."), 159 ParamProblem::UnrecognizedOption => write!(f, "unrecognized IPv6 option."), 160 ParamProblem::Unknown(id) => write!(f, "{id}"), 161 } 162 } 163 } 164 165 enum_with_unknown! { 166 /// Internet protocol control message subtype for the type "Time Exceeded". 167 pub enum TimeExceeded(u8) { 168 /// Hop limit exceeded in transit. 169 HopLimitExceeded = 0, 170 /// Fragment reassembly time exceeded. 171 FragReassemExceeded = 1 172 } 173 } 174 175 impl fmt::Display for TimeExceeded { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result176 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 177 match *self { 178 TimeExceeded::HopLimitExceeded => write!(f, "hop limit exceeded in transit"), 179 TimeExceeded::FragReassemExceeded => write!(f, "fragment reassembly time exceeded"), 180 TimeExceeded::Unknown(id) => write!(f, "{id}"), 181 } 182 } 183 } 184 185 /// A read/write wrapper around an Internet Control Message Protocol version 6 packet buffer. 186 #[derive(Debug, PartialEq, Eq, Clone)] 187 #[cfg_attr(feature = "defmt", derive(defmt::Format))] 188 pub struct Packet<T: AsRef<[u8]>> { 189 pub(super) buffer: T, 190 } 191 192 // Ranges and constants describing key boundaries in the ICMPv6 header. 193 pub(super) mod field { 194 use crate::wire::field::*; 195 196 // ICMPv6: See https://tools.ietf.org/html/rfc4443 197 pub const TYPE: usize = 0; 198 pub const CODE: usize = 1; 199 pub const CHECKSUM: Field = 2..4; 200 201 pub const UNUSED: Field = 4..8; 202 pub const MTU: Field = 4..8; 203 pub const POINTER: Field = 4..8; 204 pub const ECHO_IDENT: Field = 4..6; 205 pub const ECHO_SEQNO: Field = 6..8; 206 207 pub const HEADER_END: usize = 8; 208 209 // NDISC: See https://tools.ietf.org/html/rfc4861 210 // Router Advertisement message offsets 211 pub const CUR_HOP_LIMIT: usize = 4; 212 pub const ROUTER_FLAGS: usize = 5; 213 pub const ROUTER_LT: Field = 6..8; 214 pub const REACHABLE_TM: Field = 8..12; 215 pub const RETRANS_TM: Field = 12..16; 216 217 // Neighbor Solicitation message offsets 218 pub const TARGET_ADDR: Field = 8..24; 219 220 // Neighbor Advertisement message offsets 221 pub const NEIGH_FLAGS: usize = 4; 222 223 // Redirected Header message offsets 224 pub const DEST_ADDR: Field = 24..40; 225 226 // MLD: 227 // - https://tools.ietf.org/html/rfc3810 228 // - https://tools.ietf.org/html/rfc3810 229 // Multicast Listener Query message 230 pub const MAX_RESP_CODE: Field = 4..6; 231 pub const QUERY_RESV: Field = 6..8; 232 pub const QUERY_MCAST_ADDR: Field = 8..24; 233 pub const SQRV: usize = 24; 234 pub const QQIC: usize = 25; 235 pub const QUERY_NUM_SRCS: Field = 26..28; 236 237 // Multicast Listener Report Message 238 pub const RECORD_RESV: Field = 4..6; 239 pub const NR_MCAST_RCRDS: Field = 6..8; 240 241 // Multicast Address Record Offsets 242 pub const RECORD_TYPE: usize = 0; 243 pub const AUX_DATA_LEN: usize = 1; 244 pub const RECORD_NUM_SRCS: Field = 2..4; 245 pub const RECORD_MCAST_ADDR: Field = 4..20; 246 } 247 248 impl<T: AsRef<[u8]>> Packet<T> { 249 /// Imbue a raw octet buffer with ICMPv6 packet structure. new_unchecked(buffer: T) -> Packet<T>250 pub const fn new_unchecked(buffer: T) -> Packet<T> { 251 Packet { buffer } 252 } 253 254 /// Shorthand for a combination of [new_unchecked] and [check_len]. 255 /// 256 /// [new_unchecked]: #method.new_unchecked 257 /// [check_len]: #method.check_len new_checked(buffer: T) -> Result<Packet<T>>258 pub fn new_checked(buffer: T) -> Result<Packet<T>> { 259 let packet = Self::new_unchecked(buffer); 260 packet.check_len()?; 261 Ok(packet) 262 } 263 264 /// Ensure that no accessor method will panic if called. 265 /// Returns `Err(Error)` if the buffer is too short. check_len(&self) -> Result<()>266 pub fn check_len(&self) -> Result<()> { 267 let len = self.buffer.as_ref().len(); 268 if len < field::HEADER_END || len < self.header_len() { 269 Err(Error) 270 } else { 271 Ok(()) 272 } 273 } 274 275 /// Consume the packet, returning the underlying buffer. into_inner(self) -> T276 pub fn into_inner(self) -> T { 277 self.buffer 278 } 279 280 /// Return the message type field. 281 #[inline] msg_type(&self) -> Message282 pub fn msg_type(&self) -> Message { 283 let data = self.buffer.as_ref(); 284 Message::from(data[field::TYPE]) 285 } 286 287 /// Return the message code field. 288 #[inline] msg_code(&self) -> u8289 pub fn msg_code(&self) -> u8 { 290 let data = self.buffer.as_ref(); 291 data[field::CODE] 292 } 293 294 /// Return the checksum field. 295 #[inline] checksum(&self) -> u16296 pub fn checksum(&self) -> u16 { 297 let data = self.buffer.as_ref(); 298 NetworkEndian::read_u16(&data[field::CHECKSUM]) 299 } 300 301 /// Return the identifier field (for echo request and reply packets). 302 #[inline] echo_ident(&self) -> u16303 pub fn echo_ident(&self) -> u16 { 304 let data = self.buffer.as_ref(); 305 NetworkEndian::read_u16(&data[field::ECHO_IDENT]) 306 } 307 308 /// Return the sequence number field (for echo request and reply packets). 309 #[inline] echo_seq_no(&self) -> u16310 pub fn echo_seq_no(&self) -> u16 { 311 let data = self.buffer.as_ref(); 312 NetworkEndian::read_u16(&data[field::ECHO_SEQNO]) 313 } 314 315 /// Return the MTU field (for packet too big messages). 316 #[inline] pkt_too_big_mtu(&self) -> u32317 pub fn pkt_too_big_mtu(&self) -> u32 { 318 let data = self.buffer.as_ref(); 319 NetworkEndian::read_u32(&data[field::MTU]) 320 } 321 322 /// Return the pointer field (for parameter problem messages). 323 #[inline] param_problem_ptr(&self) -> u32324 pub fn param_problem_ptr(&self) -> u32 { 325 let data = self.buffer.as_ref(); 326 NetworkEndian::read_u32(&data[field::POINTER]) 327 } 328 329 /// Return the header length. The result depends on the value of 330 /// the message type field. header_len(&self) -> usize331 pub fn header_len(&self) -> usize { 332 match self.msg_type() { 333 Message::DstUnreachable => field::UNUSED.end, 334 Message::PktTooBig => field::MTU.end, 335 Message::TimeExceeded => field::UNUSED.end, 336 Message::ParamProblem => field::POINTER.end, 337 Message::EchoRequest => field::ECHO_SEQNO.end, 338 Message::EchoReply => field::ECHO_SEQNO.end, 339 Message::RouterSolicit => field::UNUSED.end, 340 Message::RouterAdvert => field::RETRANS_TM.end, 341 Message::NeighborSolicit => field::TARGET_ADDR.end, 342 Message::NeighborAdvert => field::TARGET_ADDR.end, 343 Message::Redirect => field::DEST_ADDR.end, 344 Message::MldQuery => field::QUERY_NUM_SRCS.end, 345 Message::MldReport => field::NR_MCAST_RCRDS.end, 346 // For packets that are not included in RFC 4443, do not 347 // include the last 32 bits of the ICMPv6 header in 348 // `header_bytes`. This must be done so that these bytes 349 // can be accessed in the `payload`. 350 _ => field::CHECKSUM.end, 351 } 352 } 353 354 /// Validate the header checksum. 355 /// 356 /// # Fuzzing 357 /// This function always returns `true` when fuzzing. verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool358 pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool { 359 if cfg!(fuzzing) { 360 return true; 361 } 362 363 let data = self.buffer.as_ref(); 364 checksum::combine(&[ 365 checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32), 366 checksum::data(data), 367 ]) == !0 368 } 369 } 370 371 impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> { 372 /// Return a pointer to the type-specific data. 373 #[inline] payload(&self) -> &'a [u8]374 pub fn payload(&self) -> &'a [u8] { 375 let data = self.buffer.as_ref(); 376 &data[self.header_len()..] 377 } 378 } 379 380 impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> { 381 /// Set the message type field. 382 #[inline] set_msg_type(&mut self, value: Message)383 pub fn set_msg_type(&mut self, value: Message) { 384 let data = self.buffer.as_mut(); 385 data[field::TYPE] = value.into() 386 } 387 388 /// Set the message code field. 389 #[inline] set_msg_code(&mut self, value: u8)390 pub fn set_msg_code(&mut self, value: u8) { 391 let data = self.buffer.as_mut(); 392 data[field::CODE] = value 393 } 394 395 /// Clear any reserved fields in the message header. 396 /// 397 /// # Panics 398 /// This function panics if the message type has not been set. 399 /// See [set_msg_type]. 400 /// 401 /// [set_msg_type]: #method.set_msg_type 402 #[inline] clear_reserved(&mut self)403 pub fn clear_reserved(&mut self) { 404 match self.msg_type() { 405 Message::RouterSolicit 406 | Message::NeighborSolicit 407 | Message::NeighborAdvert 408 | Message::Redirect => { 409 let data = self.buffer.as_mut(); 410 NetworkEndian::write_u32(&mut data[field::UNUSED], 0); 411 } 412 Message::MldQuery => { 413 let data = self.buffer.as_mut(); 414 NetworkEndian::write_u16(&mut data[field::QUERY_RESV], 0); 415 data[field::SQRV] &= 0xf; 416 } 417 Message::MldReport => { 418 let data = self.buffer.as_mut(); 419 NetworkEndian::write_u16(&mut data[field::RECORD_RESV], 0); 420 } 421 ty => panic!("Message type `{ty}` does not have any reserved fields."), 422 } 423 } 424 425 #[inline] set_checksum(&mut self, value: u16)426 pub fn set_checksum(&mut self, value: u16) { 427 let data = self.buffer.as_mut(); 428 NetworkEndian::write_u16(&mut data[field::CHECKSUM], value) 429 } 430 431 /// Set the identifier field (for echo request and reply packets). 432 /// 433 /// # Panics 434 /// This function may panic if this packet is not an echo request or reply packet. 435 #[inline] set_echo_ident(&mut self, value: u16)436 pub fn set_echo_ident(&mut self, value: u16) { 437 let data = self.buffer.as_mut(); 438 NetworkEndian::write_u16(&mut data[field::ECHO_IDENT], value) 439 } 440 441 /// Set the sequence number field (for echo request and reply packets). 442 /// 443 /// # Panics 444 /// This function may panic if this packet is not an echo request or reply packet. 445 #[inline] set_echo_seq_no(&mut self, value: u16)446 pub fn set_echo_seq_no(&mut self, value: u16) { 447 let data = self.buffer.as_mut(); 448 NetworkEndian::write_u16(&mut data[field::ECHO_SEQNO], value) 449 } 450 451 /// Set the MTU field (for packet too big messages). 452 /// 453 /// # Panics 454 /// This function may panic if this packet is not an packet too big packet. 455 #[inline] set_pkt_too_big_mtu(&mut self, value: u32)456 pub fn set_pkt_too_big_mtu(&mut self, value: u32) { 457 let data = self.buffer.as_mut(); 458 NetworkEndian::write_u32(&mut data[field::MTU], value) 459 } 460 461 /// Set the pointer field (for parameter problem messages). 462 /// 463 /// # Panics 464 /// This function may panic if this packet is not a parameter problem message. 465 #[inline] set_param_problem_ptr(&mut self, value: u32)466 pub fn set_param_problem_ptr(&mut self, value: u32) { 467 let data = self.buffer.as_mut(); 468 NetworkEndian::write_u32(&mut data[field::POINTER], value) 469 } 470 471 /// Compute and fill in the header checksum. fill_checksum(&mut self, src_addr: &IpAddress, dst_addr: &IpAddress)472 pub fn fill_checksum(&mut self, src_addr: &IpAddress, dst_addr: &IpAddress) { 473 self.set_checksum(0); 474 let checksum = { 475 let data = self.buffer.as_ref(); 476 !checksum::combine(&[ 477 checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32), 478 checksum::data(data), 479 ]) 480 }; 481 self.set_checksum(checksum) 482 } 483 484 /// Return a mutable pointer to the type-specific data. 485 #[inline] payload_mut(&mut self) -> &mut [u8]486 pub fn payload_mut(&mut self) -> &mut [u8] { 487 let range = self.header_len()..; 488 let data = self.buffer.as_mut(); 489 &mut data[range] 490 } 491 } 492 493 impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> { as_ref(&self) -> &[u8]494 fn as_ref(&self) -> &[u8] { 495 self.buffer.as_ref() 496 } 497 } 498 499 /// A high-level representation of an Internet Control Message Protocol version 6 packet header. 500 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 501 #[cfg_attr(feature = "defmt", derive(defmt::Format))] 502 #[non_exhaustive] 503 pub enum Repr<'a> { 504 DstUnreachable { 505 reason: DstUnreachable, 506 header: Ipv6Repr, 507 data: &'a [u8], 508 }, 509 PktTooBig { 510 mtu: u32, 511 header: Ipv6Repr, 512 data: &'a [u8], 513 }, 514 TimeExceeded { 515 reason: TimeExceeded, 516 header: Ipv6Repr, 517 data: &'a [u8], 518 }, 519 ParamProblem { 520 reason: ParamProblem, 521 pointer: u32, 522 header: Ipv6Repr, 523 data: &'a [u8], 524 }, 525 EchoRequest { 526 ident: u16, 527 seq_no: u16, 528 data: &'a [u8], 529 }, 530 EchoReply { 531 ident: u16, 532 seq_no: u16, 533 data: &'a [u8], 534 }, 535 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 536 Ndisc(NdiscRepr<'a>), 537 Mld(MldRepr<'a>), 538 } 539 540 impl<'a> Repr<'a> { 541 /// Parse an Internet Control Message Protocol version 6 packet and return 542 /// a high-level representation. parse<T>( src_addr: &IpAddress, dst_addr: &IpAddress, packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities, ) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized,543 pub fn parse<T>( 544 src_addr: &IpAddress, 545 dst_addr: &IpAddress, 546 packet: &Packet<&'a T>, 547 checksum_caps: &ChecksumCapabilities, 548 ) -> Result<Repr<'a>> 549 where 550 T: AsRef<[u8]> + ?Sized, 551 { 552 fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>) -> Result<(&'a [u8], Ipv6Repr)> 553 where 554 T: AsRef<[u8]> + ?Sized, 555 { 556 let ip_packet = Ipv6Packet::new_checked(packet.payload())?; 557 558 let payload = &packet.payload()[ip_packet.header_len()..]; 559 if payload.len() < 8 { 560 return Err(Error); 561 } 562 let repr = Ipv6Repr { 563 src_addr: ip_packet.src_addr(), 564 dst_addr: ip_packet.dst_addr(), 565 next_header: ip_packet.next_header(), 566 payload_len: payload.len(), 567 hop_limit: ip_packet.hop_limit(), 568 }; 569 Ok((payload, repr)) 570 } 571 // Valid checksum is expected. 572 if checksum_caps.icmpv6.rx() && !packet.verify_checksum(src_addr, dst_addr) { 573 return Err(Error); 574 } 575 576 match (packet.msg_type(), packet.msg_code()) { 577 (Message::DstUnreachable, code) => { 578 let (payload, repr) = create_packet_from_payload(packet)?; 579 Ok(Repr::DstUnreachable { 580 reason: DstUnreachable::from(code), 581 header: repr, 582 data: payload, 583 }) 584 } 585 (Message::PktTooBig, 0) => { 586 let (payload, repr) = create_packet_from_payload(packet)?; 587 Ok(Repr::PktTooBig { 588 mtu: packet.pkt_too_big_mtu(), 589 header: repr, 590 data: payload, 591 }) 592 } 593 (Message::TimeExceeded, code) => { 594 let (payload, repr) = create_packet_from_payload(packet)?; 595 Ok(Repr::TimeExceeded { 596 reason: TimeExceeded::from(code), 597 header: repr, 598 data: payload, 599 }) 600 } 601 (Message::ParamProblem, code) => { 602 let (payload, repr) = create_packet_from_payload(packet)?; 603 Ok(Repr::ParamProblem { 604 reason: ParamProblem::from(code), 605 pointer: packet.param_problem_ptr(), 606 header: repr, 607 data: payload, 608 }) 609 } 610 (Message::EchoRequest, 0) => Ok(Repr::EchoRequest { 611 ident: packet.echo_ident(), 612 seq_no: packet.echo_seq_no(), 613 data: packet.payload(), 614 }), 615 (Message::EchoReply, 0) => Ok(Repr::EchoReply { 616 ident: packet.echo_ident(), 617 seq_no: packet.echo_seq_no(), 618 data: packet.payload(), 619 }), 620 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 621 (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc), 622 (msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld), 623 _ => Err(Error), 624 } 625 } 626 627 /// Return the length of a packet that will be emitted from this high-level representation. buffer_len(&self) -> usize628 pub const fn buffer_len(&self) -> usize { 629 match self { 630 &Repr::DstUnreachable { header, data, .. } 631 | &Repr::PktTooBig { header, data, .. } 632 | &Repr::TimeExceeded { header, data, .. } 633 | &Repr::ParamProblem { header, data, .. } => { 634 field::UNUSED.end + header.buffer_len() + data.len() 635 } 636 &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => { 637 field::ECHO_SEQNO.end + data.len() 638 } 639 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 640 &Repr::Ndisc(ndisc) => ndisc.buffer_len(), 641 &Repr::Mld(mld) => mld.buffer_len(), 642 } 643 } 644 645 /// Emit a high-level representation into an Internet Control Message Protocol version 6 646 /// packet. emit<T>( &self, src_addr: &IpAddress, dst_addr: &IpAddress, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities, ) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,647 pub fn emit<T>( 648 &self, 649 src_addr: &IpAddress, 650 dst_addr: &IpAddress, 651 packet: &mut Packet<&mut T>, 652 checksum_caps: &ChecksumCapabilities, 653 ) where 654 T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, 655 { 656 fn emit_contained_packet(buffer: &mut [u8], header: Ipv6Repr, data: &[u8]) { 657 let mut ip_packet = Ipv6Packet::new_unchecked(buffer); 658 header.emit(&mut ip_packet); 659 let payload = &mut ip_packet.into_inner()[header.buffer_len()..]; 660 payload.copy_from_slice(data); 661 } 662 663 match *self { 664 Repr::DstUnreachable { 665 reason, 666 header, 667 data, 668 } => { 669 packet.set_msg_type(Message::DstUnreachable); 670 packet.set_msg_code(reason.into()); 671 672 emit_contained_packet(packet.payload_mut(), header, data); 673 } 674 675 Repr::PktTooBig { mtu, header, data } => { 676 packet.set_msg_type(Message::PktTooBig); 677 packet.set_msg_code(0); 678 packet.set_pkt_too_big_mtu(mtu); 679 680 emit_contained_packet(packet.payload_mut(), header, data); 681 } 682 683 Repr::TimeExceeded { 684 reason, 685 header, 686 data, 687 } => { 688 packet.set_msg_type(Message::TimeExceeded); 689 packet.set_msg_code(reason.into()); 690 691 emit_contained_packet(packet.payload_mut(), header, data); 692 } 693 694 Repr::ParamProblem { 695 reason, 696 pointer, 697 header, 698 data, 699 } => { 700 packet.set_msg_type(Message::ParamProblem); 701 packet.set_msg_code(reason.into()); 702 packet.set_param_problem_ptr(pointer); 703 704 emit_contained_packet(packet.payload_mut(), header, data); 705 } 706 707 Repr::EchoRequest { 708 ident, 709 seq_no, 710 data, 711 } => { 712 packet.set_msg_type(Message::EchoRequest); 713 packet.set_msg_code(0); 714 packet.set_echo_ident(ident); 715 packet.set_echo_seq_no(seq_no); 716 let data_len = cmp::min(packet.payload_mut().len(), data.len()); 717 packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len]) 718 } 719 720 Repr::EchoReply { 721 ident, 722 seq_no, 723 data, 724 } => { 725 packet.set_msg_type(Message::EchoReply); 726 packet.set_msg_code(0); 727 packet.set_echo_ident(ident); 728 packet.set_echo_seq_no(seq_no); 729 let data_len = cmp::min(packet.payload_mut().len(), data.len()); 730 packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len]) 731 } 732 733 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 734 Repr::Ndisc(ndisc) => ndisc.emit(packet), 735 736 Repr::Mld(mld) => mld.emit(packet), 737 } 738 739 if checksum_caps.icmpv6.tx() { 740 packet.fill_checksum(src_addr, dst_addr); 741 } else { 742 // make sure we get a consistently zeroed checksum, since implementations might rely on it 743 packet.set_checksum(0); 744 } 745 } 746 } 747 748 #[cfg(test)] 749 mod test { 750 use super::*; 751 use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; 752 use crate::wire::{IpProtocol, Ipv6Address, Ipv6Repr}; 753 754 static ECHO_PACKET_BYTES: [u8; 12] = [ 755 0x80, 0x00, 0x19, 0xb3, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff, 756 ]; 757 758 static ECHO_PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; 759 760 static PKT_TOO_BIG_BYTES: [u8; 60] = [ 761 0x02, 0x00, 0x0f, 0xc9, 0x00, 0x00, 0x05, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 762 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 763 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 764 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff, 765 ]; 766 767 static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] = [ 768 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 769 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00, 771 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff, 772 ]; 773 774 static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] = [ 775 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff, 776 ]; 777 echo_packet_repr() -> Repr<'static>778 fn echo_packet_repr() -> Repr<'static> { 779 Repr::EchoRequest { 780 ident: 0x1234, 781 seq_no: 0xabcd, 782 data: &ECHO_PACKET_PAYLOAD, 783 } 784 } 785 too_big_packet_repr() -> Repr<'static>786 fn too_big_packet_repr() -> Repr<'static> { 787 Repr::PktTooBig { 788 mtu: 1500, 789 header: Ipv6Repr { 790 src_addr: Ipv6Address([ 791 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 792 0x00, 0x00, 0x01, 793 ]), 794 dst_addr: Ipv6Address([ 795 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 796 0x00, 0x00, 0x02, 797 ]), 798 next_header: IpProtocol::Udp, 799 payload_len: 12, 800 hop_limit: 0x40, 801 }, 802 data: &PKT_TOO_BIG_UDP_PAYLOAD, 803 } 804 } 805 806 #[test] test_echo_deconstruct()807 fn test_echo_deconstruct() { 808 let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]); 809 assert_eq!(packet.msg_type(), Message::EchoRequest); 810 assert_eq!(packet.msg_code(), 0); 811 assert_eq!(packet.checksum(), 0x19b3); 812 assert_eq!(packet.echo_ident(), 0x1234); 813 assert_eq!(packet.echo_seq_no(), 0xabcd); 814 assert_eq!(packet.payload(), &ECHO_PACKET_PAYLOAD[..]); 815 assert!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2)); 816 assert!(!packet.msg_type().is_error()); 817 } 818 819 #[test] test_echo_construct()820 fn test_echo_construct() { 821 let mut bytes = vec![0xa5; 12]; 822 let mut packet = Packet::new_unchecked(&mut bytes); 823 packet.set_msg_type(Message::EchoRequest); 824 packet.set_msg_code(0); 825 packet.set_echo_ident(0x1234); 826 packet.set_echo_seq_no(0xabcd); 827 packet 828 .payload_mut() 829 .copy_from_slice(&ECHO_PACKET_PAYLOAD[..]); 830 packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2); 831 assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]); 832 } 833 834 #[test] test_echo_repr_parse()835 fn test_echo_repr_parse() { 836 let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]); 837 let repr = Repr::parse( 838 &MOCK_IP_ADDR_1, 839 &MOCK_IP_ADDR_2, 840 &packet, 841 &ChecksumCapabilities::default(), 842 ) 843 .unwrap(); 844 assert_eq!(repr, echo_packet_repr()); 845 } 846 847 #[test] test_echo_emit()848 fn test_echo_emit() { 849 let repr = echo_packet_repr(); 850 let mut bytes = vec![0xa5; repr.buffer_len()]; 851 let mut packet = Packet::new_unchecked(&mut bytes); 852 repr.emit( 853 &MOCK_IP_ADDR_1, 854 &MOCK_IP_ADDR_2, 855 &mut packet, 856 &ChecksumCapabilities::default(), 857 ); 858 assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]); 859 } 860 861 #[test] test_too_big_deconstruct()862 fn test_too_big_deconstruct() { 863 let packet = Packet::new_unchecked(&PKT_TOO_BIG_BYTES[..]); 864 assert_eq!(packet.msg_type(), Message::PktTooBig); 865 assert_eq!(packet.msg_code(), 0); 866 assert_eq!(packet.checksum(), 0x0fc9); 867 assert_eq!(packet.pkt_too_big_mtu(), 1500); 868 assert_eq!(packet.payload(), &PKT_TOO_BIG_IP_PAYLOAD[..]); 869 assert!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2)); 870 assert!(packet.msg_type().is_error()); 871 } 872 873 #[test] test_too_big_construct()874 fn test_too_big_construct() { 875 let mut bytes = vec![0xa5; 60]; 876 let mut packet = Packet::new_unchecked(&mut bytes); 877 packet.set_msg_type(Message::PktTooBig); 878 packet.set_msg_code(0); 879 packet.set_pkt_too_big_mtu(1500); 880 packet 881 .payload_mut() 882 .copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]); 883 packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2); 884 assert_eq!(&*packet.into_inner(), &PKT_TOO_BIG_BYTES[..]); 885 } 886 887 #[test] test_too_big_repr_parse()888 fn test_too_big_repr_parse() { 889 let packet = Packet::new_unchecked(&PKT_TOO_BIG_BYTES[..]); 890 let repr = Repr::parse( 891 &MOCK_IP_ADDR_1, 892 &MOCK_IP_ADDR_2, 893 &packet, 894 &ChecksumCapabilities::default(), 895 ) 896 .unwrap(); 897 assert_eq!(repr, too_big_packet_repr()); 898 } 899 900 #[test] test_too_big_emit()901 fn test_too_big_emit() { 902 let repr = too_big_packet_repr(); 903 let mut bytes = vec![0xa5; repr.buffer_len()]; 904 let mut packet = Packet::new_unchecked(&mut bytes); 905 repr.emit( 906 &MOCK_IP_ADDR_1, 907 &MOCK_IP_ADDR_2, 908 &mut packet, 909 &ChecksumCapabilities::default(), 910 ); 911 assert_eq!(&*packet.into_inner(), &PKT_TOO_BIG_BYTES[..]); 912 } 913 } 914