You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

581 lines
23 KiB
Rust

use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
/// This code is the unstable ip code from the rust std
/// Since I would like to build on the stable rust, I just copied it yolo
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
pub enum Ipv6MulticastScope {
InterfaceLocal,
LinkLocal,
RealmLocal,
AdminLocal,
SiteLocal,
OrganizationLocal,
Global,
}
pub trait IpAddrExt {
fn ext_is_global(&self) -> bool;
fn ext_is_documentation(&self) -> bool;
}
impl IpAddrExt for IpAddr {
/// Returns [`true`] if the address appears to be globally routable.
///
/// See the documentation for [`Ipv4Addr::is_global`][IPv4] and
/// [`Ipv6Addr::is_global`][IPv6] for more details.
///
/// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_global
/// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_global
/// [`true`]: ../../std/primitive.bool.html
///
/// # Examples
///
/// ```
/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
/// use torment_core::ip::IpAddrExt;
///
/// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).ext_is_global(), true);
/// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).ext_is_global(), true);
/// ```
fn ext_is_global(&self) -> bool {
match self {
IpAddr::V4(ip) => ip.ext_is_global(),
IpAddr::V6(ip) => ip.ext_is_global(),
}
}
/// Returns [`true`] if this address is in a range designated for documentation.
///
/// See the documentation for [`Ipv4Addr::is_documentation`][IPv4] and
/// [`Ipv6Addr::is_documentation`][IPv6] for more details.
///
/// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_documentation
/// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_documentation
/// [`true`]: ../../std/primitive.bool.html
///
/// # Examples
///
/// ```
/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
/// use torment_core::ip::IpAddrExt;
///
/// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).ext_is_documentation(), true);
/// assert_eq!(
/// IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).ext_is_documentation(),
/// true
/// );
/// ```
fn ext_is_documentation(&self) -> bool {
match self {
IpAddr::V4(ip) => ip.is_documentation(),
IpAddr::V6(ip) => ip.ext_is_documentation(),
}
}
}
pub trait Ipv4AddrExt {
fn ext_is_global(&self) -> bool;
fn ext_is_shared(&self) -> bool;
fn ext_is_ietf_protocol_assignment(&self) -> bool;
fn ext_is_benchmarking(&self) -> bool;
fn ext_is_reserved(&self) -> bool;
}
impl Ipv4AddrExt for Ipv4Addr {
/// Returns [`true`] if the address appears to be globally routable.
/// See [iana-ipv4-special-registry][ipv4-sr].
///
/// The following return false:
///
/// - private addresses (see [`is_private()`](#method.is_private))
/// - the loopback address (see [`is_loopback()`](#method.is_loopback))
/// - the link-local address (see [`is_link_local()`](#method.is_link_local))
/// - the broadcast address (see [`is_broadcast()`](#method.is_broadcast))
/// - addresses used for documentation (see [`is_documentation()`](#method.is_documentation))
/// - the unspecified address (see [`is_unspecified()`](#method.is_unspecified)), and the whole
/// 0.0.0.0/8 block
/// - addresses reserved for future protocols (see
/// [`is_ietf_protocol_assignment()`](#method.is_ietf_protocol_assignment), except
/// `192.0.0.9/32` and `192.0.0.10/32` which are globally routable
/// - addresses reserved for future use (see [`is_reserved()`](#method.is_reserved)
/// - addresses reserved for networking devices benchmarking (see
/// [`is_benchmarking`](#method.is_benchmarking))
///
/// [ipv4-sr]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
/// [`true`]: ../../std/primitive.bool.html
///
/// # Examples
///
/// ```
/// use std::net::Ipv4Addr;
/// use torment_core::ip::Ipv4AddrExt;
///
/// // private addresses are not global
/// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).ext_is_global(), false);
/// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).ext_is_global(), false);
/// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).ext_is_global(), false);
///
/// // the 0.0.0.0/8 block is not global
/// assert_eq!(Ipv4Addr::new(0, 1, 2, 3).ext_is_global(), false);
/// // in particular, the unspecified address is not global
/// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).ext_is_global(), false);
///
/// // the loopback address is not global
/// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).ext_is_global(), false);
///
/// // link local addresses are not global
/// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).ext_is_global(), false);
///
/// // the broadcast address is not global
/// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).ext_is_global(), false);
///
/// // the address space designated for documentation is not global
/// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).ext_is_global(), false);
/// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).ext_is_global(), false);
/// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).ext_is_global(), false);
///
/// // shared addresses are not global
/// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).ext_is_global(), false);
///
/// // addresses reserved for protocol assignment are not global
/// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).ext_is_global(), false);
/// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).ext_is_global(), false);
///
/// // addresses reserved for future use are not global
/// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).ext_is_global(), false);
///
/// // addresses reserved for network devices benchmarking are not global
/// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).ext_is_global(), false);
///
/// // All the other addresses are global
/// assert_eq!(Ipv4Addr::new(1, 1, 1, 1).ext_is_global(), true);
/// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).ext_is_global(), true);
/// ```
fn ext_is_global(&self) -> bool {
// check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two
// globally routable addresses in the 192.0.0.0/24 range.
if u32::from(*self) == 0xc0000009 || u32::from(*self) == 0xc000000a {
return true;
}
!self.is_private()
&& !self.is_loopback()
&& !self.is_link_local()
&& !self.is_broadcast()
&& !self.is_documentation()
&& !self.ext_is_shared()
&& !self.ext_is_ietf_protocol_assignment()
&& !self.ext_is_reserved()
&& !self.ext_is_benchmarking()
// Make sure the address is not in 0.0.0.0/8
&& self.octets()[0] != 0
}
/// Returns [`true`] if this address is part of the Shared Address Space defined in
/// [IETF RFC 6598] (`100.64.0.0/10`).
///
/// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598
/// [`true`]: ../../std/primitive.bool.html
///
/// # Examples
///
/// ```
/// use std::net::Ipv4Addr;
/// use torment_core::ip::Ipv4AddrExt;
///
/// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).ext_is_shared(), true);
/// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).ext_is_shared(), true);
/// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).ext_is_shared(), false);
/// ```
fn ext_is_shared(&self) -> bool {
self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000)
}
/// Returns [`true`] if this address is part of `192.0.0.0/24`, which is reserved to
/// IANA for IETF protocol assignments, as documented in [IETF RFC 6890].
///
/// Note that parts of this block are in use:
///
/// - `192.0.0.8/32` is the "IPv4 dummy address" (see [IETF RFC 7600])
/// - `192.0.0.9/32` is the "Port Control Protocol Anycast" (see [IETF RFC 7723])
/// - `192.0.0.10/32` is used for NAT traversal (see [IETF RFC 8155])
///
/// [IETF RFC 6890]: https://tools.ietf.org/html/rfc6890
/// [IETF RFC 7600]: https://tools.ietf.org/html/rfc7600
/// [IETF RFC 7723]: https://tools.ietf.org/html/rfc7723
/// [IETF RFC 8155]: https://tools.ietf.org/html/rfc8155
/// [`true`]: ../../std/primitive.bool.html
///
/// # Examples
///
/// ```
/// use std::net::Ipv4Addr;
/// use torment_core::ip::Ipv4AddrExt;
///
/// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).ext_is_ietf_protocol_assignment(), true);
/// assert_eq!(Ipv4Addr::new(192, 0, 0, 8).ext_is_ietf_protocol_assignment(), true);
/// assert_eq!(Ipv4Addr::new(192, 0, 0, 9).ext_is_ietf_protocol_assignment(), true);
/// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).ext_is_ietf_protocol_assignment(), true);
/// assert_eq!(Ipv4Addr::new(192, 0, 1, 0).ext_is_ietf_protocol_assignment(), false);
/// assert_eq!(Ipv4Addr::new(191, 255, 255, 255).ext_is_ietf_protocol_assignment(), false);
/// ```
fn ext_is_ietf_protocol_assignment(&self) -> bool {
self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0
}
/// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for
/// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0`
/// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`.
///
/// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544
/// [errata 423]: https://www.rfc-editor.org/errata/eid423
/// [`true`]: ../../std/primitive.bool.html
///
/// # Examples
///
/// ```
/// use std::net::Ipv4Addr;
/// use torment_core::ip::Ipv4AddrExt;
///
/// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).ext_is_benchmarking(), false);
/// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).ext_is_benchmarking(), true);
/// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).ext_is_benchmarking(), true);
/// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).ext_is_benchmarking(), false);
/// ```
fn ext_is_benchmarking(&self) -> bool {
self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18
}
/// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112]
/// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the
/// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since
/// it is obviously not reserved for future use.
///
/// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112
/// [`true`]: ../../std/primitive.bool.html
///
/// # Warning
///
/// As IANA assigns new addresses, this method will be
/// updated. This may result in non-reserved addresses being
/// treated as reserved in code that relies on an outdated version
/// of this method.
///
/// # Examples
///
/// ```
/// use std::net::Ipv4Addr;
/// use torment_core::ip::Ipv4AddrExt;
///
/// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).ext_is_reserved(), true);
/// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).ext_is_reserved(), true);
///
/// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).ext_is_reserved(), false);
/// // The broadcast address is not considered as reserved for future use by this implementation
/// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).ext_is_reserved(), false);
/// ```
fn ext_is_reserved(&self) -> bool {
self.octets()[0] & 240 == 240 && !self.is_broadcast()
}
}
pub trait Ipv6AddrExt {
fn ext_is_global(&self) -> bool;
fn ext_is_unique_local(&self) -> bool;
fn ext_is_unicast_link_local_strict(&self) -> bool;
fn ext_is_unicast_link_local(&self) -> bool;
fn ext_is_unicast_site_local(&self) -> bool;
fn ext_is_unicast_global(&self) -> bool;
fn ext_is_documentation(&self) -> bool;
fn ext_multicast_scope(&self) -> Option<Ipv6MulticastScope>;
}
impl Ipv6AddrExt for Ipv6Addr {
/// Returns [`true`] if the address appears to be globally routable.
///
/// The following return [`false`]:
///
/// - the loopback address
/// - link-local and unique local unicast addresses
/// - interface-, link-, realm-, admin- and site-local multicast addresses
///
/// [`true`]: ../../std/primitive.bool.html
/// [`false`]: ../../std/primitive.bool.html
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use torment_core::ip::Ipv6AddrExt;
///
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_is_global(), true);
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).ext_is_global(), false);
/// assert_eq!(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1).ext_is_global(), true);
/// ```
fn ext_is_global(&self) -> bool {
match Ipv6AddrExt::ext_multicast_scope(self) {
Some(Ipv6MulticastScope::Global) => true,
None => self.ext_is_unicast_global(),
_ => false,
}
}
/// Returns [`true`] if this is a unique local address (`fc00::/7`).
///
/// This property is defined in [IETF RFC 4193].
///
/// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193
/// [`true`]: ../../std/primitive.bool.html
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use torment_core::ip::Ipv6AddrExt;
///
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_is_unique_local(), false);
/// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).ext_is_unique_local(), true);
/// ```
fn ext_is_unique_local(&self) -> bool {
(self.segments()[0] & 0xfe00) == 0xfc00
}
/// Returns [`true`] if the address is a unicast link-local address (`fe80::/64`).
///
/// A common mis-conception is to think that "unicast link-local addresses start with
/// `fe80::`", but the [IETF RFC 4291] actually defines a stricter format for these addresses:
///
/// ```no_rust
/// | 10 |
/// | bits | 54 bits | 64 bits |
/// +----------+-------------------------+----------------------------+
/// |1111111010| 0 | interface ID |
/// +----------+-------------------------+----------------------------+
/// ```
///
/// This method validates the format defined in the RFC and won't recognize the following
/// addresses such as `fe80:0:0:1::` or `fe81::` as unicast link-local addresses for example.
/// If you need a less strict validation use [`is_unicast_link_local()`] instead.
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use torment_core::ip::Ipv6AddrExt;
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0);
/// assert!(ip.ext_is_unicast_link_local_strict());
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff);
/// assert!(ip.ext_is_unicast_link_local_strict());
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0);
/// assert!(!ip.ext_is_unicast_link_local_strict());
/// assert!(ip.ext_is_unicast_link_local());
///
/// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0);
/// assert!(!ip.ext_is_unicast_link_local_strict());
/// assert!(ip.ext_is_unicast_link_local());
/// ```
///
/// # See also
///
/// - [IETF RFC 4291 section 2.5.6]
/// - [RFC 4291 errata 4406]
/// - [`is_unicast_link_local()`]
///
/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
/// [IETF RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6
/// [`true`]: ../../std/primitive.bool.html
/// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406
/// [`is_unicast_link_local()`]: ../../std/net/struct.Ipv6Addr.html#method.is_unicast_link_local
///
fn ext_is_unicast_link_local_strict(&self) -> bool {
(self.segments()[0] & 0xffff) == 0xfe80
&& (self.segments()[1] & 0xffff) == 0
&& (self.segments()[2] & 0xffff) == 0
&& (self.segments()[3] & 0xffff) == 0
}
/// Returns [`true`] if the address is a unicast link-local address (`fe80::/10`).
///
/// This method returns [`true`] for addresses in the range reserved by [RFC 4291 section 2.4],
/// i.e. addresses with the following format:
///
/// ```no_rust
/// | 10 |
/// | bits | 54 bits | 64 bits |
/// +----------+-------------------------+----------------------------+
/// |1111111010| arbitratry value | interface ID |
/// +----------+-------------------------+----------------------------+
/// ```
///
/// As a result, this method consider addresses such as `fe80:0:0:1::` or `fe81::` to be
/// unicast link-local addresses, whereas [`is_unicast_link_local_strict()`] does not. If you
/// need a strict validation fully compliant with the RFC, use
/// [`is_unicast_link_local_strict()`].
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use torment_core::ip::Ipv6AddrExt;
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0);
/// assert!(ip.ext_is_unicast_link_local());
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff);
/// assert!(ip.ext_is_unicast_link_local());
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0);
/// assert!(ip.ext_is_unicast_link_local());
/// assert!(!ip.ext_is_unicast_link_local_strict());
///
/// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0);
/// assert!(ip.ext_is_unicast_link_local());
/// assert!(!ip.ext_is_unicast_link_local_strict());
/// ```
///
/// # See also
///
/// - [IETF RFC 4291 section 2.4]
/// - [RFC 4291 errata 4406]
///
/// [IETF RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
/// [`true`]: ../../std/primitive.bool.html
/// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406
/// [`is_unicast_link_local_strict()`]: ../../std/net/struct.Ipv6Addr.html#method.is_unicast_link_local_strict
///
fn ext_is_unicast_link_local(&self) -> bool {
(self.segments()[0] & 0xffc0) == 0xfe80
}
/// Returns [`true`] if this is a deprecated unicast site-local address (fec0::/10). The
/// unicast site-local address format is defined in [RFC 4291 section 2.5.7] as:
///
/// ```no_rust
/// | 10 |
/// | bits | 54 bits | 64 bits |
/// +----------+-------------------------+----------------------------+
/// |1111111011| subnet ID | interface ID |
/// +----------+-------------------------+----------------------------+
/// ```
///
/// [`true`]: ../../std/primitive.bool.html
/// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use torment_core::ip::Ipv6AddrExt;
///
/// assert_eq!(
/// Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_is_unicast_site_local(),
/// false
/// );
/// assert_eq!(Ipv6Addr::new(0xfec2, 0, 0, 0, 0, 0, 0, 0).ext_is_unicast_site_local(), true);
/// ```
///
/// # Warning
///
/// As per [RFC 3879], the whole `FEC0::/10` prefix is
/// deprecated. New software must not support site-local
/// addresses.
///
/// [RFC 3879]: https://tools.ietf.org/html/rfc3879
fn ext_is_unicast_site_local(&self) -> bool {
(self.segments()[0] & 0xffc0) == 0xfec0
}
/// Returns [`true`] if the address is a globally routable unicast address.
///
/// The following return false:
///
/// - the loopback address
/// - the link-local addresses
/// - unique local addresses
/// - the unspecified address
/// - the address range reserved for documentation
///
/// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7]
///
/// ```no_rust
/// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer
/// be supported in new implementations (i.e., new implementations must treat this prefix as
/// Global Unicast).
/// ```
///
/// [`true`]: ../../std/primitive.bool.html
/// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use torment_core::ip::Ipv6AddrExt;
///
/// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).ext_is_unicast_global(), false);
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_is_unicast_global(), true);
/// ```
fn ext_is_unicast_global(&self) -> bool {
!self.is_multicast()
&& !self.is_loopback()
&& !self.ext_is_unicast_link_local()
&& !self.ext_is_unique_local()
&& !self.is_unspecified()
&& !self.ext_is_documentation()
}
/// Returns [`true`] if this is an address reserved for documentation
/// (2001:db8::/32).
///
/// This property is defined in [IETF RFC 3849].
///
/// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849
/// [`true`]: ../../std/primitive.bool.html
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use torment_core::ip::Ipv6AddrExt;
///
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_is_documentation(), false);
/// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).ext_is_documentation(), true);
/// ```
fn ext_is_documentation(&self) -> bool {
(self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8)
}
/// Returns the address's multicast scope if the address is multicast.
///
/// # Examples
///
/// ```
/// use std::net::{Ipv6Addr};
/// use torment_core::ip::{Ipv6AddrExt, Ipv6MulticastScope};
///
/// assert_eq!(
/// Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).ext_multicast_scope(),
/// Some(Ipv6MulticastScope::Global)
/// );
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).ext_multicast_scope(), None);
/// ```
fn ext_multicast_scope(&self) -> Option<Ipv6MulticastScope> {
if self.is_multicast() {
match self.segments()[0] & 0x000f {
1 => Some(Ipv6MulticastScope::InterfaceLocal),
2 => Some(Ipv6MulticastScope::LinkLocal),
3 => Some(Ipv6MulticastScope::RealmLocal),
4 => Some(Ipv6MulticastScope::AdminLocal),
5 => Some(Ipv6MulticastScope::SiteLocal),
8 => Some(Ipv6MulticastScope::OrganizationLocal),
14 => Some(Ipv6MulticastScope::Global),
_ => None,
}
} else {
None
}
}
}