use std::cmp::Ordering;
use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr};
use std::num::ParseIntError;
use std::str::FromStr;
use std::{error, fmt};
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
struct Bits(u128);
impl Bits {
pub fn new(bits: u128) -> Self {
Bits(bits)
}
pub fn from_v4(addr: Ipv4Addr) -> Self {
Self::new(u128::from(u32::from(addr)) << 96)
}
pub fn from_v6(addr: Ipv6Addr) -> Self {
Self::new(u128::from(addr))
}
pub fn into_int(self) -> u128 {
self.0
}
pub fn into_v4(self) -> Ipv4Addr {
((self.0 >> 96) as u32).into()
}
pub fn into_v6(self) -> Ipv6Addr {
self.0.into()
}
fn is_host_zero(self, len: u8) -> bool {
self.0.trailing_zeros() >= 128u32.saturating_sub(len.into())
}
fn clear_host(self, len: u8) -> Self {
if len == 0 {
Bits(0)
} else {
Bits(self.0 & (u128::MAX << (128u8.saturating_sub(len))))
}
}
fn into_max(self, prefix_len: u8) -> Self {
if prefix_len >= 128 {
self
} else {
Self(self.0 | (u128::MAX >> prefix_len as usize))
}
}
}
impl From<u128> for Bits {
fn from(addr: u128) -> Self {
Self::new(addr)
}
}
impl From<Ipv4Addr> for Bits {
fn from(addr: Ipv4Addr) -> Self {
Self::from_v4(addr)
}
}
impl From<Ipv6Addr> for Bits {
fn from(addr: Ipv6Addr) -> Self {
Self::from_v6(addr)
}
}
impl From<IpAddr> for Bits {
fn from(addr: IpAddr) -> Self {
match addr {
IpAddr::V4(addr) => Self::from(addr),
IpAddr::V6(addr) => Self::from(addr),
}
}
}
impl From<Bits> for u128 {
fn from(addr: Bits) -> u128 {
addr.into_int()
}
}
impl From<Bits> for Ipv4Addr {
fn from(addr: Bits) -> Ipv4Addr {
addr.into_v4()
}
}
impl From<Bits> for Ipv6Addr {
fn from(addr: Bits) -> Ipv6Addr {
addr.into_v6()
}
}
impl fmt::Debug for Bits {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Bits")
.field(&format_args!("{}", self.into_v6()))
.finish()
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct FamilyAndLen(u8);
impl FamilyAndLen {
pub fn new_v4(len: u8) -> Result<Self, PrefixError> {
if len > 32 {
Err(PrefixError::LenOverflow)
} else {
Ok(Self(len))
}
}
pub fn new_v6(len: u8) -> Result<Self, PrefixError> {
match len.cmp(&128) {
Ordering::Greater => Err(PrefixError::LenOverflow),
Ordering::Equal => Ok(Self(0x40)),
Ordering::Less => Ok(Self(len ^ 0xFF)),
}
}
pub fn is_v4(self) -> bool {
self.0 & 0xc0 == 0
}
pub fn is_v6(self) -> bool {
self.0 & 0xc0 != 0
}
#[allow(clippy::len_without_is_empty)]
pub fn len(self) -> u8 {
match self.0 & 0xc0 {
0x00 => self.0,
0x40 => 128,
_ => self.0 ^ 0xFF,
}
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for FamilyAndLen {
fn arbitrary(
u: &mut arbitrary::Unstructured<'a>,
) -> arbitrary::Result<Self> {
if bool::arbitrary(u)? {
Ok(Self(u8::arbitrary(u)? % 33))
} else {
match u8::arbitrary(u)? % 129 {
128 => Ok(Self(0x40)),
val => Ok(Self(val ^ 0xFF)),
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Prefix {
family_and_len: FamilyAndLen,
bits: Bits,
}
impl Prefix {
pub fn new(addr: IpAddr, len: u8) -> Result<Self, PrefixError> {
match addr {
IpAddr::V4(addr) => Self::new_v4(addr, len),
IpAddr::V6(addr) => Self::new_v6(addr, len),
}
}
pub fn new_v4(addr: Ipv4Addr, len: u8) -> Result<Self, PrefixError> {
let family_and_len = FamilyAndLen::new_v4(len)?;
let bits = Bits::from_v4(addr);
if !bits.is_host_zero(len) {
return Err(PrefixError::NonZeroHost);
}
Ok(Prefix {
family_and_len,
bits,
})
}
pub fn new_v6(addr: Ipv6Addr, len: u8) -> Result<Self, PrefixError> {
let family_and_len = FamilyAndLen::new_v6(len)?;
let bits = Bits::from_v6(addr);
if !bits.is_host_zero(len) {
return Err(PrefixError::NonZeroHost);
}
Ok(Prefix {
family_and_len,
bits,
})
}
pub fn new_relaxed(addr: IpAddr, len: u8) -> Result<Self, PrefixError> {
match addr {
IpAddr::V4(addr) => Self::new_v4_relaxed(addr, len),
IpAddr::V6(addr) => Self::new_v6_relaxed(addr, len),
}
}
pub fn new_v4_relaxed(
addr: Ipv4Addr,
len: u8,
) -> Result<Self, PrefixError> {
let family_and_len = FamilyAndLen::new_v4(len)?;
Ok(Prefix {
bits: Bits::from_v4(addr).clear_host(len),
family_and_len,
})
}
pub fn new_v6_relaxed(
addr: Ipv6Addr,
len: u8,
) -> Result<Self, PrefixError> {
let family_and_len = FamilyAndLen::new_v6(len)?;
Ok(Prefix {
bits: Bits::from_v6(addr).clear_host(len),
family_and_len,
})
}
pub fn is_v4(self) -> bool {
self.family_and_len.is_v4()
}
pub fn is_v6(self) -> bool {
self.family_and_len.is_v6()
}
pub fn addr(self) -> IpAddr {
if self.is_v4() {
self.bits.into_v4().into()
} else {
self.bits.into_v6().into()
}
}
#[allow(clippy::len_without_is_empty)]
pub fn len(self) -> u8 {
self.family_and_len.len()
}
pub fn addr_and_len(self) -> (IpAddr, u8) {
(self.addr(), self.len())
}
pub fn min_addr(self) -> IpAddr {
self.addr()
}
pub fn max_addr(self) -> IpAddr {
let bits = self.bits.into_max(self.len());
if self.is_v4() {
bits.into_v4().into()
} else {
bits.into_v6().into()
}
}
pub fn covers(self, other: Self) -> bool {
if self.is_v4() != other.is_v4() {
return false;
}
if self.len() > other.len() {
return false;
}
if self.is_v4() {
if self.len() == 32 && other.len() == 32 {
return self == other;
}
} else if self.len() == 128 && other.len() == 128 {
return self == other;
}
self.bits.into_int()
== other.bits.into_int() & !(u128::MAX >> self.len())
}
pub fn contains(self, addr: IpAddr) -> bool {
self.min_addr() <= addr && addr <= self.max_addr()
}
}
impl PartialOrd for Prefix {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Prefix {
fn cmp(&self, other: &Self) -> Ordering {
match (self.is_v4(), other.is_v4()) {
(true, false) => Ordering::Less, (false, true) => Ordering::Greater, (_, _) => {
if self.len() == other.len() {
self.bits.0.cmp(&other.bits.0)
} else {
let minlen = std::cmp::min(self.len(), other.len());
let mask = !(u128::MAX >> minlen);
if self.bits.0 & mask == other.bits.0 & mask {
other.len().cmp(&self.len())
} else {
self.bits.0.cmp(&other.bits.0)
}
}
}
}
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Prefix {
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = Prefix;
fn expecting(
&self,
formatter: &mut fmt::Formatter,
) -> fmt::Result {
write!(formatter, "a string with an IPv4 or IPv6 prefix")
}
fn visit_str<E: serde::de::Error>(
self,
v: &str,
) -> Result<Self::Value, E> {
Prefix::from_str(v).map_err(E::custom)
}
}
deserializer.deserialize_str(Visitor)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Prefix {
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
impl FromStr for Prefix {
type Err = ParsePrefixError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Err(ParsePrefixError::Empty);
}
let slash = s.find('/').ok_or(ParsePrefixError::MissingLen)?;
let addr = IpAddr::from_str(&s[..slash])
.map_err(ParsePrefixError::InvalidAddr)?;
let len = u8::from_str(&s[slash + 1..])
.map_err(ParsePrefixError::InvalidLen)?;
Prefix::new(addr, len).map_err(ParsePrefixError::InvalidPrefix)
}
}
impl fmt::Display for Prefix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}/{}", self.addr(), self.len())
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for Prefix {
fn arbitrary(
u: &mut arbitrary::Unstructured<'a>,
) -> arbitrary::Result<Self> {
let fal = FamilyAndLen::arbitrary(u)?;
let mut bits = Bits::arbitrary(u)?;
if fal.is_v4() {
bits.0 <<= 96;
}
Ok(Self {
family_and_len: fal,
bits: bits.clear_host(fal.len()),
})
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct MaxLenPrefix {
prefix: Prefix,
max_len: Option<u8>,
}
impl MaxLenPrefix {
pub fn new(
prefix: Prefix,
max_len: Option<u8>,
) -> Result<Self, MaxLenError> {
if let Some(max_len) = max_len {
if (prefix.is_v4() && max_len > 32) || max_len > 128 {
return Err(MaxLenError::Overflow);
}
if prefix.len() > max_len {
return Err(MaxLenError::Underflow);
}
}
Ok(MaxLenPrefix { prefix, max_len })
}
pub fn saturating_new(prefix: Prefix, max_len: Option<u8>) -> Self {
let max_len = max_len.map(|max_len| {
if prefix.len() > max_len {
prefix.len()
} else if prefix.is_v4() && max_len > 32 {
32
} else if max_len > 128 {
128
} else {
max_len
}
});
MaxLenPrefix { prefix, max_len }
}
pub fn prefix(self) -> Prefix {
self.prefix
}
pub fn addr(self) -> IpAddr {
self.prefix.addr()
}
pub fn prefix_len(self) -> u8 {
self.prefix.len()
}
pub fn max_len(self) -> Option<u8> {
self.max_len
}
pub fn resolved_max_len(self) -> u8 {
self.max_len.unwrap_or_else(|| self.prefix.len())
}
}
impl PartialOrd for MaxLenPrefix {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for MaxLenPrefix {
fn cmp(&self, other: &Self) -> Ordering {
match self.prefix.cmp(&other.prefix) {
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
Ordering::Equal => match (self.max_len, other.max_len) {
(None, None) => Ordering::Equal,
(Some(_), None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater,
(Some(n), Some(m)) => m.cmp(&n),
},
}
}
}
impl From<Prefix> for MaxLenPrefix {
fn from(prefix: Prefix) -> Self {
MaxLenPrefix {
prefix,
max_len: None,
}
}
}
impl FromStr for MaxLenPrefix {
type Err = ParseMaxLenPrefixError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (prefix, max_len) =
match s.find('-') {
Some(dash) => (
Prefix::from_str(&s[..dash])
.map_err(ParseMaxLenPrefixError::InvalidPrefix)?,
Some(u8::from_str(&s[dash + 1..]).map_err(
ParseMaxLenPrefixError::InvalidMaxLenFormat,
)?),
),
None => {
let prefix = Prefix::from_str(s)
.map_err(ParseMaxLenPrefixError::InvalidPrefix)?;
(prefix, None)
}
};
Self::new(prefix, max_len)
.map_err(ParseMaxLenPrefixError::InvalidMaxLenValue)
}
}
impl fmt::Display for MaxLenPrefix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.prefix())?;
if let Some(max_len) = self.max_len {
write!(f, "-{}", max_len)?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum PrefixError {
LenOverflow,
NonZeroHost,
}
impl PrefixError {
pub fn static_description(self) -> &'static str {
match self {
PrefixError::LenOverflow => "prefix length too large",
PrefixError::NonZeroHost => "non-zero host portion",
}
}
}
impl From<PrefixError> for &'static str {
fn from(err: PrefixError) -> Self {
err.static_description()
}
}
impl fmt::Display for PrefixError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.static_description())
}
}
impl error::Error for PrefixError {}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ParsePrefixError {
Empty,
MissingLen,
InvalidAddr(AddrParseError),
InvalidLen(ParseIntError),
InvalidPrefix(PrefixError),
}
impl fmt::Display for ParsePrefixError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParsePrefixError::Empty => f.write_str("empty string"),
ParsePrefixError::MissingLen => {
f.write_str("missing length portion")
}
ParsePrefixError::InvalidAddr(err) => {
write!(f, "invalid address: {}", err)
}
ParsePrefixError::InvalidLen(err) => {
write!(f, "invalid length: {}", err)
}
ParsePrefixError::InvalidPrefix(err) => err.fmt(f),
}
}
}
impl error::Error for ParsePrefixError {}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum MaxLenError {
Overflow,
Underflow,
}
impl MaxLenError {
pub fn static_description(self) -> &'static str {
match self {
MaxLenError::Overflow => "max-length too large",
MaxLenError::Underflow => "max-length smaller than prefix length",
}
}
}
impl From<MaxLenError> for &'static str {
fn from(err: MaxLenError) -> Self {
err.static_description()
}
}
impl fmt::Display for MaxLenError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MaxLenError::Overflow => f.write_str("max-length too large"),
MaxLenError::Underflow => {
f.write_str("max-length smaller than prefix length")
}
}
}
}
impl error::Error for MaxLenError {}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ParseMaxLenPrefixError {
InvalidPrefix(ParsePrefixError),
InvalidMaxLenFormat(ParseIntError),
InvalidMaxLenValue(MaxLenError),
}
impl fmt::Display for ParseMaxLenPrefixError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParseMaxLenPrefixError::InvalidPrefix(err) => err.fmt(f),
ParseMaxLenPrefixError::InvalidMaxLenFormat(err) => {
write!(f, "invalid max length: {}", err)
}
ParseMaxLenPrefixError::InvalidMaxLenValue(err) => err.fmt(f),
}
}
}
impl error::Error for ParseMaxLenPrefixError {}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn good_family_and_len() {
for i in 0..=32 {
let fal = FamilyAndLen::new_v4(i).unwrap();
assert!(fal.is_v4());
assert!(!fal.is_v6());
assert_eq!(fal.len(), i)
}
for i in 0..=128 {
let fal = FamilyAndLen::new_v6(i).unwrap();
assert!(!fal.is_v4());
assert!(fal.is_v6());
assert_eq!(fal.len(), i)
}
}
#[test]
fn bad_family_and_len() {
for i in 33..=255 {
assert_eq!(
FamilyAndLen::new_v4(i),
Err(PrefixError::LenOverflow)
);
}
for i in 129..=255 {
assert_eq!(
FamilyAndLen::new_v6(i),
Err(PrefixError::LenOverflow)
);
}
}
#[test]
fn from_conversions() {
assert_eq!(u128::from(Bits::from(0xabcdefu128)), 0xabcdefu128);
assert_eq!(
Ipv6Addr::from(Bits::from(0xabcdefu128)),
Ipv6Addr::from(0xabcdefu128)
);
assert_eq!(
Ipv4Addr::from(Bits::from(
(192u128 << 24 | (168 << 16) | (10 << 8) | 20) << 96
)),
Ipv4Addr::new(192, 168, 10, 20),
);
let ip4 = Ipv4Addr::new(192, 168, 10, 20);
assert_eq!(Ipv4Addr::from(Bits::from(ip4)), ip4);
}
#[test]
fn prefix_from_str() {
assert_eq!(
Prefix::from_str("127.0.0.0/12").unwrap().addr_and_len(),
(IpAddr::from_str("127.0.0.0").unwrap(), 12)
);
assert_eq!(
Prefix::from_str("2001:db8:10:20::/64")
.unwrap()
.addr_and_len(),
(IpAddr::from_str("2001:db8:10:20::").unwrap(), 64)
);
assert_eq!(
Prefix::from_str("0.0.0.0/0").unwrap().addr_and_len(),
(IpAddr::from_str("0.0.0.0").unwrap(), 0)
);
assert_eq!(
Prefix::from_str("::/0").unwrap().addr_and_len(),
(IpAddr::from_str("::").unwrap(), 0)
);
assert_eq!(
Prefix::from_str("127.0.0.0"),
Err(ParsePrefixError::MissingLen)
);
assert_eq!(
Prefix::from_str("2001:db8::"),
Err(ParsePrefixError::MissingLen)
);
assert!(matches!(
Prefix::from_str("127.0.0.0/"),
Err(ParsePrefixError::InvalidLen(_))
));
assert!(matches!(
Prefix::from_str("2001:db8::/"),
Err(ParsePrefixError::InvalidLen(_))
));
assert!(matches!(Prefix::from_str(""), Err(ParsePrefixError::Empty)));
}
#[test]
fn ordering() {
assert!(
Prefix::from_str("192.168.10.0/24").unwrap()
< Prefix::from_str("192.168.20.0/24").unwrap()
);
assert!(
Prefix::from_str("192.168.10.0/24").unwrap()
< Prefix::from_str("192.168.20.0/32").unwrap()
);
assert!(
Prefix::from_str("192.168.10.0/24").unwrap()
> Prefix::from_str("192.168.9.0/25").unwrap()
);
assert!(
Prefix::from_str("192.168.10.0/24").unwrap()
< Prefix::from_str("192.0.0.0/8").unwrap()
);
assert!(
Prefix::from_str("127.0.0.1/32").unwrap()
< Prefix::from_str("::/128").unwrap()
);
assert!(
Prefix::from_str("127.0.0.1/32").unwrap()
< Prefix::from_str("::/0").unwrap()
);
assert!(
Prefix::from_str("2001:db8:10:20::/64").unwrap()
< Prefix::from_str("2001:db8::/32").unwrap()
);
assert!(
Prefix::from_str("2001:db8:10:20::/64").unwrap()
< Prefix::from_str("2001:db8:10:30::/64").unwrap()
);
assert!(
Prefix::from_str("127.0.0.1/32").unwrap()
< Prefix::from_str("2001:ff00::/24").unwrap()
);
assert!(
Prefix::from_str("2001:ff00::/24").unwrap()
> Prefix::from_str("127.0.0.1/32").unwrap()
);
assert!(matches!(
Prefix::from_str("0.0.0.0/0")
.unwrap()
.cmp(&Prefix::from_str("0.0.0.0/0").unwrap()),
Ordering::Equal
));
assert!(matches!(
Prefix::from_str("192.168.1.2/32")
.unwrap()
.cmp(&Prefix::from_str("192.168.1.2/32").unwrap()),
Ordering::Equal
));
assert!(matches!(
Prefix::from_str("::/0")
.unwrap()
.cmp(&Prefix::from_str("::/0").unwrap()),
Ordering::Equal
));
assert!(matches!(
Prefix::from_str("2001:db8:e000::/40")
.unwrap()
.cmp(&Prefix::from_str("2001:db8:e000::/40").unwrap()),
Ordering::Equal
));
assert!(matches!(
Prefix::from_str("2001:db8::1/128")
.unwrap()
.cmp(&Prefix::from_str("2001:db8::1/128").unwrap()),
Ordering::Equal
));
assert!(
Prefix::from_str("0.0.0.0/0").unwrap()
< Prefix::from_str("::/0").unwrap()
);
assert!(
Prefix::from_str("::/0").unwrap()
> Prefix::from_str("0.0.0.0/0").unwrap()
);
}
#[test]
fn prefixes() {
assert!(Prefix::new_v4(Ipv4Addr::from(0xffff0000), 16).is_ok());
assert!(Prefix::new_v6(Ipv6Addr::from(0x2001_0db8_1234 << 80), 48)
.is_ok());
assert!(matches!(
Prefix::new_v4(Ipv4Addr::from(0xffffcafe), 16),
Err(PrefixError::NonZeroHost)
));
assert!(matches!(
Prefix::new_v6(Ipv6Addr::from(0x2001_0db8_1234 << 80), 32),
Err(PrefixError::NonZeroHost)
));
}
#[test]
fn ordering_maxlenprefixes() {
assert!(matches!(
MaxLenPrefix::from_str("192.168.0.0/16-16")
.unwrap()
.cmp(&MaxLenPrefix::from_str("192.168.0.0/16-16").unwrap()),
Ordering::Equal
));
assert!(matches!(
MaxLenPrefix::from_str("192.168.0.0/16")
.unwrap()
.cmp(&MaxLenPrefix::from_str("192.168.0.0/16").unwrap()),
Ordering::Equal
));
assert!(
MaxLenPrefix::from_str("192.168.0.0/16-24").unwrap()
< MaxLenPrefix::from_str("192.168.0.0/16").unwrap()
);
assert!(
MaxLenPrefix::from_str("192.168.0.0/16-16").unwrap()
< MaxLenPrefix::from_str("192.168.0.0/16").unwrap()
);
assert!(
MaxLenPrefix::from_str("192.168.0.0/16").unwrap()
> MaxLenPrefix::from_str("192.168.0.0/16-16").unwrap()
);
assert!(
MaxLenPrefix::from_str("10.9.0.0/16").unwrap()
< MaxLenPrefix::from_str("10.10.0.0/16-24").unwrap()
);
assert!(
MaxLenPrefix::from_str("10.10.0.0/16").unwrap()
> MaxLenPrefix::from_str("10.9.0.0/16-24").unwrap()
);
}
#[test]
fn relaxed_prefixes() {
assert_eq!(
Prefix::new_relaxed(
"192.168.10.20".parse::<IpAddr>().unwrap(),
16
)
.unwrap(),
Prefix::new_v4_relaxed(
"192.168.10.20".parse::<Ipv4Addr>().unwrap(),
16
)
.unwrap()
);
assert_eq!(
Prefix::new_relaxed(
"192.168.10.20".parse::<IpAddr>().unwrap(),
16
)
.unwrap(),
Prefix::new_relaxed("192.168.0.0".parse::<IpAddr>().unwrap(), 16)
.unwrap(),
);
assert_eq!(
Prefix::new_relaxed(
"2001:db8::10:20:30:40".parse::<IpAddr>().unwrap(),
64
)
.unwrap(),
Prefix::new_v6_relaxed(
"2001:db8::10:20:30:40".parse::<Ipv6Addr>().unwrap(),
64
)
.unwrap()
);
assert_eq!(
Prefix::new_relaxed(
"2001:db8::10:20:30:40".parse::<IpAddr>().unwrap(),
64
)
.unwrap(),
Prefix::new_relaxed("2001:db8::".parse::<IpAddr>().unwrap(), 64)
.unwrap()
);
}
#[test]
fn min_max_addr() {
assert_eq!(
Prefix::from_str("192.168.0.0/16").unwrap().min_addr(),
IpAddr::from_str("192.168.0.0").unwrap()
);
assert_eq!(
Prefix::from_str("192.168.0.0/16").unwrap().max_addr(),
IpAddr::from_str("192.168.255.255").unwrap()
);
assert_eq!(
Prefix::from_str("192.168.1.1/32").unwrap().min_addr(),
IpAddr::from_str("192.168.1.1").unwrap()
);
assert_eq!(
Prefix::from_str("192.168.1.1/32").unwrap().min_addr(),
Prefix::from_str("192.168.1.1/32").unwrap().max_addr()
);
assert_eq!(
Prefix::from_str("2001:db8:10:20::/64").unwrap().min_addr(),
IpAddr::from_str("2001:db8:10:20::").unwrap()
);
assert_eq!(
Prefix::from_str("2001:db8:10:20::/64").unwrap().max_addr(),
IpAddr::from_str("2001:db8:10:20:ffff:ffff:ffff:ffff").unwrap()
);
assert_eq!(
Prefix::from_str("2001:db8:10:20::1234/128")
.unwrap()
.min_addr(),
IpAddr::from_str("2001:db8:10:20::1234").unwrap()
);
assert_eq!(
Prefix::from_str("2001:db8:10:20::1234/128")
.unwrap()
.min_addr(),
Prefix::from_str("2001:db8:10:20::1234/128")
.unwrap()
.max_addr()
);
}
#[test]
fn covers() {
assert!(Prefix::from_str("0.0.0.0/0")
.unwrap()
.covers(Prefix::from_str("192.168.10.0/24").unwrap()));
assert!(Prefix::from_str("::/0")
.unwrap()
.covers(Prefix::from_str("2001:db8:10::/48").unwrap()));
assert!(Prefix::from_str("192.168.0.0/16")
.unwrap()
.covers(Prefix::from_str("192.168.10.0/24").unwrap()));
assert!(!Prefix::from_str("192.168.10.0/24")
.unwrap()
.covers(Prefix::from_str("192.168.0.0/16").unwrap()));
assert!(Prefix::from_str("2001:db8:10::/48")
.unwrap()
.covers(Prefix::from_str("2001:db8:10:20::/64").unwrap()));
assert!(!Prefix::from_str("2001:db8:10:20::/64")
.unwrap()
.covers(Prefix::from_str("2001:db8:10::/48").unwrap()));
assert!(Prefix::from_str("192.168.10.1/32")
.unwrap()
.covers(Prefix::from_str("192.168.10.1/32").unwrap()));
assert!(!Prefix::from_str("192.168.10.1/32")
.unwrap()
.covers(Prefix::from_str("192.168.10.2/32").unwrap()));
assert!(Prefix::from_str("2001:db8:10::1234/128")
.unwrap()
.covers(Prefix::from_str("2001:db8:10::1234/128").unwrap()));
assert!(!Prefix::from_str("2001:db8:10::abcd/128")
.unwrap()
.covers(Prefix::from_str("2001:db8:10::1234/128").unwrap()));
assert!(!Prefix::from_str("192.168.10.0/24")
.unwrap()
.covers(Prefix::from_str("2001:db8::1/128").unwrap()));
assert!(!Prefix::from_str("2001:db8::1/128")
.unwrap()
.covers(Prefix::from_str("192.168.10.0/24").unwrap()));
}
#[test]
fn max_len_prefix() {
let pfx4 = Prefix::from_str("192.168.0.0/16").unwrap();
let pfx6 = Prefix::from_str("2001:db8:10::/48").unwrap();
assert!(MaxLenPrefix::new(pfx4, Some(24)).is_ok());
assert!(MaxLenPrefix::new(pfx4, Some(32)).is_ok());
assert!(MaxLenPrefix::new(pfx6, Some(64)).is_ok());
assert!(MaxLenPrefix::new(pfx6, Some(128)).is_ok());
assert_eq!(MaxLenPrefix::from(pfx4).prefix_len(), 16);
assert_eq!(MaxLenPrefix::from(pfx4).prefix(), pfx4);
assert_eq!(MaxLenPrefix::from(pfx4).max_len(), None);
assert_eq!(MaxLenPrefix::from(pfx4).resolved_max_len(), 16);
assert_eq!(MaxLenPrefix::from(pfx6).prefix_len(), 48);
assert_eq!(MaxLenPrefix::from(pfx6).prefix(), pfx6);
assert_eq!(MaxLenPrefix::from(pfx6).max_len(), None);
assert_eq!(MaxLenPrefix::from(pfx6).resolved_max_len(), 48);
assert!(matches!(
MaxLenPrefix::new(pfx4, Some(12)),
Err(MaxLenError::Underflow)
));
assert!(matches!(
MaxLenPrefix::new(pfx4, Some(33)),
Err(MaxLenError::Overflow)
));
assert!(matches!(
MaxLenPrefix::new(pfx6, Some(32)),
Err(MaxLenError::Underflow)
));
assert!(matches!(
MaxLenPrefix::new(pfx6, Some(130)),
Err(MaxLenError::Overflow)
));
for i in 0..16 {
assert_eq!(
MaxLenPrefix::saturating_new(pfx4, Some(i)),
MaxLenPrefix::new(pfx4, Some(16)).unwrap()
);
}
for i in 16..=32 {
assert_eq!(
MaxLenPrefix::saturating_new(pfx4, Some(i)),
MaxLenPrefix::new(pfx4, Some(i)).unwrap()
);
}
for i in 33..=255 {
assert_eq!(
MaxLenPrefix::saturating_new(pfx4, Some(i)),
MaxLenPrefix::new(pfx4, Some(32)).unwrap()
);
}
for i in 0..48 {
assert_eq!(
MaxLenPrefix::saturating_new(pfx6, Some(i)),
MaxLenPrefix::new(pfx6, Some(48)).unwrap()
);
}
for i in 48..=128 {
assert_eq!(
MaxLenPrefix::saturating_new(pfx6, Some(i)),
MaxLenPrefix::new(pfx6, Some(i)).unwrap()
);
}
for i in 129..=255 {
assert_eq!(
MaxLenPrefix::saturating_new(pfx6, Some(i)),
MaxLenPrefix::new(pfx6, Some(128)).unwrap()
);
}
assert_eq!(
MaxLenPrefix::new(pfx6, Some(56))
.unwrap()
.resolved_max_len(),
56
);
assert_eq!(
MaxLenPrefix::new(pfx6, None).unwrap().resolved_max_len(),
48
);
assert_eq!(
MaxLenPrefix::from_str("192.168.0.0/16-24").unwrap(),
MaxLenPrefix::new(pfx4, Some(24)).unwrap()
);
assert_eq!(
MaxLenPrefix::from_str("192.168.0.0/16").unwrap(),
MaxLenPrefix::new(pfx4, None).unwrap()
);
assert!(matches!(
MaxLenPrefix::from_str("192.168.0.0/16-"),
Err(ParseMaxLenPrefixError::InvalidMaxLenFormat(_))
));
assert!(matches!(
MaxLenPrefix::from_str("192.168.0.0/16-0"),
Err(ParseMaxLenPrefixError::InvalidMaxLenValue(_))
));
assert!(matches!(
MaxLenPrefix::from_str("192.168.0.0/16-33"),
Err(ParseMaxLenPrefixError::InvalidMaxLenValue(_))
));
}
#[test]
fn max_len_prefix_display() {
assert_eq!(
format!(
"{}",
MaxLenPrefix::from_str("192.168.0.0/16-32").unwrap()
)
.as_str(),
"192.168.0.0/16-32"
);
assert_eq!(
format!("{}", MaxLenPrefix::from_str("192.168.0.0/16").unwrap())
.as_str(),
"192.168.0.0/16"
);
}
#[test]
fn clear_host_of_zero_len_prefix() {
assert_eq!(Bits(0), Bits(12345).clear_host(0));
}
#[test]
fn prefix_contains() {
fn test(prefix: &str, addr: &str, expected: bool) {
let p = Prefix::from_str(prefix).unwrap();
let a = IpAddr::from_str(addr).unwrap();
assert_eq!(p.contains(a), expected);
}
for i in [
("10.0.0.0/8", "10.0.0.0", true),
("10.0.0.0/8", "10.1.1.1", true),
("10.0.0.0/8", "10.255.255.255", true),
("10.0.0.0/32", "10.0.0.0", true),
("10.0.0.0/8", "192.168.1.1", false),
("10.0.0.0/8", "2001:0db8::1", false),
("2001:0db8::/32", "2001:0db8::0", true),
("2001:0db8::/32", "2001:0db8::1", true),
("2001:0db8::/32", "10.0.0.1", false),
("::0/120", "0.0.0.10", false),
("0.0.0.0/24", "::1", false),
("0.0.0.1/32", "::1", false),
("0.0.0.1/32", "0.0.0.1", true),
("::1/128", "0.0.0.1", false),
("::1/128", "::1", true),
] {
test(i.0, i.1, i.2);
}
}
}