Add from_netmask()/netmask()/broadcast()/network() methods on IPv4Cidr

This commit is contained in:
寧靜 2018-02-05 23:13:52 +08:00 committed by whitequark
parent c3d5790c8e
commit 49afb3a45a
1 changed files with 152 additions and 0 deletions

View File

@ -124,6 +124,16 @@ impl Cidr {
Cidr { address, prefix_len }
}
/// Create an IPv4 CIDR block from the given address and network mask.
pub fn from_netmask(addr: Address, netmask: Address) -> Result<Cidr> {
let netmask = NetworkEndian::read_u32(&netmask.0[..]);
if netmask.leading_zeros() == 0 && netmask.trailing_zeros() == netmask.count_zeros() {
Ok(Cidr { address: addr, prefix_len: netmask.count_ones() as u8 })
} else {
Err(Error::Illegal)
}
}
/// Return the address of this IPv4 CIDR block.
pub fn address(&self) -> Address {
self.address
@ -134,6 +144,55 @@ impl Cidr {
self.prefix_len
}
/// Return the network mask of this IPv4 CIDR.
pub fn netmask(&self) -> Address {
if self.prefix_len == 0 {
return Address([0, 0, 0, 0]);
}
let number = 0xffffffffu32 << (32 - self.prefix_len);
let data = [
((number >> 24) & 0xff) as u8,
((number >> 16) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8,
];
Address(data)
}
/// Return the broadcast address of this IPv4 CIDR.
pub fn broadcast(&self) -> Option<Address> {
let network = self.network();
if network.prefix_len == 31 || network.prefix_len == 32 {
return None;
}
let network_number = NetworkEndian::read_u32(&network.address.0[..]);
let number = network_number | 0xffffffffu32 >> network.prefix_len;
let data = [
((number >> 24) & 0xff) as u8,
((number >> 16) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8,
];
Some(Address(data))
}
/// Return the network block of this IPv4 CIDR.
pub fn network(&self) -> Cidr {
let mask = self.netmask().0;
let network = [
self.address.0[0] & mask[0],
self.address.0[1] & mask[1],
self.address.0[2] & mask[2],
self.address.0[3] & mask[3],
];
Cidr { address: Address(network), prefix_len: self.prefix_len }
}
/// Query whether the subnetwork described by this IPv4 CIDR block contains
/// the given address.
pub fn contains_addr(&self, addr: &Address) -> bool {
@ -829,4 +888,97 @@ mod test {
let cidr_without_prefix = Cidr::new(cidr.address(), 0);
assert!(cidr_without_prefix.contains_addr(&Address::new(127, 0, 0, 1)));
}
#[test]
fn test_cidr_from_netmask() {
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(),
true);
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(),
true);
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(),
Cidr::new(Address([0, 0, 0, 1]), 24));
assert_eq!(Cidr::from_netmask(Address([192, 168, 0, 1]), Address([255, 255, 0, 0])).unwrap(),
Cidr::new(Address([192, 168, 0, 1]), 16));
assert_eq!(Cidr::from_netmask(Address([172, 16, 0, 1]), Address([255, 240, 0, 0])).unwrap(),
Cidr::new(Address([172, 16, 0, 1]), 12));
assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 1]), Address([255, 255, 255, 0])).unwrap(),
Cidr::new(Address([255, 255, 255, 1]), 24));
assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 255]), Address([255, 255, 255, 255])).unwrap(),
Cidr::new(Address([255, 255, 255, 255]), 32));
}
#[test]
fn test_cidr_netmask() {
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).netmask(),
Address([0, 0, 0, 0]));
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).netmask(),
Address([255, 255, 255, 0]));
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).netmask(),
Address([255, 255, 255, 255]));
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).netmask(),
Address([255, 0, 0, 0]));
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).netmask(),
Address([255, 255, 0, 0]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).netmask(),
Address([255, 255, 0, 0]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).netmask(),
Address([255, 255, 128, 0]));
assert_eq!(Cidr::new(Address([172, 16, 0, 0]), 12).netmask(),
Address([255, 240, 0, 0]));
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).netmask(),
Address([255, 255, 255, 0]));
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).netmask(),
Address([255, 255, 255, 255]));
}
#[test]
fn test_cidr_broadcast() {
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(),
Address([255, 255, 255, 255]));
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(),
Address([0, 0, 0, 255]));
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(),
None);
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(),
Address([127, 255, 255, 255]));
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).broadcast().unwrap(),
Address([192, 168, 255, 255]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).broadcast().unwrap(),
Address([192, 168, 255, 255]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).broadcast().unwrap(),
Address([192, 168, 127, 255]));
assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).broadcast().unwrap(),
Address([172, 31, 255, 255]));
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).broadcast().unwrap(),
Address([255, 255, 255, 255]));
assert_eq!(Cidr::new(Address([255, 255, 255, 254]), 31).broadcast(),
None);
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).broadcast(),
None);
}
#[test]
fn test_cidr_network() {
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).network(),
Cidr::new(Address([0, 0, 0, 0]), 0));
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).network(),
Cidr::new(Address([0, 0, 0, 0]), 24));
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).network(),
Cidr::new(Address([0, 0, 0, 0]), 32));
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).network(),
Cidr::new(Address([127, 0, 0, 0]), 8));
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).network(),
Cidr::new(Address([192, 168, 0, 0]), 16));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).network(),
Cidr::new(Address([192, 168, 0, 0]), 16));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).network(),
Cidr::new(Address([192, 168, 0, 0]), 17));
assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).network(),
Cidr::new(Address([172, 16, 0, 0]), 12));
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).network(),
Cidr::new(Address([255, 255, 255, 0]), 24));
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).network(),
Cidr::new(Address([255, 255, 255, 255]), 32));
}
}