blob: 45d2b5b4f79e2668559d67fefa374b0a208f5e85 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO: Edit this doc comment (it's copy+pasted from the Netstack3 core)
//! IP protocol types.
//!
//! We provide the following types:
//!
//! # IP Versions
//!
//! * [`IpVersion`]: An enum representing IPv4 or IPv6
//! * [`Ip`]: An IP version trait that can be used write code which is generic
//! over both versions of the IP protocol
//!
//! # IP Addresses
//!
//! * [`Ipv4Addr`]: A concrete IPv4 address
//! * [`Ipv6Addr`]: A concrete IPv6 address
//! * [`IpAddr`]: An enum representing either a v4 or v6 address.
//! * [`IpAddress`]: An IP address trait that can be used to write code which is
//! generic over both types of IP address
//!
//! # Subnets
//!
//! * [`Subnet`]: A v4 or v6 subnet, as specified by the type parameter.
//! * [`SubnetEither`]: An enum of either a v4 subnet or a v6 subnet.
//!
//! # Address + Subnet Pairs:
//!
//! * [`AddrSubnet`]: A v4 or v6 subnet + address pair, as specified by the type
//! parameter.
//! * [`AddrSubnetEither`]: An enum of either a v4 or a v6 subnet + address
//! pair.
//!
//! [`IpVersion`]: crate::ip::IpVersion
//! [`Ip`]: crate::ip::Ip
//! [`Ipv4Addr`]: crate::ip::Ipv4Addr
//! [`Ipv6Addr`]: crate::ip::Ipv6Addr
//! [`IpAddr`]: crate::ip::IpAddr
//! [`IpAddress`]: crate::ip::IpAddress
//! [`Subnet`]: crate::ip::Subnet
//! [`SubnetEither`]: crate::ip::SubnetEither
//! [`AddrSubnet`]: crate::ip::AddrSubnet
//! [`AddrSubnetEither`]: crate::ip::AddrSubnetEither
// TODO(joshlf): Add RFC references for various standards such as the global
// broadcast address or the Class E subnet.
use core::fmt::{self, Debug, Display, Formatter};
use core::hash::Hash;
use core::ops::Deref;
#[cfg(std)]
use std::net;
use zerocopy::{AsBytes, FromBytes, Unaligned};
use crate::{
sealed, LinkLocalAddr, LinkLocalAddress, MulticastAddr, MulticastAddress, Scope,
ScopeableAddress, SpecifiedAddr, SpecifiedAddress, UnicastAddr, UnicastAddress, Witness,
};
// NOTE on passing by reference vs by value: Clippy advises us to pass IPv4
// addresses by value, and IPv6 addresses by reference. For concrete types, we
// do the right thing. For the IpAddress trait, we use references in order to
// optimize (albeit very slightly) for IPv6 performance.
/// An IP protocol version.
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum IpVersion {
V4,
V6,
}
/// A ZST that carries IP version information.
///
/// Typically used by types that need to receive external information of which
/// IP version the type is specialized for, but without any other associated data.
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct IpVersionMarker<I> {
_marker: core::marker::PhantomData<I>,
}
impl<I: Ip> Default for IpVersionMarker<I> {
fn default() -> Self {
Self { _marker: core::marker::PhantomData }
}
}
impl<I: Ip> Debug for IpVersionMarker<I> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "IpVersionMarker<{}>", I::NAME)
}
}
/// An IP address.
///
/// By default, the contained address types are `Ipv4Addr` and `Ipv6Addr`.
/// However, any types can be provided. This is intended to support types like
/// `IpAddr<SpecifiedAddr<Ipv4Addr>, SpecifiedAddr<Ipv6Addr>>`. `From` is
/// implemented to support conversions in both directions between
/// `IpAddr<SpecifiedAddr<Ipv4Addr>, SpecifiedAddr<Ipv6Addr>>` and
/// `SpecifiedAddr<IpAddr>`, and similarly for other witness types.
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum IpAddr<V4 = Ipv4Addr, V6 = Ipv6Addr> {
V4(V4),
V6(V6),
}
impl<A: IpAddress> From<A> for IpAddr {
#[inline]
fn from(addr: A) -> IpAddr {
addr.into_ip_addr()
}
}
#[cfg(std)]
impl From<net::IpAddr> for IpAddr {
#[inline]
fn from(addr: net::IpAddr) -> IpAddr {
match addr {
net::IpAddr::V4(addr) => IpAddr::V4(addr.into()),
net::IpAddr::V6(addr) => IpAddr::V6(addr.into()),
}
}
}
#[cfg(std)]
impl From<IpAddr> for net::IpAddr {
fn from(addr: IpAddr) -> net::IpAddr {
match addr {
IpAddr::V4(addr) => net::IpAddr::V4(addr.into()),
IpAddr::V6(addr) => net::IpAddr::V6(addr.into()),
}
}
}
impl IpVersion {
/// The number for this IP protocol version.
///
/// 4 for `V4` and 6 for `V6`.
#[inline]
pub fn version_number(self) -> u8 {
match self {
IpVersion::V4 => 4,
IpVersion::V6 => 6,
}
}
/// Is this IPv4?
#[inline]
pub fn is_v4(self) -> bool {
self == IpVersion::V4
}
/// Is this IPv6?
#[inline]
pub fn is_v6(self) -> bool {
self == IpVersion::V6
}
}
/// A trait for IP protocol versions.
///
/// `Ip` encapsulates the details of a version of the IP protocol. It includes
/// the [`IpVersion`] enum (`VERSION`) and an [`IpAddress`] type (`Addr`). It is
/// implemented by [`Ipv4`] and [`Ipv6`]. This trait is sealed, and there are
/// guaranteed to be no other implementors besides these. Code - including
/// unsafe code - may rely on this assumption for its correctness and soundness.
///
/// Note that the implementors of this trait are not meant to be instantiated
/// (in fact, they can't be instantiated). They are only meant to exist at the
/// type level.
pub trait Ip:
Sized
+ Clone
+ Copy
+ Debug
+ Default
+ Eq
+ Hash
+ Ord
+ PartialEq
+ PartialOrd
+ Send
+ Sync
+ sealed::Sealed
+ 'static
{
/// The IP version.
///
/// `V4` for IPv4 and `V6` for IPv6.
const VERSION: IpVersion;
/// The unspecified address.
///
/// This is 0.0.0.0 for IPv4 and :: for IPv6.
const UNSPECIFIED_ADDRESS: Self::Addr;
/// The default loopback address.
///
/// When sending packets to a loopback interface, this address is used as
/// the source address. It is an address in the loopback subnet.
const LOOPBACK_ADDRESS: SpecifiedAddr<Self::Addr>;
/// The subnet of loopback addresses.
///
/// Addresses in this subnet must not appear outside a host, and may only be
/// used for loopback interfaces.
const LOOPBACK_SUBNET: Subnet<Self::Addr>;
/// The subnet of multicast addresses.
const MULTICAST_SUBNET: Subnet<Self::Addr>;
/// The subnet of link-local unicast addresses.
///
/// Note that some multicast addresses are also link-local. In IPv4, these
/// are contained in the [link-local multicast subnet]. In IPv6, the
/// link-local multicast addresses are not organized into a single subnet;
/// instead, whether a multicast IPv6 address is link-local is a function of
/// its scope.
///
/// [link-local multicast subnet]: Ipv4::LINK_LOCAL_MULTICAST_SUBNET
const LINK_LOCAL_UNICAST_SUBNET: Subnet<Self::Addr>;
/// "IPv4" or "IPv6".
const NAME: &'static str;
/// The minimum link MTU for this version.
///
/// Every internet link supporting this IP version must have a maximum
/// transmission unit (MTU) of at least this many bytes. This MTU applies to
/// the size of an IP packet, and does not include any extra bytes used by
/// encapsulating packets (Ethernet frames, GRE packets, etc).
const MINIMUM_LINK_MTU: u16;
/// The address type for this IP version.
///
/// [`Ipv4Addr`] for IPv4 and [`Ipv6Addr`] for IPv6.
type Addr: IpAddress<Version = Self>;
}
/// IPv4.
///
/// `Ipv4` implements `Ip` for IPv4.
///
/// Note that this type has no value constructor. It is used purely at the type
/// level. Attempting to construct it by calling `Default::default` will panic.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Ipv4 {}
impl Default for Ipv4 {
fn default() -> Ipv4 {
panic!("Ipv4 default")
}
}
impl sealed::Sealed for Ipv4 {}
impl Ip for Ipv4 {
const VERSION: IpVersion = IpVersion::V4;
const UNSPECIFIED_ADDRESS: Ipv4Addr = Ipv4Addr::new([0, 0, 0, 0]);
// https://tools.ietf.org/html/rfc5735#section-3
const LOOPBACK_ADDRESS: SpecifiedAddr<Ipv4Addr> =
unsafe { SpecifiedAddr::new_unchecked(Ipv4Addr::new([127, 0, 0, 1])) };
const LOOPBACK_SUBNET: Subnet<Ipv4Addr> =
Subnet { network: Ipv4Addr::new([127, 0, 0, 0]), prefix: 8 };
const MULTICAST_SUBNET: Subnet<Ipv4Addr> =
Subnet { network: Ipv4Addr::new([224, 0, 0, 0]), prefix: 4 };
/// The subnet of link-local unicast addresses, outlined in [RFC 3927
/// Section 2.1].
///
/// [RFC 3927 Section 2.1]: https://tools.ietf.org/html/rfc3927#section-2.1
const LINK_LOCAL_UNICAST_SUBNET: Subnet<Ipv4Addr> =
Subnet { network: Ipv4Addr::new([169, 254, 0, 0]), prefix: 16 };
const NAME: &'static str = "IPv4";
/// The IPv4 minimum link MTU.
///
/// Per [RFC 791 Section 3.2], "[\e\]very internet module must be able to
/// forward a datagram of 68 octets without further fragmentation."
///
/// [RFC 791 Section 3.2]: https://tools.ietf.org/html/rfc791#section-3.2
const MINIMUM_LINK_MTU: u16 = 68;
type Addr = Ipv4Addr;
}
impl Ipv4 {
/// The global broadcast address.
///
/// This address is considered to be a broadcast address on all networks
/// regardless of subnet address. This is distinct from the subnet-specific
/// broadcast address (e.g., 192.168.255.255 on the subnet 192.168.0.0/16).
pub const GLOBAL_BROADCAST_ADDRESS: SpecifiedAddr<Ipv4Addr> =
unsafe { SpecifiedAddr::new_unchecked(Ipv4Addr::new([255, 255, 255, 255])) };
/// The Class E subnet.
///
/// The Class E subnet is meant for experimental purposes, and should not be
/// used on the general internet. [RFC 1812 Section 5.3.7] suggests that
/// routers SHOULD discard packets with a source address in the Class E
/// subnet.
///
/// [RFC 1812 Section 5.3.7]: https://tools.ietf.org/html/rfc1812#section-5.3.7
pub const CLASS_E_SUBNET: Subnet<Ipv4Addr> =
Subnet { network: Ipv4Addr::new([240, 0, 0, 0]), prefix: 4 };
/// The subnet of link-local multicast addresses, outlined in [RFC 5771
/// Section 4].
///
/// [RFC 5771 Section 4]: https://tools.ietf.org/html/rfc5771#section-4
pub const LINK_LOCAL_MULTICAST_SUBNET: Subnet<Ipv4Addr> =
Subnet { network: Ipv4Addr::new([169, 254, 0, 0]), prefix: 16 };
/// The multicast address subscribed to by all routers on the local network.
pub const ALL_ROUTERS_MULTICAST_ADDRESS: MulticastAddr<Ipv4Addr> =
unsafe { MulticastAddr::new_unchecked(Ipv4Addr::new([224, 0, 0, 2])) };
}
/// IPv6.
///
/// `Ipv6` implements `Ip` for IPv6.
///
/// Note that this type has no value constructor. It is used purely at the type
/// level. Attempting to construct it by calling `Default::default` will panic.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Ipv6 {}
impl Default for Ipv6 {
fn default() -> Ipv6 {
panic!("Ipv6 default")
}
}
impl sealed::Sealed for Ipv6 {}
impl Ip for Ipv6 {
const VERSION: IpVersion = IpVersion::V6;
const UNSPECIFIED_ADDRESS: Ipv6Addr = Ipv6Addr::new([0; 16]);
const LOOPBACK_ADDRESS: SpecifiedAddr<Ipv6Addr> = unsafe {
SpecifiedAddr::new_unchecked(Ipv6Addr::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
]))
};
const LOOPBACK_SUBNET: Subnet<Ipv6Addr> = Subnet {
network: Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
prefix: 128,
};
const MULTICAST_SUBNET: Subnet<Ipv6Addr> = Subnet {
network: Ipv6Addr::new([0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
prefix: 8,
};
/// The subnet of link-local unicast addresses, defined in [RFC 4291 Section
/// 2.4].
///
/// Note that multicast addresses can also be link-local. However, there is no
/// single subnet of link-local multicast addresses. For more details on
/// link-local multicast addresses, see [RFC 4291 Section 2.7].
///
/// [RFC 4291 Section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
/// [RFC 4291 Section 2.7]: https://tools.ietf.org/html/rfc4291#section-2.7
const LINK_LOCAL_UNICAST_SUBNET: Subnet<Ipv6Addr> = Subnet {
network: Ipv6Addr::new([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
prefix: 10,
};
const NAME: &'static str = "IPv6";
/// The IPv6 minimum link MTU.
///
/// Per [RFC 8200 Section 5]:
///
/// > IPv6 requires that every link in the Internet have an MTU of 1280
/// > octets or greater. This is known as the IPv6 minimum link MTU. On any
/// > link that cannot convey a 1280-octet packet in one piece, link-
/// > specific fragmentation and reassembly must be provided at a layer
/// > below IPv6.
///
/// [RFC 8200 Section 5]: https://tools.ietf.org/html/rfc8200#section-5
const MINIMUM_LINK_MTU: u16 = 1280;
type Addr = Ipv6Addr;
}
impl Ipv6 {
/// The IPv6 All Nodes multicast address in link-local scope, as defined in
/// [RFC 4291 Section 2.7.1].
///
/// [RFC 4291 Section 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS: MulticastAddr<Ipv6Addr> = unsafe {
MulticastAddr::new_unchecked(Ipv6Addr::new([
0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
]))
};
/// The IPv6 All Routers multicast address in link-local scope, as defined
/// in [RFC 4291 Section 2.7.1].
///
/// [RFC 4291 Section 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const ALL_ROUTERS_LINK_LOCAL_MULTICAST_ADDRESS: MulticastAddr<Ipv6Addr> = unsafe {
MulticastAddr::new_unchecked(Ipv6Addr::new([
0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
]))
};
/// The (deprecated) subnet of site-local unicast addresses, defined in [RFC
/// 3513 Section 2.5.6].
///
/// The site-local unicast subnet was deprecated in [RFC 3879]:
///
/// > The special behavior of this prefix MUST no longer be supported in new
/// > implementations. The prefix MUST NOT be reassigned for other use
/// > except by a future IETF standards action... However, router
/// > implementations SHOULD be configured to prevent routing of this prefix
/// > by default.
///
/// [RFC 3513 Section 2.5.6]: https://tools.ietf.org/html/rfc3513#section-2.5.6
/// [RFC 3879]: https://tools.ietf.org/html/rfc3879
pub const SITE_LOCAL_UNICAST_SUBNET: Subnet<Ipv6Addr> = Subnet {
network: Ipv6Addr::new([0xfe, 0xC0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
prefix: 10,
};
/// The length, in bits, of the interface identifier portion of unicast IPv6
/// addresses *except* for addresses which start with the binary value 000.
///
/// According to [RFC 4291 Section 2.5.1], "\[f\]or all unicast addresses,
/// except those that start with the binary value 000, Interface IDs are
/// required to be 64 bits."
///
/// Note that, per [RFC 4862 Section 5.5.3], "a future revision of the
/// address architecture \[RFC4291\] and a future link-type-specific
/// document, which will still be consistent with each other, could
/// potentially allow for an interface identifier of length other than the
/// value defined in the current documents. Thus, an implementation should
/// not assume a particular constant. Rather, it should expect any lengths
/// of interface identifiers." In other words, this constant may be used to
/// generate addresses or subnet prefix lengths, but should *not* be used to
/// validate addresses or subnet prefix lengths generated by other software
/// or other machines, as it might be valid for other software or other
/// machines to use an interface identifier length different from this one.
///
/// [RFC 4291 Section 2.5.1]: https://tools.ietf.org/html/rfc4291#section-2.5.1
/// [RFC 4862 Section 5.5.3]: https://tools.ietf.org/html/rfc4862#section-5.5.3
pub const UNICAST_INTERFACE_IDENTIFIER_BITS: u8 = 64;
}
/// An IPv4 or IPv6 address.
///
/// `IpAddress` is implemented by [`Ipv4Addr`] and [`Ipv6Addr`]. It is sealed,
/// and there are guaranteed to be no other implementors besides these. Code -
/// including unsafe code - may rely on this assumption for its correctness and
/// soundness.
pub trait IpAddress:
Sized
+ Eq
+ PartialEq
+ Hash
+ Copy
+ Display
+ Debug
+ Default
+ Sync
+ Send
+ LinkLocalAddress
+ ScopeableAddress
+ sealed::Sealed
+ 'static
{
/// The number of bytes in an address of this type.
///
/// 4 for IPv4 and 16 for IPv6.
const BYTES: u8;
/// The IP version type of this address.
///
/// `Ipv4` for `Ipv4Addr` and `Ipv6` for `Ipv6Addr`.
type Version: Ip<Addr = Self>;
/// Gets the underlying bytes of the address.
fn bytes(&self) -> &[u8];
/// Masks off the top bits of the address.
///
/// Returns a copy of `self` where all but the top `bits` bits are set to
/// 0.
///
/// # Panics
///
/// `mask` panics if `bits` is out of range - if it is greater than 32 for
/// IPv4 or greater than 128 for IPv6.
fn mask(&self, bits: u8) -> Self;
/// Converts a statically-typed IP address into a dynamically-typed one.
fn into_ip_addr(self) -> IpAddr;
/// Is this a loopback address?
///
/// `is_loopback` returns `true` if this address is a member of the loopback
/// subnet.
#[inline]
fn is_loopback(&self) -> bool {
Self::Version::LOOPBACK_SUBNET.contains(self)
}
/// Is this a unicast address contained in the given subnet?
///
/// `is_unicast_in_subnet` returns `true` if a given subnet contains this
/// address and the address is none of:
/// - a multicast address
/// - the IPv4 global broadcast address
/// - the IPv4 subnet-specific broadcast address for the given `subnet`
/// - an IPv4 address whose host bits (those bits following the network
/// prefix) are all 0
/// - the unspecified address
/// - an IPv4 Class E address
///
/// Note two exceptions to these rules: If `subnet` is an IPv4 /32, then the
/// single unicast address in the subnet is also technically the subnet
/// broadcast address. If `subnet` is an IPv4 /31, then both addresses in
/// that subnet are broadcast addresses. In either case, the "no
/// subnet-specific broadcast" and "no address with a host part of all
/// zeroes" rules don't apply. Note further that this exception *doesn't*
/// apply to the unspecified address, which is never considered a unicast
/// address regardless of what subnet it's in.
///
/// # RFC Deep Dive
///
/// ## IPv4 addresses ending in zeroes
///
/// In this section, we justify the rule that IPv4 addresses whose host bits
/// are all 0 are not considered unicast addresses.
///
/// In earlier standards, an IPv4 address whose bits were all 0 after the
/// network prefix (e.g., 192.168.0.0 in the subnet 192.168.0.0/16) were a
/// form of "network-prefix-directed" broadcast addresses. Similarly,
/// 0.0.0.0 was considered a form of "limited broadcast address". These have
/// since been deprecated (in the case of 0.0.0.0, it is now considered the
/// "unspecified" address).
///
/// As evidence that this deprecation is official, consider [RFC 1812
/// Section 5.3.5]. In reference to these types of addresses, it states that
/// "packets addressed to any of these addresses SHOULD be silently
/// discarded [by routers]". This not only deprecates them as broadcast
/// addresses, but also as unicast addresses (after all, unicast addresses
/// are not particularly useful if packets destined to them are discarded by
/// routers).
///
/// ## IPv4 /31 and /32 exceptions
///
/// In this section, we justify the exceptions that all addresses in IPv4
/// /31 and /32 subnets are considered unicast.
///
/// For /31 subnets, the case is easy. [RFC 3021 Section 2.1] states that
/// both addresses in a /31 subnet "MUST be interpreted as host addresses."
///
/// For /32, the case is a bit more vague. RFC 3021 makes no mention of /32
/// subnets. However, the same reasoning applies - if an exception is not
/// made, then there do not exist any host addresses in a /32 subnet. [RFC
/// 4632 Section 3.1] also vaguely implies this interpretation by referring
/// to addresses in /32 subnets as "host routes."
///
/// [RFC 1812 Section 5.3.5]: https://tools.ietf.org/html/rfc1812#page-92
/// [RFC 4632 Section 3.1]: https://tools.ietf.org/html/rfc4632#section-3.1
fn is_unicast_in_subnet(&self, subnet: &Subnet<Self>) -> bool;
/// Invokes one function on this address if it is an [`Ipv4Addr`] and
/// another if it is an [`Ipv6Addr`].
///
/// Watch out for the common pitfall that, if `v4` and `v6` are closures,
/// even though only one will be invoked in practice, the Rust borrow
/// checker will still not allow them to both borrow the same mutable state
/// simultaneously. If this becomes a problem, consider separate calls to
/// [`with_v4`] and [`with_v6`] instead.
///
/// [`with_v4`]: crate::ip::IpAddress::with_v4
/// [`with_v6`]: crate::ip::IpAddress::with_v6
fn with<O, F4: FnOnce(Ipv4Addr) -> O, F6: FnOnce(Ipv6Addr) -> O>(self, v4: F4, v6: F6) -> O;
/// Invokes a function on this address if it is an [`Ipv4Addr`] or return
/// `default` if it is an [`Ipv6Addr`].
fn with_v4<O, F: FnOnce(Ipv4Addr) -> O>(self, f: F, default: O) -> O {
self.with(f, |_| default)
}
/// Invokes a function on this address if it is an [`Ipv6Addr`] or return
/// `default` if it is an [`Ipv4Addr`].
fn with_v6<O, F: FnOnce(Ipv6Addr) -> O>(self, f: F, default: O) -> O {
self.with(|_| default, f)
}
// Functions used to implement internal types. These functions aren't
// particularly useful to users, but allow us to implement certain
// specialization-like behavior without actually relying on the unstable
// `specialization` feature.
#[doc(hidden)]
fn into_subnet_either(subnet: Subnet<Self>) -> SubnetEither;
}
/// An witness of an [`IpAddress`].
///
/// `IpAddressWitnessExt` extends [`Witness<A>`] for `A: IpAddress`, adding
/// extra IP address-specific functionality.
///
/// [`Witness<A>`]: crate::Witness
pub trait IpAddressWitnessExt<A: IpAddress>: Witness<A> {
/// This witness type instantiated on the concrete address type
/// [`Ipv4Addr`].
type V4: Witness<Ipv4Addr>;
/// This witness type instantiated on the concrete address type
/// [`Ipv6Addr`].
type V6: Witness<Ipv6Addr>;
/// Invokes one function on this address if it is a witness of [`Ipv4Addr`]
/// and another if it is a witness of [`Ipv6Addr`].
///
/// Watch out for the common pitfall that, if `v4` and `v6` are closures,
/// even though only one will be invoked in practice, the Rust borrow
/// checker will still not allow them to both borrow the same mutable state
/// simultaneously. If this becomes a problem, consider separate calls to
/// [`with_v4`] and [`with_v6`] instead.
///
/// [`with_v4`]: crate::ip::IpAddressWitnessExt::with_v4
/// [`with_v6`]: crate::ip::IpAddressWitnessExt::with_v6
fn with<O, F4: FnOnce(Self::V4) -> O, F6: FnOnce(Self::V6) -> O>(self, v4: F4, v6: F6) -> O;
/// Invokes a function on this address if it is a witness of [`Ipv4Addr`] or return
/// `default` if it is a witness of [`Ipv6Addr`].
fn with_v4<O, F: FnOnce(Self::V4) -> O>(self, f: F, default: O) -> O {
self.with(f, |_| default)
}
/// Invokes a function on this address if it is a witness of [`Ipv6Addr`] or
/// return `default` if it is a witness of [`Ipv4Addr`].
fn with_v6<O, F: FnOnce(Self::V6) -> O>(self, f: F, default: O) -> O {
self.with(|_| default, f)
}
}
macro_rules! impl_ip_address_witness_ext {
($ty:ident) => {
impl<A: IpAddress> IpAddressWitnessExt<A> for $ty<A> {
type V4 = $ty<Ipv4Addr>;
type V6 = $ty<Ipv6Addr>;
fn with<O, F4: FnOnce(Self::V4) -> O, F6: FnOnce(Self::V6) -> O>(
self,
v4: F4,
v6: F6,
) -> O {
self.into_addr().with(
|addr| v4(unsafe { $ty::new_unchecked(addr) }),
|addr| v6(unsafe { $ty::new_unchecked(addr) }),
)
}
}
};
($ty:ident, $($tys:ident),*) => {
impl_ip_address_witness_ext!($ty);
impl_ip_address_witness_ext!($($tys),*);
}
}
impl_ip_address_witness_ext!(SpecifiedAddr, MulticastAddr, LinkLocalAddr);
impl<A: IpAddress> SpecifiedAddress for A {
/// Is this an address other than the unspecified address?
///
/// `is_specified` returns true if `self` is not equal to [`A::Version::UNSPECIFIED_ADDRESS`].
///
/// [`A::Version::UNSPECIFIED_ADDRESS`]: crate::ip::Ip::UNSPECIFIED_ADDRESS
#[inline]
fn is_specified(&self) -> bool {
self != &A::Version::UNSPECIFIED_ADDRESS
}
}
/// Map a method over an `IpAddr`, calling it after matching on the type of IP
/// address.
macro_rules! map_ip_addr {
($val:expr, $method:ident) => {
match $val {
IpAddr::V4(a) => a.$method(),
IpAddr::V6(a) => a.$method(),
}
};
}
impl SpecifiedAddress for IpAddr {
/// Is this an address other than the unspecified address?
///
/// `is_specified` returns true if `self` is not equal to
/// [`Ip::UNSPECIFIED_ADDRESS`] for the IP version of this address.
#[inline]
fn is_specified(&self) -> bool {
map_ip_addr!(self, is_specified)
}
}
impl<A: IpAddress> MulticastAddress for A {
/// Is this address in the multicast subnet?
///
/// `is_multicast` returns true if `self` is in
/// [`A::Version::MULTICAST_SUBNET`].
///
/// [`A::Version::MULTICAST_SUBNET`]: crate::ip::Ip::MULTICAST_SUBNET
#[inline]
fn is_multicast(&self) -> bool {
<A as IpAddress>::Version::MULTICAST_SUBNET.contains(self)
}
}
impl MulticastAddress for IpAddr {
/// Is this an address in the multicast subnet?
///
/// `is_multicast` returns true if `self` is in [`Ip::MULTICAST_SUBNET`] for
/// the IP version of this address.
#[inline]
fn is_multicast(&self) -> bool {
map_ip_addr!(self, is_multicast)
}
}
impl LinkLocalAddress for Ipv4Addr {
/// Is this address in the link-local subnet?
///
/// `is_linklocal` returns true if `self` is in
/// [`Ipv4::LINK_LOCAL_UNICAST_SUBNET`] or
/// [`Ipv4::LINK_LOCAL_MULTICAST_SUBNET`].
///
/// [`Ipv4::LINK_LOCAL_UNICAST_SUBNET`]: crate::ip::Ip::LINK_LOCAL_UNICAST_SUBNET
/// [`Ipv4::LINK_LOCAL_MULTICAST_SUBNET`]: crate::ip::Ipv4::LINK_LOCAL_MULTICAST_SUBNET
#[inline]
fn is_linklocal(&self) -> bool {
Ipv4::LINK_LOCAL_UNICAST_SUBNET.contains(self)
|| Ipv4::LINK_LOCAL_MULTICAST_SUBNET.contains(self)
}
}
impl LinkLocalAddress for Ipv6Addr {
/// Is this address in the link-local subnet?
///
/// `is_linklocal` returns true if `self` is in
/// [`Ipv6::LINK_LOCAL_UNICAST_SUBNET`], is a multicast address whose scope
/// is link-local, or is the address [`Ipv6::LOOPBACK_ADDRESS`] (per [RFC
/// 4291 Section 2.5.3], the loopback address is considered to have
/// link-local scope).
///
/// [`Ipv6::LINK_LOCAL_UNICAST_SUBNET`]: crate::ip::Ip::LINK_LOCAL_UNICAST_SUBNET
/// [`Ipv6::LOOPBACK_ADDRESS`]: crate::ip::Ip::LOOPBACK_ADDRESS
/// [RFC 4291 Section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
#[inline]
fn is_linklocal(&self) -> bool {
const LINK_LOCAL_SCOPE: u8 = 0x02;
// TODO(joshlf): Stop doing this manually once we have a general-purpose
// mechanism for extracting the scope from a multicast address.
Ipv6::LINK_LOCAL_UNICAST_SUBNET.contains(self)
|| (self.is_multicast() && self.0[1] & 0x0F == LINK_LOCAL_SCOPE)
|| self == Ipv6::LOOPBACK_ADDRESS.deref()
}
}
impl LinkLocalAddress for IpAddr {
/// Is this address link-local?
#[inline]
fn is_linklocal(&self) -> bool {
map_ip_addr!(self, is_linklocal)
}
}
impl ScopeableAddress for Ipv4Addr {
type Scope = ();
/// The scope of this address.
///
/// Although IPv4 defines a link local subnet, IPv4 addresses are always
/// considered to be in the global scope.
fn scope(&self) -> () {
()
}
}
/// The list of IPv6 scopes.
///
/// These scopes are defined by [RFC 4291 Section 2.7].
///
/// [RFC 4291 Section 2.7]: https://tools.ietf.org/html/rfc4291#section-2.7
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Ipv6Scope {
/// The interface-local scope.
InterfaceLocal,
/// The link-local scope.
LinkLocal,
/// The admin-local scope.
AdminLocal,
/// The (deprecated) site-local scope.
///
/// The site-local scope was deprecated in [RFC 3879]. While this scope
/// is returned for both site-local unicast and site-local multicast
/// addresses, RFC 3879 says the following about site-local unicast addresses
/// in particular ("this prefix" refers to the [site-local unicast subnet]):
///
/// > The special behavior of this prefix MUST no longer be supported in new
/// > implementations. The prefix MUST NOT be reassigned for other use
/// > except by a future IETF standards action... However, router
/// > implementations SHOULD be configured to prevent routing of this prefix
/// > by default.
///
/// [RFC 3879]: https://tools.ietf.org/html/rfc3879
/// [site-local unicast subnet]: Ipv6::SITE_LOCAL_UNICAST_SUBNET
SiteLocal,
/// The organization-local scope.
OrganizationLocal,
/// The global scope.
Global,
/// Scopes which are reserved for future use by [RFC 4291 Section 2.7].
///
/// [RFC 4291 Section 2.7]: https://tools.ietf.org/html/rfc4291#section-2.7
Reserved(Ipv6ReservedScope),
/// Scopes which are available for local definition by administrators.
Unassigned(Ipv6UnassignedScope),
}
/// The list of IPv6 scopes which are reserved for future use by [RFC 4291
/// Section 2.7].
///
/// [RFC 4291 Section 2.7]: https://tools.ietf.org/html/rfc4291#section-2.7
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Ipv6ReservedScope {
/// The scope with numerical value 0.
Scope0 = 0,
/// The scope with numerical value 3.
Scope3 = 3,
/// The scope with numerical value 0xF.
ScopeF = 0xF,
}
/// The list of IPv6 scopes which are available for local definition by
/// administrators.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Ipv6UnassignedScope {
/// The scope with numerical value 6.
Scope6 = 6,
/// The scope with numerical value 7.
Scope7 = 7,
/// The scope with numerical value 9.
Scope9 = 9,
/// The scope with numerical value 0xA.
ScopeA = 0xA,
/// The scope with numerical value 0xB.
ScopeB = 0xB,
/// The scope with numerical value 0xC.
ScopeC = 0xC,
/// The scope with numerical value 0xD.
ScopeD = 0xD,
}
impl Scope for Ipv6Scope {
#[inline]
fn can_have_zone(&self) -> bool {
// Per RFC 6874 Section 4:
//
// > [I]mplementations MUST NOT allow use of this format except for
// > well-defined usages, such as sending to link-local addresses under
// > prefix fe80::/10. At the time of writing, this is the only
// > well-defined usage known.
//
// While this directive applies particularly to the human-readable
// string representation of IPv6 addresses and zone identifiers, it
// seems reasonable to limit the in-memory representation in the same
// way.
//
// Note that, if interpreted literally, this quote would bar the use of
// zone identifiers on link-local multicast addresses (they are not
// under the prefix fe80::/10). However, it seems clear that this is not
// the interpretation that was intended. Link-local multicast addresses
// have the same need for a zone-identifier as link-local unicast
// addresses, and indeed, real systems like Linux allow link-local
// multicast addresses to be accompanied by zone identifiers.
matches!(self, Ipv6Scope::LinkLocal)
}
}
impl core::cmp::PartialOrd for Ipv6Scope {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl core::cmp::Ord for Ipv6Scope {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
// RFC 4291, section 2.7 defines these scope IDs.
// 0 Reserved
// 1 Interface-Local scope
// 2 Link-Local scope
// 3 Reserved
// 4 Admin-Local scope
// 5 Site-Local scope
// 6 Unassigned
// 7 Unassigned
// 8 Organization-Local scope
// 9-D Unassigned
// E Global scope
// F Reserved
let scope_id = |scope: &Ipv6Scope| match scope {
Ipv6Scope::Reserved(Ipv6ReservedScope::Scope0) => 0x00,
Ipv6Scope::InterfaceLocal => 0x01,
Ipv6Scope::LinkLocal => 0x02,
Ipv6Scope::Reserved(Ipv6ReservedScope::Scope3) => 0x03,
Ipv6Scope::AdminLocal => 0x04,
Ipv6Scope::SiteLocal => 0x05,
Ipv6Scope::Unassigned(Ipv6UnassignedScope::Scope6) => 0x06,
Ipv6Scope::Unassigned(Ipv6UnassignedScope::Scope7) => 0x07,
Ipv6Scope::OrganizationLocal => 0x08,
Ipv6Scope::Unassigned(Ipv6UnassignedScope::Scope9) => 0x09,
Ipv6Scope::Unassigned(Ipv6UnassignedScope::ScopeA) => 0x0A,
Ipv6Scope::Unassigned(Ipv6UnassignedScope::ScopeB) => 0x0B,
Ipv6Scope::Unassigned(Ipv6UnassignedScope::ScopeC) => 0x0C,
Ipv6Scope::Unassigned(Ipv6UnassignedScope::ScopeD) => 0x0D,
Ipv6Scope::Global => 0xE,
Ipv6Scope::Reserved(Ipv6ReservedScope::ScopeF) => 0x0F,
};
scope_id(self).cmp(&scope_id(other))
}
}
impl ScopeableAddress for Ipv6Addr {
type Scope = Ipv6Scope;
/// The scope of this address.
#[inline]
fn scope(&self) -> Ipv6Scope {
if self.is_multicast() {
use Ipv6ReservedScope::*;
use Ipv6Scope::*;
use Ipv6UnassignedScope::*;
// The "scop" field of a multicast address is the last 4 bits of the
// second byte of the address (see
// https://tools.ietf.org/html/rfc4291#section-2.7).
match self.0[1] & 0xF {
0 => Reserved(Scope0),
1 => InterfaceLocal,
2 => LinkLocal,
3 => Reserved(Scope3),
4 => AdminLocal,
5 => SiteLocal,
6 => Unassigned(Scope6),
7 => Unassigned(Scope7),
8 => OrganizationLocal,
9 => Unassigned(Scope9),
0xA => Unassigned(ScopeA),
0xB => Unassigned(ScopeB),
0xC => Unassigned(ScopeC),
0xD => Unassigned(ScopeD),
0xE => Global,
0xF => Reserved(ScopeF),
_ => unreachable!(),
}
} else if self.is_linklocal() {
Ipv6Scope::LinkLocal
} else if self.is_site_local() {
Ipv6Scope::SiteLocal
} else {
Ipv6Scope::Global
}
}
}
impl Scope for IpAddr<(), Ipv6Scope> {
#[inline]
fn can_have_zone(&self) -> bool {
match self {
IpAddr::V4(scope) => scope.can_have_zone(),
IpAddr::V6(scope) => scope.can_have_zone(),
}
}
}
impl ScopeableAddress for IpAddr {
type Scope = IpAddr<(), Ipv6Scope>;
#[inline]
fn scope(&self) -> IpAddr<(), Ipv6Scope> {
match self {
IpAddr::V4(addr) => IpAddr::V4(addr.scope()),
IpAddr::V6(addr) => IpAddr::V6(addr.scope()),
}
}
}
/// The definition of each trait for `IpAddr` is equal to the definition of that
/// trait for whichever of `Ipv4Addr` and `Ipv6Addr` is actually present in the
/// enum. Thus, we can convert between `$witness<IpvXAddr>`, `$witness<IpAddr>`,
/// and `IpAddr<$witness<Ipv4Addr>, $witness<Ipv6Addr>>` arbitrarily.
macro_rules! impl_from_witness {
($witness:ident) => {
impl_from_witness!($witness, Ipv4Addr);
impl_from_witness!($witness, Ipv6Addr);
impl From<IpAddr<$witness<Ipv4Addr>, $witness<Ipv6Addr>>> for $witness<IpAddr> {
fn from(addr: IpAddr<$witness<Ipv4Addr>, $witness<Ipv6Addr>>) -> $witness<IpAddr> {
unsafe {
$witness::new_unchecked(match addr {
IpAddr::V4(addr) => IpAddr::V4(addr.into_addr()),
IpAddr::V6(addr) => IpAddr::V6(addr.into_addr()),
})
}
}
}
impl From<$witness<IpAddr>> for IpAddr<$witness<Ipv4Addr>, $witness<Ipv6Addr>> {
fn from(addr: $witness<IpAddr>) -> IpAddr<$witness<Ipv4Addr>, $witness<Ipv6Addr>> {
unsafe {
match addr.into_addr() {
IpAddr::V4(addr) => IpAddr::V4($witness::new_unchecked(addr)),
IpAddr::V6(addr) => IpAddr::V6($witness::new_unchecked(addr)),
}
}
}
}
};
($witness:ident, $ipaddr:ident) => {
impl From<$witness<$ipaddr>> for $witness<IpAddr> {
fn from(addr: $witness<$ipaddr>) -> $witness<IpAddr> {
unsafe { $witness::new_unchecked(addr.get().into()) }
}
}
impl From<$witness<$ipaddr>> for $ipaddr {
fn from(addr: $witness<$ipaddr>) -> $ipaddr {
addr.into_addr()
}
}
};
}
impl_from_witness!(SpecifiedAddr);
impl_from_witness!(MulticastAddr);
impl_from_witness!(LinkLocalAddr);
impl_from_witness!(UnicastAddr, Ipv6Addr);
/// An IPv4 address.
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, FromBytes, AsBytes, Unaligned)]
#[repr(transparent)]
pub struct Ipv4Addr([u8; 4]);
impl Ipv4Addr {
/// Creates a new IPv4 address.
#[inline]
pub const fn new(bytes: [u8; 4]) -> Self {
Ipv4Addr(bytes)
}
/// Gets the bytes of the IPv4 address.
#[inline]
pub const fn ipv4_bytes(self) -> [u8; 4] {
self.0
}
/// Is this the global broadcast address?
///
/// `is_global_broadcast` is a shorthand for comparing against
/// [`Ipv4::GLOBAL_BROADCAST_ADDRESS`].
#[inline]
pub fn is_global_broadcast(self) -> bool {
self == Ipv4::GLOBAL_BROADCAST_ADDRESS.into_addr()
}
/// Is this a Class E address?
///
/// `is_class_e` is a shorthand for checking membership in
/// [`Ipv4::CLASS_E_SUBNET`].
#[inline]
pub fn is_class_e(self) -> bool {
Ipv4::CLASS_E_SUBNET.contains(&self)
}
/// Calculates the common prefix length between this address and `other`.
pub fn common_prefix_length(&self, other: &Ipv4Addr) -> u8 {
let Ipv4Addr(me) = self;
let Ipv4Addr(other) = other;
common_prefix_len(me.into_iter().copied().zip(other.into_iter().copied()))
}
/// Converts the address to an IPv4-mapped IPv6 address according to
/// [RFC 4291 Section 2.5.5.2].
///
/// [RFC 4291 Section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
pub fn to_v6_mapped(self) -> Ipv6Addr {
let Self(self_bytes) = self;
let mut bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0];
bytes[12..].copy_from_slice(&self_bytes[..]);
Ipv6Addr::new(bytes)
}
}
impl sealed::Sealed for Ipv4Addr {}
impl IpAddress for Ipv4Addr {
const BYTES: u8 = 4;
type Version = Ipv4;
#[inline]
fn mask(&self, bits: u8) -> Self {
assert!(bits <= 32);
if bits == 0 {
// shifting left by the size of the value is undefined
Ipv4Addr([0; 4])
} else {
let mask = <u32>::max_value() << (32 - bits);
Self::new((u32::from_be_bytes(self.0) & mask).to_be_bytes())
}
}
#[inline]
fn bytes(&self) -> &[u8] {
&self.0
}
#[inline]
fn into_ip_addr(self) -> IpAddr {
IpAddr::V4(self)
}
#[inline]
fn is_unicast_in_subnet(&self, subnet: &Subnet<Self>) -> bool {
!self.is_multicast()
&& !self.is_global_broadcast()
// This clause implements the rules that (the subnet broadcast is
// not unicast AND the address with an all-zeroes host part is not
// unicast) UNLESS the prefix length is 31 or 32.
&& (subnet.prefix() == 32
|| subnet.prefix() == 31
|| (*self != subnet.broadcast() && *self != subnet.network()))
&& self.is_specified()
&& !self.is_class_e()
&& subnet.contains(self)
}
#[inline]
fn with<O, F4: FnOnce(Ipv4Addr) -> O, F6: FnOnce(Ipv6Addr) -> O>(self, v4: F4, _v6: F6) -> O {
v4(self)
}
fn into_subnet_either(subnet: Subnet<Ipv4Addr>) -> SubnetEither {
SubnetEither::V4(subnet)
}
}
impl From<[u8; 4]> for Ipv4Addr {
#[inline]
fn from(bytes: [u8; 4]) -> Ipv4Addr {
Ipv4Addr(bytes)
}
}
#[cfg(std)]
impl From<net::Ipv4Addr> for Ipv4Addr {
#[inline]
fn from(ip: net::Ipv4Addr) -> Ipv4Addr {
Ipv4Addr::new(ip.octets())
}
}
#[cfg(std)]
impl From<Ipv4Addr> for net::Ipv4Addr {
#[inline]
fn from(ip: Ipv4Addr) -> net::Ipv4Addr {
Ipv4Addr::from(ip.0)
}
}
impl Display for Ipv4Addr {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}.{}.{}.{}", self.0[0], self.0[1], self.0[2], self.0[3])
}
}
impl Debug for Ipv4Addr {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
Display::fmt(self, f)
}
}
/// An IPv6 address.
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, FromBytes, AsBytes, Unaligned)]
#[repr(transparent)]
pub struct Ipv6Addr([u8; 16]);
impl Ipv6Addr {
/// Creates a new IPv6 address.
#[inline]
pub const fn new(bytes: [u8; 16]) -> Self {
Ipv6Addr(bytes)
}
/// Gets the bytes of the IPv6 address.
#[inline]
pub const fn ipv6_bytes(&self) -> [u8; 16] {
self.0
}
/// Converts this `Ipv6Addr` to the IPv6 Solicited-Node Address, used in
/// Neighbor Discovery. Defined in [RFC 4291 Section 2.7.1].
///
/// [RFC 4291 Section 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1
#[inline]
pub const fn to_solicited_node_address(&self) -> MulticastAddr<Self> {
// TODO(brunodalbo) benchmark this generation and evaluate if using
// bit operations with u128 could be faster. This is very likely
// going to be on a hot path.
// We know we are not breaking the guarantee that `MulticastAddr` provides
// when calling `new_unchecked` because the address we provide it is
// a multicast address as defined by RFC 4291 section 2.7.1.
unsafe {
MulticastAddr::new_unchecked(Self::new([
0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xff, self.0[13], self.0[14],
self.0[15],
]))
}
}
/// Checks whether `self` is a valid unicast address.
///
/// A valid unicast address is any unicast address that can be bound to an
/// interface (not the unspecified or loopback addresses).
#[inline]
pub fn is_valid_unicast(&self) -> bool {
!(self.is_loopback() || !self.is_specified() || self.is_multicast())
}
/// Is this address in the (deprecated) site-local unicast subnet?
///
/// `is_site_local` returns true if `self` is in the (deprecated)
/// [`Ipv6::SITE_LOCAL_UNICAST_SUBNET`]. See that constant's documentation
/// for more details on deprecation and how the subnet should be used in
/// light of deprecation.
#[inline]
pub fn is_site_local(&self) -> bool {
Ipv6::SITE_LOCAL_UNICAST_SUBNET.contains(self)
}
/// Is this address a unicast link-local address?
///
/// Shorthand for `self.is_unicast_in_subnet(Ipv6::LINK_LOCAL_UNICAST_SUBNET)`.
#[inline]
pub fn is_unicast_linklocal(&self) -> bool {
self.is_unicast_in_subnet(&Ipv6::LINK_LOCAL_UNICAST_SUBNET)
}
/// Calculates the common prefix length between this address and `other`.
pub fn common_prefix_length(&self, other: &Ipv6Addr) -> u8 {
let Ipv6Addr(me) = self;
let Ipv6Addr(other) = other;
common_prefix_len(me.into_iter().copied().zip(other.into_iter().copied()))
}
}
impl sealed::Sealed for Ipv6Addr {}
/// [`Ipv4Addr`] is convertible into [`Ipv6Addr`] through
/// [`Ipv4addr::to_v6_mapped`].
impl From<Ipv4Addr> for Ipv6Addr {
fn from(addr: Ipv4Addr) -> Self {
addr.to_v6_mapped()
}
}
impl IpAddress for Ipv6Addr {
const BYTES: u8 = 16;
type Version = Ipv6;
#[inline]
fn mask(&self, bits: u8) -> Self {
assert!(bits <= 128);
if bits == 0 {
// shifting left by the size of the value is undefined
Ipv6Addr([0; 16])
} else {
let mask = <u128>::max_value() << (128 - bits);
Self::new((u128::from_be_bytes(self.0) & mask).to_be_bytes())
}
}
#[inline]
fn bytes(&self) -> &[u8] {
&self.0
}
#[inline]
fn into_ip_addr(self) -> IpAddr {
IpAddr::V6(self)
}
#[inline]
fn is_unicast_in_subnet(&self, subnet: &Subnet<Self>) -> bool {
!self.is_multicast() && self.is_specified() && subnet.contains(self)
}
#[inline]
fn with<O, F4: FnOnce(Ipv4Addr) -> O, F6: FnOnce(Ipv6Addr) -> O>(self, _v4: F4, v6: F6) -> O {
v6(self)
}
fn into_subnet_either(subnet: Subnet<Ipv6Addr>) -> SubnetEither {
SubnetEither::V6(subnet)
}
}
impl UnicastAddress for Ipv6Addr {
#[inline]
fn is_unicast(&self) -> bool {
!self.is_multicast() && self.is_specified()
}
}
impl From<[u8; 16]> for Ipv6Addr {
#[inline]
fn from(bytes: [u8; 16]) -> Ipv6Addr {
Ipv6Addr(bytes)
}
}
#[cfg(std)]
impl From<net::Ipv6Addr> for Ipv6Addr {
#[inline]
fn from(ip: net::Ipv6Addr) -> Ipv6Addr {
Ipv6Addr::new(ip.octets())
}
}
#[cfg(std)]
impl From<Ipv6Addr> for net::Ipv6Addr {
#[inline]
fn from(ip: Ipv6Addr) -> net::Ipv6Addr {
net::Ipv6Addr::from(ip.0)
}
}
impl Display for Ipv6Addr {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
// TODO(joshlf): Implement canonicalization even when the `std` feature
// is not enabled.
use core::convert::TryInto;
let to_u16 = |idx| u16::from_be_bytes(self.0[idx..idx + 2].try_into().unwrap());
#[cfg(std)]
return Display::fmt(
&net::Ipv6Addr::new(
to_u16(0),
to_u16(2),
to_u16(4),
to_u16(6),
to_u16(8),
to_u16(10),
to_u16(12),
to_u16(14),
),
f,
);
#[cfg(not(std))]
return write!(
f,
"{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
to_u16(0),
to_u16(2),
to_u16(4),
to_u16(6),
to_u16(8),
to_u16(10),
to_u16(12),
to_u16(14),
);
}
}
impl Debug for Ipv6Addr {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
Display::fmt(self, f)
}
}
/// The error returned from [`Subnet::new`] and [`SubnetEither::new`].
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SubnetError {
/// The network prefix is longer than the number of bits in the address (32
/// for IPv4/128 for IPv6).
PrefixTooLong,
/// The network address has some bits in the host part (past the network
/// prefix) set to one.
HostBitsSet,
}
/// An IP subnet.
///
/// `Subnet` is a combination of an IP network address and a prefix length.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Subnet<A> {
// invariant: only contains prefix bits
network: A,
prefix: u8,
}
// TODO(joshlf): Currently, we need a separate new_unchecked because trait
// bounds other than Sized are not supported in const fns. Once that
// restriction is lifted, we can make new a const fn.
impl<A> Subnet<A> {
/// Creates a new subnet without enforcing correctness.
///
/// Unlike `new`, `new_unchecked` does not validate that `prefix` is in the
/// proper range, and does not check that `network` has only the top
/// `prefix` bits set. It is up to the caller to guarantee that `prefix` is
/// in the proper range, and that none of the bits of `network` beyond the
/// prefix are set.
#[inline]
pub const unsafe fn new_unchecked(network: A, prefix: u8) -> Subnet<A> {
Subnet { network, prefix }
}
}
impl<A: IpAddress> Subnet<A> {
/// Creates a new subnet.
///
/// `new` creates a new subnet with the given network address and prefix
/// length. It returns `Err` if `prefix` is longer than the number of bits
/// in this type of IP address (32 for IPv4 and 128 for IPv6) or if any of
/// the host bits (beyond the first `prefix` bits) are set in `network`.
#[inline]
pub fn new(network: A, prefix: u8) -> Result<Subnet<A>, SubnetError> {
if prefix > A::BYTES * 8 {
return Err(SubnetError::PrefixTooLong);
}
// TODO(joshlf): Is there a more efficient way we can perform this
// check?
if network != network.mask(prefix) {
return Err(SubnetError::HostBitsSet);
}
Ok(Subnet { network, prefix })
}
/// Gets the network address component of this subnet.
///
/// `network` returns the network address component of this subnet. Any bits
/// beyond the prefix will be zero.
#[inline]
pub fn network(&self) -> A {
self.network
}
/// Gets the prefix length component of this subnet.
#[inline]
pub fn prefix(&self) -> u8 {
self.prefix
}
/// Tests whether an address is in this subnet.
///
/// Tests whether `address` is in this subnet by testing whether the prefix
/// bits match the prefix bits of the subnet's network address. This is
/// equivalent to `subnet.network() == address.mask(subnet.prefix())`.
#[inline]
pub fn contains(&self, address: &A) -> bool {
self.network == address.mask(self.prefix)
}
}
impl Subnet<Ipv4Addr> {
// TODO(joshlf): Introduce a `BroadcastAddr` witness type, and have
// `broadcast` return `BroadcastAddr<Ipv4Addr>`.
/// Gets the broadcast address in this IPv4 subnet.
#[inline]
pub fn broadcast(self) -> Ipv4Addr {
if self.prefix == 32 {
// shifting right by the size of the value is undefined
self.network
} else {
let mask = <u32>::max_value() >> self.prefix;
Ipv4Addr::new((u32::from_be_bytes(self.network.0) | mask).to_be_bytes())
}
}
}
impl<A: IpAddress> Display for Subnet<A> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}/{}", self.network, self.prefix)
}
}
impl<A: IpAddress> Debug for Subnet<A> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}/{}", self.network, self.prefix)
}
}
/// An IPv4 subnet or an IPv6 subnet.
///
/// `SubnetEither` is an enum of [`Subnet<Ipv4Addr>`] and `Subnet<Ipv6Addr>`.
///
/// [`Subnet<Ipv4Addr>`]: crate::ip::Subnet
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum SubnetEither {
V4(Subnet<Ipv4Addr>),
V6(Subnet<Ipv6Addr>),
}
impl SubnetEither {
/// Creates a new subnet.
///
/// `new` creates a new subnet with the given network address and prefix
/// length. It returns `Err` if `prefix` is longer than the number of bits
/// in this type of IP address (32 for IPv4 and 128 for IPv6) or if any of
/// the host bits (beyond the first `prefix` bits) are set in `network`.
#[inline]
pub fn new(network: IpAddr, prefix: u8) -> Result<SubnetEither, SubnetError> {
Ok(match network {
IpAddr::V4(network) => SubnetEither::V4(Subnet::new(network, prefix)?),
IpAddr::V6(network) => SubnetEither::V6(Subnet::new(network, prefix)?),
})
}
/// Gets the network and prefix for this `SubnetEither`.
#[inline]
pub fn into_net_prefix(self) -> (IpAddr, u8) {
match self {
SubnetEither::V4(v4) => (v4.network.into(), v4.prefix),
SubnetEither::V6(v6) => (v6.network.into(), v6.prefix),
}
}
}
impl<A: IpAddress> From<Subnet<A>> for SubnetEither {
fn from(subnet: Subnet<A>) -> SubnetEither {
A::into_subnet_either(subnet)
}
}
/// The error returned from [`AddrSubnet::new`] and [`AddrSubnetEither::new`].
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum AddrSubnetError {
/// The network prefix is longer than the number of bits in the address (32
/// for IPv4/128 for IPv6).
PrefixTooLong,
/// The address is not a unicast address in the given subnet (see
/// [`IpAddress::is_unicast_in_subnet`]).
NotUnicastInSubnet,
/// The address does not satisfy the requirements of the witness type.
InvalidWitness,
}
// TODO(joshlf): Is the unicast restriction always necessary, or will some users
// want the AddrSubnet functionality without that restriction?
/// An address and that address' subnet.
///
/// An `AddrSubnet` is a pair of an address and a subnet which maintains the
/// invariant that the address is guaranteed to be a unicast address in the
/// subnet. `S` is the type of address ([`Ipv4Addr`] or [`Ipv6Addr`]), and `A`
/// is the type of the address in the subnet, which is always a witness wrapper
/// around `S`. By default, it is `SpecifiedAddr<S>`.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub struct AddrSubnet<S: IpAddress, A: Witness<S> = SpecifiedAddr<S>> {
// TODO(joshlf): Would it be more performant to store these as just an
// address and subnet mask? It would make the object smaller and so cheaper
// to pass around, but it would make certain operations more expensive.
addr: A,
subnet: Subnet<S>,
}
impl<S: IpAddress, A: Witness<S>> AddrSubnet<S, A> {
/// Creates a new `AddrSubnet`.
///
/// `new` creates a new `AddrSubnet` with the given address and prefix
/// length. The network address of the subnet is taken to be the first
/// `prefix` bits of the address. It returns `Err` if `prefix` is longer
/// than the number of bits in this type of IP address (32 for IPv4 and 128
/// for IPv6), if `addr` is not a unicast address in the resulting subnet
/// (see [`IpAddress::is_unicast_in_subnet`]), or if `addr` does not satisfy
/// the requirements of the witness type `A`.
#[inline]
pub fn new(addr: S, prefix: u8) -> Result<AddrSubnet<S, A>, AddrSubnetError> {
if prefix > S::BYTES * 8 {
return Err(AddrSubnetError::PrefixTooLong);
}
let subnet = Subnet { network: addr.mask(prefix), prefix };
if !addr.is_unicast_in_subnet(&subnet) {
return Err(AddrSubnetError::NotUnicastInSubnet);
}
let addr = A::new(addr).ok_or(AddrSubnetError::InvalidWitness)?;
Ok(AddrSubnet { addr, subnet })
}
/// Gets the subnet.
#[inline]
pub fn subnet(&self) -> Subnet<S> {
self.subnet
}
/// Consumes the `AddrSubnet` and returns the address.
#[inline]
pub fn into_addr(self) -> A {
self.addr
}
/// Consumes the `AddrSubnet` and returns the subnet.
#[inline]
pub fn into_subnet(self) -> Subnet<S> {
self.subnet
}
/// Consumes the `AddrSubnet` and returns the address and subnet
/// individually.
#[inline]
pub fn into_addr_subnet(self) -> (A, Subnet<S>) {
(self.addr, self.subnet)
}
/// Converts the `AddrSubnet` into an `AddrSubnet` of a different witness
/// type.
#[inline]
pub fn into_witness<B: Witness<S>>(self) -> AddrSubnet<S, B>
where
A: Into<B>,
{
AddrSubnet { addr: self.addr.into(), subnet: self.subnet }
}
}
impl<S: IpAddress, A: Witness<S> + Copy> AddrSubnet<S, A> {
/// Gets the address.
#[inline]
pub fn addr(&self) -> A {
self.addr
}
}
/// A type which is a witness to some property about an `IpAddress`.
///
/// `IpAddrWitness` extends [`Witness`] of [`IpAddr`] by adding associated types
/// for the IPv4- and IPv6-specific versions of the same witness type. For
/// example, the following implementation is provided for
/// `SpecifiedAddr<IpAddr>`:
///
/// ```rust,ignore
/// impl IpAddrWitness for SpecifiedAddr<IpAddr> {
/// type V4 = SpecifiedAddr<Ipv4Addr>;
/// type V6 = SpecifiedAddr<Ipv6Addr>;
/// }
/// ```
pub trait IpAddrWitness: Witness<IpAddr> {
type V4: Witness<Ipv4Addr> + Into<Self>;
type V6: Witness<Ipv6Addr> + Into<Self>;
}
macro_rules! impl_ip_addr_witness {
($witness:ident) => {
impl IpAddrWitness for $witness<IpAddr> {
type V4 = $witness<Ipv4Addr>;
type V6 = $witness<Ipv6Addr>;
}
};
}
impl_ip_addr_witness!(SpecifiedAddr);
impl_ip_addr_witness!(MulticastAddr);
impl_ip_addr_witness!(LinkLocalAddr);
/// An address and that address' subnet, either IPv4 or IPv6.
///
/// `AddrSubnetEither` is an enum of [`AddrSubnet<Ipv4Addr>`] and
/// `AddrSubnet<Ipv6Addr>`.
///
/// [`AddrSubnet<Ipv4Addr>`]: crate::ip::AddrSubnet
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum AddrSubnetEither<A: IpAddrWitness = SpecifiedAddr<IpAddr>> {
V4(AddrSubnet<Ipv4Addr, A::V4>),
V6(AddrSubnet<Ipv6Addr, A::V6>),
}
impl<A: IpAddrWitness> AddrSubnetEither<A> {
/// Creates a new `AddrSubnetEither`.
///
/// `new` creates a new `AddrSubnetEither` with the given address and prefix
/// length. It returns `Err` under the same conditions as
/// [`AddrSubnet::new`].
#[inline]
pub fn new(addr: IpAddr, prefix: u8) -> Result<AddrSubnetEither<A>, AddrSubnetError> {
Ok(match addr {
IpAddr::V4(addr) => AddrSubnetEither::V4(AddrSubnet::new(addr, prefix)?),
IpAddr::V6(addr) => AddrSubnetEither::V6(AddrSubnet::new(addr, prefix)?),
})
}
/// Gets the contained IP address and prefix in this `AddrSubnetEither`.
#[inline]
pub fn into_addr_prefix(self) -> (A, u8) {
match self {
AddrSubnetEither::V4(v4) => (v4.addr.into(), v4.subnet.prefix),
AddrSubnetEither::V6(v6) => (v6.addr.into(), v6.subnet.prefix),
}
}
/// Gets the IP address and subnet in this `AddrSubnetEither`.
#[inline]
pub fn into_addr_subnet(self) -> (A, SubnetEither) {
match self {
AddrSubnetEither::V4(v4) => (v4.addr.into(), SubnetEither::V4(v4.subnet)),
AddrSubnetEither::V6(v6) => (v6.addr.into(), SubnetEither::V6(v6.subnet)),
}
}
}
/// Helper function to calculate common prefix length in an iterator of tuple of
/// bytes.
fn common_prefix_len(it: impl Iterator<Item = (u8, u8)>) -> u8 {
let mut len = 0;
for (a, b) in it {
let v = u8::leading_ones(!(a ^ b));
// Cast to u8 is always safe because leading ones can't return more than
// 8.
len += v as u8;
if v != 8 {
break;
}
}
len
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_loopback_unicast() {
// The loopback addresses are constructed as `SpecifiedAddr`s directly,
// bypassing the actual check against `is_specified`. Test that that's
// actually valid.
assert!(Ipv4::LOOPBACK_ADDRESS.0.is_specified());
assert!(Ipv6::LOOPBACK_ADDRESS.0.is_specified());
}
#[test]
fn test_specified() {
// For types that implement SpecifiedAddress,
// UnicastAddress::is_unicast, MulticastAddress::is_multicast, and
// LinkLocalAddress::is_linklocal all imply
// SpecifiedAddress::is_specified. Test that that's true for both IPv4
// and IPv6.
assert!(!Ipv6::UNSPECIFIED_ADDRESS.is_specified());
assert!(!Ipv4::UNSPECIFIED_ADDRESS.is_specified());
// Unicast
assert!(!Ipv6::UNSPECIFIED_ADDRESS.is_unicast());
let unicast = Ipv6Addr([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
assert!(unicast.is_unicast());
assert!(unicast.is_specified());
// Multicast
assert!(!Ipv4::UNSPECIFIED_ADDRESS.is_multicast());
assert!(!Ipv6::UNSPECIFIED_ADDRESS.is_multicast());
let multicast = Ipv4::MULTICAST_SUBNET.network;
assert!(multicast.is_multicast());
assert!(multicast.is_specified());
let multicast = Ipv6::MULTICAST_SUBNET.network;
assert!(multicast.is_multicast());
assert!(multicast.is_specified());
// Link-local
assert!(!Ipv4::UNSPECIFIED_ADDRESS.is_linklocal());
assert!(!Ipv6::UNSPECIFIED_ADDRESS.is_linklocal());
let link_local = Ipv4::LINK_LOCAL_UNICAST_SUBNET.network;
assert!(link_local.is_linklocal());
assert!(link_local.is_specified());
let link_local = Ipv4::LINK_LOCAL_MULTICAST_SUBNET.network;
assert!(link_local.is_linklocal());
assert!(link_local.is_specified());
let link_local = Ipv6::LINK_LOCAL_UNICAST_SUBNET.network;
assert!(link_local.is_linklocal());
assert!(link_local.is_specified());
let mut link_local = Ipv6::MULTICAST_SUBNET.network;
link_local.0[1] = 0x02;
assert!(link_local.is_linklocal());
assert!(link_local.is_specified());
assert!(Ipv6::LOOPBACK_ADDRESS.is_linklocal());
}
#[test]
fn test_linklocal() {
// IPv4
assert!(Ipv4::LINK_LOCAL_UNICAST_SUBNET.network.is_linklocal());
assert!(Ipv4::LINK_LOCAL_MULTICAST_SUBNET.network.is_linklocal());
// IPv6
assert!(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network.is_linklocal());
assert!(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network.is_unicast_linklocal());
let mut addr = Ipv6::MULTICAST_SUBNET.network;
for flags in 0..=0x0F {
// Set the scope to link-local and the flags to `flags`.
addr.0[1] = (flags << 4) | 0x02;
// Test that a link-local multicast address is always considered
// link-local regardless of which flags are set.
assert!(addr.is_linklocal());
assert!(!addr.is_unicast_linklocal());
}
// Test that a non-multicast address (outside of the link-local subnet)
// is never considered link-local even if the bits are set that, in a
// multicast address, would put it in the link-local scope.
let mut addr = Ipv6::LOOPBACK_ADDRESS.get();
// Explicitly set the scope to link-local.
addr.0[1] = 0x02;
assert!(!addr.is_linklocal());
}
#[test]
fn test_subnet_new() {
Subnet::new(Ipv4Addr::new([255, 255, 255, 255]), 32).unwrap();
// Prefix exceeds 32 bits
assert_eq!(
Subnet::new(Ipv4Addr::new([255, 255, 0, 0]), 33),
Err(SubnetError::PrefixTooLong)
);
// Network address has more than top 8 bits set
assert_eq!(Subnet::new(Ipv4Addr::new([255, 255, 0, 0]), 8), Err(SubnetError::HostBitsSet));
AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv4Addr::new([1, 2, 3, 4]), 32).unwrap();
// The unspecified address is not considered to be a unicast address in
// any subnet (use assert, not assert_eq, because AddrSubnet doesn't
// impl Debug)
assert!(
AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv4::UNSPECIFIED_ADDRESS, 16)
== Err(AddrSubnetError::NotUnicastInSubnet)
);
assert!(
AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv6::UNSPECIFIED_ADDRESS, 64)
== Err(AddrSubnetError::NotUnicastInSubnet)
);
// Prefix exceeds 32/128 bits
assert!(
AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv4Addr::new([1, 2, 3, 4]), 33)
== Err(AddrSubnetError::PrefixTooLong)
);
assert!(
AddrSubnet::<_, SpecifiedAddr<_>>::new(
Ipv6Addr::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
129,
) == Err(AddrSubnetError::PrefixTooLong)
);
// Global broadcast
assert!(
AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv4::GLOBAL_BROADCAST_ADDRESS.into_addr(), 16)
== Err(AddrSubnetError::NotUnicastInSubnet)
);
// Subnet broadcast
assert!(
AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv4Addr::new([192, 168, 255, 255]), 16)
== Err(AddrSubnetError::NotUnicastInSubnet)
);
// Multicast
assert!(
AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv4Addr::new([224, 0, 0, 1]), 16)
== Err(AddrSubnetError::NotUnicastInSubnet)
);
assert!(
AddrSubnet::<_, SpecifiedAddr<_>>::new(
Ipv6Addr::new([0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
64,
) == Err(AddrSubnetError::NotUnicastInSubnet)
);
// If we use the `LinkLocalAddr` witness type, then non link-local
// addresses are rejected. Note that this address was accepted above
// when `SpecifiedAddr` was used.
assert!(
AddrSubnet::<_, LinkLocalAddr<_>>::new(Ipv4Addr::new([1, 2, 3, 4]), 32)
== Err(AddrSubnetError::InvalidWitness)
);
}
#[test]
fn test_is_unicast_in_subnet() {
// Valid unicast in subnet
let subnet =
Subnet::new(Ipv4Addr::new([1, 2, 0, 0]), 16).expect("1.2.0.0/16 is a valid subnet");
assert!(Ipv4Addr::new([1, 2, 3, 4]).is_unicast_in_subnet(&subnet));
assert!(!Ipv4Addr::new([2, 2, 3, 4]).is_unicast_in_subnet(&subnet));
let subnet =
Subnet::new(Ipv6Addr::new([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), 64)
.expect("1::/64 is a valid subnet");
assert!(Ipv6Addr::new([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
.is_unicast_in_subnet(&subnet));
assert!(!Ipv6Addr::new([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
.is_unicast_in_subnet(&subnet));
// Unspecified address
assert!(!Ipv4::UNSPECIFIED_ADDRESS
.is_unicast_in_subnet(&Subnet::new(Ipv4::UNSPECIFIED_ADDRESS, 16).unwrap()));
assert!(!Ipv6::UNSPECIFIED_ADDRESS
.is_unicast_in_subnet(&Subnet::new(Ipv6::UNSPECIFIED_ADDRESS, 64).unwrap()));
// The "31- or 32-bit prefix" exception doesn't apply to the unspecified
// address (IPv4 only).
assert!(!Ipv4::UNSPECIFIED_ADDRESS
.is_unicast_in_subnet(&Subnet::new(Ipv4::UNSPECIFIED_ADDRESS, 31).unwrap()));
assert!(!Ipv4::UNSPECIFIED_ADDRESS
.is_unicast_in_subnet(&Subnet::new(Ipv4::UNSPECIFIED_ADDRESS, 32).unwrap()));
// All-zeroes host part (IPv4 only)
assert!(!Ipv4Addr::new([1, 2, 0, 0])
.is_unicast_in_subnet(&Subnet::new(Ipv4Addr::new([1, 2, 0, 0]), 16).unwrap()));
// Exception: 31- or 32-bit prefix (IPv4 only)
assert!(Ipv4Addr::new([1, 2, 3, 0])
.is_unicast_in_subnet(&Subnet::new(Ipv4Addr::new([1, 2, 3, 0]), 31).unwrap()));
assert!(Ipv4Addr::new([1, 2, 3, 0])
.is_unicast_in_subnet(&Subnet::new(Ipv4Addr::new([1, 2, 3, 0]), 32).unwrap()));
// Global broadcast (IPv4 only)
assert!(!Ipv4::GLOBAL_BROADCAST_ADDRESS
.into_addr()
.is_unicast_in_subnet(&Subnet::new(Ipv4Addr::new([128, 0, 0, 0]), 1).unwrap()));
// Subnet broadcast (IPv4 only)
assert!(!Ipv4Addr::new([1, 2, 255, 255])
.is_unicast_in_subnet(&Subnet::new(Ipv4Addr::new([1, 2, 0, 0]), 16).unwrap()));
// Exception: 31- or 32-bit prefix (IPv4 only)
assert!(Ipv4Addr::new([1, 2, 255, 255])
.is_unicast_in_subnet(&Subnet::new(Ipv4Addr::new([1, 2, 255, 254]), 31).unwrap()));
assert!(Ipv4Addr::new([1, 2, 255, 255])
.is_unicast_in_subnet(&Subnet::new(Ipv4Addr::new([1, 2, 255, 255]), 32).unwrap()));
// Multicast
assert!(!Ipv4Addr::new([224, 0, 0, 1]).is_unicast_in_subnet(&Ipv4::MULTICAST_SUBNET));
assert!(!Ipv6Addr::new([0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
.is_unicast_in_subnet(&Ipv6::MULTICAST_SUBNET));
// Class E (IPv4 only)
assert!(!Ipv4Addr::new([240, 0, 0, 1]).is_unicast_in_subnet(&Ipv4::CLASS_E_SUBNET));
}
macro_rules! add_mask_test {
($name:ident, $addr:ident, $from_ip:expr => {
$($mask:expr => $to_ip:expr),*
}) => {
#[test]
fn $name() {
let from = $addr::new($from_ip);
$(assert_eq!(from.mask($mask), $addr::new($to_ip), "(`{}`.mask({}))", from, $mask);)*
}
};
($name:ident, $addr:ident, $from_ip:expr => {
$($mask:expr => $to_ip:expr),*,
}) => {
add_mask_test!($name, $addr, $from_ip => { $($mask => $to_ip),* });
};
}
add_mask_test!(v4_full_mask, Ipv4Addr, [255, 254, 253, 252] => {
32 => [255, 254, 253, 252],
28 => [255, 254, 253, 240],
24 => [255, 254, 253, 0],
20 => [255, 254, 240, 0],
16 => [255, 254, 0, 0],
12 => [255, 240, 0, 0],
8 => [255, 0, 0, 0],
4 => [240, 0, 0, 0],
0 => [0, 0, 0, 0],
});
add_mask_test!(v6_full_mask, Ipv6Addr,
[0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2, 0xF1, 0xF0] => {
128 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2, 0xF1, 0xF0],
112 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2, 0x00, 0x00],
96 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0x00, 0x00, 0x00, 0x00],
80 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
64 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
48 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
32 => [0xFF, 0xFE, 0xFD, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
16 => [0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
8 => [0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
0 => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
}
);
#[test]
fn test_ipv6_solicited_node() {
let addr = Ipv6Addr::new([
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x52, 0xe5, 0x49, 0xff, 0xfe, 0xb5, 0x5a, 0xa0,
]);
let solicited =
Ipv6Addr::new([0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xff, 0xb5, 0x5a, 0xa0]);
assert_eq!(addr.to_solicited_node_address().get(), solicited);
}
#[test]
fn test_ipv6_address_types() {
assert!(!Ipv6Addr::new([0; 16]).is_specified());
assert!(Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]).is_loopback());
let link_local = Ipv6Addr::new([
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x52, 0xe5, 0x49, 0xff, 0xfe, 0xb5, 0x5a, 0xa0,
]);
assert!(link_local.is_linklocal());
assert!(link_local.is_valid_unicast());
assert!(link_local.to_solicited_node_address().is_multicast());
let global_unicast = Ipv6Addr::new([
0x00, 0x80, 0, 0, 0, 0, 0, 0, 0x52, 0xe5, 0x49, 0xff, 0xfe, 0xb5, 0x5a, 0xa0,
]);
assert!(global_unicast.is_valid_unicast());
assert!(global_unicast.to_solicited_node_address().is_multicast());
let multi =
Ipv6Addr::new([0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xff, 0xb5, 0x5a, 0xa0]);
assert!(multi.is_multicast());
assert!(!multi.is_valid_unicast());
}
#[test]
fn test_const_witness() {
// Test that all of the addresses that we initialize at compile time
// using `new_unchecked` constructors are valid for their witness types.
assert!(Ipv4::LOOPBACK_ADDRESS.0.is_specified());
assert!(Ipv6::LOOPBACK_ADDRESS.0.is_specified());
assert!(Ipv4::GLOBAL_BROADCAST_ADDRESS.0.is_specified());
assert!(Ipv4::ALL_ROUTERS_MULTICAST_ADDRESS.0.is_multicast());
assert!(Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS.0.is_multicast());
assert!(Ipv6::ALL_ROUTERS_LINK_LOCAL_MULTICAST_ADDRESS.0.is_multicast());
}
#[test]
fn test_ipv6_scope() {
use Ipv6ReservedScope::*;
use Ipv6Scope::*;
use Ipv6UnassignedScope::*;
// Test unicast scopes.
assert_eq!(Ipv6::SITE_LOCAL_UNICAST_SUBNET.network.scope(), SiteLocal);
assert_eq!(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network.scope(), LinkLocal);
assert_eq!(Ipv6::UNSPECIFIED_ADDRESS.scope(), Global);
// Test multicast scopes.
let assert_scope = |value, scope| {
let mut addr = Ipv6::MULTICAST_SUBNET.network;
// Set the "scop" field manually.
addr.0[1] |= value;
assert_eq!(addr.scope(), scope);
};
assert_scope(0, Reserved(Scope0));
assert_scope(1, InterfaceLocal);
assert_scope(2, LinkLocal);
assert_scope(3, Reserved(Scope3));
assert_scope(4, AdminLocal);
assert_scope(5, SiteLocal);
assert_scope(6, Unassigned(Scope6));
assert_scope(7, Unassigned(Scope7));
assert_scope(8, OrganizationLocal);
assert_scope(9, Unassigned(Scope9));
assert_scope(0xA, Unassigned(ScopeA));
assert_scope(0xB, Unassigned(ScopeB));
assert_scope(0xC, Unassigned(ScopeC));
assert_scope(0xD, Unassigned(ScopeD));
assert_scope(0xE, Global);
assert_scope(0xF, Reserved(ScopeF));
}
#[test]
fn test_ipv6_scope_ord() {
const ALL_SCOPES: [Ipv6Scope; 16] = [
Ipv6Scope::Reserved(Ipv6ReservedScope::Scope0),
Ipv6Scope::InterfaceLocal,
Ipv6Scope::LinkLocal,
Ipv6Scope::Reserved(Ipv6ReservedScope::Scope3),
Ipv6Scope::AdminLocal,
Ipv6Scope::SiteLocal,
Ipv6Scope::Unassigned(Ipv6UnassignedScope::Scope6),
Ipv6Scope::Unassigned(Ipv6UnassignedScope::Scope7),
Ipv6Scope::OrganizationLocal,
Ipv6Scope::Unassigned(Ipv6UnassignedScope::Scope9),
Ipv6Scope::Unassigned(Ipv6UnassignedScope::ScopeA),
Ipv6Scope::Unassigned(Ipv6UnassignedScope::ScopeB),
Ipv6Scope::Unassigned(Ipv6UnassignedScope::ScopeC),
Ipv6Scope::Unassigned(Ipv6UnassignedScope::ScopeD),
Ipv6Scope::Global,
Ipv6Scope::Reserved(Ipv6ReservedScope::ScopeF),
];
for (i, a) in ALL_SCOPES.iter().enumerate() {
for (j, b) in ALL_SCOPES.iter().enumerate() {
assert_eq!(a.cmp(b), i.cmp(&j));
}
}
}
#[test]
fn test_ipv6_from_ipv4() {
assert_eq!(
Ipv6Addr::from(Ipv4Addr::new([1, 2, 3, 4])),
Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 1, 2, 3, 4]),
);
assert_eq!(
Ipv6Addr::from(Ipv4Addr::new([192, 168, 0, 1])),
Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 192, 168, 0, 1]),
);
assert_eq!(
Ipv6Addr::from(Ipv4Addr::new([129, 144, 52, 38])),
Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 129, 144, 52, 38]),
);
}
#[test]
fn test_common_prefix_len_ipv6() {
let ip1 = Ipv6Addr::new([0xFF, 0xFF, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let ip2 = Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let ip3 = Ipv6Addr::new([0xFF, 0xFF, 0x80, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let ip4 = Ipv6Addr::new([0xFF, 0xFF, 0xC0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let compare_with_ip1 = |target, expect| {
assert_eq!(ip1.common_prefix_length(&target), expect, "{} <=> {}", ip1, target);
};
let () = compare_with_ip1(ip1, 128);
let () = compare_with_ip1(ip2, 0);
let () = compare_with_ip1(ip3, 24);
let () = compare_with_ip1(ip4, 17);
}
#[test]
fn test_common_prefix_len_ipv4() {
let ip1 = Ipv4Addr::new([0xFF, 0xFF, 0x80, 0]);
let ip2 = Ipv4Addr::new([0, 0, 0, 0]);
let ip3 = Ipv4Addr::new([0xFF, 0xFF, 0x80, 0xFF]);
let ip4 = Ipv4Addr::new([0xFF, 0xFF, 0xC0, 0x20]);
let compare_with_ip1 = |target, expect| {
assert_eq!(ip1.common_prefix_length(&target), expect, "{} <=> {}", ip1, target);
};
let () = compare_with_ip1(ip1, 32);
let () = compare_with_ip1(ip2, 0);
let () = compare_with_ip1(ip3, 24);
let () = compare_with_ip1(ip4, 17);
}
}