Skip to main content

smoltcp/wire/
ipv6.rs

1#![deny(missing_docs)]
2
3use byteorder::{ByteOrder, NetworkEndian};
4use core::fmt;
5
6use super::{Error, Result};
7use crate::wire::HardwareAddress;
8use crate::wire::ip::pretty_print_ip_payload;
9
10pub use super::IpProtocol as Protocol;
11
12/// Minimum MTU required of all links supporting IPv6. See [RFC 8200 § 5].
13///
14/// [RFC 8200 § 5]: https://tools.ietf.org/html/rfc8200#section-5
15pub const MIN_MTU: usize = 1280;
16
17/// Size of IPv6 adderess in octets.
18///
19/// [RFC 8200 § 2]: https://www.rfc-editor.org/rfc/rfc4291#section-2
20pub const ADDR_SIZE: usize = 16;
21
22/// The link-local [all nodes multicast address].
23///
24/// [all nodes multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
25pub const LINK_LOCAL_ALL_NODES: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1);
26
27/// The link-local [all routers multicast address].
28///
29/// [all routers multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
30pub const LINK_LOCAL_ALL_ROUTERS: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2);
31
32/// The link-local [all MLVDv2-capable routers multicast address].
33///
34/// [all MLVDv2-capable routers multicast address]: https://tools.ietf.org/html/rfc3810#section-11
35pub const LINK_LOCAL_ALL_MLDV2_ROUTERS: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 0x16);
36
37/// The link-local [all RPL nodes multicast address].
38///
39/// [all RPL nodes multicast address]: https://www.rfc-editor.org/rfc/rfc6550.html#section-20.19
40pub const LINK_LOCAL_ALL_RPL_NODES: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 0x1a);
41
42/// The [scope] of an address.
43///
44/// [scope]: https://www.rfc-editor.org/rfc/rfc4291#section-2.7
45#[repr(u8)]
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub(crate) enum MulticastScope {
48    /// Interface Local scope
49    InterfaceLocal = 0x1,
50    /// Link local scope
51    LinkLocal = 0x2,
52    /// Administratively configured
53    AdminLocal = 0x4,
54    /// Single site scope
55    SiteLocal = 0x5,
56    /// Organization scope
57    OrganizationLocal = 0x8,
58    /// Global scope
59    Global = 0xE,
60    /// Unknown scope
61    Unknown = 0xFF,
62}
63
64impl From<u8> for MulticastScope {
65    fn from(value: u8) -> Self {
66        match value {
67            0x1 => Self::InterfaceLocal,
68            0x2 => Self::LinkLocal,
69            0x4 => Self::AdminLocal,
70            0x5 => Self::SiteLocal,
71            0x8 => Self::OrganizationLocal,
72            0xE => Self::Global,
73            _ => Self::Unknown,
74        }
75    }
76}
77
78pub use core::net::Ipv6Addr as Address;
79
80pub(crate) trait AddressExt {
81    /// Create an IPv6 address based on the provided prefix and hardware identifier.
82    fn from_link_prefix(
83        link_prefix: &Cidr,
84        interface_identifier: HardwareAddress,
85    ) -> Option<Address>;
86
87    /// Query whether the IPv6 address is an [unicast address].
88    ///
89    /// [unicast address]: https://tools.ietf.org/html/rfc4291#section-2.5
90    ///
91    /// `x_` prefix is to avoid a collision with the still-unstable method in `core::ip`.
92    fn x_is_unicast(&self) -> bool;
93
94    /// Query whether the IPv6 address is a [global unicast address].
95    ///
96    /// [global unicast address]: https://datatracker.ietf.org/doc/html/rfc3587
97    fn is_global_unicast(&self) -> bool;
98
99    /// Query whether the IPv6 address is in the [link-local] scope.
100    ///
101    /// [link-local]: https://tools.ietf.org/html/rfc4291#section-2.5.6
102    fn is_link_local(&self) -> bool;
103
104    /// Helper function used to mask an address given a prefix.
105    ///
106    /// # Panics
107    /// This function panics if `mask` is greater than 128.
108    fn mask(&self, mask: u8) -> [u8; ADDR_SIZE];
109
110    /// The solicited node for the given unicast address.
111    ///
112    /// # Panics
113    /// This function panics if the given address is not
114    /// unicast.
115    fn solicited_node(&self) -> Address;
116
117    /// Return the scope of the address.
118    ///
119    /// `x_` prefix is to avoid a collision with the still-unstable method in `core::ip`.
120    fn x_multicast_scope(&self) -> MulticastScope;
121
122    /// Query whether the IPv6 address is a [solicited-node multicast address].
123    ///
124    /// [Solicited-node multicast address]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1
125    fn is_solicited_node_multicast(&self) -> bool;
126
127    /// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`,
128    /// where `prefix_len` is the number of leading zeroes. Return `None` otherwise.
129    fn prefix_len(&self) -> Option<u8>;
130}
131
132impl AddressExt for Address {
133    fn from_link_prefix(
134        link_prefix: &Cidr,
135        interface_identifier: HardwareAddress,
136    ) -> Option<Address> {
137        if let Some(eui64) = interface_identifier.as_eui_64() {
138            if link_prefix.prefix_len() != 64 {
139                return None;
140            }
141            let mut bytes = [0; 16];
142            bytes[0..8].copy_from_slice(&link_prefix.address().octets()[0..8]);
143            bytes[8..16].copy_from_slice(&eui64);
144            Some(Address::from_octets(bytes))
145        } else {
146            None
147        }
148    }
149
150    fn x_is_unicast(&self) -> bool {
151        !(self.is_multicast() || self.is_unspecified())
152    }
153
154    fn is_global_unicast(&self) -> bool {
155        (self.octets()[0] >> 5) == 0b001
156    }
157
158    fn is_link_local(&self) -> bool {
159        self.octets()[0..8] == [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
160    }
161
162    fn mask(&self, mask: u8) -> [u8; ADDR_SIZE] {
163        assert!(mask <= 128);
164        let mut bytes = [0u8; ADDR_SIZE];
165        let idx = (mask as usize) / 8;
166        let modulus = (mask as usize) % 8;
167        let octets = self.octets();
168        let (first, second) = octets.split_at(idx);
169        bytes[0..idx].copy_from_slice(first);
170        if idx < ADDR_SIZE {
171            let part = second[0];
172            bytes[idx] = part & (!(0xff >> modulus) as u8);
173        }
174        bytes
175    }
176
177    fn solicited_node(&self) -> Address {
178        assert!(self.x_is_unicast());
179        let o = self.octets();
180        Address::from([
181            0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, o[13],
182            o[14], o[15],
183        ])
184    }
185
186    fn x_multicast_scope(&self) -> MulticastScope {
187        if self.is_multicast() {
188            return MulticastScope::from(self.octets()[1] & 0b1111);
189        }
190
191        if self.is_link_local() {
192            MulticastScope::LinkLocal
193        } else if self.is_unique_local() || self.is_global_unicast() {
194            // ULA are considered global scope
195            // https://www.rfc-editor.org/rfc/rfc6724#section-3.1
196            MulticastScope::Global
197        } else {
198            MulticastScope::Unknown
199        }
200    }
201
202    fn is_solicited_node_multicast(&self) -> bool {
203        self.octets()[0..13]
204            == [
205                0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
206            ]
207    }
208
209    fn prefix_len(&self) -> Option<u8> {
210        let mut ones = true;
211        let mut prefix_len = 0;
212        for byte in self.octets() {
213            let mut mask = 0x80;
214            for _ in 0..8 {
215                let one = byte & mask != 0;
216                if ones {
217                    // Expect 1s until first 0
218                    if one {
219                        prefix_len += 1;
220                    } else {
221                        ones = false;
222                    }
223                } else if one {
224                    // 1 where 0 was expected
225                    return None;
226                }
227                mask >>= 1;
228            }
229        }
230        Some(prefix_len)
231    }
232}
233
234/// A specification of an IPv6 CIDR block, containing an address and a variable-length
235/// subnet masking prefix length.
236#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
237pub struct Cidr {
238    address: Address,
239    prefix_len: u8,
240}
241
242impl Cidr {
243    /// The [solicited node prefix].
244    ///
245    /// [solicited node prefix]: https://tools.ietf.org/html/rfc4291#section-2.7.1
246    pub const SOLICITED_NODE_PREFIX: Cidr = Cidr {
247        address: Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0),
248        prefix_len: 104,
249    };
250
251    /// The link-local address prefix.
252    pub const LINK_LOCAL_PREFIX: Cidr = Cidr {
253        address: Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
254        prefix_len: 10,
255    };
256
257    /// Create an IPv6 CIDR block from the given address and prefix length.
258    ///
259    /// # Panics
260    /// This function panics if the prefix length is larger than 128.
261    pub const fn new(address: Address, prefix_len: u8) -> Cidr {
262        assert!(prefix_len <= 128);
263        Cidr {
264            address,
265            prefix_len,
266        }
267    }
268
269    /// Create an IPv6 CIDR based on the provided prefix and hardware identifier.
270    pub fn from_link_prefix(
271        link_prefix: &Cidr,
272        interface_identifier: HardwareAddress,
273    ) -> Option<Self> {
274        Address::from_link_prefix(link_prefix, interface_identifier)
275            .map(|address| Self::new(address, link_prefix.prefix_len()))
276    }
277
278    /// Return the address of this IPv6 CIDR block.
279    pub const fn address(&self) -> Address {
280        self.address
281    }
282
283    /// Return the prefix length of this IPv6 CIDR block.
284    pub const fn prefix_len(&self) -> u8 {
285        self.prefix_len
286    }
287
288    /// Query whether the subnetwork described by this IPv6 CIDR block contains
289    /// the given address.
290    pub fn contains_addr(&self, addr: &Address) -> bool {
291        // right shift by 128 is not legal
292        if self.prefix_len == 0 {
293            return true;
294        }
295
296        self.address.mask(self.prefix_len) == addr.mask(self.prefix_len)
297    }
298
299    /// Query whether the subnetwork described by this IPV6 CIDR block contains
300    /// the subnetwork described by the given IPv6 CIDR block.
301    pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
302        self.prefix_len <= subnet.prefix_len && self.contains_addr(&subnet.address)
303    }
304}
305
306impl fmt::Display for Cidr {
307    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308        // https://tools.ietf.org/html/rfc4291#section-2.3
309        write!(f, "{}/{}", self.address, self.prefix_len)
310    }
311}
312
313#[cfg(feature = "defmt")]
314impl defmt::Format for Cidr {
315    fn format(&self, f: defmt::Formatter) {
316        defmt::write!(f, "{}/{=u8}", self.address, self.prefix_len);
317    }
318}
319
320/// A read/write wrapper around an Internet Protocol version 6 packet buffer.
321#[derive(Debug, PartialEq, Eq, Clone)]
322#[cfg_attr(feature = "defmt", derive(defmt::Format))]
323pub struct Packet<T: AsRef<[u8]>> {
324    buffer: T,
325}
326
327// Ranges and constants describing the IPv6 header
328//
329// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
330// |Version| Traffic Class |           Flow Label                  |
331// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
332// |         Payload Length        |  Next Header  |   Hop Limit   |
333// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
334// |                                                               |
335// +                                                               +
336// |                                                               |
337// +                         Source Address                        +
338// |                                                               |
339// +                                                               +
340// |                                                               |
341// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
342// |                                                               |
343// +                                                               +
344// |                                                               |
345// +                      Destination Address                      +
346// |                                                               |
347// +                                                               +
348// |                                                               |
349// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
350//
351// See https://tools.ietf.org/html/rfc2460#section-3 for details.
352mod field {
353    use crate::wire::field::*;
354    // 4-bit version number, 8-bit traffic class, and the
355    // 20-bit flow label.
356    pub const VER_TC_FLOW: Field = 0..4;
357    // 16-bit value representing the length of the payload.
358    // Note: Options are included in this length.
359    pub const LENGTH: Field = 4..6;
360    // 8-bit value identifying the type of header following this
361    // one. Note: The same numbers are used in IPv4.
362    pub const NXT_HDR: usize = 6;
363    // 8-bit value decremented by each node that forwards this
364    // packet. The packet is discarded when the value is 0.
365    pub const HOP_LIMIT: usize = 7;
366    // IPv6 address of the source node.
367    pub const SRC_ADDR: Field = 8..24;
368    // IPv6 address of the destination node.
369    pub const DST_ADDR: Field = 24..40;
370}
371
372/// Length of an IPv6 header.
373pub const HEADER_LEN: usize = field::DST_ADDR.end;
374
375impl<T: AsRef<[u8]>> Packet<T> {
376    /// Create a raw octet buffer with an IPv6 packet structure.
377    #[inline]
378    pub const fn new_unchecked(buffer: T) -> Packet<T> {
379        Packet { buffer }
380    }
381
382    /// Shorthand for a combination of [new_unchecked] and [check_len].
383    ///
384    /// [new_unchecked]: #method.new_unchecked
385    /// [check_len]: #method.check_len
386    #[inline]
387    pub fn new_checked(buffer: T) -> Result<Packet<T>> {
388        let packet = Self::new_unchecked(buffer);
389        packet.check_len()?;
390        Ok(packet)
391    }
392
393    /// Ensure that no accessor method will panic if called.
394    /// Returns `Err(Error)` if the buffer is too short.
395    ///
396    /// The result of this check is invalidated by calling [set_payload_len].
397    ///
398    /// [set_payload_len]: #method.set_payload_len
399    #[inline]
400    pub fn check_len(&self) -> Result<()> {
401        let len = self.buffer.as_ref().len();
402        if len < field::DST_ADDR.end || len < self.total_len() {
403            Err(Error)
404        } else {
405            Ok(())
406        }
407    }
408
409    /// Consume the packet, returning the underlying buffer.
410    #[inline]
411    pub fn into_inner(self) -> T {
412        self.buffer
413    }
414
415    /// Return the header length.
416    #[inline]
417    pub const fn header_len(&self) -> usize {
418        // This is not a strictly necessary function, but it makes
419        // code more readable.
420        field::DST_ADDR.end
421    }
422
423    /// Return the version field.
424    #[inline]
425    pub fn version(&self) -> u8 {
426        let data = self.buffer.as_ref();
427        data[field::VER_TC_FLOW.start] >> 4
428    }
429
430    /// Return the traffic class.
431    #[inline]
432    pub fn traffic_class(&self) -> u8 {
433        let data = self.buffer.as_ref();
434        ((NetworkEndian::read_u16(&data[0..2]) & 0x0ff0) >> 4) as u8
435    }
436
437    /// Return the flow label field.
438    #[inline]
439    pub fn flow_label(&self) -> u32 {
440        let data = self.buffer.as_ref();
441        NetworkEndian::read_u24(&data[1..4]) & 0x000fffff
442    }
443
444    /// Return the payload length field.
445    #[inline]
446    pub fn payload_len(&self) -> u16 {
447        let data = self.buffer.as_ref();
448        NetworkEndian::read_u16(&data[field::LENGTH])
449    }
450
451    /// Return the payload length added to the known header length.
452    #[inline]
453    pub fn total_len(&self) -> usize {
454        self.header_len() + self.payload_len() as usize
455    }
456
457    /// Return the next header field.
458    #[inline]
459    pub fn next_header(&self) -> Protocol {
460        let data = self.buffer.as_ref();
461        Protocol::from(data[field::NXT_HDR])
462    }
463
464    /// Return the hop limit field.
465    #[inline]
466    pub fn hop_limit(&self) -> u8 {
467        let data = self.buffer.as_ref();
468        data[field::HOP_LIMIT]
469    }
470
471    /// Return the source address field.
472    #[inline]
473    pub fn src_addr(&self) -> Address {
474        let data = self.buffer.as_ref();
475        Address::from_octets(data[field::SRC_ADDR].try_into().unwrap())
476    }
477
478    /// Return the destination address field.
479    #[inline]
480    pub fn dst_addr(&self) -> Address {
481        let data = self.buffer.as_ref();
482        Address::from_octets(data[field::DST_ADDR].try_into().unwrap())
483    }
484}
485
486impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
487    /// Return a pointer to the payload.
488    #[inline]
489    pub fn payload(&self) -> &'a [u8] {
490        let data = self.buffer.as_ref();
491        let range = self.header_len()..self.total_len();
492        &data[range]
493    }
494}
495
496impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
497    /// Set the version field.
498    #[inline]
499    pub fn set_version(&mut self, value: u8) {
500        let data = self.buffer.as_mut();
501        // Make sure to retain the lower order bits which contain
502        // the higher order bits of the traffic class
503        data[0] = (data[0] & 0x0f) | ((value & 0x0f) << 4);
504    }
505
506    /// Set the traffic class field.
507    #[inline]
508    pub fn set_traffic_class(&mut self, value: u8) {
509        let data = self.buffer.as_mut();
510        // Put the higher order 4-bits of value in the lower order
511        // 4-bits of the first byte
512        data[0] = (data[0] & 0xf0) | ((value & 0xf0) >> 4);
513        // Put the lower order 4-bits of value in the higher order
514        // 4-bits of the second byte
515        data[1] = (data[1] & 0x0f) | ((value & 0x0f) << 4);
516    }
517
518    /// Set the flow label field.
519    #[inline]
520    pub fn set_flow_label(&mut self, value: u32) {
521        let data = self.buffer.as_mut();
522        // Retain the lower order 4-bits of the traffic class
523        let raw = (((data[1] & 0xf0) as u32) << 16) | (value & 0x0fffff);
524        NetworkEndian::write_u24(&mut data[1..4], raw);
525    }
526
527    /// Set the payload length field.
528    #[inline]
529    pub fn set_payload_len(&mut self, value: u16) {
530        let data = self.buffer.as_mut();
531        NetworkEndian::write_u16(&mut data[field::LENGTH], value);
532    }
533
534    /// Set the next header field.
535    #[inline]
536    pub fn set_next_header(&mut self, value: Protocol) {
537        let data = self.buffer.as_mut();
538        data[field::NXT_HDR] = value.into();
539    }
540
541    /// Set the hop limit field.
542    #[inline]
543    pub fn set_hop_limit(&mut self, value: u8) {
544        let data = self.buffer.as_mut();
545        data[field::HOP_LIMIT] = value;
546    }
547
548    /// Set the source address field.
549    #[inline]
550    pub fn set_src_addr(&mut self, value: Address) {
551        let data = self.buffer.as_mut();
552        data[field::SRC_ADDR].copy_from_slice(&value.octets());
553    }
554
555    /// Set the destination address field.
556    #[inline]
557    pub fn set_dst_addr(&mut self, value: Address) {
558        let data = self.buffer.as_mut();
559        data[field::DST_ADDR].copy_from_slice(&value.octets());
560    }
561
562    /// Return a mutable pointer to the payload.
563    #[inline]
564    pub fn payload_mut(&mut self) -> &mut [u8] {
565        let range = self.header_len()..self.total_len();
566        let data = self.buffer.as_mut();
567        &mut data[range]
568    }
569}
570
571impl<T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&T> {
572    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
573        match Repr::parse(self) {
574            Ok(repr) => write!(f, "{repr}"),
575            Err(err) => {
576                write!(f, "IPv6 ({err})")?;
577                Ok(())
578            }
579        }
580    }
581}
582
583impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
584    fn as_ref(&self) -> &[u8] {
585        self.buffer.as_ref()
586    }
587}
588
589/// A high-level representation of an Internet Protocol version 6 packet header.
590#[derive(Debug, PartialEq, Eq, Clone, Copy)]
591pub struct Repr {
592    /// IPv6 address of the source node.
593    pub src_addr: Address,
594    /// IPv6 address of the destination node.
595    pub dst_addr: Address,
596    /// Protocol contained in the next header.
597    pub next_header: Protocol,
598    /// Length of the payload including the extension headers.
599    pub payload_len: usize,
600    /// The 8-bit hop limit field.
601    pub hop_limit: u8,
602}
603
604impl Repr {
605    /// Parse an Internet Protocol version 6 packet and return a high-level representation.
606    pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr> {
607        // Ensure basic accessors will work
608        packet.check_len()?;
609        if packet.version() != 6 {
610            return Err(Error);
611        }
612        Ok(Repr {
613            src_addr: packet.src_addr(),
614            dst_addr: packet.dst_addr(),
615            next_header: packet.next_header(),
616            payload_len: packet.payload_len() as usize,
617            hop_limit: packet.hop_limit(),
618        })
619    }
620
621    /// Return the length of a header that will be emitted from this high-level representation.
622    pub const fn buffer_len(&self) -> usize {
623        // This function is not strictly necessary, but it can make client code more readable.
624        field::DST_ADDR.end
625    }
626
627    /// Emit a high-level representation into an Internet Protocol version 6 packet.
628    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
629        // Make no assumptions about the original state of the packet buffer.
630        // Make sure to set every byte.
631        packet.set_version(6);
632        packet.set_traffic_class(0);
633        packet.set_flow_label(0);
634        packet.set_payload_len(self.payload_len as u16);
635        packet.set_hop_limit(self.hop_limit);
636        packet.set_next_header(self.next_header);
637        packet.set_src_addr(self.src_addr);
638        packet.set_dst_addr(self.dst_addr);
639    }
640}
641
642impl fmt::Display for Repr {
643    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
644        write!(
645            f,
646            "IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
647            self.src_addr, self.dst_addr, self.next_header, self.hop_limit
648        )
649    }
650}
651
652#[cfg(feature = "defmt")]
653impl defmt::Format for Repr {
654    fn format(&self, fmt: defmt::Formatter) {
655        defmt::write!(
656            fmt,
657            "IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
658            self.src_addr,
659            self.dst_addr,
660            self.next_header,
661            self.hop_limit
662        )
663    }
664}
665
666use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
667
668// TODO: This is very similar to the implementation for IPv4. Make
669// a way to have less copy and pasted code here.
670impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
671    fn pretty_print(
672        buffer: &dyn AsRef<[u8]>,
673        f: &mut fmt::Formatter,
674        indent: &mut PrettyIndent,
675    ) -> fmt::Result {
676        let (ip_repr, payload) = match Packet::new_checked(buffer) {
677            Err(err) => return write!(f, "{indent}({err})"),
678            Ok(ip_packet) => match Repr::parse(&ip_packet) {
679                Err(_) => return Ok(()),
680                Ok(ip_repr) => {
681                    write!(f, "{indent}{ip_repr}")?;
682                    (ip_repr, ip_packet.payload())
683                }
684            },
685        };
686
687        pretty_print_ip_payload(f, indent, ip_repr, payload)
688    }
689}
690
691#[cfg(test)]
692pub(crate) mod test {
693    use super::*;
694    use crate::wire::pretty_print::PrettyPrinter;
695
696    #[allow(unused)]
697    pub(crate) const MOCK_IP_ADDR_1: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
698    #[allow(unused)]
699    pub(crate) const MOCK_IP_ADDR_2: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2);
700    #[allow(unused)]
701    pub(crate) const MOCK_IP_ADDR_3: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 3);
702    #[allow(unused)]
703    pub(crate) const MOCK_IP_ADDR_4: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 4);
704    #[allow(unused)]
705    pub(crate) const MOCK_UNSPECIFIED: Address = Address::UNSPECIFIED;
706
707    const LINK_LOCAL_ADDR: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
708    const UNIQUE_LOCAL_ADDR: Address = Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1);
709    const GLOBAL_UNICAST_ADDR: Address = Address::new(0x2001, 0xdb8, 0x3, 0, 0, 0, 0, 1);
710
711    const TEST_SOL_NODE_MCAST_ADDR: Address = Address::new(0xff02, 0, 0, 0, 0, 1, 0xff01, 101);
712
713    #[test]
714    fn test_basic_multicast() {
715        assert!(!LINK_LOCAL_ALL_ROUTERS.is_unspecified());
716        assert!(LINK_LOCAL_ALL_ROUTERS.is_multicast());
717        assert!(!LINK_LOCAL_ALL_ROUTERS.is_link_local());
718        assert!(!LINK_LOCAL_ALL_ROUTERS.is_loopback());
719        assert!(!LINK_LOCAL_ALL_ROUTERS.is_unique_local());
720        assert!(!LINK_LOCAL_ALL_ROUTERS.is_global_unicast());
721        assert!(!LINK_LOCAL_ALL_ROUTERS.is_solicited_node_multicast());
722        assert!(!LINK_LOCAL_ALL_NODES.is_unspecified());
723        assert!(LINK_LOCAL_ALL_NODES.is_multicast());
724        assert!(!LINK_LOCAL_ALL_NODES.is_link_local());
725        assert!(!LINK_LOCAL_ALL_NODES.is_loopback());
726        assert!(!LINK_LOCAL_ALL_NODES.is_unique_local());
727        assert!(!LINK_LOCAL_ALL_NODES.is_global_unicast());
728        assert!(!LINK_LOCAL_ALL_NODES.is_solicited_node_multicast());
729    }
730
731    #[test]
732    fn test_basic_link_local() {
733        assert!(!LINK_LOCAL_ADDR.is_unspecified());
734        assert!(!LINK_LOCAL_ADDR.is_multicast());
735        assert!(LINK_LOCAL_ADDR.is_link_local());
736        assert!(!LINK_LOCAL_ADDR.is_loopback());
737        assert!(!LINK_LOCAL_ADDR.is_unique_local());
738        assert!(!LINK_LOCAL_ADDR.is_global_unicast());
739        assert!(!LINK_LOCAL_ADDR.is_solicited_node_multicast());
740    }
741
742    #[test]
743    fn test_basic_loopback() {
744        assert!(!Address::LOCALHOST.is_unspecified());
745        assert!(!Address::LOCALHOST.is_multicast());
746        assert!(!Address::LOCALHOST.is_link_local());
747        assert!(Address::LOCALHOST.is_loopback());
748        assert!(!Address::LOCALHOST.is_unique_local());
749        assert!(!Address::LOCALHOST.is_global_unicast());
750        assert!(!Address::LOCALHOST.is_solicited_node_multicast());
751    }
752
753    #[test]
754    fn test_unique_local() {
755        assert!(!UNIQUE_LOCAL_ADDR.is_unspecified());
756        assert!(!UNIQUE_LOCAL_ADDR.is_multicast());
757        assert!(!UNIQUE_LOCAL_ADDR.is_link_local());
758        assert!(!UNIQUE_LOCAL_ADDR.is_loopback());
759        assert!(UNIQUE_LOCAL_ADDR.is_unique_local());
760        assert!(!UNIQUE_LOCAL_ADDR.is_global_unicast());
761        assert!(!UNIQUE_LOCAL_ADDR.is_solicited_node_multicast());
762    }
763
764    #[test]
765    fn test_global_unicast() {
766        assert!(!GLOBAL_UNICAST_ADDR.is_unspecified());
767        assert!(!GLOBAL_UNICAST_ADDR.is_multicast());
768        assert!(!GLOBAL_UNICAST_ADDR.is_link_local());
769        assert!(!GLOBAL_UNICAST_ADDR.is_loopback());
770        assert!(!GLOBAL_UNICAST_ADDR.is_unique_local());
771        assert!(GLOBAL_UNICAST_ADDR.is_global_unicast());
772        assert!(!GLOBAL_UNICAST_ADDR.is_solicited_node_multicast());
773    }
774
775    #[test]
776    fn test_sollicited_node_multicast() {
777        assert!(!TEST_SOL_NODE_MCAST_ADDR.is_unspecified());
778        assert!(TEST_SOL_NODE_MCAST_ADDR.is_multicast());
779        assert!(!TEST_SOL_NODE_MCAST_ADDR.is_link_local());
780        assert!(!TEST_SOL_NODE_MCAST_ADDR.is_loopback());
781        assert!(!TEST_SOL_NODE_MCAST_ADDR.is_unique_local());
782        assert!(!TEST_SOL_NODE_MCAST_ADDR.is_global_unicast());
783        assert!(TEST_SOL_NODE_MCAST_ADDR.is_solicited_node_multicast());
784    }
785
786    #[test]
787    fn test_mask() {
788        let addr = Address::new(0x0123, 0x4567, 0x89ab, 0, 0, 0, 0, 1);
789        assert_eq!(
790            addr.mask(11),
791            [0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
792        );
793        assert_eq!(
794            addr.mask(15),
795            [0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
796        );
797        assert_eq!(
798            addr.mask(26),
799            [0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
800        );
801        assert_eq!(
802            addr.mask(128),
803            [
804                0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
805            ]
806        );
807        assert_eq!(
808            addr.mask(127),
809            [
810                0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
811            ]
812        );
813    }
814
815    #[test]
816    fn test_cidr() {
817        // fe80::1/56
818        // 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
819        // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
820        let cidr = Cidr::new(LINK_LOCAL_ADDR, 56);
821
822        let inside_subnet = [
823            // fe80::2
824            [
825                0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
826                0x00, 0x02,
827            ],
828            // fe80::1122:3344:5566:7788
829            [
830                0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
831                0x77, 0x88,
832            ],
833            // fe80::ff00:0:0:0
834            [
835                0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
836                0x00, 0x00,
837            ],
838            // fe80::ff
839            [
840                0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
841                0x00, 0xff,
842            ],
843        ];
844
845        let outside_subnet = [
846            // fe80:0:0:101::1
847            [
848                0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
849                0x00, 0x01,
850            ],
851            // ::1
852            [
853                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
854                0x00, 0x01,
855            ],
856            // ff02::1
857            [
858                0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
859                0x00, 0x01,
860            ],
861            // ff02::2
862            [
863                0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
864                0x00, 0x02,
865            ],
866        ];
867
868        let subnets = [
869            // fe80::ffff:ffff:ffff:ffff/65
870            (
871                [
872                    0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
873                    0xff, 0xff, 0xff,
874                ],
875                65,
876            ),
877            // fe80::1/128
878            (
879                [
880                    0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
881                    0x00, 0x00, 0x01,
882                ],
883                128,
884            ),
885            // fe80::1234:5678/96
886            (
887                [
888                    0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
889                    0x34, 0x56, 0x78,
890                ],
891                96,
892            ),
893        ];
894
895        let not_subnets = [
896            // fe80::101:ffff:ffff:ffff:ffff/55
897            (
898                [
899                    0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
900                    0xff, 0xff, 0xff,
901                ],
902                55,
903            ),
904            // fe80::101:ffff:ffff:ffff:ffff/56
905            (
906                [
907                    0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
908                    0xff, 0xff, 0xff,
909                ],
910                56,
911            ),
912            // fe80::101:ffff:ffff:ffff:ffff/57
913            (
914                [
915                    0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
916                    0xff, 0xff, 0xff,
917                ],
918                57,
919            ),
920            // ::1/128
921            (
922                [
923                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
924                    0x00, 0x00, 0x01,
925                ],
926                128,
927            ),
928        ];
929
930        for addr in inside_subnet.iter().map(|a| Address::from_octets(*a)) {
931            assert!(cidr.contains_addr(&addr));
932        }
933
934        for addr in outside_subnet.iter().map(|a| Address::from_octets(*a)) {
935            assert!(!cidr.contains_addr(&addr));
936        }
937
938        for subnet in subnets.iter().map(|&(a, p)| Cidr::new(Address::from(a), p)) {
939            assert!(cidr.contains_subnet(&subnet));
940        }
941
942        for subnet in not_subnets
943            .iter()
944            .map(|&(a, p)| Cidr::new(Address::from(a), p))
945        {
946            assert!(!cidr.contains_subnet(&subnet));
947        }
948
949        let cidr_without_prefix = Cidr::new(LINK_LOCAL_ADDR, 0);
950        assert!(cidr_without_prefix.contains_addr(&Address::LOCALHOST));
951    }
952
953    #[test]
954    fn test_from_eui_64() {
955        #[cfg(feature = "medium-ethernet")]
956        use crate::wire::EthernetAddress;
957        #[cfg(feature = "medium-ieee802154")]
958        use crate::wire::Ieee802154Address;
959        let tests: std::vec::Vec<(HardwareAddress, Address)> = vec![
960            #[cfg(feature = "medium-ethernet")]
961            (
962                HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
963                    0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
964                ])),
965                Address::new(0x2001, 0xdb8, 0x3, 0x0, 0xa8bb, 0xccff, 0xfedd, 0xeeff),
966            ),
967            #[cfg(feature = "medium-ieee802154")]
968            (
969                HardwareAddress::Ieee802154(Ieee802154Address::from_bytes(&[
970                    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
971                ])),
972                Address::new(0x2001, 0xdb8, 0x3, 0x0, 0x0211, 0x2233, 0x4455, 0x6677),
973            ),
974        ];
975
976        let prefix = Cidr::new(GLOBAL_UNICAST_ADDR.mask(64).into(), 64);
977        let wrong_prefix = Cidr::new(GLOBAL_UNICAST_ADDR.mask(72).into(), 72);
978
979        for (hardware, result) in tests {
980            let generated = Address::from_link_prefix(&prefix, hardware).unwrap();
981            assert!(prefix.contains_addr(&generated));
982            assert_eq!(generated, result);
983            assert!(Address::from_link_prefix(&wrong_prefix, hardware).is_none());
984
985            let generated = Cidr::from_link_prefix(&prefix, hardware).unwrap();
986            assert!(prefix.contains_subnet(&generated));
987            assert_eq!(generated.address(), result);
988            assert!(Cidr::from_link_prefix(&wrong_prefix, hardware).is_none());
989        }
990    }
991
992    #[test]
993    fn test_scope() {
994        use super::*;
995        assert_eq!(
996            Address::new(0xff01, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
997            MulticastScope::InterfaceLocal
998        );
999        assert_eq!(
1000            Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
1001            MulticastScope::LinkLocal
1002        );
1003        assert_eq!(
1004            Address::new(0xff03, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
1005            MulticastScope::Unknown
1006        );
1007        assert_eq!(
1008            Address::new(0xff04, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
1009            MulticastScope::AdminLocal
1010        );
1011        assert_eq!(
1012            Address::new(0xff05, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
1013            MulticastScope::SiteLocal
1014        );
1015        assert_eq!(
1016            Address::new(0xff08, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
1017            MulticastScope::OrganizationLocal
1018        );
1019        assert_eq!(
1020            Address::new(0xff0e, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
1021            MulticastScope::Global
1022        );
1023
1024        assert_eq!(
1025            LINK_LOCAL_ALL_NODES.x_multicast_scope(),
1026            MulticastScope::LinkLocal
1027        );
1028
1029        // For source address selection, unicast addresses also have a scope:
1030        assert_eq!(
1031            LINK_LOCAL_ADDR.x_multicast_scope(),
1032            MulticastScope::LinkLocal
1033        );
1034        assert_eq!(
1035            GLOBAL_UNICAST_ADDR.x_multicast_scope(),
1036            MulticastScope::Global
1037        );
1038        assert_eq!(
1039            UNIQUE_LOCAL_ADDR.x_multicast_scope(),
1040            MulticastScope::Global
1041        );
1042    }
1043
1044    static REPR_PACKET_BYTES: [u8; 52] = [
1045        0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
1046        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
1047        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00,
1048        0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
1049    ];
1050    static REPR_PAYLOAD_BYTES: [u8; 12] = [
1051        0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
1052    ];
1053
1054    const fn packet_repr() -> Repr {
1055        Repr {
1056            src_addr: Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
1057            dst_addr: LINK_LOCAL_ALL_NODES,
1058            next_header: Protocol::Udp,
1059            payload_len: 12,
1060            hop_limit: 64,
1061        }
1062    }
1063
1064    #[test]
1065    fn test_packet_deconstruction() {
1066        let packet = Packet::new_unchecked(&REPR_PACKET_BYTES[..]);
1067        assert_eq!(packet.check_len(), Ok(()));
1068        assert_eq!(packet.version(), 6);
1069        assert_eq!(packet.traffic_class(), 0);
1070        assert_eq!(packet.flow_label(), 0);
1071        assert_eq!(packet.total_len(), 0x34);
1072        assert_eq!(packet.payload_len() as usize, REPR_PAYLOAD_BYTES.len());
1073        assert_eq!(packet.next_header(), Protocol::Udp);
1074        assert_eq!(packet.hop_limit(), 0x40);
1075        assert_eq!(packet.src_addr(), Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
1076        assert_eq!(packet.dst_addr(), LINK_LOCAL_ALL_NODES);
1077        assert_eq!(packet.payload(), &REPR_PAYLOAD_BYTES[..]);
1078    }
1079
1080    #[test]
1081    fn test_packet_construction() {
1082        let mut bytes = [0xff; 52];
1083        let mut packet = Packet::new_unchecked(&mut bytes[..]);
1084        // Version, Traffic Class, and Flow Label are not
1085        // byte aligned. make sure the setters and getters
1086        // do not interfere with each other.
1087        packet.set_version(6);
1088        assert_eq!(packet.version(), 6);
1089        packet.set_traffic_class(0x99);
1090        assert_eq!(packet.version(), 6);
1091        assert_eq!(packet.traffic_class(), 0x99);
1092        packet.set_flow_label(0x54321);
1093        assert_eq!(packet.traffic_class(), 0x99);
1094        assert_eq!(packet.flow_label(), 0x54321);
1095        packet.set_payload_len(0xc);
1096        packet.set_next_header(Protocol::Udp);
1097        packet.set_hop_limit(0xfe);
1098        packet.set_src_addr(LINK_LOCAL_ALL_ROUTERS);
1099        packet.set_dst_addr(LINK_LOCAL_ALL_NODES);
1100        packet
1101            .payload_mut()
1102            .copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
1103        let mut expected_bytes = [
1104            0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
1105            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x02, 0x00, 0x00,
1106            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
1107            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1108        ];
1109        let start = expected_bytes.len() - REPR_PAYLOAD_BYTES.len();
1110        expected_bytes[start..].copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
1111        assert_eq!(packet.check_len(), Ok(()));
1112        assert_eq!(&*packet.into_inner(), &expected_bytes[..]);
1113    }
1114
1115    #[test]
1116    fn test_overlong() {
1117        let mut bytes = vec![];
1118        bytes.extend(&REPR_PACKET_BYTES[..]);
1119        bytes.push(0);
1120
1121        assert_eq!(
1122            Packet::new_unchecked(&bytes).payload().len(),
1123            REPR_PAYLOAD_BYTES.len()
1124        );
1125        assert_eq!(
1126            Packet::new_unchecked(&mut bytes).payload_mut().len(),
1127            REPR_PAYLOAD_BYTES.len()
1128        );
1129    }
1130
1131    #[test]
1132    fn test_total_len_overflow() {
1133        let mut bytes = vec![];
1134        bytes.extend(&REPR_PACKET_BYTES[..]);
1135        Packet::new_unchecked(&mut bytes).set_payload_len(0x80);
1136
1137        assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error);
1138    }
1139
1140    #[test]
1141    fn test_repr_parse_valid() {
1142        let packet = Packet::new_unchecked(&REPR_PACKET_BYTES[..]);
1143        let repr = Repr::parse(&packet).unwrap();
1144        assert_eq!(repr, packet_repr());
1145    }
1146
1147    #[test]
1148    fn test_repr_parse_bad_version() {
1149        let mut bytes = [0; 40];
1150        let mut packet = Packet::new_unchecked(&mut bytes[..]);
1151        packet.set_version(4);
1152        packet.set_payload_len(0);
1153        let packet = Packet::new_unchecked(&*packet.into_inner());
1154        assert_eq!(Repr::parse(&packet), Err(Error));
1155    }
1156
1157    #[test]
1158    fn test_repr_parse_smaller_than_header() {
1159        let mut bytes = [0; 40];
1160        let mut packet = Packet::new_unchecked(&mut bytes[..]);
1161        packet.set_version(6);
1162        packet.set_payload_len(39);
1163        let packet = Packet::new_unchecked(&*packet.into_inner());
1164        assert_eq!(Repr::parse(&packet), Err(Error));
1165    }
1166
1167    #[test]
1168    fn test_repr_parse_smaller_than_payload() {
1169        let mut bytes = [0; 40];
1170        let mut packet = Packet::new_unchecked(&mut bytes[..]);
1171        packet.set_version(6);
1172        packet.set_payload_len(1);
1173        let packet = Packet::new_unchecked(&*packet.into_inner());
1174        assert_eq!(Repr::parse(&packet), Err(Error));
1175    }
1176
1177    #[test]
1178    fn test_basic_repr_emit() {
1179        let repr = packet_repr();
1180        let mut bytes = vec![0xff; repr.buffer_len() + REPR_PAYLOAD_BYTES.len()];
1181        let mut packet = Packet::new_unchecked(&mut bytes);
1182        repr.emit(&mut packet);
1183        packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES);
1184        assert_eq!(&*packet.into_inner(), &REPR_PACKET_BYTES[..]);
1185    }
1186
1187    #[test]
1188    fn test_pretty_print() {
1189        assert_eq!(
1190            format!(
1191                "{}",
1192                PrettyPrinter::<Packet<&'static [u8]>>::new("\n", &&REPR_PACKET_BYTES[..])
1193            ),
1194            "\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4"
1195        );
1196    }
1197}