use std::{error, fmt};
use std::cmp::Ordering;
use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr};
use std::num::ParseIntError;
use std::str::FromStr;
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
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
}
}
}
#[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())
}
}
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 = "repository")]
impl From<crate::repository::roa::FriendlyRoaIpAddress> for Prefix {
fn from(addr: crate::repository::roa::FriendlyRoaIpAddress) -> Self {
Prefix::new(
addr.address(), addr.address_length()
).expect("ROA IP address with illegal prefix length")
}
}
#[cfg(feature = "repository")]
impl From<Prefix> for crate::repository::resources::IpBlock {
fn from(src: Prefix) -> Self {
crate::repository::resources::Prefix::new(
src.addr(), src.len()
).into()
}
}
#[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())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
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));
}
}