| // 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; |
| |
| #[cfg(std)] |
| use std::net; |
| |
| use byteorder::{ByteOrder, NetworkEndian}; |
| use zerocopy::{AsBytes, FromBytes, Unaligned}; |
| |
| use crate::{ |
| sealed, LinkLocalAddr, LinkLocalAddress, MulticastAddr, MulticastAddress, SpecifiedAddr, |
| SpecifiedAddress, 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, |
| } |
| |
| /// 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 addresses. |
| const LINK_LOCAL_SUBNET: Subnet<Self::Addr>; |
| |
| /// 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> = SpecifiedAddr(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 }; |
| /// Outlined in [RFC 3927 Section 2.1]. |
| /// |
| /// [RFC 3927 Section 2.1]: https://tools.ietf.org/html/rfc3927#section-2.1 |
| const LINK_LOCAL_SUBNET: Subnet<Ipv4Addr> = |
| Subnet { network: Ipv4Addr::new([169, 254, 0, 0]), prefix: 16 }; |
| 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> = |
| SpecifiedAddr(Ipv4Addr::new([255, 255, 255, 255])); |
| |
| /// The Class E subnet. |
| /// |
| /// The Class E subnet is meant for experimental purposes. |
| pub const CLASS_E_SUBNET: Subnet<Ipv4Addr> = |
| Subnet { network: Ipv4Addr::new([240, 0, 0, 0]), prefix: 4 }; |
| } |
| |
| /// 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> = |
| SpecifiedAddr(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, |
| }; |
| /// Defined in [RFC 4291 Section 2.5.6]. |
| /// |
| /// [RFC 4291 Section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 |
| const LINK_LOCAL_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, |
| }; |
| 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_ADDRESS: Ipv6Addr = |
| 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_ADDRESS: Ipv6Addr = |
| Ipv6Addr::new([0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]); |
| } |
| |
| /// 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 |
| + 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 in the context of the given subnet? |
| /// |
| /// `is_unicast_in_subnet` returns `true` if this address is none of: |
| /// - a multicast address |
| /// - the IPv4 global broadcast address |
| /// - the IPv4 subnet-specific broadcast address for the given `subnet` |
| /// - the unspecified address |
| /// |
| /// Note one exception to these rules: If `subnet` is an IPv4 /32, then the |
| /// single unicast address in the subnet is also technically the subnet |
| /// broadcast address. In this case, the "no subnet-specific broadcast" rule |
| /// doesn't apply. |
| 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`]. |
| fn with<O, F4: Fn(&Ipv4Addr) -> O, F6: Fn(&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: Fn(&Ipv4Addr) -> O>(&self, f: F, default: O) -> O; |
| |
| /// Invokes a function on this address if it is an [`Ipv6Addr`] or return |
| /// `default` if it is an [`Ipv4Addr`]. |
| fn with_v6<O, F: Fn(&Ipv6Addr) -> O>(&self, f: F, default: O) -> O; |
| |
| // 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; |
| } |
| |
| 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<A: IpAddress> LinkLocalAddress for A { |
| /// Is this address in the link-local subnet? |
| /// |
| /// `is_linklocal` returns true if `self` is in |
| /// [`A::Version::LINK_LOCAL_SUBNET`]. |
| /// |
| /// [`A::Version::LINK_LOCAL_SUBNET`]: crate::ip::Ip::LINK_LOCAL_SUBNET |
| #[inline] |
| fn is_linklocal(&self) -> bool { |
| <A as IpAddress>::Version::LINK_LOCAL_SUBNET.contains(self) |
| } |
| } |
| |
| impl LinkLocalAddress for IpAddr { |
| /// Is this address in the link-local subnet? |
| /// |
| /// `is_linklocal` returns true if `self` is in [`Ip::LINK_LOCAL_SUBNET`] |
| /// for the IP version of this address. |
| #[inline] |
| fn is_linklocal(&self) -> bool { |
| map_ip_addr!(self, is_linklocal) |
| } |
| } |
| |
| /// 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); |
| |
| /// 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) |
| } |
| } |
| |
| 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); |
| let masked = NetworkEndian::read_u32(&self.0) & mask; |
| let mut ret = Ipv4Addr::default(); |
| NetworkEndian::write_u32(&mut ret.0, masked); |
| ret |
| } |
| } |
| |
| #[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() |
| && (subnet.prefix() == 32 || *self != subnet.broadcast()) |
| && self.is_specified() |
| } |
| |
| #[inline] |
| fn with<O, F4: Fn(&Ipv4Addr) -> O, F6: Fn(&Ipv6Addr) -> O>(&self, v4: F4, _v6: F6) -> O { |
| v4(self) |
| } |
| |
| #[inline] |
| fn with_v4<O, F: Fn(&Ipv4Addr) -> O>(&self, f: F, _default: O) -> O { |
| f(self) |
| } |
| |
| #[inline] |
| fn with_v6<O, F: Fn(&Ipv6Addr) -> O>(&self, _f: F, default: O) -> O { |
| default |
| } |
| |
| 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()) |
| } |
| } |
| |
| impl sealed::Sealed for Ipv6Addr {} |
| |
| 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); |
| let masked = NetworkEndian::read_u128(&self.0) & mask; |
| let mut ret = Ipv6Addr::default(); |
| NetworkEndian::write_u128(&mut ret.0, masked); |
| ret |
| } |
| } |
| |
| #[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() |
| } |
| |
| #[inline] |
| fn with<O, F4: Fn(&Ipv4Addr) -> O, F6: Fn(&Ipv6Addr) -> O>(&self, _v4: F4, v6: F6) -> O { |
| v6(self) |
| } |
| |
| #[inline] |
| fn with_v4<O, F: Fn(&Ipv4Addr) -> O>(&self, _f: F, default: O) -> O { |
| default |
| } |
| |
| #[inline] |
| fn with_v6<O, F: Fn(&Ipv6Addr) -> O>(&self, f: F, _default: O) -> O { |
| f(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. |
| |
| let to_u16 = |idx| NetworkEndian::read_u16(&self.0[idx..idx + 2]); |
| #[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) |
| } |
| } |
| |
| /// 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 `None` 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 lower bits (beyond the first `prefix` bits) are set in `network`. |
| #[inline] |
| pub fn new(network: A, prefix: u8) -> Option<Subnet<A>> { |
| // TODO(joshlf): Is there a more efficient way we can perform this |
| // check? |
| if prefix > A::BYTES * 8 || network != network.mask(prefix) { |
| return None; |
| } |
| Some(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; |
| let masked = NetworkEndian::read_u32(&self.network.0) | mask; |
| let mut ret = Ipv4Addr::default(); |
| NetworkEndian::write_u32(&mut ret.0, masked); |
| ret |
| } |
| } |
| } |
| |
| 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 `None` 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 lower bits (beyond the first `prefix` bits) are set in `network`. |
| #[inline] |
| pub fn new(network: IpAddr, prefix: u8) -> Option<SubnetEither> { |
| Some(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) |
| } |
| } |
| |
| // 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 `None` 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) -> Option<AddrSubnet<S, A>> { |
| if prefix > S::BYTES * 8 { |
| return None; |
| } |
| let subnet = Subnet { network: addr.mask(prefix), prefix }; |
| if !addr.is_unicast_in_subnet(&subnet) { |
| return None; |
| } |
| let addr = A::new(addr)?; |
| Some(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) |
| } |
| } |
| |
| 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`] 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 `None` under the same conditions as |
| /// [`AddrSubnet::new`]. |
| #[inline] |
| pub fn new(addr: IpAddr, prefix: u8) -> Option<AddrSubnetEither<A>> { |
| Some(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)), |
| } |
| } |
| } |
| |
| #[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_SUBNET.network; |
| assert!(link_local.is_linklocal()); |
| assert!(link_local.is_specified()); |
| let link_local = Ipv6::LINK_LOCAL_SUBNET.network; |
| assert!(link_local.is_linklocal()); |
| assert!(link_local.is_specified()); |
| } |
| |
| #[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), None); |
| // Network address has more than top 8 bits set |
| assert_eq!(Subnet::new(Ipv4Addr::new([255, 255, 0, 0]), 8), None); |
| |
| 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) == None); |
| assert!(AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv6::UNSPECIFIED_ADDRESS, 64) == None); |
| // Prefix exceeds 32/128 bits |
| assert!(AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv4Addr::new([1, 2, 3, 4]), 33) == None); |
| assert!( |
| AddrSubnet::<_, SpecifiedAddr<_>>::new( |
| Ipv6Addr::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]), |
| 129 |
| ) == None |
| ); |
| // Global broadcast |
| assert!( |
| AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv4::GLOBAL_BROADCAST_ADDRESS.into_addr(), 16) |
| == None |
| ); |
| // Subnet broadcast |
| assert!( |
| AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv4Addr::new([192, 168, 255, 255]), 16) == None |
| ); |
| // Multicast |
| assert!(AddrSubnet::<_, SpecifiedAddr<_>>::new(Ipv4Addr::new([224, 0, 0, 1]), 16) == None); |
| assert!( |
| AddrSubnet::<_, SpecifiedAddr<_>>::new( |
| Ipv6Addr::new([0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), |
| 64 |
| ) == None |
| ); |
| |
| // 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) == None); |
| } |
| |
| #[test] |
| fn test_is_unicast_in_subnet() { |
| // Valid unicast in subnet |
| assert!(Ipv4Addr::new([1, 2, 3, 4]) |
| .is_unicast_in_subnet(&Subnet::new(Ipv4Addr::new([1, 2, 0, 0]), 16).unwrap())); |
| assert!(Ipv6Addr::new([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) |
| .is_unicast_in_subnet( |
| &Subnet::new(Ipv6Addr::new([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), 64) |
| .unwrap() |
| )); |
| // 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())); |
| // Global broadcast |
| assert!(!Ipv4::GLOBAL_BROADCAST_ADDRESS |
| .into_addr() |
| .is_unicast_in_subnet(&Subnet::new(Ipv4Addr::new([255, 255, 0, 0]), 16).unwrap())); |
| // Subnet broadcast |
| assert!(!Ipv4Addr::new([1, 2, 255, 255]) |
| .is_unicast_in_subnet(&Subnet::new(Ipv4Addr::new([1, 2, 0, 0]), 16).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)); |
| } |
| |
| 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()); |
| } |
| } |