Skip to main content

ya_smoltcp/wire/
icmpv6.rs

1use byteorder::{ByteOrder, NetworkEndian};
2use core::{cmp, fmt};
3
4use crate::phy::ChecksumCapabilities;
5use crate::wire::ip::checksum;
6use crate::wire::MldRepr;
7#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
8use crate::wire::NdiscRepr;
9use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
10use crate::{Error, Result};
11
12enum_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
44impl 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
50    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
58    pub 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
73    pub fn is_mld(&self) -> bool {
74        match *self {
75            Message::MldQuery | Message::MldReport => true,
76            _ => false,
77        }
78    }
79}
80
81impl fmt::Display for Message {
82    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
102enum_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
122impl fmt::Display for DstUnreachable {
123    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
142enum_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
154impl fmt::Display for ParamProblem {
155    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
165enum_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
175impl fmt::Display for TimeExceeded {
176    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, Clone)]
187#[cfg_attr(feature = "defmt", derive(defmt::Format))]
188pub struct Packet<T: AsRef<[u8]>> {
189    pub(super) buffer: T,
190}
191
192// Ranges and constants describing key boundaries in the ICMPv6 header.
193pub(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
248impl<T: AsRef<[u8]>> Packet<T> {
249    /// Imbue a raw octet buffer with ICMPv6 packet structure.
250    pub 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
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::Truncated)` if the buffer is too short.
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::Truncated)
270        } else {
271            Ok(())
272        }
273    }
274
275    /// Consume the packet, returning the underlying buffer.
276    pub fn into_inner(self) -> T {
277        self.buffer
278    }
279
280    /// Return the message type field.
281    #[inline]
282    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]
289    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]
296    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]
303    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]
310    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]
317    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]
324    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.
331    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.
358    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
371impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
372    /// Return a pointer to the type-specific data.
373    #[inline]
374    pub fn payload(&self) -> &'a [u8] {
375        let data = self.buffer.as_ref();
376        &data[self.header_len()..]
377    }
378}
379
380impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
381    /// Set the message type field.
382    #[inline]
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]
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]
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 `{}` does not have any reserved fields.", ty),
422        }
423    }
424
425    #[inline]
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]
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]
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]
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]
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.
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]
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
493impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
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]
503pub 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
540impl<'a> Repr<'a> {
541    /// Parse an Internet Control Message Protocol version 6 packet and return
542    /// a high-level representation.
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() as usize..];
559            if payload.len() < 8 {
560                return Err(Error::Truncated);
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::Checksum);
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::Unrecognized),
624        }
625    }
626
627    /// Return the length of a packet that will be emitted from this high-level representation.
628    pub 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.
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)]
749mod 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
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
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]
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]
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]
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]
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]
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]
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]
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]
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}