s2n_quic_core/inet/
ipv6.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::inet::{
5    ip, ipv4::IpV4Address, unspecified::Unspecified, ExplicitCongestionNotification, IpAddress,
6    SocketAddress, SocketAddressV4,
7};
8use core::fmt;
9use s2n_codec::zerocopy::U16;
10
11//= https://www.rfc-editor.org/rfc/rfc2373#section-2.0
12//# IPv6 addresses are 128-bit identifiers for interfaces and sets of interfaces.
13const IPV6_LEN: usize = 128 / 8;
14
15define_inet_type!(
16    pub struct IpV6Address {
17        octets: [u8; IPV6_LEN],
18    }
19);
20
21impl IpV6Address {
22    /// An unspecified IpV6Address
23    pub const UNSPECIFIED: Self = Self {
24        octets: [0; IPV6_LEN],
25    };
26
27    #[inline]
28    pub const fn segments(&self) -> [u16; 8] {
29        let octets = &self.octets;
30        [
31            u16::from_be_bytes([octets[0], octets[1]]),
32            u16::from_be_bytes([octets[2], octets[3]]),
33            u16::from_be_bytes([octets[4], octets[5]]),
34            u16::from_be_bytes([octets[6], octets[7]]),
35            u16::from_be_bytes([octets[8], octets[9]]),
36            u16::from_be_bytes([octets[10], octets[11]]),
37            u16::from_be_bytes([octets[12], octets[13]]),
38            u16::from_be_bytes([octets[14], octets[15]]),
39        ]
40    }
41
42    /// Converts the IP address into IPv4 if it is mapped, otherwise the address is unchanged
43    #[inline]
44    pub const fn unmap(self) -> IpAddress {
45        match self.segments() {
46            // special-case unspecified and loopback
47            [0, 0, 0, 0, 0, 0, 0, 0] => IpAddress::Ipv6(self),
48            [0, 0, 0, 0, 0, 0, 0, 1] => IpAddress::Ipv6(self),
49
50            //= https://www.rfc-editor.org/rfc/rfc4291#section-2.5.5.1
51            //# The format of the "IPv4-Compatible IPv6 address" is as
52            //# follows:
53            //#
54            //# |                80 bits               | 16 |      32 bits        |
55            //# +--------------------------------------+--------------------------+
56            //# |0000..............................0000|0000|    IPv4 address     |
57            //# +--------------------------------------+----+---------------------+
58
59            //= https://www.rfc-editor.org/rfc/rfc4291#section-2.5.5.2
60            //# The format of the "IPv4-mapped IPv6
61            //# address" is as follows:
62            //#
63            //# |                80 bits               | 16 |      32 bits        |
64            //# +--------------------------------------+--------------------------+
65            //# |0000..............................0000|FFFF|    IPv4 address     |
66            //# +--------------------------------------+----+---------------------+
67
68            //= https://www.rfc-editor.org/rfc/rfc6052#section-2.1
69            //# This document reserves a "Well-Known Prefix" for use in an
70            //# algorithmic mapping.  The value of this IPv6 prefix is:
71            //#
72            //#   64:ff9b::/96
73            [0, 0, 0, 0, 0, 0, ab, cd]
74            | [0, 0, 0, 0, 0, 0xffff, ab, cd]
75            | [0x64, 0xff9b, 0, 0, 0, 0, ab, cd] => {
76                let [a, b] = u16::to_be_bytes(ab);
77                let [c, d] = u16::to_be_bytes(cd);
78                IpAddress::Ipv4(IpV4Address {
79                    octets: [a, b, c, d],
80                })
81            }
82            _ => IpAddress::Ipv6(self),
83        }
84    }
85
86    /// Returns the [`ip::UnicastScope`] for the given address
87    ///
88    /// See the [IANA Registry](https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml)
89    /// for more details.
90    ///
91    /// ```
92    /// use s2n_quic_core::inet::{IpV4Address, IpV6Address, ip::UnicastScope::*};
93    ///
94    /// assert_eq!(IpV6Address::from([0, 0, 0, 0, 0, 0, 0, 0]).unicast_scope(), None);
95    /// assert_eq!(IpV6Address::from([0, 0, 0, 0, 0, 0, 0, 1]).unicast_scope(), Some(Loopback));
96    /// assert_eq!(IpV6Address::from([0xff0e, 0, 0, 0, 0, 0, 0, 0]).unicast_scope(), None);
97    /// assert_eq!(IpV6Address::from([0xfe80, 0, 0, 0, 0, 0, 0, 0]).unicast_scope(), Some(LinkLocal));
98    /// assert_eq!(IpV6Address::from([0xfc02, 0, 0, 0, 0, 0, 0, 0]).unicast_scope(), Some(Private));
99    /// // documentation
100    /// assert_eq!(IpV6Address::from([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]).unicast_scope(), None);
101    /// // benchmarking
102    /// assert_eq!(IpV6Address::from([0x2001, 0x0200, 0, 0, 0, 0, 0, 0]).unicast_scope(), None);
103    /// // IPv4-mapped address
104    /// assert_eq!(IpV4Address::from([92, 88, 99, 123]).to_ipv6_mapped().unicast_scope(), Some(Global));
105    /// ```
106    #[inline]
107    pub const fn unicast_scope(self) -> Option<ip::UnicastScope> {
108        use ip::UnicastScope::*;
109
110        // If this is an IpV4 ip, delegate to that implementation
111        if let IpAddress::Ipv4(ip) = self.unmap() {
112            return ip.unicast_scope();
113        }
114
115        // https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml
116        match self.segments() {
117            //= https://www.rfc-editor.org/rfc/rfc4291#section-2.5.2
118            //# The address 0:0:0:0:0:0:0:0 is called the unspecified address.
119            [0, 0, 0, 0, 0, 0, 0, 0] => None,
120
121            //= https://www.rfc-editor.org/rfc/rfc4291#section-2.5.3
122            //# The unicast address 0:0:0:0:0:0:0:1 is called the loopback address.
123            [0, 0, 0, 0, 0, 0, 0, 1] => Some(Loopback),
124
125            //= https://www.rfc-editor.org/rfc/rfc6666#section-4
126            //# Per this document, IANA has recorded the allocation of the IPv6
127            //# address prefix 0100::/64 as a Discard-Only Prefix in the "Internet
128            //# Protocol Version 6 Address Space" and added the prefix to the "IANA
129            //# IPv6 Special Purpose Address Registry" [IANA-IPV6REG].
130            [0x0100, 0, 0, 0, ..] => None,
131
132            //= https://www.rfc-editor.org/rfc/rfc7723#section-4.2
133            //# +----------------------+-------------------------------------------+
134            //# | Attribute            | Value                                     |
135            //# +----------------------+-------------------------------------------+
136            //# | Address Block        | 2001:1::1/128                             |
137            //# | Name                 | Port Control Protocol Anycast             |
138            //# | RFC                  | RFC 7723 (this document)                  |
139            //# | Allocation Date      | October 2015                              |
140            //# | Termination Date     | N/A                                       |
141            //# | Source               | True                                      |
142            //# | Destination          | True                                      |
143            //# | Forwardable          | True                                      |
144            //# | Global               | True                                      |
145            //# | Reserved-by-Protocol | False                                     |
146            //# +----------------------+-------------------------------------------+
147            [0x2001, 0x1, 0, 0, 0, 0, 0, 0x1] => Some(Global),
148
149            //= https://www.rfc-editor.org/rfc/rfc8155#section-8.2
150            //# +----------------------+-------------------------------------------+
151            //# | Attribute            | Value                                     |
152            //# +----------------------+-------------------------------------------+
153            //# | Address Block        | 2001:1::2/128                             |
154            //# | Name                 | Traversal Using Relays around NAT Anycast |
155            //# | RFC                  | RFC 8155                                  |
156            //# | Allocation Date      | 2017-02                                   |
157            //# | Termination Date     | N/A                                       |
158            //# | Source               | True                                      |
159            //# | Destination          | True                                      |
160            //# | Forwardable          | True                                      |
161            //# | Global               | True                                      |
162            //# | Reserved-by-Protocol | False                                     |
163            //# +----------------------+-------------------------------------------+
164            [0x2001, 0x1, 0, 0, 0, 0, 0, 0x2] => Some(Global),
165
166            //= https://www.rfc-editor.org/rfc/rfc6890#section-2.2.3
167            //# +----------------------+---------------------------+
168            //# | Attribute            | Value                     |
169            //# +----------------------+---------------------------+
170            //# | Address Block        | 2001::/23                 |
171            //# | Name                 | IETF Protocol Assignments |
172            //# | RFC                  | [RFC2928]                 |
173            //# | Allocation Date      | September 2000            |
174            //# | Termination Date     | N/A                       |
175            //# | Source               | False[1]                  |
176            //# | Destination          | False[1]                  |
177            //# | Forwardable          | False[1]                  |
178            //# | Global               | False[1]                  |
179            //# | Reserved-by-Protocol | False                     |
180            //# +----------------------+---------------------------+
181            [0x2001, 0x0..=0x01ff, ..] => None,
182
183            //= https://www.rfc-editor.org/rfc/rfc5180#section-8
184            //# The IANA has allocated 2001:0200::/48 for IPv6 benchmarking, which is
185            //# a 48-bit prefix from the RFC 4773 pool.
186            [0x2001, 0x0200, 0, ..] => None,
187
188            //= https://www.rfc-editor.org/rfc/rfc3849#section-4
189            //# IANA is to record the allocation of the IPv6 global unicast address
190            //# prefix  2001:DB8::/32 as a documentation-only prefix  in the IPv6
191            //# address registry.
192            [0x2001, 0xdb8, ..] => None,
193
194            //= https://www.rfc-editor.org/rfc/rfc4193#section-8
195            //# The IANA has assigned the FC00::/7 prefix to "Unique Local Unicast".
196            [0xfc00..=0xfdff, ..] => {
197                //= https://www.rfc-editor.org/rfc/rfc4193#section-1
198                //# They are not
199                //# expected to be routable on the global Internet.  They are routable
200                //# inside of a more limited area such as a site.  They may also be
201                //# routed between a limited set of sites.
202                Some(Private)
203            }
204
205            //= https://www.rfc-editor.org/rfc/rfc4291#section-2.5.6
206            //# Link-Local addresses have the following format:
207            //# |   10     |
208            //# |  bits    |         54 bits         |          64 bits           |
209            //# +----------+-------------------------+----------------------------+
210            //# |1111111010|           0             |       interface ID         |
211            //# +----------+-------------------------+----------------------------+
212            [0xfe80..=0xfebf, ..] => Some(LinkLocal),
213
214            //= https://www.rfc-editor.org/rfc/rfc4291#section-2.7
215            //# binary 11111111 at the start of the address identifies the address
216            //# as being a multicast address.
217            [0xff00..=0xffff, ..] => None,
218
219            // Everything else is considered globally-reachable
220            _ => Some(Global),
221        }
222    }
223
224    #[inline]
225    pub fn with_port(self, port: u16) -> SocketAddressV6 {
226        SocketAddressV6 {
227            ip: self,
228            port: port.into(),
229        }
230    }
231}
232
233impl fmt::Debug for IpV6Address {
234    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
235        write!(fmt, "IPv6Address({self})")
236    }
237}
238
239impl fmt::Display for IpV6Address {
240    #[allow(clippy::many_single_char_names)]
241    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
242        match self.segments() {
243            [0, 0, 0, 0, 0, 0, 0, 0] => write!(fmt, "::"),
244            [0, 0, 0, 0, 0, 0, 0, 1] => write!(fmt, "::1"),
245            // Ipv4 Compatible address
246            [0, 0, 0, 0, 0, 0, g, h] => write!(
247                fmt,
248                "::{}.{}.{}.{}",
249                (g >> 8) as u8,
250                g as u8,
251                (h >> 8) as u8,
252                h as u8
253            ),
254            // Ipv4-Mapped address
255            [0, 0, 0, 0, 0, 0xffff, g, h] => write!(
256                fmt,
257                "::ffff:{}.{}.{}.{}",
258                (g >> 8) as u8,
259                g as u8,
260                (h >> 8) as u8,
261                h as u8
262            ),
263            // TODO better formatting
264            [a, b, c, d, e, f, g, h] => {
265                write!(fmt, "{a:x}:{b:x}:{c:x}:{d:x}:{e:x}:{f:x}:{g:x}:{h:x}")
266            }
267        }
268    }
269}
270
271impl Unspecified for IpV6Address {
272    #[inline]
273    fn is_unspecified(&self) -> bool {
274        Self::UNSPECIFIED.eq(self)
275    }
276}
277
278test_inet_snapshot!(ipv6, ipv6_snapshot_test, IpV6Address);
279
280define_inet_type!(
281    pub struct SocketAddressV6 {
282        ip: IpV6Address,
283        port: U16,
284    }
285);
286
287impl SocketAddressV6 {
288    /// An unspecified SocketAddressV6
289    pub const UNSPECIFIED: Self = Self {
290        ip: IpV6Address::UNSPECIFIED,
291        port: U16::ZERO,
292    };
293
294    #[inline]
295    pub const fn ip(&self) -> &IpV6Address {
296        &self.ip
297    }
298
299    #[inline]
300    pub fn port(&self) -> u16 {
301        self.port.into()
302    }
303
304    #[inline]
305    pub fn set_port(&mut self, port: u16) {
306        self.port.set(port)
307    }
308
309    /// Converts the IP address into IPv4 if it is mapped, otherwise the address is unchanged
310    #[inline]
311    pub fn unmap(self) -> SocketAddress {
312        match self.ip.unmap() {
313            IpAddress::Ipv4(addr) => SocketAddressV4::new(addr, self.port).into(),
314            IpAddress::Ipv6(_) => self.into(),
315        }
316    }
317
318    #[inline]
319    pub const fn unicast_scope(&self) -> Option<ip::UnicastScope> {
320        self.ip.unicast_scope()
321    }
322}
323
324impl fmt::Debug for SocketAddressV6 {
325    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
326        write!(fmt, "SocketAddressV6({self})")
327    }
328}
329
330impl fmt::Display for SocketAddressV6 {
331    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
332        write!(fmt, "[{}]:{:?}", self.ip, self.port)
333    }
334}
335
336impl Unspecified for SocketAddressV6 {
337    #[inline]
338    fn is_unspecified(&self) -> bool {
339        Self::UNSPECIFIED.eq(self)
340    }
341}
342
343test_inet_snapshot!(socket_v6, socket_v6_snapshot_test, SocketAddressV6);
344
345impl From<[u8; IPV6_LEN]> for IpV6Address {
346    #[inline]
347    fn from(octets: [u8; IPV6_LEN]) -> Self {
348        Self { octets }
349    }
350}
351
352impl From<[u16; IPV6_LEN / 2]> for IpV6Address {
353    #[inline]
354    fn from(octets: [u16; IPV6_LEN / 2]) -> Self {
355        macro_rules! convert {
356            ($($segment:ident),*) => {{
357                let [$($segment),*] = octets;
358                $(
359                    let $segment = u16::to_be_bytes($segment);
360                )*
361                Self {
362                    octets: [
363                        $(
364                            $segment[0],
365                            $segment[1],
366                        )*
367                    ]
368                }
369            }}
370        }
371        convert!(a, b, c, d, e, f, g, h)
372    }
373}
374
375impl From<IpV6Address> for [u8; IPV6_LEN] {
376    #[inline]
377    fn from(v: IpV6Address) -> Self {
378        v.octets
379    }
380}
381
382impl From<IpV6Address> for [u16; IPV6_LEN / 2] {
383    #[inline]
384    fn from(v: IpV6Address) -> Self {
385        v.segments()
386    }
387}
388
389//= https://www.rfc-editor.org/rfc/rfc8200#section-3
390//#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
391//#   |Version| Traffic Class |           Flow Label                  |
392//#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393//#   |         Payload Length        |  Next Header  |   Hop Limit   |
394//#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395//#   |                                                               |
396//#   +                                                               +
397//#   |                                                               |
398//#   +                         Source Address                        +
399//#   |                                                               |
400//#   +                                                               +
401//#   |                                                               |
402//#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
403//#   |                                                               |
404//#   +                                                               +
405//#   |                                                               |
406//#   +                      Destination Address                      +
407//#   |                                                               |
408//#   +                                                               +
409//#   |                                                               |
410//#   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411
412define_inet_type!(
413    pub struct Header {
414        vtcfl: Vtcfl,
415        payload_len: U16,
416        next_header: ip::Protocol,
417        hop_limit: u8,
418        source: IpV6Address,
419        destination: IpV6Address,
420    }
421);
422
423impl fmt::Debug for Header {
424    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
425        f.debug_struct("ipv6::Header")
426            .field("version", &self.vtcfl.version())
427            .field("dscp", &self.vtcfl.dscp())
428            .field("ecn", &self.vtcfl.ecn())
429            .field(
430                "flow_label",
431                &format_args!("0x{:05x}", self.vtcfl.flow_label()),
432            )
433            .field("payload_len", &self.payload_len)
434            .field("next_header", &self.next_header)
435            .field("hop_limit", &self.hop_limit)
436            .field("source", &self.source)
437            .field("destination", &self.destination)
438            .finish()
439    }
440}
441
442impl Header {
443    /// Swaps the direction of the header
444    #[inline]
445    pub fn swap(&mut self) {
446        core::mem::swap(&mut self.source, &mut self.destination)
447    }
448
449    #[inline]
450    pub const fn vtcfl(&self) -> &Vtcfl {
451        &self.vtcfl
452    }
453
454    #[inline]
455    pub fn vtcfl_mut(&mut self) -> &mut Vtcfl {
456        &mut self.vtcfl
457    }
458
459    #[inline]
460    pub const fn payload_len(&self) -> &U16 {
461        &self.payload_len
462    }
463
464    #[inline]
465    pub fn payload_len_mut(&mut self) -> &mut U16 {
466        &mut self.payload_len
467    }
468
469    #[inline]
470    pub const fn next_header(&self) -> &ip::Protocol {
471        &self.next_header
472    }
473
474    #[inline]
475    pub fn next_header_mut(&mut self) -> &mut ip::Protocol {
476        &mut self.next_header
477    }
478
479    #[inline]
480    pub const fn hop_limit(&self) -> &u8 {
481        &self.hop_limit
482    }
483
484    #[inline]
485    pub fn hop_limit_mut(&mut self) -> &mut u8 {
486        &mut self.hop_limit
487    }
488
489    #[inline]
490    pub const fn source(&self) -> &IpV6Address {
491        &self.source
492    }
493
494    #[inline]
495    pub fn source_mut(&mut self) -> &mut IpV6Address {
496        &mut self.source
497    }
498
499    #[inline]
500    pub const fn destination(&self) -> &IpV6Address {
501        &self.destination
502    }
503
504    #[inline]
505    pub fn destination_mut(&mut self) -> &mut IpV6Address {
506        &mut self.destination
507    }
508}
509
510// This struct covers the bits for Version, Traffic Class, and Flow Label.
511//
512// Rust doesn't have the ability to do arbitrary bit sized values so we have to round up to the
513// nearest byte.
514define_inet_type!(
515    pub struct Vtcfl {
516        octets: [u8; 4],
517    }
518);
519
520impl fmt::Debug for Vtcfl {
521    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
522        f.debug_struct("ipv6::Vtf")
523            .field("version", &self.version())
524            .field("dscp", &self.dscp())
525            .field("ecn", &self.ecn())
526            .field("flow_label", &format_args!("0x{:05x}", self.flow_label()))
527            .finish()
528    }
529}
530
531impl Vtcfl {
532    #[inline]
533    pub const fn version(&self) -> u8 {
534        self.octets[0] >> 4
535    }
536
537    #[inline]
538    pub fn set_version(&mut self, version: u8) -> &mut Self {
539        self.octets[0] = (version << 4) | self.octets[0] & 0x0F;
540        self
541    }
542
543    #[inline]
544    pub fn dscp(&self) -> u8 {
545        let value = (self.octets[0] << 4) | (self.octets[1] >> 4);
546        value >> 2
547    }
548
549    #[inline]
550    pub fn set_dscp(&mut self, value: u8) -> &mut Self {
551        let value = value << 2;
552        self.octets[0] = self.octets[0] & 0xF0 | (value >> 4);
553        self.octets[1] = (value << 4) | self.octets[1] & 0b11_1111;
554        self
555    }
556
557    #[inline]
558    pub fn ecn(&self) -> ExplicitCongestionNotification {
559        ExplicitCongestionNotification::new((self.octets[1] >> 4) & 0b11)
560    }
561
562    #[inline]
563    pub fn set_ecn(&mut self, ecn: ExplicitCongestionNotification) -> &mut Self {
564        self.octets[1] = (self.octets[1] & !(0b11 << 4)) | ((ecn as u8) << 4);
565        self
566    }
567
568    #[inline]
569    pub const fn flow_label(&self) -> u32 {
570        u32::from_be_bytes([0, self.octets[1] & 0x0F, self.octets[2], self.octets[3]])
571    }
572
573    #[inline]
574    pub fn set_flow_label(&mut self, flow_label: u32) -> &mut Self {
575        let bytes = flow_label.to_be_bytes();
576        self.octets[1] = self.octets[1] & 0xF0 | bytes[1] & 0x0F;
577        self.octets[2] = bytes[2];
578        self.octets[3] = bytes[3];
579        self
580    }
581}
582
583#[cfg(any(test, feature = "std"))]
584mod std_conversion {
585    use super::*;
586    use std::net;
587
588    impl From<net::Ipv6Addr> for IpV6Address {
589        fn from(address: net::Ipv6Addr) -> Self {
590            (&address).into()
591        }
592    }
593
594    impl From<&net::Ipv6Addr> for IpV6Address {
595        fn from(address: &net::Ipv6Addr) -> Self {
596            address.octets().into()
597        }
598    }
599
600    impl From<IpV6Address> for net::Ipv6Addr {
601        fn from(address: IpV6Address) -> Self {
602            address.octets.into()
603        }
604    }
605
606    impl From<net::SocketAddrV6> for SocketAddressV6 {
607        fn from(address: net::SocketAddrV6) -> Self {
608            let ip = address.ip().into();
609            let port = address.port().into();
610            Self { ip, port }
611        }
612    }
613
614    impl From<(net::Ipv6Addr, u16)> for SocketAddressV6 {
615        fn from((ip, port): (net::Ipv6Addr, u16)) -> Self {
616            Self::new(ip, port)
617        }
618    }
619
620    impl From<SocketAddressV6> for net::SocketAddrV6 {
621        fn from(address: SocketAddressV6) -> Self {
622            let ip = address.ip.into();
623            let port = address.port.into();
624            Self::new(ip, port, 0, 0)
625        }
626    }
627
628    impl From<&SocketAddressV6> for net::SocketAddrV6 {
629        fn from(address: &SocketAddressV6) -> Self {
630            let ip = address.ip.into();
631            let port = address.port.into();
632            Self::new(ip, port, 0, 0)
633        }
634    }
635
636    impl From<SocketAddressV6> for net::SocketAddr {
637        fn from(address: SocketAddressV6) -> Self {
638            let addr: net::SocketAddrV6 = address.into();
639            addr.into()
640        }
641    }
642
643    impl From<&SocketAddressV6> for net::SocketAddr {
644        fn from(address: &SocketAddressV6) -> Self {
645            let addr: net::SocketAddrV6 = address.into();
646            addr.into()
647        }
648    }
649
650    impl net::ToSocketAddrs for SocketAddressV6 {
651        type Iter = std::iter::Once<net::SocketAddr>;
652
653        fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
654            let ip = self.ip.into();
655            let port = self.port.into();
656            let addr = net::SocketAddrV6::new(ip, port, 0, 0);
657            Ok(std::iter::once(addr.into()))
658        }
659    }
660}
661
662#[cfg(test)]
663mod tests {
664    use super::*;
665    use bolero::{check, generator::*};
666    use s2n_codec::{DecoderBuffer, DecoderBufferMut};
667
668    /// Asserts the UnicastScope returned matches a known implementation
669    #[test]
670    #[cfg_attr(kani, kani::proof, kani::unwind(17), kani::solver(kissat))]
671    fn scope_test() {
672        let g = produce::<[u8; 16]>().map_gen(IpV6Address::from);
673        check!().with_generator(g).cloned().for_each(|subject| {
674            use ip::UnicastScope::*;
675
676            // the ipv4 scopes are tested elsewhere there so we just make sure the scopes match
677            if let IpAddress::Ipv4(ipv4) = subject.unmap() {
678                assert_eq!(ipv4.unicast_scope(), subject.unicast_scope());
679                return;
680            }
681
682            let expected = std::net::Ipv6Addr::from(subject);
683            let network = ip_network::Ipv6Network::from(expected);
684
685            match subject.unicast_scope() {
686                Some(Global) => {
687                    // Site-local addresses are deprecated but the `ip_network` still partitions
688                    // them out
689                    // See: https://datatracker.ietf.org/doc/html/rfc3879
690
691                    assert!(network.is_global() || network.is_unicast_site_local());
692                }
693                Some(Private) => {
694                    assert!(network.is_unique_local());
695                }
696                Some(Loopback) => {
697                    assert!(expected.is_loopback());
698                }
699                Some(LinkLocal) => {
700                    assert!(network.is_unicast_link_local());
701                }
702                None => {
703                    assert!(
704                        expected.is_multicast()
705                            || expected.is_unspecified()
706                            // Discard space
707                            || subject.segments()[0] == 0x0100
708                            // IETF Reserved
709                            || subject.segments()[0] == 0x2001
710                    );
711                }
712            }
713        })
714    }
715
716    #[test]
717    #[cfg_attr(miri, ignore)]
718    fn snapshot_test() {
719        let mut buffer = vec![0u8; core::mem::size_of::<Header>()];
720        for (idx, byte) in buffer.iter_mut().enumerate() {
721            *byte = idx as u8;
722        }
723        let decoder = DecoderBuffer::new(&buffer);
724        let (header, _) = decoder.decode::<&Header>().unwrap();
725        insta::assert_debug_snapshot!("snapshot_test", header);
726
727        buffer.fill(255);
728        let decoder = DecoderBuffer::new(&buffer);
729        let (header, _) = decoder.decode::<&Header>().unwrap();
730        insta::assert_debug_snapshot!("snapshot_filled_test", header);
731    }
732
733    #[test]
734    #[cfg_attr(kani, kani::proof, kani::unwind(17), kani::solver(kissat))]
735    fn header_getter_setter_test() {
736        check!().with_type::<Header>().for_each(|expected| {
737            let mut buffer = [255u8; core::mem::size_of::<Header>()];
738            let decoder = DecoderBufferMut::new(&mut buffer);
739            let (header, _) = decoder.decode::<&mut Header>().unwrap();
740            {
741                // use all of the getters and setters to copy over each field
742                header
743                    .vtcfl_mut()
744                    .set_version(expected.vtcfl().version())
745                    .set_dscp(expected.vtcfl().dscp())
746                    .set_ecn(expected.vtcfl().ecn())
747                    .set_flow_label(expected.vtcfl().flow_label());
748                *header.hop_limit_mut() = *expected.hop_limit();
749                *header.next_header_mut() = *expected.next_header();
750                header.payload_len_mut().set(expected.payload_len().get());
751                *header.source_mut() = *expected.source();
752                *header.destination_mut() = *expected.destination();
753            }
754
755            let decoder = DecoderBuffer::new(&buffer);
756            let (actual, _) = decoder.decode::<&Header>().unwrap();
757            {
758                // make sure all of the values match
759                assert_eq!(expected.vtcfl().version(), expected.vtcfl().version());
760                assert_eq!(expected.vtcfl().dscp(), expected.vtcfl().dscp());
761                assert_eq!(expected.vtcfl().ecn(), expected.vtcfl().ecn());
762                assert_eq!(expected.vtcfl().flow_label(), expected.vtcfl().flow_label());
763                assert_eq!(
764                    expected.vtcfl(),
765                    actual.vtcfl(),
766                    "\nexpected: {:?}\n  actual: {:?}",
767                    expected.as_bytes(),
768                    actual.as_bytes()
769                );
770                assert_eq!(expected.hop_limit(), actual.hop_limit());
771                assert_eq!(expected.next_header(), actual.next_header());
772                assert_eq!(expected.payload_len(), actual.payload_len());
773                assert_eq!(expected.source(), actual.source());
774                assert_eq!(expected.destination(), actual.destination());
775                assert_eq!(
776                    expected,
777                    actual,
778                    "\nexpected: {:?}\n  actual: {:?}",
779                    expected.as_bytes(),
780                    actual.as_bytes()
781                );
782            }
783        })
784    }
785
786    #[test]
787    fn header_round_trip_test() {
788        check!().for_each(|buffer| {
789            s2n_codec::assert_codec_round_trip_bytes!(Header, buffer);
790        });
791    }
792}