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