use core::{
convert::TryFrom,
error::Error,
fmt::{Debug, Display, Formatter},
net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr},
num::ParseIntError,
ops::{Deref, Range},
str::FromStr,
};
const IPV4_ALL_BITS: Ipv4Addr = Ipv4Addr::new(0xff, 0xff, 0xff, 0xff);
const IPV6_ALL_BITS: Ipv6Addr = Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff);
#[derive(Debug, PartialEq, Eq)]
pub struct CidrOverflowError(u8, u8);
impl Display for CidrOverflowError {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), core::fmt::Error> {
let Self(from, to) = self;
write!(fmt, "invalid CIDR: {from} must be <= {to}")
}
}
#[derive(Debug, PartialEq)]
pub enum IpNetParseError {
ExpectedIpNetwork,
ExpectedIpAddr,
AddrParseError(AddrParseError),
ExpectedIpCidr,
CidrParseError(ParseIntError),
CidrOverflow(CidrOverflowError),
}
impl Display for IpNetParseError {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), core::fmt::Error> {
match self {
Self::ExpectedIpNetwork => {
write!(fmt, "expected IP network")
}
Self::ExpectedIpAddr => {
write!(fmt, "expected IP address")
}
Self::AddrParseError(err) => {
write!(fmt, "IP address parse error: {err}")
}
Self::ExpectedIpCidr => {
write!(fmt, "expected IP CIDR")
}
Self::CidrParseError(err) => {
write!(fmt, "CIDR parse error: {err}")
}
Self::CidrOverflow(err) => {
write!(fmt, "CIDR overflow: {err}")
}
}
}
}
impl Error for IpNetParseError {}
impl From<AddrParseError> for IpNetParseError {
#[inline]
fn from(err: AddrParseError) -> Self {
Self::AddrParseError(err)
}
}
impl From<CidrOverflowError> for IpNetParseError {
#[inline]
fn from(err: CidrOverflowError) -> Self {
Self::CidrOverflow(err)
}
}
#[inline]
pub fn ipv4_mask_from_cidr(cidr: u8) -> Result<Ipv4Addr, CidrOverflowError> {
if cidr <= 32 {
let mask = !(u32::MAX.checked_shr(cidr as u32).unwrap_or_default());
Ok(mask.into())
} else {
Err(CidrOverflowError(cidr, 32))
}
}
#[inline]
pub fn ipv6_mask_from_cidr(cidr: u8) -> Result<Ipv6Addr, CidrOverflowError> {
if cidr <= 128 {
let mask = !(u128::MAX.checked_shr(cidr as u32).unwrap_or_default());
Ok(mask.into())
} else {
Err(CidrOverflowError(cidr, 128))
}
}
#[inline]
const fn ipv4_adjacent_bit(a1: u32, m1: u32, a2: u32, m2: u32) -> Option<u32> {
if m1 != m2 {
return None;
}
let d = a1 ^ a2;
if d != 0 && d & (d - 1) == 0 { Some(d) } else { None }
}
#[inline]
const fn ipv6_adjacent_bit(a1: u128, m1: u128, a2: u128, m2: u128) -> Option<u128> {
if m1 != m2 {
return None;
}
let d = a1 ^ a2;
if d != 0 && d & (d - 1) == 0 { Some(d) } else { None }
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum IpNetwork {
V4(Ipv4Network),
V6(Ipv6Network),
}
impl IpNetwork {
pub fn parse(buf: &str) -> Result<Self, IpNetParseError> {
match Ipv4Network::parse(buf) {
Ok(net) => {
return Ok(Self::V4(net));
}
Err(IpNetParseError::AddrParseError(..)) => {}
Err(err) => {
return Err(err);
}
}
let net = Ipv6Network::parse(buf)?;
Ok(Self::V6(net))
}
#[inline]
#[must_use]
pub const fn addr(&self) -> IpAddr {
match self {
Self::V4(net) => IpAddr::V4(*net.addr()),
Self::V6(net) => IpAddr::V6(*net.addr()),
}
}
#[inline]
#[must_use]
pub const fn mask(&self) -> IpAddr {
match self {
Self::V4(net) => IpAddr::V4(*net.mask()),
Self::V6(net) => IpAddr::V6(*net.mask()),
}
}
#[inline]
#[must_use]
pub const fn contains(&self, other: &Self) -> bool {
match (self, other) {
(Self::V4(a), Self::V4(b)) => a.contains(b),
(Self::V6(a), Self::V6(b)) => a.contains(b),
_ => false,
}
}
#[inline]
#[must_use]
pub fn to_contiguous(&self) -> Self {
match self {
Self::V4(net) => Self::V4(net.to_contiguous()),
Self::V6(net) => Self::V6(net.to_contiguous()),
}
}
#[inline]
#[must_use]
pub const fn is_contiguous(&self) -> bool {
match self {
Self::V4(net) => net.is_contiguous(),
Self::V6(net) => net.is_contiguous(),
}
}
#[inline]
#[must_use]
pub const fn prefix(&self) -> Option<u8> {
match self {
Self::V4(net) => net.prefix(),
Self::V6(net) => net.prefix(),
}
}
#[inline]
#[must_use]
pub fn last_addr(&self) -> IpAddr {
match self {
Self::V4(net) => IpAddr::V4(net.last_addr()),
Self::V6(net) => IpAddr::V6(net.last_addr()),
}
}
#[inline]
#[must_use]
pub const fn intersects(&self, other: &Self) -> bool {
match (self, other) {
(Self::V4(a), Self::V4(b)) => a.intersects(b),
(Self::V6(a), Self::V6(b)) => a.intersects(b),
_ => false,
}
}
#[inline]
#[must_use]
pub const fn is_disjoint(&self, other: &Self) -> bool {
!self.intersects(other)
}
#[inline]
#[must_use]
pub const fn intersection(&self, other: &Self) -> Option<Self> {
match (self, other) {
(Self::V4(a), Self::V4(b)) => match a.intersection(b) {
Some(net) => Some(Self::V4(net)),
None => None,
},
(Self::V6(a), Self::V6(b)) => match a.intersection(b) {
Some(net) => Some(Self::V6(net)),
None => None,
},
_ => None,
}
}
#[inline]
#[must_use]
pub const fn is_adjacent(&self, other: &Self) -> bool {
match (self, other) {
(Self::V4(a), Self::V4(b)) => a.is_adjacent(b),
(Self::V6(a), Self::V6(b)) => a.is_adjacent(b),
_ => false,
}
}
#[inline]
#[must_use]
pub const fn merge(&self, other: &Self) -> Option<Self> {
match (self, other) {
(Self::V4(a), Self::V4(b)) => match a.merge(b) {
Some(net) => Some(Self::V4(net)),
None => None,
},
(Self::V6(a), Self::V6(b)) => match a.merge(b) {
Some(net) => Some(Self::V6(net)),
None => None,
},
_ => None,
}
}
}
impl Display for IpNetwork {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), core::fmt::Error> {
match self {
IpNetwork::V4(net) => Display::fmt(&net, fmt),
IpNetwork::V6(net) => Display::fmt(&net, fmt),
}
}
}
impl From<Ipv4Addr> for IpNetwork {
#[inline]
fn from(addr: Ipv4Addr) -> Self {
Self::V4(Ipv4Network(addr, IPV4_ALL_BITS))
}
}
impl TryFrom<(Ipv4Addr, u8)> for IpNetwork {
type Error = CidrOverflowError;
fn try_from((addr, cidr): (Ipv4Addr, u8)) -> Result<Self, Self::Error> {
Ok(Self::V4(Ipv4Network::new(addr, ipv4_mask_from_cidr(cidr)?)))
}
}
impl From<(Ipv4Addr, Ipv4Addr)> for IpNetwork {
fn from((addr, mask): (Ipv4Addr, Ipv4Addr)) -> Self {
Self::V4(Ipv4Network::new(addr, mask))
}
}
impl From<Ipv4Network> for IpNetwork {
#[inline]
fn from(net: Ipv4Network) -> Self {
Self::V4(net)
}
}
impl From<Ipv6Addr> for IpNetwork {
fn from(addr: Ipv6Addr) -> Self {
Self::V6(Ipv6Network(addr, IPV6_ALL_BITS))
}
}
impl TryFrom<(Ipv6Addr, u8)> for IpNetwork {
type Error = CidrOverflowError;
fn try_from((addr, cidr): (Ipv6Addr, u8)) -> Result<Self, Self::Error> {
Ok(Self::V6(Ipv6Network::new(addr, ipv6_mask_from_cidr(cidr)?)))
}
}
impl From<(Ipv6Addr, Ipv6Addr)> for IpNetwork {
fn from((addr, mask): (Ipv6Addr, Ipv6Addr)) -> Self {
Self::V6(Ipv6Network::new(addr, mask))
}
}
impl From<Ipv6Network> for IpNetwork {
#[inline]
fn from(net: Ipv6Network) -> Self {
Self::V6(net)
}
}
impl From<IpAddr> for IpNetwork {
#[inline]
fn from(addr: IpAddr) -> Self {
match addr {
IpAddr::V4(addr) => Self::from(addr),
IpAddr::V6(addr) => Self::from(addr),
}
}
}
impl FromStr for IpNetwork {
type Err = IpNetParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
IpNetwork::parse(s)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Ipv4Network(Ipv4Addr, Ipv4Addr);
impl Ipv4Network {
pub const UNSPECIFIED: Self = Self(Ipv4Addr::UNSPECIFIED, Ipv4Addr::UNSPECIFIED);
#[inline]
#[must_use]
pub const fn new(addr: Ipv4Addr, mask: Ipv4Addr) -> Self {
let addr = addr.to_bits() & mask.to_bits();
Self(Ipv4Addr::from_bits(addr), mask)
}
pub fn parse(buf: &str) -> Result<Self, IpNetParseError> {
let mut parts = buf.splitn(2, '/');
let addr = parts.next().ok_or(IpNetParseError::ExpectedIpAddr)?;
let addr: Ipv4Addr = addr.parse()?;
match parts.next() {
Some(mask) => match mask.parse::<u8>() {
Ok(cidr) => Ok(Self::try_from((addr, cidr))?),
Err(..) => {
let mask: Ipv4Addr = mask.parse()?;
Ok(Self::new(addr, mask))
}
},
None => Ok(Self(addr, IPV4_ALL_BITS)),
}
}
#[inline]
#[must_use]
pub const fn addr(&self) -> &Ipv4Addr {
match self {
Self(addr, ..) => addr,
}
}
#[inline]
#[must_use]
pub const fn mask(&self) -> &Ipv4Addr {
match self {
Self(.., mask) => mask,
}
}
#[inline]
#[must_use]
pub const fn from_bits(addr: u32, mask: u32) -> Self {
Self::new(Ipv4Addr::from_bits(addr), Ipv4Addr::from_bits(mask))
}
#[inline]
#[must_use]
pub const fn to_bits(&self) -> (u32, u32) {
match self {
Self(addr, mask) => (addr.to_bits(), mask.to_bits()),
}
}
#[inline]
#[must_use]
pub const fn contains(&self, other: &Ipv4Network) -> bool {
let (a1, m1) = self.to_bits();
let (a2, m2) = other.to_bits();
(a2 & m1 == a1) && (m2 & m1 == m1)
}
#[inline]
#[must_use]
pub const fn intersects(&self, other: &Self) -> bool {
self.intersection(other).is_some()
}
#[inline]
#[must_use]
pub const fn is_disjoint(&self, other: &Ipv4Network) -> bool {
!self.intersects(other)
}
#[inline]
#[must_use]
pub const fn intersection(&self, other: &Self) -> Option<Self> {
let (a1, m1) = self.to_bits();
let (a2, m2) = other.to_bits();
if (a1 & m2) != (a2 & m1) {
return None;
}
let addr = a1 | a2;
let mask = m1 | m2;
let addr = Ipv4Addr::from_bits(addr);
let mask = Ipv4Addr::from_bits(mask);
Some(Self(addr, mask))
}
#[inline]
#[must_use]
pub const fn difference(&self, other: &Self) -> Ipv4NetworkDiff {
Ipv4NetworkDiff::new(self, other)
}
#[inline]
#[must_use]
pub const fn is_contiguous(&self) -> bool {
let mask = self.mask().to_bits();
mask | mask.wrapping_sub(1) == u32::MAX
}
#[inline]
#[must_use]
pub const fn prefix(&self) -> Option<u8> {
let mask = self.mask().to_bits();
let ones = mask.leading_ones();
if mask.count_ones() == ones {
Some(ones as u8)
} else {
None
}
}
#[must_use]
pub fn to_contiguous(&self) -> Self {
let (addr, mask) = self.to_bits();
Self::try_from((addr.into(), mask.leading_ones() as u8)).expect("CIDR must be in range [0; 32]")
}
#[must_use]
pub fn supernet_for(&self, nets: &[Ipv4Network]) -> Ipv4Network {
let (addr, mut mask) = self.to_bits();
for net in nets {
let (a, m) = net.to_bits();
mask &= m & !(addr ^ a);
}
Self::new(addr.into(), mask.into())
}
#[inline]
#[must_use]
pub const fn is_adjacent(&self, other: &Self) -> bool {
let (a1, m1) = self.to_bits();
let (a2, m2) = other.to_bits();
ipv4_adjacent_bit(a1, m1, a2, m2).is_some()
}
#[inline]
#[must_use]
pub const fn merge(&self, other: &Self) -> Option<Self> {
let (a1, m1) = self.to_bits();
let (a2, m2) = other.to_bits();
if m1 == m2 {
if a1 == a2 {
return Some(*self);
}
if let Some(d) = ipv4_adjacent_bit(a1, m1, a2, m2) {
let m = m1 ^ d;
let a = a1 & m;
return Some(Self(Ipv4Addr::from_bits(a), Ipv4Addr::from_bits(m)));
}
return None;
}
if self.contains(other) {
return Some(*self);
}
if other.contains(self) {
return Some(*other);
}
None
}
#[inline]
#[must_use]
pub const fn to_ipv6_mapped(&self) -> Ipv6Network {
match self {
Ipv4Network(addr, mask) => {
let [a, b, c, d] = mask.octets();
let [a, b, c, d]: [u16; 4] = [a as u16, b as u16, c as u16, d as u16];
Ipv6Network(
addr.to_ipv6_mapped(),
Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, a << 8 | b, c << 8 | d),
)
}
}
}
#[inline]
#[must_use]
pub const fn last_addr(&self) -> Ipv4Addr {
let (addr, mask) = self.to_bits();
Ipv4Addr::from_bits(addr | !mask)
}
#[inline]
#[must_use]
pub fn addrs(self) -> Ipv4NetworkAddrs {
let (addr, mask) = self.to_bits();
let host_bits = (!mask).count_ones();
let back = u32::MAX.checked_shr(32 - host_bits).unwrap_or(0);
Ipv4NetworkAddrs { base: addr, mask, front: 0, back }
}
}
impl Display for Ipv4Network {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), core::fmt::Error> {
match self {
Self(addr, mask) => {
match self.prefix() {
Some(prefix) => fmt.write_fmt(format_args!("{}/{}", addr, prefix)),
None => {
fmt.write_fmt(format_args!("{}/{}", addr, mask))
}
}
}
}
}
}
impl From<Ipv4Addr> for Ipv4Network {
fn from(addr: Ipv4Addr) -> Self {
Self(addr, IPV4_ALL_BITS)
}
}
impl TryFrom<(Ipv4Addr, u8)> for Ipv4Network {
type Error = CidrOverflowError;
fn try_from((addr, cidr): (Ipv4Addr, u8)) -> Result<Self, Self::Error> {
let mask = ipv4_mask_from_cidr(cidr)?;
Ok(Self::new(addr, mask))
}
}
impl FromStr for Ipv4Network {
type Err = IpNetParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ipv4Network::parse(s)
}
}
impl AsRef<Ipv4Network> for Ipv4Network {
#[inline]
fn as_ref(&self) -> &Ipv4Network {
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ipv4NetworkAddrs {
base: u32,
mask: u32,
front: u32,
back: u32,
}
impl Iterator for Ipv4NetworkAddrs {
type Item = Ipv4Addr;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.front > self.back {
return None;
}
let index = self.front;
self.front = self.front.wrapping_add(1);
if self.front == 0 {
self.front = 1;
self.back = 0;
}
Some(Ipv4Addr::from_bits(ipv4_bits_from_host_index(
self.base, self.mask, index,
)))
}
#[inline]
fn count(self) -> usize {
self.len()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if self.front > self.back {
return (0, Some(0));
}
let rem = self.back.wrapping_sub(self.front).wrapping_add(1);
if rem == 0 {
(usize::MAX, None)
} else {
let n = rem as usize;
(n, Some(n))
}
}
}
impl DoubleEndedIterator for Ipv4NetworkAddrs {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.front > self.back {
return None;
}
let index = self.back;
if self.front == self.back {
self.front = 1;
self.back = 0;
} else {
self.back = self.back.wrapping_sub(1);
}
Some(Ipv4Addr::from_bits(ipv4_bits_from_host_index(
self.base, self.mask, index,
)))
}
}
impl ExactSizeIterator for Ipv4NetworkAddrs {
#[inline]
fn len(&self) -> usize {
if self.front > self.back {
return 0;
}
let rem = self.back.wrapping_sub(self.front).wrapping_add(1);
if rem == 0 { 1usize << 32 } else { rem as usize }
}
}
#[derive(Debug, Clone, Copy)]
pub struct Ipv4NetworkDiff {
addr: u32,
mask: u32,
processed: u32,
remaining: u32,
}
impl Ipv4NetworkDiff {
#[inline]
#[must_use]
pub const fn new(source: &Ipv4Network, other: &Ipv4Network) -> Self {
let (addr, mask) = source.to_bits();
match source.intersection(other) {
None => {
let b = mask & mask.wrapping_neg();
Self {
addr: addr ^ b,
mask: mask ^ b,
processed: 0,
remaining: b,
}
}
Some(intersected) => {
let (intersected_addr, intersected_mask) = intersected.to_bits();
let d = intersected_mask & !mask;
if d == 0 {
Self {
addr: 0,
mask: 0,
processed: 0,
remaining: 0,
}
} else {
Self {
addr: intersected_addr,
mask,
processed: 0,
remaining: d,
}
}
}
}
}
}
impl Iterator for Ipv4NetworkDiff {
type Item = Ipv4Network;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let b = 0x8000_0000u32 >> self.remaining.leading_zeros();
let mask = self.mask | self.processed | b;
let addr = (self.addr ^ b) & mask;
self.processed |= b;
self.remaining ^= b;
Some(Ipv4Network::from_bits(addr, mask))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.len();
(n, Some(n))
}
}
impl ExactSizeIterator for Ipv4NetworkDiff {
#[inline]
fn len(&self) -> usize {
self.remaining.count_ones() as usize
}
}
#[inline]
const fn ipv4_bits_from_host_index(base: u32, mask: u32, index: u32) -> u32 {
let host_mask = !mask;
if host_mask & host_mask.wrapping_add(1) == 0 {
base | index
} else {
base | pdep_u32(index, host_mask)
}
}
#[inline]
const fn pdep_u32(mut src: u32, mut mask: u32) -> u32 {
let mut out = 0u32;
while mask != 0 {
let t = mask & mask.wrapping_neg();
if src & 1 != 0 {
out |= t;
}
src >>= 1;
mask ^= t;
}
out
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Ipv6Network(Ipv6Addr, Ipv6Addr);
impl Ipv6Network {
pub const UNSPECIFIED: Self = Self(Ipv6Addr::UNSPECIFIED, Ipv6Addr::UNSPECIFIED);
#[inline]
#[must_use]
pub const fn new(addr: Ipv6Addr, mask: Ipv6Addr) -> Self {
let addr = addr.to_bits() & mask.to_bits();
Self(Ipv6Addr::from_bits(addr), mask)
}
pub fn parse(buf: &str) -> Result<Self, IpNetParseError> {
let mut parts = buf.splitn(2, '/');
let addr = parts.next().ok_or(IpNetParseError::ExpectedIpNetwork)?;
let addr: Ipv6Addr = addr.parse()?;
match parts.next() {
Some(mask) => match mask.parse::<u8>() {
Ok(mask) => Ok(Self::try_from((addr, mask))?),
Err(..) => {
let mask: Ipv6Addr = mask.parse()?;
Ok(Self::new(addr, mask))
}
},
None => Ok(Self(addr, IPV6_ALL_BITS)),
}
}
#[inline]
#[must_use]
pub const fn addr(&self) -> &Ipv6Addr {
match self {
Ipv6Network(addr, ..) => addr,
}
}
#[inline]
#[must_use]
pub const fn mask(&self) -> &Ipv6Addr {
match self {
Ipv6Network(.., mask) => mask,
}
}
#[inline]
#[must_use]
pub const fn from_bits(addr: u128, mask: u128) -> Self {
Self::new(Ipv6Addr::from_bits(addr), Ipv6Addr::from_bits(mask))
}
#[inline]
#[must_use]
pub const fn to_bits(&self) -> (u128, u128) {
match self {
Self(addr, mask) => (addr.to_bits(), mask.to_bits()),
}
}
#[inline]
#[must_use]
pub const fn contains(&self, other: &Ipv6Network) -> bool {
let (a1, m1) = self.to_bits();
let (a2, m2) = other.to_bits();
(a2 & m1 == a1) && (m2 & m1 == m1)
}
#[inline]
#[must_use]
pub const fn intersects(&self, other: &Self) -> bool {
self.intersection(other).is_some()
}
#[inline]
#[must_use]
pub const fn is_disjoint(&self, other: &Ipv6Network) -> bool {
!self.intersects(other)
}
#[inline]
#[must_use]
pub const fn intersection(&self, other: &Self) -> Option<Self> {
let (a1, m1) = self.to_bits();
let (a2, m2) = other.to_bits();
if (a1 & m2) != (a2 & m1) {
return None;
}
let addr = a1 | a2;
let mask = m1 | m2;
let addr = Ipv6Addr::from_bits(addr);
let mask = Ipv6Addr::from_bits(mask);
Some(Self(addr, mask))
}
#[inline]
#[must_use]
pub const fn difference(&self, other: &Self) -> Ipv6NetworkDiff {
Ipv6NetworkDiff::new(self, other)
}
#[inline]
#[must_use]
pub const fn is_contiguous(&self) -> bool {
let mask = self.mask().to_bits();
mask | mask.wrapping_sub(1) == u128::MAX
}
#[inline]
#[must_use]
pub const fn is_ipv4_mapped_ipv6(&self) -> bool {
let (addr, mask) = self.to_bits();
#[allow(clippy::needless_return)]
return addr & 0xffffffffffffffffffffffff00000000 == 0x00000000000000000000ffff00000000
&& mask & 0xffffffffffffffffffffffff00000000 == 0xffffffffffffffffffffffff00000000;
}
#[must_use]
pub fn to_contiguous(&self) -> Self {
let (addr, mask) = self.to_bits();
Self::try_from((addr.into(), mask.leading_ones() as u8)).expect("CIDR must be in range [0; 128]")
}
#[inline]
#[must_use]
pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Network> {
if self.is_ipv4_mapped_ipv6() {
let (addr, mask) = self.to_bits();
let addr: u32 = (addr & 0xffffffff) as u32;
let mask: u32 = (mask & 0xffffffff) as u32;
Some(Ipv4Network::from_bits(addr, mask))
} else {
None
}
}
#[inline]
#[must_use]
pub const fn prefix(&self) -> Option<u8> {
let mask = self.mask().to_bits();
let ones = mask.leading_ones();
if mask.count_ones() == ones {
Some(ones as u8)
} else {
None
}
}
#[must_use]
pub fn supernet_for(&self, nets: &[Ipv6Network]) -> Ipv6Network {
let (addr, mut mask) = self.to_bits();
for net in nets {
let (a, m) = net.to_bits();
mask &= m & !(addr ^ a);
}
Self::new(addr.into(), mask.into())
}
#[inline]
#[must_use]
pub const fn is_adjacent(&self, other: &Self) -> bool {
let (a1, m1) = self.to_bits();
let (a2, m2) = other.to_bits();
ipv6_adjacent_bit(a1, m1, a2, m2).is_some()
}
#[inline]
#[must_use]
pub const fn merge(&self, other: &Self) -> Option<Self> {
let (a1, m1) = self.to_bits();
let (a2, m2) = other.to_bits();
if m1 == m2 {
if a1 == a2 {
return Some(*self);
}
if let Some(d) = ipv6_adjacent_bit(a1, m1, a2, m2) {
let m = m1 ^ d;
let a = a1 & m;
return Some(Self(Ipv6Addr::from_bits(a), Ipv6Addr::from_bits(m)));
}
return None;
}
if self.contains(other) {
return Some(*self);
}
if other.contains(self) {
return Some(*other);
}
None
}
#[inline]
#[must_use]
pub const fn last_addr(&self) -> Ipv6Addr {
let (addr, mask) = self.to_bits();
Ipv6Addr::from_bits(addr | !mask)
}
#[inline]
#[must_use]
pub fn addrs(self) -> Ipv6NetworkAddrs {
let (addr, mask) = self.to_bits();
let host_bits = (!mask).count_ones();
let back = u128::MAX.checked_shr(128 - host_bits).unwrap_or(0);
Ipv6NetworkAddrs { base: addr, mask, front: 0, back }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ipv6NetworkAddrs {
base: u128,
mask: u128,
front: u128,
back: u128,
}
impl Iterator for Ipv6NetworkAddrs {
type Item = Ipv6Addr;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.front > self.back {
return None;
}
let index = self.front;
self.front = self.front.wrapping_add(1);
if self.front == 0 {
self.front = 1;
self.back = 0;
}
Some(Ipv6Addr::from_bits(ipv6_bits_from_host_index(
self.base, self.mask, index,
)))
}
#[inline]
fn count(self) -> usize {
let (n, ..) = self.size_hint();
n
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if self.front > self.back {
return (0, Some(0));
}
let rem = self.back.wrapping_sub(self.front).wrapping_add(1);
if rem == 0 {
(usize::MAX, None)
} else {
match usize::try_from(rem) {
Ok(n) => (n, Some(n)),
Err(..) => (usize::MAX, None),
}
}
}
}
impl DoubleEndedIterator for Ipv6NetworkAddrs {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.front > self.back {
return None;
}
let bits = ipv6_bits_from_host_index(self.base, self.mask, self.back);
if self.front == self.back {
self.front = 1;
self.back = 0;
} else {
self.back = self.back.wrapping_sub(1);
}
Some(Ipv6Addr::from_bits(bits))
}
}
#[inline]
const fn ipv6_bits_from_host_index(base: u128, mask: u128, index: u128) -> u128 {
let host_mask = !mask;
if host_mask & host_mask.wrapping_add(1) == 0 {
base | index
} else {
base | pdep_u128(index, host_mask)
}
}
#[inline]
const fn pdep_u128(mut src: u128, mut mask: u128) -> u128 {
let mut out = 0u128;
while mask != 0 {
let t = mask & mask.wrapping_neg();
if src & 1 != 0 {
out |= t;
}
src >>= 1;
mask ^= t;
}
out
}
#[derive(Debug, Clone, Copy)]
pub struct Ipv6NetworkDiff {
addr: u128,
mask: u128,
processed: u128,
remaining: u128,
}
impl Ipv6NetworkDiff {
#[inline]
#[must_use]
pub const fn new(source: &Ipv6Network, other: &Ipv6Network) -> Self {
let (addr, mask) = source.to_bits();
match source.intersection(other) {
None => {
let b = mask & mask.wrapping_neg();
Self {
addr: addr ^ b,
mask: mask ^ b,
processed: 0,
remaining: b,
}
}
Some(intersected) => {
let (inter_addr, inter_mask) = intersected.to_bits();
let d = inter_mask & !mask;
if d == 0 {
Self {
addr: 0,
mask: 0,
processed: 0,
remaining: 0,
}
} else {
Self {
addr: inter_addr,
mask,
processed: 0,
remaining: d,
}
}
}
}
}
}
impl Iterator for Ipv6NetworkDiff {
type Item = Ipv6Network;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let b = 1u128 << (127 - self.remaining.leading_zeros());
let mask = self.mask | self.processed | b;
let addr = (self.addr ^ b) & mask;
self.processed |= b;
self.remaining ^= b;
Some(Ipv6Network::from_bits(addr, mask))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.len();
(n, Some(n))
}
}
impl ExactSizeIterator for Ipv6NetworkDiff {
#[inline]
fn len(&self) -> usize {
self.remaining.count_ones() as usize
}
}
impl Display for Ipv6Network {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), core::fmt::Error> {
match self {
Ipv6Network(addr, mask) => {
match self.prefix() {
Some(prefix) => fmt.write_fmt(format_args!("{}/{}", addr, prefix)),
None => {
fmt.write_fmt(format_args!("{}/{}", addr, mask))
}
}
}
}
}
}
impl From<Ipv4Network> for Ipv6Network {
#[inline]
fn from(net: Ipv4Network) -> Self {
net.to_ipv6_mapped()
}
}
impl From<Ipv6Addr> for Ipv6Network {
fn from(addr: Ipv6Addr) -> Self {
Ipv6Network(addr, IPV6_ALL_BITS)
}
}
impl TryFrom<(Ipv6Addr, u8)> for Ipv6Network {
type Error = CidrOverflowError;
fn try_from((addr, cidr): (Ipv6Addr, u8)) -> Result<Self, Self::Error> {
let mask = ipv6_mask_from_cidr(cidr)?;
Ok(Ipv6Network::new(addr, mask))
}
}
impl FromStr for Ipv6Network {
type Err = IpNetParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ipv6Network::parse(s)
}
}
impl AsRef<Ipv6Network> for Ipv6Network {
#[inline]
fn as_ref(&self) -> &Ipv6Network {
self
}
}
#[must_use]
pub fn ipv4_binary_split<T>(nets: &[T]) -> Option<(Ipv4Network, Range<usize>)>
where
T: AsRef<Ipv4Network>,
{
ip_binary_split(nets)
}
#[must_use]
pub fn ipv6_binary_split<T>(nets: &[T]) -> Option<(Ipv6Network, Range<usize>)>
where
T: AsRef<Ipv6Network>,
{
ip_binary_split(nets)
}
#[inline]
fn ip_binary_split<T, U>(nets: &[T]) -> Option<(U, Range<usize>)>
where
T: AsRef<U>,
U: BinarySplit + Copy,
{
if nets.len() < 3 {
return None;
}
let size = nets.len().div_ceil(2);
let window_supernet = |window: &[T]| -> U {
let mut supernet = *window[0].as_ref();
for elem in &window[1..] {
supernet = supernet.supernet_for(&[*elem.as_ref()]);
}
supernet
};
let mut range = 0..nets.len();
let mut candidate = window_supernet(nets);
for (idx, window) in nets.windows(size).enumerate() {
let supernet = window_supernet(window);
if supernet.mask() > candidate.mask() {
range = idx..(idx + size);
candidate = supernet;
}
}
while range.start > 0 {
if candidate.contains(nets[range.start - 1].as_ref()) {
range.start -= 1;
} else {
break;
}
}
while range.end < nets.len() {
if candidate.contains(nets[range.end].as_ref()) {
range.end += 1;
} else {
break;
}
}
Some((candidate, range))
}
trait BinarySplit: Sized {
type Mask: PartialOrd;
fn mask(&self) -> &Self::Mask;
fn contains(&self, other: &Self) -> bool;
fn supernet_for(&self, nets: &[Self]) -> Self;
}
impl BinarySplit for Ipv4Network {
type Mask = Ipv4Addr;
#[inline]
fn mask(&self) -> &Self::Mask {
self.mask()
}
#[inline]
fn contains(&self, other: &Self) -> bool {
self.contains(other)
}
#[inline]
fn supernet_for(&self, nets: &[Self]) -> Ipv4Network {
self.supernet_for(nets)
}
}
impl BinarySplit for Ipv6Network {
type Mask = Ipv6Addr;
#[inline]
fn mask(&self) -> &Self::Mask {
self.mask()
}
#[inline]
fn contains(&self, other: &Self) -> bool {
self.contains(other)
}
#[inline]
fn supernet_for(&self, nets: &[Self]) -> Ipv6Network {
self.supernet_for(nets)
}
}
pub fn ipv4_aggregate(nets: &mut [Ipv4Network]) -> &mut [Ipv4Network] {
let len = ip_aggregate(nets);
&mut nets[..len]
}
pub fn ipv6_aggregate(nets: &mut [Ipv6Network]) -> &mut [Ipv6Network] {
let len = ip_aggregate(nets);
&mut nets[..len]
}
trait Aggregate: Ord + Copy {
fn merge(&self, other: &Self) -> Option<Self>;
}
impl Aggregate for Ipv4Network {
#[inline]
fn merge(&self, other: &Self) -> Option<Self> {
self.merge(other)
}
}
impl Aggregate for Ipv6Network {
#[inline]
fn merge(&self, other: &Self) -> Option<Self> {
self.merge(other)
}
}
fn ip_aggregate<T: Aggregate>(nets: &mut [T]) -> usize {
if nets.len() <= 1 {
return nets.len();
}
nets.sort_unstable();
let mut w = 0usize;
for r in 1..nets.len() {
let mut merged_at = None;
for k in (0..=w).rev() {
if let Some(m) = nets[k].merge(&nets[r]) {
merged_at = Some((k, nets[k] != m));
nets[k] = m;
break;
}
}
match merged_at {
Some((mut k, true)) => {
let mut changed = true;
while core::mem::replace(&mut changed, false) {
let mut j = 0;
while j <= w {
if j != k
&& let Some(m) = nets[k].merge(&nets[j])
{
nets[k] = m;
for s in j..w {
nets[s] = nets[s + 1];
}
w -= 1;
if j < k {
k -= 1;
}
changed = true;
break;
}
j += 1;
}
}
}
Some(..) => {}
None => {
w += 1;
nets[w] = nets[r];
}
}
}
w + 1
}
#[derive(Debug, PartialEq)]
pub enum ContiguousIpNetParseError {
NonContiguousNetwork,
IpNetParseError(IpNetParseError),
}
impl From<IpNetParseError> for ContiguousIpNetParseError {
#[inline]
fn from(err: IpNetParseError) -> Self {
ContiguousIpNetParseError::IpNetParseError(err)
}
}
impl Display for ContiguousIpNetParseError {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), core::fmt::Error> {
match self {
Self::NonContiguousNetwork => write!(fmt, "non-contiguous network"),
Self::IpNetParseError(err) => write!(fmt, "{}", err),
}
}
}
impl Error for ContiguousIpNetParseError {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Contiguous<T>(T);
impl Contiguous<IpNetwork> {
pub fn parse(buf: &str) -> Result<Self, ContiguousIpNetParseError> {
let net = IpNetwork::parse(buf)?;
if net.is_contiguous() {
Ok(Self(net))
} else {
Err(ContiguousIpNetParseError::NonContiguousNetwork)
}
}
#[inline]
#[must_use]
pub const fn prefix(&self) -> u8 {
match self {
Self(IpNetwork::V4(net)) => Contiguous(*net).prefix(),
Self(IpNetwork::V6(net)) => Contiguous(*net).prefix(),
}
}
}
impl Contiguous<Ipv4Network> {
pub fn parse(buf: &str) -> Result<Self, ContiguousIpNetParseError> {
let net = Ipv4Network::parse(buf)?;
if net.is_contiguous() {
Ok(Self(net))
} else {
Err(ContiguousIpNetParseError::NonContiguousNetwork)
}
}
#[inline]
#[must_use]
pub const fn prefix(&self) -> u8 {
match self {
Self(net) => {
let mask = net.mask().to_bits();
let ones = mask.leading_ones();
ones as u8
}
}
}
}
impl Contiguous<Ipv6Network> {
pub fn parse(buf: &str) -> Result<Self, ContiguousIpNetParseError> {
let net = Ipv6Network::parse(buf)?;
if net.is_contiguous() {
Ok(Self(net))
} else {
Err(ContiguousIpNetParseError::NonContiguousNetwork)
}
}
#[inline]
#[must_use]
pub const fn prefix(&self) -> u8 {
match self {
Self(net) => {
let mask = net.mask().to_bits();
let ones = mask.leading_ones();
ones as u8
}
}
}
}
impl<T> Deref for Contiguous<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
match self {
Self(net) => net,
}
}
}
impl<T> Display for Contiguous<T>
where
T: Display,
{
#[inline]
fn fmt(&self, fmt: &mut Formatter) -> Result<(), core::fmt::Error> {
match self {
Self(net) => Display::fmt(net, fmt),
}
}
}
impl FromStr for Contiguous<IpNetwork> {
type Err = ContiguousIpNetParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
impl FromStr for Contiguous<Ipv4Network> {
type Err = ContiguousIpNetParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
impl FromStr for Contiguous<Ipv6Network> {
type Err = ContiguousIpNetParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
#[cfg(test)]
mod test {
use proptest::prelude::*;
use super::*;
#[test]
fn test_ipv4_mask_from_cidr() {
let masks = &[
0u32,
0b10000000000000000000000000000000,
0b11000000000000000000000000000000,
0b11100000000000000000000000000000,
0b11110000000000000000000000000000,
0b11111000000000000000000000000000,
0b11111100000000000000000000000000,
0b11111110000000000000000000000000,
0b11111111000000000000000000000000,
0b11111111100000000000000000000000,
0b11111111110000000000000000000000,
0b11111111111000000000000000000000,
0b11111111111100000000000000000000,
0b11111111111110000000000000000000,
0b11111111111111000000000000000000,
0b11111111111111100000000000000000,
0b11111111111111110000000000000000,
0b11111111111111111000000000000000,
0b11111111111111111100000000000000,
0b11111111111111111110000000000000,
0b11111111111111111111000000000000,
0b11111111111111111111100000000000,
0b11111111111111111111110000000000,
0b11111111111111111111111000000000,
0b11111111111111111111111100000000,
0b11111111111111111111111110000000,
0b11111111111111111111111111000000,
0b11111111111111111111111111100000,
0b11111111111111111111111111110000,
0b11111111111111111111111111111000,
0b11111111111111111111111111111100,
0b11111111111111111111111111111110,
0b11111111111111111111111111111111,
];
for (idx, &mask) in masks.iter().enumerate() {
assert_eq!(Ipv4Addr::from(mask), ipv4_mask_from_cidr(idx as u8).unwrap());
}
assert!(ipv4_mask_from_cidr(33).is_err());
}
#[test]
fn test_ipv6_mask_from_cidr() {
assert_eq!(Ipv6Addr::UNSPECIFIED, ipv6_mask_from_cidr(0).unwrap());
assert_eq!(
Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x8000, 0),
ipv6_mask_from_cidr(97).unwrap()
);
assert_eq!(
Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
ipv6_mask_from_cidr(128).unwrap()
);
assert!(ipv6_mask_from_cidr(129).is_err());
}
#[test]
fn parse_ipv4() {
assert_eq!(
Ipv4Network::new("127.0.0.1".parse().unwrap(), "255.255.255.255".parse().unwrap()),
Ipv4Network::parse("127.0.0.1").unwrap()
);
}
#[test]
fn parse_ipv4_cidr() {
assert_eq!(
Ipv4Network::new("192.168.0.0".parse().unwrap(), "255.255.255.0".parse().unwrap()),
Ipv4Network::parse("192.168.0.1/24").unwrap()
);
assert_eq!(
Ipv4Network::new("192.168.0.1".parse().unwrap(), "255.255.255.255".parse().unwrap()),
Ipv4Network::parse("192.168.0.1/32").unwrap()
);
}
#[test]
fn parse_ipv4_explicit_mask() {
assert_eq!(
Ipv4Network::new("192.168.0.0".parse().unwrap(), "255.255.255.0".parse().unwrap()),
Ipv4Network::parse("192.168.0.1/255.255.255.0").unwrap()
);
}
#[test]
fn ipv4_contains() {
assert!(
Ipv4Network::parse("0.0.0.0/0")
.unwrap()
.contains(&Ipv4Network::parse("127.0.0.1").unwrap())
);
assert!(
Ipv4Network::parse("0.0.0.0/8")
.unwrap()
.contains(&Ipv4Network::parse("0.0.0.0/9").unwrap())
);
}
#[test]
fn ipv4_not_contains() {
assert!(
!Ipv4Network::parse("0.0.0.0/9")
.unwrap()
.contains(&Ipv4Network::parse("0.0.0.0/8").unwrap())
);
}
#[test]
fn ipv4_contains_self() {
assert!(
Ipv4Network::parse("127.0.0.1")
.unwrap()
.contains(&Ipv4Network::parse("127.0.0.1").unwrap())
);
}
#[test]
fn ipv4_addrs_non_contiguous_matches_expected_grid() {
let net = Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 1), Ipv4Addr::new(255, 255, 0, 255));
let mut expected: Vec<_> = (0u16..256).map(|a| Ipv4Addr::new(192, 168, a as u8, 1)).collect();
let mut actual: Vec<_> = net.addrs().collect();
expected.sort();
actual.sort();
assert_eq!(expected, actual);
}
#[test]
fn ipv4_addrs_slash24_matches_incrementing_u32() {
let net = Ipv4Network::parse("192.168.1.0/24").unwrap();
let mut ip = net.addr().to_bits();
for a in net.addrs() {
assert_eq!(a.to_bits(), ip);
ip = ip.wrapping_add(1);
}
assert_eq!(256, net.addrs().count());
}
#[test]
fn ipv4_addrs_slash32_single() {
let net = Ipv4Network::parse("10.0.0.1/32").unwrap();
assert_eq!(net.addrs().next(), net.addrs().next_back());
let mut addrs = net.addrs();
assert_eq!(addrs.next(), Some(Ipv4Addr::new(10, 0, 0, 1)));
assert_eq!(addrs.next(), None);
assert_eq!(addrs.next_back(), None);
}
#[test]
fn ipv4_addrs_non_contiguous_unique_and_endpoints() {
let net = Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 1), Ipv4Addr::new(255, 255, 0, 255));
let addrs: Vec<_> = net.addrs().collect();
assert_eq!(256, addrs.len());
let mut sorted = addrs.clone();
sorted.sort();
sorted.dedup();
assert_eq!(sorted.len(), addrs.len());
assert_eq!(addrs.first().copied(), Some(*net.addr()));
assert_eq!(addrs.last().copied(), Some(net.last_addr()));
}
#[test]
fn ipv4_addrs_interleaved_exhausts_uniquely() {
let net = Ipv4Network::parse("192.168.1.0/24").unwrap();
let mut addrs = net.addrs();
let mut seen = Vec::new();
loop {
match (addrs.next(), addrs.next_back()) {
(Some(a), Some(b)) => {
seen.push(a);
seen.push(b);
}
(Some(a), None) => seen.push(a),
(None, Some(b)) => seen.push(b),
(None, None) => break,
}
}
assert_eq!(256, seen.len());
seen.sort();
seen.dedup();
assert_eq!(256, seen.len());
}
#[test]
fn ipv4_addrs_unspecified_both_ends() {
let net = Ipv4Network::UNSPECIFIED;
let mut addrs = net.addrs();
assert_eq!(addrs.next(), Some(Ipv4Addr::new(0, 0, 0, 0)));
assert_eq!(addrs.next_back(), Some(Ipv4Addr::new(255, 255, 255, 255)));
}
#[test]
fn ipv6_addrs_slash120_matches_incrementing_u128() {
let net = Ipv6Network::parse("2a02:6b8:c00::1234:0:0/120").unwrap();
let mut ip = net.addr().to_bits();
for addr in net.addrs() {
assert_eq!(addr.to_bits(), ip);
ip = ip.wrapping_add(1);
}
assert_eq!(256, net.addrs().count());
}
#[test]
fn ipv6_addrs_slash128_single() {
let net = Ipv6Network::parse("::1/128").unwrap();
assert_eq!(net.addrs().next(), net.addrs().next_back());
let mut addrs = net.addrs();
assert_eq!(addrs.next(), Some(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
assert_eq!(addrs.next(), None);
assert_eq!(addrs.next_back(), None);
}
#[test]
fn ipv6_addrs_non_contiguous_unique_and_endpoints() {
let net = Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0xc00, 0, 0, 0x1234, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
);
let addrs: Vec<_> = net.addrs().collect();
assert_eq!(256, addrs.len());
let mut sorted = addrs.clone();
sorted.sort();
sorted.dedup();
assert_eq!(sorted.len(), addrs.len());
assert_eq!(addrs.first().copied(), Some(*net.addr()));
assert_eq!(addrs.last().copied(), Some(net.last_addr()));
}
#[test]
fn ipv6_addrs_interleaved_exhausts_uniquely() {
let net = Ipv6Network::parse("2a02:6b8:c00::1234:0:0/120").unwrap();
let mut addrs = net.addrs();
let mut seen = Vec::new();
loop {
match (addrs.next(), addrs.next_back()) {
(Some(a), Some(b)) => {
seen.push(a);
seen.push(b);
}
(Some(a), None) => seen.push(a),
(None, Some(b)) => seen.push(b),
(None, None) => break,
}
}
assert_eq!(256, seen.len());
seen.sort();
seen.dedup();
assert_eq!(256, seen.len());
}
#[test]
fn ipv6_addrs_unspecified_both_ends() {
let net = Ipv6Network::UNSPECIFIED;
let mut addrs = net.addrs();
assert_eq!(addrs.next(), Some(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)));
assert_eq!(
addrs.next_back(),
Some(Ipv6Addr::new(
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
))
);
}
#[test]
fn ipv6_addrs_non_contiguous_matches_expected_grid() {
let net = Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0x0c00, 0, 0, 0, 0, 1),
Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
);
let mut expected: Vec<_> = (0u16..256)
.map(|a| Ipv6Addr::new(0x2a02, 0x6b8, 0x0c00 | a, 0, 0, 0, 0, 1))
.collect();
let mut actual: Vec<_> = net.addrs().collect();
expected.sort();
actual.sort();
assert_eq!(expected, actual);
}
#[test]
fn ipv4_supernet_for() {
let net0 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 0), 25)).unwrap();
let net1 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 128), 25)).unwrap();
let expected = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 0), 24)).unwrap();
assert_eq!(expected, net0.supernet_for(&[net1]));
assert_eq!(expected, net1.supernet_for(&[net0]));
}
#[test]
fn ipv4_supernet_for_equal_nets() {
let net0 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 128), 25)).unwrap();
let net1 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 128), 25)).unwrap();
let expected = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 128), 25)).unwrap();
assert_eq!(expected, net0.supernet_for(&[net1]));
assert_eq!(expected, net1.supernet_for(&[net0]));
}
#[test]
fn ipv4_supernet_for_many() {
let net0 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 0), 27)).unwrap();
let net1 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 32), 27)).unwrap();
let net2 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 64), 27)).unwrap();
let net3 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 96), 27)).unwrap();
let net4 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 128), 27)).unwrap();
let net5 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 160), 27)).unwrap();
let net6 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 192), 27)).unwrap();
let net7 = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 224), 27)).unwrap();
let expected = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 0), 24)).unwrap();
assert_eq!(expected, net0.supernet_for(&[net1, net2, net3, net4, net5, net6, net7]));
assert_eq!(expected, net1.supernet_for(&[net0, net2, net3, net4, net5, net6, net7]));
assert_eq!(expected, net2.supernet_for(&[net0, net1, net3, net4, net5, net6, net7]));
assert_eq!(expected, net3.supernet_for(&[net0, net1, net2, net4, net5, net6, net7]));
assert_eq!(expected, net4.supernet_for(&[net0, net1, net2, net3, net5, net6, net7]));
assert_eq!(expected, net5.supernet_for(&[net0, net1, net2, net3, net4, net6, net7]));
assert_eq!(expected, net6.supernet_for(&[net0, net1, net2, net3, net4, net5, net7]));
assert_eq!(expected, net7.supernet_for(&[net0, net1, net2, net3, net4, net5, net6]));
}
#[test]
fn ipv4_supernet_for_many_more() {
let nets = &[
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 0), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 16), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 32), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 48), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 64), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 80), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 96), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 112), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 128), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 144), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 160), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 176), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 192), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 208), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 224), 28)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 240), 28)).unwrap(),
];
let expected = Ipv4Network::try_from((Ipv4Addr::new(192, 0, 2, 0), 24)).unwrap();
assert_eq!(expected, nets[0].supernet_for(&nets[1..]).to_contiguous());
}
#[test]
fn ipv4_supernet_for_wide() {
let nets = &[
Ipv4Network::try_from((Ipv4Addr::new(10, 40, 101, 1), 32)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(10, 40, 102, 1), 32)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(10, 40, 103, 1), 32)).unwrap(),
];
let expected = Ipv4Network::try_from((Ipv4Addr::new(10, 40, 100, 0), 22)).unwrap();
assert_eq!(expected, nets[0].supernet_for(&nets[1..]).to_contiguous());
}
#[test]
fn ipv4_supernet_for_wide_more() {
let nets = &[
Ipv4Network::try_from((Ipv4Addr::new(10, 40, 101, 1), 32)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(10, 40, 102, 1), 32)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(11, 40, 103, 1), 32)).unwrap(),
];
let expected = Ipv4Network::try_from((Ipv4Addr::new(10, 0, 0, 0), 7)).unwrap();
assert_eq!(expected, nets[0].supernet_for(&nets[1..]).to_contiguous());
}
#[test]
fn ipv4_supernet_for_min() {
let nets = &[
Ipv4Network::try_from((Ipv4Addr::new(192, 168, 0, 0), 24)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 168, 1, 0), 24)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 168, 2, 0), 24)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 168, 100, 0), 24)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 168, 200, 0), 24)).unwrap(),
];
let expected = Ipv4Network::try_from((Ipv4Addr::new(192, 168, 0, 0), 16)).unwrap();
assert_eq!(expected, nets[0].supernet_for(&nets[1..]).to_contiguous());
}
#[test]
fn ipv4_supernet_for_unspecified() {
let nets = &[
Ipv4Network::try_from((Ipv4Addr::new(128, 0, 0, 0), 24)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(192, 0, 0, 0), 24)).unwrap(),
Ipv4Network::try_from((Ipv4Addr::new(65, 0, 0, 0), 24)).unwrap(),
];
let expected = Ipv4Network::try_from((Ipv4Addr::new(0, 0, 0, 0), 0)).unwrap();
assert_eq!(expected, nets[0].supernet_for(&nets[1..]).to_contiguous());
}
#[test]
fn ipv4_supernet_for_non_contiguous() {
let nets = &[
Ipv4Network::new(Ipv4Addr::new(10, 40, 0, 1), Ipv4Addr::new(255, 255, 0, 255)),
Ipv4Network::new(Ipv4Addr::new(10, 40, 0, 2), Ipv4Addr::new(255, 255, 0, 255)),
Ipv4Network::new(Ipv4Addr::new(11, 40, 0, 3), Ipv4Addr::new(255, 255, 0, 255)),
];
let expected = Ipv4Network::try_from((Ipv4Addr::new(10, 0, 0, 0), 7)).unwrap();
assert_eq!(expected, nets[0].supernet_for(&nets[1..]).to_contiguous());
}
#[test]
fn ipv4_supernet_for_different_prefix_lengths() {
let narrow = Ipv4Network::parse("10.0.0.0/24").unwrap();
let wide = Ipv4Network::parse("10.0.0.0/16").unwrap();
assert_eq!(wide, narrow.supernet_for(&[wide]));
assert_eq!(wide, wide.supernet_for(&[narrow]));
}
#[test]
fn ipv6_supernet_for_different_prefix_lengths() {
let narrow = Ipv6Network::parse("2001:db8::/48").unwrap();
let wide = Ipv6Network::parse("2001:db8::/32").unwrap();
assert_eq!(wide, narrow.supernet_for(&[wide]));
assert_eq!(wide, wide.supernet_for(&[narrow]));
}
#[test]
fn ipv4_last_addr() {
let net = Ipv4Network::new(Ipv4Addr::new(192, 168, 1, 0), Ipv4Addr::new(255, 255, 255, 0));
assert_eq!(Ipv4Addr::new(192, 168, 1, 255), net.last_addr());
let net = Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 0), Ipv4Addr::new(255, 255, 255, 0));
assert_eq!(Ipv4Addr::new(10, 0, 0, 255), net.last_addr());
let net = Ipv4Network::new(Ipv4Addr::new(172, 16, 0, 0), Ipv4Addr::new(255, 255, 0, 0));
assert_eq!(Ipv4Addr::new(172, 16, 255, 255), net.last_addr());
let net = Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 1), Ipv4Addr::new(255, 255, 255, 255));
assert_eq!(Ipv4Addr::new(10, 0, 0, 1), net.last_addr());
let net = Ipv4Network::UNSPECIFIED;
assert_eq!(Ipv4Addr::new(255, 255, 255, 255), net.last_addr());
let net = Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 1), Ipv4Addr::new(255, 255, 0, 255));
assert_eq!(Ipv4Addr::new(192, 168, 255, 1), net.last_addr());
let net = Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 42), Ipv4Addr::new(255, 0, 255, 255));
assert_eq!(Ipv4Addr::new(10, 255, 0, 42), net.last_addr());
let net = Ipv4Network::new(Ipv4Addr::new(255, 255, 255, 255), Ipv4Addr::new(255, 255, 255, 255));
assert_eq!(Ipv4Addr::new(255, 255, 255, 255), net.last_addr());
let net = Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 0), Ipv4Addr::new(255, 0, 0, 0));
assert_eq!(Ipv4Addr::new(127, 255, 255, 255), net.last_addr());
}
#[test]
fn parse_ipv6_explicit_mask_normalizes_addr() {
assert_eq!(
Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0xc00, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0, 0, 0, 0, 0),
),
Ipv6Network::parse("2a02:6b8:c00:1:2:3:4:5/ffff:ffff:ff00::").unwrap()
);
}
#[test]
fn test_ipv6_addr_as_u128() {
assert_eq!(
0x2a02_06b8_0c00_0000_0000_f800_0000_0000u128,
(*Ipv6Network::parse("2a02:6b8:c00::f800:0:0/ffff:ffff:ff00:0:ffff:f800::")
.unwrap()
.addr())
.into()
);
}
#[test]
fn test_net_ipv6_contains() {
assert!(
Ipv6Network::parse("::/0")
.unwrap()
.contains(&Ipv6Network::parse("::1").unwrap())
);
assert!(
Ipv6Network::parse("::/32")
.unwrap()
.contains(&Ipv6Network::parse("::/33").unwrap())
);
assert!(
!Ipv6Network::parse("::/33")
.unwrap()
.contains(&Ipv6Network::parse("::/32").unwrap())
);
}
#[test]
fn test_net_ipv6_contains_self() {
assert!(
Ipv6Network::parse("::1")
.unwrap()
.contains(&Ipv6Network::parse("::1").unwrap())
);
}
#[test]
fn test_net_ipv6_non_contiguous_contains_contiguous() {
assert!(
Ipv6Network::parse("2a02:6b8:c00::4d71:0:0/ffff:ffff:ff00::ffff:ffff:0:0")
.unwrap()
.contains(&Ipv6Network::parse("2a02:6b8:c00:1234:0:4d71::1").unwrap())
);
}
#[test]
fn test_net_ipv6_is_contiguous() {
assert!(Ipv6Network::parse("::/0").unwrap().is_contiguous());
assert!(Ipv6Network::parse("::1").unwrap().is_contiguous());
assert!(Ipv6Network::parse("2a02:6b8:c00::/40").unwrap().is_contiguous());
assert!(
Ipv6Network::parse("2a02:6b8:c00:1:2:3:4:1/127")
.unwrap()
.is_contiguous()
);
assert!(
Ipv6Network::parse("2a02:6b8:c00:1:2:3:4:1/128")
.unwrap()
.is_contiguous()
);
assert!(Ipv6Network::parse("2a02:6b8:c00:1:2:3:4:1").unwrap().is_contiguous());
assert!(
!Ipv6Network::parse("2a02:6b8:c00::f800:0:0/ffff:ffff:ff00::ffff:ffff:0:0")
.unwrap()
.is_contiguous()
);
assert!(
!Ipv6Network::parse("2a02:6b8:c00::f800:0:0/ffff:ffff:ff00:0:ffff:f800::")
.unwrap()
.is_contiguous()
);
assert!(
!Ipv6Network::parse("2a02:6b8:0:0:1234:5678::/ffff:ffff:0:0:ffff:ffff:0:0")
.unwrap()
.is_contiguous()
);
assert!(
!Ipv6Network::parse("2a02:6b8:0:0:1234:5678::/ffff:ffff:0:0:f0f0:f0f0:f0f0:f0f0")
.unwrap()
.is_contiguous()
);
}
#[test]
fn ipv6_supernet_for() {
let nets = &[
Ipv6Network::parse("2001:db8:1::/32").unwrap(),
Ipv6Network::parse("2001:db8:2::/39").unwrap(),
];
let expected = Ipv6Network::parse("2001:db8::/32").unwrap();
assert_eq!(expected, nets[0].supernet_for(&nets[1..]));
}
#[test]
fn ipv6_supernet_for_wide() {
let nets = &[
Ipv6Network::parse("2013:db8:1::1/64").unwrap(),
Ipv6Network::parse("2013:db8:2::1/64").unwrap(),
];
let expected = Ipv6Network::new(
Ipv6Addr::new(0x2013, 0xdb8, 0, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xfffc, 0xffff, 0, 0, 0, 0),
);
assert_eq!(expected, nets[0].supernet_for(&nets[1..]));
}
#[test]
fn ipv6_supernet_for_unspecified() {
let nets = &[
Ipv6Network::parse("aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa/128").unwrap(),
Ipv6Network::parse("5555:5555:5555:5555:5555:5555:5555:5555/128").unwrap(),
];
let expected = Ipv6Network::parse("::/0").unwrap();
assert_eq!(expected, nets[0].supernet_for(&nets[1..]));
}
#[test]
fn ipv6_supernet_for_partial_agreement() {
let nets = &[
Ipv6Network::parse("8001:db8:1::/34").unwrap(),
Ipv6Network::parse("2013:db8:2::/32").unwrap(),
];
let expected = Ipv6Network::new(
Ipv6Addr::new(0x0001, 0x0db8, 0, 0, 0, 0, 0, 0),
Ipv6Addr::new(0x5fed, 0xffff, 0, 0, 0, 0, 0, 0),
);
assert_eq!(expected, nets[0].supernet_for(&nets[1..]));
}
#[test]
fn ipv6_supernet_for_non_contiguous() {
let nets = &[
Ipv6Network::parse("2a02:6b8:c00::48aa:0:0/ffff:ffff:ff00::ffff:ffff:0:0").unwrap(),
Ipv6Network::parse("2a02:6b8:c00::4707:0:0/ffff:ffff:ff00::ffff:ffff:0:0").unwrap(),
];
let expected = Ipv6Network::parse("2a02:6b8:c00::4002:0:0/ffff:ffff:ff00::ffff:f052:0:0").unwrap();
assert_eq!(expected, nets[0].supernet_for(&nets[1..]));
}
#[test]
fn ipv6_supernet_for_non_contiguous_different_aggregates() {
let nets = &[
Ipv6Network::parse("2a02:6b8:c00::48aa:0:0/ffff:ffff:ff00::ffff:ffff:0:0").unwrap(),
Ipv6Network::parse("2a02:6b8:fc00::4707:0:0/ffff:ffff:ff00::ffff:ffff:0:0").unwrap(),
];
let expected = Ipv6Network::parse("2a02:6b8:c00::4002:0:0/ffff:ffff:f00::ffff:f052:0:0").unwrap();
assert_eq!(expected, nets[0].supernet_for(&nets[1..]));
}
#[test]
fn test_net_display_addr() {
let net = Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), Ipv4Addr::new(255, 255, 255, 255));
assert_eq!("127.0.0.1/32", &format!("{net}"));
let net = Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0, 1),
Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
);
assert_eq!("2a02:6b8::1/128", &format!("{net}"));
}
#[test]
fn test_net_display_cidr() {
let net = Ipv4Network::parse("10.0.0.0/24").unwrap();
assert_eq!("10.0.0.0/24", &format!("{net}"));
let net = Ipv6Network::parse("2a02:6b8:c00:0:1:2::/96").unwrap();
assert_eq!("2a02:6b8:c00:0:1:2::/96", &format!("{net}"));
}
#[test]
fn test_net_display_non_contiguous() {
let net = Ipv4Network::parse("10.0.1.0/255.0.255.0").unwrap();
assert_eq!("10.0.1.0/255.0.255.0", &format!("{net}"));
let net = Ipv6Network(
"2a02:6b8:0:0:0:1234::".parse().unwrap(),
"ffff:ffff:0:0:ffff:ffff:0:0".parse().unwrap(),
);
assert_eq!("2a02:6b8::1234:0:0/ffff:ffff::ffff:ffff:0:0", &format!("{net}"));
let net = Ipv6Network(
"2a02:6b8:0:0:1234:5678::".parse().unwrap(),
"ffff:ffff:0:0:ffff:ffff:0:0".parse().unwrap(),
);
assert_eq!("2a02:6b8::1234:5678:0:0/ffff:ffff::ffff:ffff:0:0", &format!("{net}"));
}
#[test]
fn ipv6_difference_disjoint() {
let a = Ipv6Network::parse("2001:db8::/32").unwrap();
let b = Ipv6Network::parse("fe80::/10").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(diff, vec![a]);
}
#[test]
fn ipv6_difference_subset() {
let a = Ipv6Network::parse("2001:db8::/64").unwrap();
let b = Ipv6Network::parse("2001:db8::/48").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert!(diff.is_empty());
}
#[test]
fn ipv6_difference_same_network() {
let a = Ipv6Network::parse("2001:db8::/32").unwrap();
let diff: Vec<_> = a.difference(&a).collect();
assert!(diff.is_empty());
}
#[test]
fn ipv6_difference_superset() {
let a = Ipv6Network::parse("2001:db8::/48").unwrap();
let b = Ipv6Network::parse("2001:db8::/64").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(diff.len(), 16);
for r in &diff {
assert!(a.contains(r), "part {r} not in A");
assert!(r.is_disjoint(&b), "part {r} intersects B");
}
for i in 0..diff.len() {
for j in (i + 1)..diff.len() {
assert!(diff[i].is_disjoint(&diff[j]), "parts {i} and {j} overlap");
}
}
}
#[test]
fn ipv6_difference_universe_minus_host() {
let a = Ipv6Network::parse("::/0").unwrap();
let b = Ipv6Network::parse("2001:db8::1/128").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(diff.len(), 128);
for r in &diff {
assert!(r.is_contiguous(), "non-contiguous mask in {r}");
assert!(a.contains(r), "part {r} not in A");
assert!(r.is_disjoint(&b), "part {r} intersects B");
}
}
#[test]
fn ipv6_difference_host_minus_universe() {
let a = Ipv6Network::parse("2001:db8::1/128").unwrap();
let b = Ipv6Network::parse("::/0").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert!(diff.is_empty());
}
#[test]
fn ipv6_difference_hosts_same() {
let a = Ipv6Network::parse("2001:db8::1/128").unwrap();
let b = Ipv6Network::parse("2001:db8::1/128").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert!(diff.is_empty());
}
#[test]
fn ipv6_difference_hosts_different() {
let a = Ipv6Network::parse("2001:db8::1/128").unwrap();
let b = Ipv6Network::parse("2001:db8::2/128").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(diff, vec![a]);
}
#[test]
fn ipv6_difference_non_contiguous() {
let a = Ipv6Network::from_bits(
0x2001_0000_0000_0000_0000_0000_0000_0000,
0xffff_0000_0000_0000_0000_0000_0000_0000,
);
let b = Ipv6Network::from_bits(
0x2001_0000_0000_0000_0000_0000_0000_0001,
0xffff_0000_0000_0000_0000_0000_0000_00ff,
);
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(diff.len(), 8);
for r in &diff {
assert!(a.contains(r), "part {r} not in A");
assert!(r.is_disjoint(&b), "part {r} intersects B");
}
}
#[test]
fn ipv6_difference_exact_size() {
let a = Ipv6Network::parse("2001:db8::/48").unwrap();
let b = Ipv6Network::parse("2001:db8::/64").unwrap();
let mut diff = a.difference(&b);
assert_eq!(diff.len(), 16);
diff.next();
assert_eq!(diff.len(), 15);
}
#[test]
fn test_ipv4_binary_split_empty() {
assert!(ipv4_binary_split::<Ipv4Network>(&[]).is_none());
}
#[test]
fn test_ipv4_binary_split_less_than_required_networks() {
let nets = &["127.0.0.1", "127.0.0.2"];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv4Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
assert!(ipv4_binary_split(&nets).is_none());
}
#[test]
fn test_ipv4_binary_split_three_nets() {
let nets = &["127.0.0.1", "127.0.0.2", "127.0.0.3"];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv4Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
let (supernet, range) = ipv4_binary_split(&nets).unwrap();
assert_eq!(Ipv4Network::parse("127.0.0.2/31").unwrap(), supernet);
for (idx, net) in nets.iter().enumerate() {
assert_eq!(range.contains(&idx), supernet.contains(net));
}
}
#[test]
fn test_ipv4_binary_split_more_nets() {
let nets = &[
"10.204.46.68/31",
"10.204.49.12/31",
"10.204.49.14/31",
"10.204.49.18/31",
"10.204.49.32/31",
"10.204.49.34/31",
"10.204.49.36/31",
"10.204.49.38/31",
"10.204.49.40/31",
];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv4Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
let (supernet, range) = ipv4_binary_split(&nets).unwrap();
assert_eq!(Ipv4Network::parse("10.204.49.32/28").unwrap(), supernet);
for (idx, net) in nets.iter().enumerate() {
assert_eq!(range.contains(&idx), supernet.contains(net));
}
}
#[test]
fn test_ipv4_binary_split_even_number_of_nets() {
let nets = &[
"10.204.46.54/31",
"10.204.46.68/31",
"10.204.49.12/31",
"10.204.49.14/31",
"10.204.49.18/31",
"10.204.49.32/31",
"10.204.49.34/31",
"10.204.49.36/31",
"10.204.49.38/31",
"10.204.49.40/31",
];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv4Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
let (supernet, range) = ipv4_binary_split(&nets).unwrap();
assert_eq!(Ipv4Network::parse("10.204.49.32/28").unwrap(), supernet);
for (idx, net) in nets.iter().enumerate() {
assert_eq!(range.contains(&idx), supernet.contains(net));
}
}
#[test]
fn test_ipv4_binary_split_in_the_middle() {
let nets = &[
"10.204.46.50/31",
"10.204.46.54/31",
"10.204.47.22/31",
"10.204.49.24/31",
"10.204.49.26/31",
"10.204.49.28/31",
"10.204.49.32/31",
"10.204.49.34/31",
"10.204.49.36/31",
"10.204.50.38/31",
"10.204.50.40/31",
];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv4Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
let (supernet, range) = ipv4_binary_split(&nets).unwrap();
assert_eq!(Ipv4Network::parse("10.204.49.0/26").unwrap(), supernet);
for (idx, net) in nets.iter().enumerate() {
assert_eq!(range.contains(&idx), supernet.contains(net));
}
}
#[test]
fn test_ipv6_binary_split_empty() {
assert!(ipv6_binary_split::<Ipv6Network>(&[]).is_none());
}
#[test]
fn test_ipv6_binary_split_less_than_required_networks() {
let nets = &["::1", "::2"];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv6Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
assert!(ipv6_binary_split(&nets).is_none());
}
#[test]
fn test_ipv6_binary_split_three_nets() {
let nets = &["::1", "::2", "::3"];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv6Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
let (supernet, range) = ipv6_binary_split(&nets).unwrap();
assert_eq!(Ipv6Network::parse("::2/127").unwrap(), supernet);
for (idx, net) in nets.iter().enumerate() {
assert_eq!(range.contains(&idx), supernet.contains(net));
}
}
#[test]
fn test_ipv6_binary_split_more_nets() {
let nets = &[
"2a02:6b8:0:2a03::/64",
"2a02:6b8:0:2a13::/64",
"2a02:6b8:0:2a16::/64",
"2a02:6b8:0:2a18::/64",
"2a02:6b8:0:2a19::/64",
"2a02:6b8:0:2a1c::/64",
"2a02:6b8:0:2a1d::/64",
"2a02:6b8:0:2a24::/64",
"2a02:6b8:0:2800::/55",
];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv6Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
let (supernet, range) = ipv6_binary_split(&nets).unwrap();
assert_eq!(Ipv6Network::parse("2a02:6b8:0:2a10::/60").unwrap(), supernet);
for (idx, net) in nets.iter().enumerate() {
assert_eq!(range.contains(&idx), supernet.contains(net));
}
}
#[test]
fn test_ipv6_binary_split_even_number_of_nets() {
let nets = &[
"2a02:6b8:0:2318::/64",
"2a02:6b8:0:231e::/64",
"2a02:6b8:0:231f::/64",
"2a02:6b8:0:2302::/64",
"2a02:6b8:0:2303::/64",
"2a02:6b8:0:2305::/64",
"2a02:6b8:0:2306::/64",
"2a02:6b8:0:2307::/64",
"2a02:6b8:0:2311::/64",
"2a02:6b8:0:2314::/64",
];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv6Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
let (supernet, range) = ipv6_binary_split(&nets).unwrap();
assert_eq!(Ipv6Network::parse("2a02:6b8:0:2300::/61").unwrap(), supernet);
for (idx, net) in nets.iter().enumerate() {
assert_eq!(range.contains(&idx), supernet.contains(net));
}
}
#[test]
fn test_ipv6_binary_split_in_the_middle() {
let nets = &[
"2a02:6b8:0:2302::/64",
"2a02:6b8:0:2303::/64",
"2a02:6b8:0:2305::/64",
"2a02:6b8:0:2308::/64",
"2a02:6b8:0:2309::/64",
"2a02:6b8:0:230a::/64",
"2a02:6b8:0:230b::/64",
"2a02:6b8:0:230c::/64",
"2a02:6b8:0:230d::/64",
"2a02:6b8:0:2314::/64",
"2a02:6b8:0:231f::/64",
];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv6Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
let (supernet, range) = ipv6_binary_split(&nets).unwrap();
assert_eq!(Ipv6Network::parse("2a02:6b8:0:2308::/61").unwrap(), supernet);
for (idx, net) in nets.iter().enumerate() {
assert_eq!(range.contains(&idx), supernet.contains(net));
}
}
#[test]
fn test_ipv6_binary_split_non_contiguous() {
let nets = &[
"2a02:6b8:c00::4510:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::4511:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::4512:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::4513:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::4514:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::4515:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::4516:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::4517:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::4518:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::4519:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::451a:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv6Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
let (supernet, range) = ipv6_binary_split(&nets).unwrap();
assert_eq!(
Ipv6Network::parse("2a02:6b8:c00::4510:0:0/ffff:ffff:ff00:0:ffff:fff8::").unwrap(),
supernet
);
for (idx, net) in nets.iter().enumerate() {
assert_eq!(range.contains(&idx), supernet.contains(net));
}
}
#[test]
fn test_ipv6_binary_split_non_contiguous_in_the_middle() {
let nets = &[
"2a02:6b8:c00::2302:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::2303:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::2305:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::2308:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::2309:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::230a:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::230b:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::230c:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::230d:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::2314:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
"2a02:6b8:c00::231f:0:0/ffff:ffff:ff00::ffff:ffff:0:0",
];
let mut nets: Vec<_> = nets.iter().map(|net| Ipv6Network::parse(net).unwrap()).collect();
nets.sort();
nets.dedup();
let (supernet, range) = ipv6_binary_split(&nets).unwrap();
assert_eq!(
Ipv6Network::parse("2a02:6b8:c00::2308:0:0/ffff:ffff:ff00:0:ffff:fff8::").unwrap(),
supernet
);
for (idx, net) in nets.iter().enumerate() {
assert_eq!(range.contains(&idx), supernet.contains(net));
}
}
#[test]
fn test_ipnetwork_parse_v4() {
let expected = IpNetwork::V4(Ipv4Network::new(
Ipv4Addr::new(192, 168, 1, 0),
Ipv4Addr::new(255, 255, 255, 0),
));
assert_eq!(expected, IpNetwork::parse("192.168.1.0/24").unwrap());
}
#[test]
fn test_ipnetwork_parse_v6() {
let expected = IpNetwork::V6(Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0xc00, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0, 0, 0, 0, 0),
));
assert_eq!(expected, IpNetwork::parse("2a02:6b8:c00::/40").unwrap());
}
#[test]
fn test_ipnetwork_parse_invalid() {
assert_eq!(
IpNetParseError::CidrOverflow(CidrOverflowError(33, 32)),
IpNetwork::parse("192.168.1.0/33").unwrap_err(),
);
}
#[test]
fn test_ipnetwork_parse_invalid_v6() {
assert_eq!(
IpNetParseError::CidrOverflow(CidrOverflowError(129, 128)),
IpNetwork::parse("2a02:6b8:c00::/129").unwrap_err(),
);
}
#[test]
fn test_contiguous_ip_network_parse_v4() {
let expected = Contiguous(Ipv4Network::new(
Ipv4Addr::new(192, 168, 1, 0),
Ipv4Addr::new(255, 255, 255, 0),
));
assert_eq!(expected, Contiguous::<Ipv4Network>::parse("192.168.1.0/24").unwrap(),);
}
#[test]
fn test_contiguous_ip_network_parse_v4_invalid() {
assert_eq!(
ContiguousIpNetParseError::NonContiguousNetwork,
Contiguous::<Ipv4Network>::parse("192.168.0.1/255.255.0.255").unwrap_err(),
);
}
#[test]
fn test_contiguous_ip_network_parse_v6() {
let expected = Contiguous(Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0xc00, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0, 0, 0, 0, 0),
));
assert_eq!(expected, Contiguous::<Ipv6Network>::parse("2a02:6b8:c00::/40").unwrap(),);
}
#[test]
fn test_contiguous_ip_network_parse_v6_invalid() {
assert_eq!(
ContiguousIpNetParseError::NonContiguousNetwork,
Contiguous::<Ipv6Network>::parse("2a02:6b8:c00::1234:0:0/ffff:ffff:ff00::ffff:ffff:0:0").unwrap_err(),
);
}
#[test]
fn ipv6_last_addr() {
let net = Ipv6Network::new(
Ipv6Addr::new(0x2001, 0xdb8, 0x1, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 0),
);
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0x1, 0, 0, 0, 0xffff, 0xffff),
net.last_addr()
);
let net = Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0xc00, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0, 0, 0, 0, 0),
);
assert_eq!(
Ipv6Addr::new(0x2a02, 0x6b8, 0x0cff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
net.last_addr()
);
let net = Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0),
);
assert_eq!(
Ipv6Addr::new(0x2a02, 0x6b8, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
net.last_addr()
);
let net = Ipv6Network::new(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0x1),
Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
);
assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0x1), net.last_addr());
let net = Ipv6Network::UNSPECIFIED;
assert_eq!(
Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
net.last_addr()
);
let net = Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0xc00, 0, 0, 0x1234, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0, 0xffff, 0xffff, 0, 0),
);
assert_eq!(
Ipv6Addr::new(0x2a02, 0x6b8, 0x0cff, 0xffff, 0, 0x1234, 0xffff, 0xffff),
net.last_addr()
);
let net = Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0xc00, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0, 0, 0, 0, 0),
);
assert_eq!(
Ipv6Addr::new(0x2a02, 0x6b8, 0x0cff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
net.last_addr()
);
let net = Ipv6Network::new(
Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
);
assert_eq!(
Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff),
net.last_addr()
);
}
#[test]
fn ipnetwork_last_addr() {
use core::net::IpAddr;
let net = IpNetwork::parse("192.168.1.0/24").unwrap();
assert_eq!(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 255)), net.last_addr());
let net = IpNetwork::parse("10.0.0.1").unwrap();
assert_eq!(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), net.last_addr());
let net = IpNetwork::parse("0.0.0.0/0").unwrap();
assert_eq!(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), net.last_addr());
let net = IpNetwork::parse("2001:db8:1::/64").unwrap();
assert_eq!(
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0x1, 0, 0xffff, 0xffff, 0xffff, 0xffff)),
net.last_addr()
);
let net = IpNetwork::parse("2a02:6b8::1").unwrap();
assert_eq!(
IpAddr::V6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0, 0x1)),
net.last_addr()
);
let net = IpNetwork::parse("::/0").unwrap();
assert_eq!(
IpAddr::V6(Ipv6Addr::new(
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
)),
net.last_addr()
);
}
#[test]
fn ipv4_intersects_overlapping_contiguous() {
let a = Ipv4Network::parse("192.168.0.0/16").unwrap();
let b = Ipv4Network::parse("192.168.1.0/24").unwrap();
assert!(a.intersects(&b));
assert!(b.intersects(&a));
assert!(!a.is_disjoint(&b));
assert!(!b.is_disjoint(&a));
}
#[test]
fn ipv4_intersects_disjoint_contiguous() {
let a = Ipv4Network::parse("192.168.0.0/16").unwrap();
let b = Ipv4Network::parse("10.0.0.0/8").unwrap();
assert!(!a.intersects(&b));
assert!(!b.intersects(&a));
assert!(a.is_disjoint(&b));
}
#[test]
fn ipv4_intersects_self() {
let a = Ipv4Network::parse("10.0.0.0/8").unwrap();
assert!(a.intersects(&a));
assert!(!a.is_disjoint(&a));
}
#[test]
fn ipv4_intersects_non_contiguous() {
let a = Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 1), Ipv4Addr::new(255, 0, 0, 255));
let b = Ipv4Network::new(Ipv4Addr::new(10, 1, 0, 0), Ipv4Addr::new(255, 255, 0, 0));
assert!(a.intersects(&b));
assert!(b.intersects(&a));
}
#[test]
fn ipv4_intersects_non_contiguous_disjoint() {
let a = Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 1), Ipv4Addr::new(255, 0, 0, 255));
let b = Ipv4Network::new(Ipv4Addr::new(11, 0, 0, 0), Ipv4Addr::new(255, 0, 0, 0));
assert!(!a.intersects(&b));
assert!(a.is_disjoint(&b));
}
#[test]
fn ipv4_intersection_containment() {
let a = Ipv4Network::parse("192.168.0.0/16").unwrap();
let b = Ipv4Network::parse("192.168.1.0/24").unwrap();
assert_eq!(Some(b), a.intersection(&b));
assert_eq!(Some(b), b.intersection(&a));
}
#[test]
fn ipv4_intersection_identical() {
let a = Ipv4Network::parse("10.0.0.0/8").unwrap();
assert_eq!(Some(a), a.intersection(&a));
}
#[test]
fn ipv4_intersection_disjoint() {
let a = Ipv4Network::parse("192.168.0.0/16").unwrap();
let b = Ipv4Network::parse("10.0.0.0/8").unwrap();
assert_eq!(None, a.intersection(&b));
}
#[test]
fn ipv4_intersection_non_contiguous() {
let a = Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 1), Ipv4Addr::new(255, 0, 0, 255));
let b = Ipv4Network::new(Ipv4Addr::new(10, 1, 0, 0), Ipv4Addr::new(255, 255, 0, 0));
let expected = Ipv4Network::new(Ipv4Addr::new(10, 1, 0, 1), Ipv4Addr::new(255, 255, 0, 255));
assert_eq!(Some(expected), a.intersection(&b));
assert_eq!(Some(expected), b.intersection(&a));
}
#[test]
fn ipv4_intersection_both_non_contiguous() {
let a = Ipv4Network::new(Ipv4Addr::new(10, 0, 10, 0), Ipv4Addr::new(255, 0, 255, 0));
let b = Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 5), Ipv4Addr::new(255, 0, 0, 255));
let expected = Ipv4Network::new(Ipv4Addr::new(10, 0, 10, 5), Ipv4Addr::new(255, 0, 255, 255));
assert_eq!(Some(expected), a.intersection(&b));
assert_eq!(Some(expected), b.intersection(&a));
}
#[test]
fn ipv4_intersection_with_unspecified() {
let unspecified = Ipv4Network::UNSPECIFIED;
let b = Ipv4Network::parse("192.168.1.0/24").unwrap();
assert_eq!(Some(b), unspecified.intersection(&b));
assert_eq!(Some(b), b.intersection(&unspecified));
}
#[test]
fn ipv4_intersection_hosts_same() {
let a = Ipv4Network::parse("10.0.0.1/32").unwrap();
assert_eq!(Some(a), a.intersection(&a));
}
#[test]
fn ipv4_intersection_hosts_different() {
let a = Ipv4Network::parse("10.0.0.1/32").unwrap();
let b = Ipv4Network::parse("10.0.0.2/32").unwrap();
assert_eq!(None, a.intersection(&b));
}
#[test]
fn ipv4_intersection_alternating_masks() {
let a = Ipv4Network::new(Ipv4Addr::new(0xaa, 0, 0xaa, 0), Ipv4Addr::new(0xaa, 0x55, 0xaa, 0x55));
let b = Ipv4Network::new(Ipv4Addr::new(0, 0xaa, 0, 0xaa), Ipv4Addr::new(0x55, 0xaa, 0x55, 0xaa));
assert!(a.intersects(&b));
let expected = Ipv4Network::new(
Ipv4Addr::new(0xaa, 0xaa, 0xaa, 0xaa),
Ipv4Addr::new(0xff, 0xff, 0xff, 0xff),
);
assert_eq!(Some(expected), a.intersection(&b));
}
#[test]
fn ipv4_difference_disjoint() {
let a = Ipv4Network::parse("10.0.0.0/8").unwrap();
let b = Ipv4Network::parse("192.168.0.0/16").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(vec![a], diff);
}
#[test]
fn ipv4_difference_subset() {
let a = Ipv4Network::parse("192.168.1.0/24").unwrap();
let b = Ipv4Network::parse("192.168.0.0/16").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert!(diff.is_empty());
}
#[test]
fn ipv4_difference_same_network() {
let a = Ipv4Network::parse("10.0.0.0/8").unwrap();
let diff: Vec<_> = a.difference(&a).collect();
assert!(diff.is_empty());
}
#[test]
fn ipv4_difference_superset() {
let a = Ipv4Network::parse("192.168.0.0/16").unwrap();
let b = Ipv4Network::parse("192.168.1.0/24").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(8, diff.len());
for r in &diff {
assert!(a.contains(r), "part {r} not in A");
assert!(r.is_disjoint(&b), "part {r} intersects B");
}
for i in 0..diff.len() {
for j in (i + 1)..diff.len() {
assert!(diff[i].is_disjoint(&diff[j]), "parts {i} and {j} overlap");
}
}
}
#[test]
fn ipv4_difference_universe_minus_host() {
let a = Ipv4Network::parse("0.0.0.0/0").unwrap();
let b = Ipv4Network::parse("1.2.3.4/32").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(32, diff.len());
}
#[test]
fn ipv4_difference_host_minus_universe() {
let a = Ipv4Network::parse("1.2.3.4/32").unwrap();
let b = Ipv4Network::parse("0.0.0.0/0").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert!(diff.is_empty());
}
#[test]
fn ipv4_difference_hosts_same() {
let a = Ipv4Network::parse("1.2.3.4/32").unwrap();
let b = Ipv4Network::parse("1.2.3.4/32").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert!(diff.is_empty());
}
#[test]
fn ipv4_difference_hosts_different() {
let a = Ipv4Network::parse("1.2.3.4/32").unwrap();
let b = Ipv4Network::parse("5.6.7.8/32").unwrap();
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(vec![a], diff);
}
#[test]
fn ipv4_difference_verified_by_hand() {
let a = Ipv4Network::UNSPECIFIED;
let b = Ipv4Network::new(Ipv4Addr::new(8, 8, 8, 8), Ipv4Addr::new(255, 255, 255, 255));
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(32, diff.len());
for r in &diff {
assert!(r.is_contiguous(), "non-contiguous mask in {r}");
assert!(a.contains(r), "part {r} not in A");
assert!(r.is_disjoint(&b), "part {r} intersects B");
}
let expected = vec![
Ipv4Network::parse("128.0.0.0/1").unwrap(),
Ipv4Network::parse("64.0.0.0/2").unwrap(),
Ipv4Network::parse("32.0.0.0/3").unwrap(),
Ipv4Network::parse("16.0.0.0/4").unwrap(),
Ipv4Network::parse("0.0.0.0/5").unwrap(),
Ipv4Network::parse("12.0.0.0/6").unwrap(),
Ipv4Network::parse("10.0.0.0/7").unwrap(),
Ipv4Network::parse("9.0.0.0/8").unwrap(),
Ipv4Network::parse("8.128.0.0/9").unwrap(),
Ipv4Network::parse("8.64.0.0/10").unwrap(),
Ipv4Network::parse("8.32.0.0/11").unwrap(),
Ipv4Network::parse("8.16.0.0/12").unwrap(),
Ipv4Network::parse("8.0.0.0/13").unwrap(),
Ipv4Network::parse("8.12.0.0/14").unwrap(),
Ipv4Network::parse("8.10.0.0/15").unwrap(),
Ipv4Network::parse("8.9.0.0/16").unwrap(),
Ipv4Network::parse("8.8.128.0/17").unwrap(),
Ipv4Network::parse("8.8.64.0/18").unwrap(),
Ipv4Network::parse("8.8.32.0/19").unwrap(),
Ipv4Network::parse("8.8.16.0/20").unwrap(),
Ipv4Network::parse("8.8.0.0/21").unwrap(),
Ipv4Network::parse("8.8.12.0/22").unwrap(),
Ipv4Network::parse("8.8.10.0/23").unwrap(),
Ipv4Network::parse("8.8.9.0/24").unwrap(),
Ipv4Network::parse("8.8.8.128/25").unwrap(),
Ipv4Network::parse("8.8.8.64/26").unwrap(),
Ipv4Network::parse("8.8.8.32/27").unwrap(),
Ipv4Network::parse("8.8.8.16/28").unwrap(),
Ipv4Network::parse("8.8.8.0/29").unwrap(),
Ipv4Network::parse("8.8.8.12/30").unwrap(),
Ipv4Network::parse("8.8.8.10/31").unwrap(),
Ipv4Network::parse("8.8.8.9/32").unwrap(),
];
assert_eq!(expected, diff);
}
#[test]
fn ipv4_difference_non_contiguous() {
let a = Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 0), Ipv4Addr::new(255, 0, 0, 0));
let b = Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 1), Ipv4Addr::new(255, 0, 0, 255));
let diff: Vec<_> = a.difference(&b).collect();
assert_eq!(diff.len(), 8);
for r in &diff {
assert!(a.contains(r), "part {r} not in A");
assert!(r.is_disjoint(&b), "part {r} intersects B");
}
let expected = vec![
Ipv4Network::parse("10.0.0.128/255.0.0.128").unwrap(),
Ipv4Network::parse("10.0.0.64/255.0.0.192").unwrap(),
Ipv4Network::parse("10.0.0.32/255.0.0.224").unwrap(),
Ipv4Network::parse("10.0.0.16/255.0.0.240").unwrap(),
Ipv4Network::parse("10.0.0.8/255.0.0.248").unwrap(),
Ipv4Network::parse("10.0.0.4/255.0.0.252").unwrap(),
Ipv4Network::parse("10.0.0.2/255.0.0.254").unwrap(),
Ipv4Network::parse("10.0.0.0/255.0.0.255").unwrap(),
];
assert_eq!(expected, diff);
}
#[test]
fn ipv4_difference_exact_size() {
let a = Ipv4Network::parse("192.168.0.0/16").unwrap();
let b = Ipv4Network::parse("192.168.1.0/24").unwrap();
let mut diff = a.difference(&b);
assert_eq!(8, diff.len());
diff.next();
assert_eq!(7, diff.len());
diff.next();
assert_eq!(6, diff.len());
diff.next();
assert_eq!(5, diff.len());
diff.next();
assert_eq!(4, diff.len());
diff.next();
assert_eq!(3, diff.len());
diff.next();
assert_eq!(2, diff.len());
diff.next();
assert_eq!(1, diff.len());
diff.next();
assert_eq!(0, diff.len());
assert_eq!(None, diff.next());
}
#[test]
fn ipv6_intersects_overlapping_contiguous() {
let a = Ipv6Network::parse("2001:db8::/32").unwrap();
let b = Ipv6Network::parse("2001:db8:1::/48").unwrap();
assert!(a.intersects(&b));
assert!(b.intersects(&a));
assert!(!a.is_disjoint(&b));
assert!(!b.is_disjoint(&a));
}
#[test]
fn ipv6_intersects_disjoint_contiguous() {
let a = Ipv6Network::parse("2001:db8::/32").unwrap();
let b = Ipv6Network::parse("fe80::/10").unwrap();
assert!(!a.intersects(&b));
assert!(!b.intersects(&a));
assert!(a.is_disjoint(&b));
}
#[test]
fn ipv6_intersects_self() {
let a = Ipv6Network::parse("2001:db8::/32").unwrap();
assert!(a.intersects(&a));
assert!(!a.is_disjoint(&a));
}
#[test]
fn ipv6_intersects_non_contiguous() {
let a = Ipv6Network::new(
Ipv6Addr::new(0x2001, 0, 0, 0, 0, 0, 0, 1),
Ipv6Addr::new(0xffff, 0, 0, 0, 0, 0, 0, 0xffff),
);
let b = Ipv6Network::new(
Ipv6Addr::new(0x2001, 1, 0, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0),
);
assert!(a.intersects(&b));
assert!(b.intersects(&a));
}
#[test]
fn ipv6_intersects_non_contiguous_disjoint() {
let a = Ipv6Network::new(
Ipv6Addr::new(0x2001, 0, 0, 0, 0, 0, 0, 1),
Ipv6Addr::new(0xffff, 0, 0, 0, 0, 0, 0, 0xffff),
);
let b = Ipv6Network::new(
Ipv6Addr::new(0x2002, 0, 0, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0, 0, 0, 0, 0, 0, 0),
);
assert!(!a.intersects(&b));
assert!(a.is_disjoint(&b));
}
#[test]
fn ipv6_intersection_containment() {
let a = Ipv6Network::parse("2001:db8::/32").unwrap();
let b = Ipv6Network::parse("2001:db8:1::/48").unwrap();
assert_eq!(Some(b), a.intersection(&b));
assert_eq!(Some(b), b.intersection(&a));
}
#[test]
fn ipv6_intersection_identical() {
let a = Ipv6Network::parse("2001:db8::/32").unwrap();
assert_eq!(Some(a), a.intersection(&a));
}
#[test]
fn ipv6_intersection_disjoint() {
let a = Ipv6Network::parse("2001:db8::/32").unwrap();
let b = Ipv6Network::parse("fe80::/10").unwrap();
assert_eq!(None, a.intersection(&b));
}
#[test]
fn ipv6_intersection_non_contiguous() {
let a = Ipv6Network::new(
Ipv6Addr::new(0x2001, 0, 0, 0, 0, 0, 0, 1),
Ipv6Addr::new(0xffff, 0, 0, 0, 0, 0, 0, 0xffff),
);
let b = Ipv6Network::new(
Ipv6Addr::new(0x2001, 1, 0, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0),
);
let expected = Ipv6Network::new(
Ipv6Addr::new(0x2001, 1, 0, 0, 0, 0, 0, 1),
Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0xffff),
);
assert_eq!(Some(expected), a.intersection(&b));
assert_eq!(Some(expected), b.intersection(&a));
}
#[test]
fn ipv6_intersection_both_non_contiguous() {
let a = Ipv6Network::new(
Ipv6Addr::new(0x2001, 0, 0x000a, 0, 0, 0, 0, 0),
Ipv6Addr::new(0xffff, 0, 0xffff, 0, 0, 0, 0, 0),
);
let b = Ipv6Network::new(
Ipv6Addr::new(0x2001, 0, 0, 0, 0, 0, 0, 5),
Ipv6Addr::new(0xffff, 0, 0, 0, 0, 0, 0, 0xffff),
);
let expected = Ipv6Network::new(
Ipv6Addr::new(0x2001, 0, 0x000a, 0, 0, 0, 0, 5),
Ipv6Addr::new(0xffff, 0, 0xffff, 0, 0, 0, 0, 0xffff),
);
assert_eq!(Some(expected), a.intersection(&b));
assert_eq!(Some(expected), b.intersection(&a));
}
#[test]
fn ipv6_intersection_with_unspecified() {
let unspecified = Ipv6Network::UNSPECIFIED;
let b = Ipv6Network::parse("2001:db8:1::/48").unwrap();
assert_eq!(Some(b), unspecified.intersection(&b));
assert_eq!(Some(b), b.intersection(&unspecified));
}
#[test]
fn ipv6_intersection_hosts_same() {
let a = Ipv6Network::parse("::1/128").unwrap();
assert_eq!(Some(a), a.intersection(&a));
}
#[test]
fn ipv6_intersection_hosts_different() {
let a = Ipv6Network::parse("::1/128").unwrap();
let b = Ipv6Network::parse("::2/128").unwrap();
assert_eq!(None, a.intersection(&b));
}
#[test]
fn ipv6_intersection_alternating_masks() {
let a = Ipv6Network::new(
Ipv6Addr::new(0xaa00, 0, 0xaa00, 0, 0xaa00, 0, 0xaa00, 0),
Ipv6Addr::new(0xff00, 0x00ff, 0xff00, 0x00ff, 0xff00, 0x00ff, 0xff00, 0x00ff),
);
let b = Ipv6Network::new(
Ipv6Addr::new(0x00bb, 0, 0x00bb, 0, 0x00bb, 0, 0x00bb, 0),
Ipv6Addr::new(0x00ff, 0xff00, 0x00ff, 0xff00, 0x00ff, 0xff00, 0x00ff, 0xff00),
);
assert!(a.intersects(&b));
let expected = Ipv6Network::new(
Ipv6Addr::new(0xaabb, 0, 0xaabb, 0, 0xaabb, 0, 0xaabb, 0),
Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
);
assert_eq!(Some(expected), a.intersection(&b));
}
#[test]
fn ipv4_merge_identical() {
let a = Ipv4Network::parse("192.168.1.0/24").unwrap();
assert_eq!(Some(a), a.merge(&a));
}
#[test]
fn ipv4_merge_adjacent_contiguous() {
let a = Ipv4Network::parse("192.168.0.0/24").unwrap();
let b = Ipv4Network::parse("192.168.1.0/24").unwrap();
let expected = Ipv4Network::parse("192.168.0.0/23").unwrap();
assert_eq!(Some(expected), a.merge(&b));
assert_eq!(Some(expected), b.merge(&a));
}
#[test]
fn ipv4_merge_adjacent_non_contiguous() {
let a = Ipv4Network::parse("192.168.0.0/255.255.255.0").unwrap();
let b = Ipv4Network::parse("192.168.2.0/255.255.255.0").unwrap();
let expected = Ipv4Network::parse("192.168.0.0/255.255.253.0").unwrap();
assert_eq!(Some(expected), a.merge(&b));
assert_eq!(Some(expected), b.merge(&a));
}
#[test]
fn ipv4_merge_non_adjacent() {
let a = Ipv4Network::parse("192.168.0.0/24").unwrap();
let b = Ipv4Network::parse("192.168.3.0/24").unwrap();
assert_eq!(None, a.merge(&b));
assert_eq!(None, b.merge(&a));
}
#[test]
fn ipv4_merge_containment() {
let a = Ipv4Network::parse("10.0.0.0/8").unwrap();
let b = Ipv4Network::parse("10.1.0.0/16").unwrap();
assert_eq!(Some(a), a.merge(&b));
assert_eq!(Some(a), b.merge(&a));
}
#[test]
fn ipv4_merge_different_masks_no_containment() {
let a = Ipv4Network::parse("10.0.0.0/8").unwrap();
let b = Ipv4Network::parse("172.16.0.0/16").unwrap();
assert_eq!(None, a.merge(&b));
assert_eq!(None, b.merge(&a));
}
#[test]
fn ipv4_merge_non_contiguous_masks() {
let a = Ipv4Network::parse("10.0.0.1/255.255.0.255").unwrap();
let b = Ipv4Network::parse("10.1.0.1/255.255.0.255").unwrap();
let expected = Ipv4Network::parse("10.0.0.1/255.254.0.255").unwrap();
assert_eq!(Some(expected), a.merge(&b));
assert_eq!(Some(expected), b.merge(&a));
}
#[test]
fn ipv6_merge_adjacent() {
let a = Ipv6Network::parse("2001:db8::/48").unwrap();
let b = Ipv6Network::parse("2001:db8:1::/48").unwrap();
let expected = Ipv6Network::parse("2001:db8::/47").unwrap();
assert_eq!(Some(expected), a.merge(&b));
assert_eq!(Some(expected), b.merge(&a));
}
#[test]
fn ipv6_merge_containment() {
let a = Ipv6Network::parse("2001:db8::/32").unwrap();
let b = Ipv6Network::parse("2001:db8:1::/48").unwrap();
assert_eq!(Some(a), a.merge(&b));
assert_eq!(Some(a), b.merge(&a));
}
#[test]
fn ipv6_merge_non_contiguous() {
let a = Ipv6Network::parse("2001::1/ffff:ff00::ffff").unwrap();
let b = Ipv6Network::parse("2001:100::1/ffff:ff00::ffff").unwrap();
let expected = Ipv6Network::parse("2001::1/ffff:fe00::ffff").unwrap();
assert_eq!(Some(expected), a.merge(&b));
assert_eq!(Some(expected), b.merge(&a));
}
#[test]
fn ipv6_merge_non_contiguous_none() {
let a = Ipv6Network::parse("2001::1/ffff:ff00::ffff").unwrap();
let b = Ipv6Network::parse("2001:300::1/ffff:ff00::ffff").unwrap();
assert_eq!(None, a.merge(&b));
}
#[test]
fn ipv4_is_adjacent_contiguous() {
let a = Ipv4Network::parse("192.168.0.0/24").unwrap();
let b = Ipv4Network::parse("192.168.1.0/24").unwrap();
assert!(a.is_adjacent(&b));
assert!(b.is_adjacent(&a));
}
#[test]
fn ipv4_is_adjacent_non_contiguous() {
let a = Ipv4Network::parse("10.0.0.1/255.255.0.255").unwrap();
let b = Ipv4Network::parse("10.1.0.1/255.255.0.255").unwrap();
assert!(a.is_adjacent(&b));
assert!(b.is_adjacent(&a));
}
#[test]
fn ipv4_is_adjacent_identical() {
let a = Ipv4Network::parse("192.168.0.0/24").unwrap();
assert!(!a.is_adjacent(&a));
}
#[test]
fn ipv4_is_adjacent_different_masks() {
let a = Ipv4Network::parse("192.168.0.0/24").unwrap();
let b = Ipv4Network::parse("192.168.0.0/16").unwrap();
assert!(!a.is_adjacent(&b));
}
#[test]
fn ipv4_is_adjacent_non_adjacent_same_mask() {
let a = Ipv4Network::parse("192.168.0.0/24").unwrap();
let b = Ipv4Network::parse("192.168.3.0/24").unwrap();
assert!(!a.is_adjacent(&b));
}
#[test]
fn ipv6_is_adjacent_contiguous() {
let a = Ipv6Network::parse("2001:db8::/48").unwrap();
let b = Ipv6Network::parse("2001:db8:1::/48").unwrap();
assert!(a.is_adjacent(&b));
assert!(b.is_adjacent(&a));
}
#[test]
fn ipv6_is_adjacent_non_contiguous() {
let a = Ipv6Network::parse("2a02:6b8:c00::1234:0:0/ffff:ffff:ff00::ffff:ffff:0:0").unwrap();
let b = Ipv6Network::parse("2a02:6b8:c00::1235:0:0/ffff:ffff:ff00::ffff:ffff:0:0").unwrap();
assert!(a.is_adjacent(&b));
assert!(b.is_adjacent(&a));
}
#[test]
fn ipv6_is_adjacent_identical() {
let a = Ipv6Network::parse("2001:db8::/48").unwrap();
assert!(!a.is_adjacent(&a));
}
#[test]
fn ipv6_is_adjacent_different_masks() {
let a = Ipv6Network::parse("2001:db8::/48").unwrap();
let b = Ipv6Network::parse("2001:db8::/32").unwrap();
assert!(!a.is_adjacent(&b));
}
#[test]
fn ipv6_is_adjacent_non_adjacent_same_mask() {
let a = Ipv6Network::parse("2001:db8::/48").unwrap();
let b = Ipv6Network::parse("2001:db8:5::/48").unwrap();
assert!(!a.is_adjacent(&b));
}
#[test]
fn ip_is_adjacent_mixed_families() {
let v4 = IpNetwork::parse("10.0.0.0/8").unwrap();
let v6 = IpNetwork::parse("2001:db8::/32").unwrap();
assert!(!v4.is_adjacent(&v6));
}
#[test]
fn ipv4_aggregate_empty() {
let result = ipv4_aggregate(&mut []);
assert!(result.is_empty());
}
#[test]
fn ipv4_aggregate_single() {
let net = Ipv4Network::parse("10.0.0.0/8").unwrap();
let mut nets = [net];
let result = ipv4_aggregate(&mut nets);
assert_eq!(result, &[net]);
}
#[test]
fn ipv4_aggregate_duplicates() {
let net = Ipv4Network::parse("10.0.0.0/8").unwrap();
let mut nets = [net, net, net];
let result = ipv4_aggregate(&mut nets);
assert_eq!(result, &[net]);
}
#[test]
fn ipv4_aggregate_containment() {
let mut nets = [
Ipv4Network::parse("10.0.0.0/8").unwrap(),
Ipv4Network::parse("10.1.0.0/16").unwrap(),
Ipv4Network::parse("10.1.1.0/24").unwrap(),
];
let result = ipv4_aggregate(&mut nets);
assert_eq!(result, &[Ipv4Network::parse("10.0.0.0/8").unwrap()]);
}
#[test]
fn ipv4_aggregate_simple_merge() {
let mut nets = [
Ipv4Network::parse("192.168.0.0/24").unwrap(),
Ipv4Network::parse("192.168.1.0/24").unwrap(),
];
let result = ipv4_aggregate(&mut nets);
assert_eq!(result, &[Ipv4Network::parse("192.168.0.0/23").unwrap()]);
}
#[test]
fn ipv4_aggregate_multi_level_merge() {
let mut nets = [
Ipv4Network::parse("192.168.0.0/24").unwrap(),
Ipv4Network::parse("192.168.1.0/24").unwrap(),
Ipv4Network::parse("192.168.2.0/24").unwrap(),
Ipv4Network::parse("192.168.3.0/24").unwrap(),
];
let result = ipv4_aggregate(&mut nets);
assert_eq!(result, &[Ipv4Network::parse("192.168.0.0/22").unwrap()]);
}
#[test]
fn ipv4_aggregate_non_adjacent() {
let mut nets = [
Ipv4Network::parse("192.168.0.0/24").unwrap(),
Ipv4Network::parse("192.168.3.0/24").unwrap(),
];
let result = ipv4_aggregate(&mut nets);
assert_eq!(
result,
&[
Ipv4Network::parse("192.168.0.0/24").unwrap(),
Ipv4Network::parse("192.168.3.0/24").unwrap(),
]
);
}
#[test]
fn ipv4_aggregate_mixed_containment_and_merge() {
let mut nets = [
Ipv4Network::parse("10.0.0.0/8").unwrap(),
Ipv4Network::parse("10.1.0.0/16").unwrap(),
Ipv4Network::parse("192.168.0.0/24").unwrap(),
Ipv4Network::parse("192.168.1.0/24").unwrap(),
];
let result = ipv4_aggregate(&mut nets);
assert_eq!(
result,
&[
Ipv4Network::parse("10.0.0.0/8").unwrap(),
Ipv4Network::parse("192.168.0.0/23").unwrap(),
]
);
}
#[test]
fn ipv4_aggregate_already_minimal() {
let mut nets = [
Ipv4Network::parse("10.0.0.0/8").unwrap(),
Ipv4Network::parse("172.16.0.0/12").unwrap(),
Ipv4Network::parse("192.168.0.0/16").unwrap(),
];
let result = ipv4_aggregate(&mut nets);
assert_eq!(
result,
&[
Ipv4Network::parse("10.0.0.0/8").unwrap(),
Ipv4Network::parse("172.16.0.0/12").unwrap(),
Ipv4Network::parse("192.168.0.0/16").unwrap(),
]
);
}
#[test]
fn ipv4_aggregate_non_contiguous_containment() {
let mut nets = [
Ipv4Network::parse("10.0.0.1/255.0.0.255").unwrap(), Ipv4Network::parse("10.1.0.0/255.255.0.0").unwrap(), Ipv4Network::parse("10.2.0.1/255.255.0.255").unwrap(), ];
let result = ipv4_aggregate(&mut nets);
assert_eq!(
result,
&[
Ipv4Network::parse("10.0.0.1/255.0.0.255").unwrap(),
Ipv4Network::parse("10.1.0.0/255.255.0.0").unwrap(),
]
);
}
#[test]
fn ipv4_aggregate_non_contiguous_merge_breaks_sort_order() {
let mut nets = [
Ipv4Network::from_bits(0, 0x80000001), Ipv4Network::from_bits(0, 0x80000002), Ipv4Network::from_bits(2, 0x80000002), ];
let result = ipv4_aggregate(&mut nets);
assert_eq!(result, &[Ipv4Network::from_bits(0, 0x80000000)]);
}
#[test]
fn ipv4_aggregate_non_contiguous_merge() {
let mut nets = [
Ipv4Network::parse("10.0.0.1/255.0.0.255").unwrap(),
Ipv4Network::parse("10.0.0.0/255.0.0.255").unwrap(),
];
let result = ipv4_aggregate(&mut nets);
assert_eq!(result, &[Ipv4Network::parse("10.0.0.0/255.0.0.254").unwrap()]);
}
#[test]
fn ipv4_aggregate_full_octet() {
let mut nets: Vec<_> = (0..=255u32)
.map(|i| Ipv4Network::from_bits((10 << 24) | (0 << 16) | (i << 8), 0xFFFFFF00))
.collect();
let result = ipv4_aggregate(&mut nets);
assert_eq!(result, &[Ipv4Network::parse("10.0.0.0/16").unwrap()]);
}
#[test]
fn ipv6_aggregate_simple_merge() {
let mut nets = [
Ipv6Network::parse("2001:db8::/48").unwrap(),
Ipv6Network::parse("2001:db8:1::/48").unwrap(),
];
let result = ipv6_aggregate(&mut nets);
assert_eq!(result, &[Ipv6Network::parse("2001:db8::/47").unwrap()]);
}
#[test]
fn ipv6_aggregate_containment_and_merge() {
let mut nets = [
Ipv6Network::parse("fe80::/10").unwrap(),
Ipv6Network::parse("fe80::/64").unwrap(),
Ipv6Network::parse("2001:db8::/48").unwrap(),
Ipv6Network::parse("2001:db8:1::/48").unwrap(),
];
let result = ipv6_aggregate(&mut nets);
assert_eq!(
result,
&[
Ipv6Network::parse("2001:db8::/47").unwrap(),
Ipv6Network::parse("fe80::/10").unwrap(),
]
);
}
#[test]
fn ipnetwork_addr_v4() {
let net = IpNetwork::parse("192.168.1.0/24").unwrap();
assert_eq!(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 0)), net.addr());
}
#[test]
fn ipnetwork_addr_v6() {
let net = IpNetwork::parse("2001:db8::/32").unwrap();
assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)), net.addr());
}
#[test]
fn ipnetwork_mask_v4() {
let net = IpNetwork::parse("10.0.0.0/8").unwrap();
assert_eq!(IpAddr::V4(Ipv4Addr::new(255, 0, 0, 0)), net.mask());
}
#[test]
fn ipnetwork_mask_v6() {
let net = IpNetwork::parse("2001:db8::/32").unwrap();
assert_eq!(IpAddr::V6(Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0)), net.mask());
}
#[test]
fn ipnetwork_mask_non_contiguous_v4() {
let net = IpNetwork::parse("192.168.0.1/255.255.0.255").unwrap();
assert_eq!(IpAddr::V4(Ipv4Addr::new(255, 255, 0, 255)), net.mask());
}
#[test]
fn ipnetwork_mask_non_contiguous_v6() {
let net = IpNetwork::parse("2001:db8::1/ffff:ffff::ffff:ffff:0:0").unwrap();
assert_eq!(
IpAddr::V6(Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0xffff, 0xffff, 0, 0)),
net.mask()
);
}
#[test]
fn ipnetwork_contains_v4() {
let a = IpNetwork::parse("192.168.0.0/16").unwrap();
let b = IpNetwork::parse("192.168.1.0/24").unwrap();
assert!(a.contains(&b));
assert!(!b.contains(&a));
}
#[test]
fn ipnetwork_contains_v6() {
let a = IpNetwork::parse("2001:db8::/32").unwrap();
let b = IpNetwork::parse("2001:db8:1::/48").unwrap();
assert!(a.contains(&b));
assert!(!b.contains(&a));
}
#[test]
fn ipnetwork_contains_different_families() {
let v4 = IpNetwork::parse("10.0.0.0/8").unwrap();
let v6 = IpNetwork::parse("2001:db8::/32").unwrap();
assert!(!v4.contains(&v6));
assert!(!v6.contains(&v4));
}
#[test]
fn ipnetwork_contains_non_contiguous_v4() {
let a = IpNetwork::parse("10.0.0.0/255.0.0.0").unwrap();
let b = IpNetwork::parse("10.0.0.1/255.0.0.255").unwrap();
assert!(a.contains(&b));
assert!(!b.contains(&a));
}
#[test]
fn ipnetwork_contains_non_contiguous_v6() {
let a = IpNetwork::parse("2001:db8::/ffff:ffff::").unwrap();
let b = IpNetwork::parse("2001:db8::1/ffff:ffff::ffff").unwrap();
assert!(a.contains(&b));
assert!(!b.contains(&a));
}
#[test]
fn ipnetwork_to_contiguous_v4() {
let net = IpNetwork::parse("192.168.0.1/255.255.0.255").unwrap();
let expected = IpNetwork::parse("192.168.0.0/16").unwrap();
assert_eq!(expected, net.to_contiguous());
}
#[test]
fn ipnetwork_to_contiguous_v6() {
let net = IpNetwork::parse("2001:db8::1/ffff:ffff:ff00::ffff:ffff:0:0").unwrap();
let expected = IpNetwork::parse("2001:db8::/40").unwrap();
assert_eq!(expected, net.to_contiguous());
}
#[test]
fn ipnetwork_to_contiguous_already_contiguous_v4() {
let net = IpNetwork::parse("10.0.0.0/8").unwrap();
assert_eq!(net, net.to_contiguous());
}
#[test]
fn ipnetwork_to_contiguous_already_contiguous_v6() {
let net = IpNetwork::parse("2001:db8::/32").unwrap();
assert_eq!(net, net.to_contiguous());
}
fn arb_ipv4_network() -> impl Strategy<Value = Ipv4Network> {
(any::<u32>(), any::<u32>()).prop_map(|(a, m)| Ipv4Network::from_bits(a, m))
}
fn arb_ipv6_network() -> impl Strategy<Value = Ipv6Network> {
(any::<u128>(), any::<u128>()).prop_map(|(a, m)| Ipv6Network::from_bits(a, m))
}
proptest! {
#[test]
fn prop_ipv4_intersects_is_not_disjoint(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
prop_assert_eq!(a.intersects(&b), !a.is_disjoint(&b));
}
#[test]
fn prop_ipv4_intersection_commutativity(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
prop_assert_eq!(a.intersection(&b), b.intersection(&a));
}
#[test]
fn prop_ipv4_contains_implies_intersection_eq_inner(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
if a.contains(&b) {
prop_assert_eq!(a.intersection(&b), Some(b));
}
}
#[test]
fn prop_ipv4_intersection_subset_of_both(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
if let Some(c) = a.intersection(&b) {
prop_assert!(a.contains(&c), "intersection not contained in a: a={a}, b={b}, c={c}");
prop_assert!(b.contains(&c), "intersection not contained in b: a={a}, b={b}, c={c}");
}
}
#[test]
fn prop_ipv4_self_intersection_is_self(
a in arb_ipv4_network(),
) {
prop_assert_eq!(a.intersection(&a), Some(a));
}
#[test]
fn prop_ipv4_intersection_is_normalized(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
if let Some(c) = a.intersection(&b) {
let (addr, mask) = c.to_bits();
prop_assert_eq!(addr & mask, addr, "intersection not normalized: a={}, b={}, c={}", a, b, c);
}
}
#[test]
fn prop_ipv4_intersection_membership_brute_force(
a_addr in 0u32..=255,
a_mask in 0u32..=255,
b_addr in 0u32..=255,
b_mask in 0u32..=255,
) {
let a = Ipv4Network::from_bits((a_addr & a_mask) << 24, a_mask << 24);
let b = Ipv4Network::from_bits((b_addr & b_mask) << 24, b_mask << 24);
let result = a.intersection(&b);
let (a_a, a_m) = a.to_bits();
let (b_a, b_m) = b.to_bits();
for x in 0u32..=255 {
let xaddr = x << 24;
let in_a = (xaddr & a_m) == a_a;
let in_b = (xaddr & b_m) == b_a;
let in_result = match result {
Some(r) => {
let (r_a, r_m) = r.to_bits();
(xaddr & r_m) == r_a
}
None => false,
};
prop_assert_eq!(
in_a && in_b,
in_result,
"x={}, a={}, b={}, result={:?}", x, a, b, result
);
}
}
#[test]
fn prop_ipv4_merge_commutativity(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
prop_assert_eq!(a.merge(&b), b.merge(&a));
}
#[test]
fn prop_ipv4_merge_self_is_self(
a in arb_ipv4_network(),
) {
prop_assert_eq!(a.merge(&a), Some(a));
}
#[test]
fn prop_ipv4_merge_result_contains_both(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
if let Some(c) = a.merge(&b) {
prop_assert!(c.contains(&a), "merge result {c} must contain {a}");
prop_assert!(c.contains(&b), "merge result {c} must contain {b}");
}
}
#[test]
fn prop_ipv4_merge_is_normalized(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
if let Some(c) = a.merge(&b) {
let (addr, mask) = c.to_bits();
prop_assert_eq!(addr & mask, addr, "merge result not normalized: a={}, b={}, c={}", a, b, c);
}
}
#[test]
fn prop_ipv4_merge_equals_supernet_when_some(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
if let Some(c) = a.merge(&b) {
let supernet = a.supernet_for(&[b]);
prop_assert_eq!(c, supernet, "merge must equal supernet_for when Some: a={}, b={}", a, b);
}
}
#[test]
fn prop_ipv4_merge_membership_brute_force(
a_addr in 0u32..=255,
a_mask in 0u32..=255,
b_addr in 0u32..=255,
b_mask in 0u32..=255,
) {
let a = Ipv4Network::from_bits((a_addr & a_mask) << 24, a_mask << 24);
let b = Ipv4Network::from_bits((b_addr & b_mask) << 24, b_mask << 24);
let result = a.merge(&b);
let (a_a, a_m) = a.to_bits();
let (b_a, b_m) = b.to_bits();
for x in 0u32..=255 {
let xaddr = x << 24;
let in_a = (xaddr & a_m) == a_a;
let in_b = (xaddr & b_m) == b_a;
let in_result = match result {
Some(r) => {
let (r_a, r_m) = r.to_bits();
(xaddr & r_m) == r_a
}
None => false,
};
if in_a || in_b {
if let Some(..) = result {
prop_assert!(
in_result,
"x={x} in A∪B but not in merge result: a={a}, b={b}, result={result:?}"
);
}
}
if in_result {
prop_assert!(
in_a || in_b,
"x={x} in merge result but not in A∪B: a={a}, b={b}, result={result:?}"
);
}
}
}
#[test]
fn prop_ipv4_is_adjacent_commutativity(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
prop_assert_eq!(a.is_adjacent(&b), b.is_adjacent(&a));
}
#[test]
fn prop_ipv4_adjacent_implies_merge_some(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
if a.is_adjacent(&b) {
prop_assert!(a.merge(&b).is_some(), "adjacent but merge returned None: a={a}, b={b}");
}
}
#[test]
fn prop_ipv4_same_mask_not_adjacent_not_identical_implies_merge_none(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
let (_, m1) = a.to_bits();
let (_, m2) = b.to_bits();
if m1 == m2 && !a.is_adjacent(&b) && a != b {
prop_assert_eq!(a.merge(&b), None, "same mask, not adjacent, not identical, but merge returned Some: a={}, b={}", a, b);
}
}
#[test]
fn prop_ipv6_intersects_is_not_disjoint(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
prop_assert_eq!(a.intersects(&b), !a.is_disjoint(&b));
}
#[test]
fn prop_ipv6_intersection_commutativity(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
prop_assert_eq!(a.intersection(&b), b.intersection(&a));
}
#[test]
fn prop_ipv6_contains_implies_intersection_eq_inner(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
if a.contains(&b) {
prop_assert_eq!(a.intersection(&b), Some(b));
}
}
#[test]
fn prop_ipv6_intersection_subset_of_both(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
if let Some(c) = a.intersection(&b) {
prop_assert!(a.contains(&c), "intersection not contained in a: a={a}, b={b}, c={c}");
prop_assert!(b.contains(&c), "intersection not contained in b: a={a}, b={b}, c={c}");
}
}
#[test]
fn prop_ipv6_self_intersection_is_self(
a in arb_ipv6_network(),
) {
prop_assert_eq!(a.intersection(&a), Some(a));
}
#[test]
fn prop_ipv6_intersection_is_normalized(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
if let Some(c) = a.intersection(&b) {
let (addr, mask) = c.to_bits();
prop_assert_eq!(addr & mask, addr, "intersection not normalized: a={}, b={}, c={}", a, b, c);
}
}
#[test]
fn prop_ipv6_intersection_membership_brute_force(
a_addr in 0u128..=255,
a_mask in 0u128..=255,
b_addr in 0u128..=255,
b_mask in 0u128..=255,
) {
let a = Ipv6Network::from_bits((a_addr & a_mask) << 120, a_mask << 120);
let b = Ipv6Network::from_bits((b_addr & b_mask) << 120, b_mask << 120);
let result = a.intersection(&b);
let (a_a, a_m) = a.to_bits();
let (b_a, b_m) = b.to_bits();
for x in 0u128..=255 {
let xaddr = x << 120;
let in_a = (xaddr & a_m) == a_a;
let in_b = (xaddr & b_m) == b_a;
let in_result = match result {
Some(r) => {
let (r_a, r_m) = r.to_bits();
(xaddr & r_m) == r_a
}
None => false,
};
prop_assert_eq!(
in_a && in_b,
in_result,
"x={}, a={}, b={}, result={:?}", x, a, b, result
);
}
}
#[test]
fn prop_ipv4_difference_parts_in_source(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
for r in a.difference(&b) {
prop_assert!(a.contains(&r), "part {r} not in a={a}");
}
}
#[test]
fn prop_ipv4_difference_parts_disjoint_from_other(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
for r in a.difference(&b) {
prop_assert!(b.is_disjoint(&r), "part {r} intersects b={b}");
}
}
#[test]
fn prop_ipv4_difference_pairwise_disjoint(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
let parts: Vec<_> = a.difference(&b).collect();
for i in 0..parts.len() {
for j in (i + 1)..parts.len() {
prop_assert!(
parts[i].is_disjoint(&parts[j]),
"parts {i} and {j} overlap: {} vs {}", parts[i], parts[j]
);
}
}
}
#[test]
fn prop_ipv4_difference_self_is_empty(
a in arb_ipv4_network(),
) {
prop_assert_eq!(a.difference(&a).len(), 0);
}
#[test]
fn prop_ipv4_difference_completeness(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
let size = |n: &Ipv4Network| -> u64 {
let (.., m) = n.to_bits();
1u64 << (!m).count_ones()
};
let a_size = size(&a);
let inter_size = a.intersection(&b).map_or(0, |c| size(&c));
let diff_size: u64 = a.difference(&b).map(|r| size(&r)).sum();
prop_assert_eq!(
diff_size + inter_size,
a_size,
"a={}, b={}, inter={:?}", a, b, a.intersection(&b)
);
}
#[test]
fn prop_ipv6_difference_parts_in_source(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
for r in a.difference(&b) {
prop_assert!(a.contains(&r), "part {r} not in a={a}");
}
}
#[test]
fn prop_ipv6_difference_parts_disjoint_from_other(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
for r in a.difference(&b) {
prop_assert!(b.is_disjoint(&r), "part {r} intersects b={b}");
}
}
#[test]
fn prop_ipv6_difference_pairwise_disjoint(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
let parts: Vec<_> = a.difference(&b).collect();
for i in 0..parts.len() {
for j in (i + 1)..parts.len() {
prop_assert!(
parts[i].is_disjoint(&parts[j]),
"parts {i} and {j} overlap: {} vs {}", parts[i], parts[j]
);
}
}
}
#[test]
fn prop_ipv6_difference_self_is_empty(
a in arb_ipv6_network(),
) {
prop_assert_eq!(a.difference(&a).len(), 0);
}
#[test]
fn prop_ipv6_difference_completeness(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
let size = |n: &Ipv6Network| -> u128 {
let (.., m) = n.to_bits();
1u128 << (!m).count_ones()
};
let a_size = size(&a);
let inter_size = a.intersection(&b).map_or(0, |c| size(&c));
let diff_size: u128 = a.difference(&b).map(|r| size(&r)).sum();
prop_assert_eq!(
diff_size + inter_size,
a_size,
"a={}, b={}, inter={:?}", a, b, a.intersection(&b)
);
}
#[test]
fn prop_ipv4_supernet_for_contains_all(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
let supernet = a.supernet_for(&[b]);
prop_assert!(supernet.contains(&a), "supernet {supernet} must contain {a}");
prop_assert!(supernet.contains(&b), "supernet {supernet} must contain {b}");
}
#[test]
fn prop_ipv6_supernet_for_contains_all(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
let supernet = a.supernet_for(&[b]);
prop_assert!(supernet.contains(&a), "supernet {supernet} must contain {a}");
prop_assert!(supernet.contains(&b), "supernet {supernet} must contain {b}");
}
#[test]
fn prop_ipv4_supernet_for_diff_intersection(
a in arb_ipv4_network(),
b in arb_ipv4_network(),
) {
let diff: Vec<_> = a.difference(&b).collect();
let intersected = a.intersection(&b);
if diff.is_empty() && intersected.is_none() {
return Ok(());
}
let mut decomposed = diff;
if let Some(c) = intersected {
decomposed.push(c);
}
let supernet = decomposed[0].supernet_for(&decomposed[1..]);
prop_assert_eq!(supernet, a, "supernet_for(A\\B ∪ A∩B) must equal A");
}
#[test]
fn prop_ipv6_supernet_for_diff_intersection(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
let diff: Vec<_> = a.difference(&b).collect();
let intersected = a.intersection(&b);
if diff.is_empty() && intersected.is_none() {
return Ok(());
}
let mut decomposed = diff;
if let Some(c) = intersected {
decomposed.push(c);
}
let supernet = decomposed[0].supernet_for(&decomposed[1..]);
prop_assert_eq!(supernet, a, "supernet_for(A\\B ∪ A∩B) must equal A");
}
#[test]
fn prop_ipv6_merge_commutativity(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
prop_assert_eq!(a.merge(&b), b.merge(&a));
}
#[test]
fn prop_ipv6_merge_self_is_self(
a in arb_ipv6_network(),
) {
prop_assert_eq!(a.merge(&a), Some(a));
}
#[test]
fn prop_ipv6_merge_result_contains_both(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
if let Some(c) = a.merge(&b) {
prop_assert!(c.contains(&a), "merge result {c} must contain {a}");
prop_assert!(c.contains(&b), "merge result {c} must contain {b}");
}
}
#[test]
fn prop_ipv6_merge_is_normalized(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
if let Some(c) = a.merge(&b) {
let (addr, mask) = c.to_bits();
prop_assert_eq!(addr & mask, addr, "merge result not normalized: a={}, b={}, c={}", a, b, c);
}
}
#[test]
fn prop_ipv6_merge_equals_supernet_when_some(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
if let Some(c) = a.merge(&b) {
let supernet = a.supernet_for(&[b]);
prop_assert_eq!(c, supernet, "merge must equal supernet_for when Some: a={}, b={}", a, b);
}
}
#[test]
fn prop_ipv6_merge_membership_brute_force(
a_addr in 0u128..=255,
a_mask in 0u128..=255,
b_addr in 0u128..=255,
b_mask in 0u128..=255,
) {
let a = Ipv6Network::from_bits((a_addr & a_mask) << 120, a_mask << 120);
let b = Ipv6Network::from_bits((b_addr & b_mask) << 120, b_mask << 120);
let result = a.merge(&b);
let (a_a, a_m) = a.to_bits();
let (b_a, b_m) = b.to_bits();
for x in 0u128..=255 {
let xaddr = x << 120;
let in_a = (xaddr & a_m) == a_a;
let in_b = (xaddr & b_m) == b_a;
let in_result = match result {
Some(r) => {
let (r_a, r_m) = r.to_bits();
(xaddr & r_m) == r_a
}
None => false,
};
if in_a || in_b {
if let Some(..) = result {
prop_assert!(
in_result,
"x={x} in A∪B but not in merge result: a={a}, b={b}, result={result:?}"
);
}
}
if in_result {
prop_assert!(
in_a || in_b,
"x={x} in merge result but not in A∪B: a={a}, b={b}, result={result:?}"
);
}
}
}
#[test]
fn prop_ipv6_is_adjacent_commutativity(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
prop_assert_eq!(a.is_adjacent(&b), b.is_adjacent(&a));
}
#[test]
fn prop_ipv6_adjacent_implies_merge_some(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
if a.is_adjacent(&b) {
prop_assert!(a.merge(&b).is_some(), "adjacent but merge returned None: a={a}, b={b}");
}
}
#[test]
fn prop_ipv6_same_mask_not_adjacent_not_identical_implies_merge_none(
a in arb_ipv6_network(),
b in arb_ipv6_network(),
) {
let (_, m1) = a.to_bits();
let (_, m2) = b.to_bits();
if m1 == m2 && !a.is_adjacent(&b) && a != b {
prop_assert_eq!(a.merge(&b), None, "same mask, not adjacent, not identical, but merge returned Some: a={}, b={}", a, b);
}
}
#[test]
fn ipv4_aggregate_preserves_addresses(
raw_nets in prop::collection::vec(
(0u32..=255, 24u8..=28),
1..=32,
)
) {
let mut nets: Vec<Ipv4Network> = raw_nets
.iter()
.map(|&(third_octet, prefix)| {
let mask = if prefix == 0 { 0 } else { !0u32 << (32 - prefix) };
let addr = (192 << 24) | (168 << 16) | (third_octet << 8);
Ipv4Network::from_bits(addr, mask)
})
.collect();
let mut before_addrs: Vec<u32> = Vec::new();
for net in &nets {
for addr in (*net).addrs() {
before_addrs.push(addr.to_bits());
}
}
before_addrs.sort_unstable();
before_addrs.dedup();
let result = ipv4_aggregate(&mut nets);
let mut after_addrs: Vec<u32> = Vec::new();
for net in result.iter() {
for addr in (*net).addrs() {
after_addrs.push(addr.to_bits());
}
}
after_addrs.sort_unstable();
after_addrs.dedup();
prop_assert_eq!(before_addrs, after_addrs);
}
#[test]
fn ipv4_aggregate_result_is_minimal(
raw_nets in prop::collection::vec(
(0u32..=255, 24u8..=28),
1..=32,
)
) {
let mut nets: Vec<Ipv4Network> = raw_nets
.iter()
.map(|&(third_octet, prefix)| {
let mask = if prefix == 0 { 0 } else { !0u32 << (32 - prefix) };
let addr = (192 << 24) | (168 << 16) | (third_octet << 8);
Ipv4Network::from_bits(addr, mask)
})
.collect();
let result = ipv4_aggregate(&mut nets);
for i in 0..result.len() {
for j in (i + 1)..result.len() {
prop_assert_ne!(result[i], result[j]);
}
}
for i in 0..result.len() {
for j in 0..result.len() {
if i != j {
prop_assert!(!result[i].contains(&result[j]));
}
}
}
for i in 0..result.len() {
for j in (i + 1)..result.len() {
prop_assert!(result[i].merge(&result[j]).is_none());
}
}
}
#[test]
fn ipv4_aggregate_non_contiguous_preserves_addresses(
raw_nets in prop::collection::vec(
(0u32..=0xFF, 0u32..=0xFF),
1..=8,
)
) {
let mut nets: Vec<Ipv4Network> = raw_nets
.iter()
.map(|&(addr_low, mask_low)| {
let addr = (10 << 24) | addr_low;
let mask = 0xFFFFFF00 | mask_low;
Ipv4Network::from_bits(addr, mask)
})
.collect();
let mut before_addrs: Vec<u32> = Vec::new();
for net in &nets {
for addr in (*net).addrs() {
before_addrs.push(addr.to_bits());
}
}
before_addrs.sort_unstable();
before_addrs.dedup();
let result = ipv4_aggregate(&mut nets);
let mut after_addrs: Vec<u32> = Vec::new();
for net in result.iter() {
for addr in (*net).addrs() {
after_addrs.push(addr.to_bits());
}
}
after_addrs.sort_unstable();
after_addrs.dedup();
prop_assert_eq!(before_addrs, after_addrs);
}
#[test]
fn ipnetwork_addr_mask_roundtrip_v4(net in arb_ipv4_network()) {
let ip_net = IpNetwork::V4(net);
prop_assert_eq!(IpAddr::V4(*net.addr()), ip_net.addr());
prop_assert_eq!(IpAddr::V4(*net.mask()), ip_net.mask());
}
#[test]
fn ipnetwork_contains_self_v4(net in arb_ipv4_network()) {
let ip_net = IpNetwork::V4(net);
prop_assert!(ip_net.contains(&ip_net));
}
#[test]
fn ipnetwork_contains_self_v6(net in arb_ipv6_network()) {
let ip_net = IpNetwork::V6(net);
prop_assert!(ip_net.contains(&ip_net));
}
#[test]
fn ipnetwork_to_contiguous_is_contiguous_v4(net in arb_ipv4_network()) {
prop_assert!(IpNetwork::V4(net).to_contiguous().is_contiguous());
}
#[test]
fn ipnetwork_to_contiguous_is_contiguous_v6(net in arb_ipv6_network()) {
prop_assert!(IpNetwork::V6(net).to_contiguous().is_contiguous());
}
#[test]
fn ipnetwork_to_contiguous_contains_original_v4(net in arb_ipv4_network()) {
let ip_net = IpNetwork::V4(net);
prop_assert!(ip_net.to_contiguous().contains(&ip_net));
}
#[test]
fn ipnetwork_to_contiguous_contains_original_v6(net in arb_ipv6_network()) {
let ip_net = IpNetwork::V6(net);
prop_assert!(ip_net.to_contiguous().contains(&ip_net));
}
}
}