mod extras;
pub use extras::*;
use std::net::{Ipv4Addr, Ipv6Addr};
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("invalid_cidr_block_format")]
InvalidBlock,
#[error("invalid_prefix_length")]
InvalidPrefixLength,
}
pub fn v6_num_ips(block: &str) -> Result<u128, Error> {
let parts: Vec<&str> = block.split('/').collect();
if parts.len() != 2 {
return Err(Error::InvalidBlock);
}
let prefix_length: u32 = parts[1].parse().map_err(|_| Error::InvalidPrefixLength)?;
if prefix_length > 128 {
return Err(Error::InvalidPrefixLength);
}
Ok(1 << (128 - prefix_length))
}
pub fn v4_num_ips(block: &str) -> Result<u32, Error> {
let parts: Vec<&str> = block.split('/').collect();
if parts.len() != 2 {
return Err(Error::InvalidBlock);
}
let prefix_length: u32 = parts[1].parse().map_err(|_| Error::InvalidPrefixLength)?;
if prefix_length > 32 {
return Err(Error::InvalidPrefixLength);
}
Ok(1 << (32 - prefix_length))
}
pub fn next_ip_v6(ip: Ipv6Addr, prefix_length: u8, n: u128) -> Result<Ipv6Addr, Error> {
if prefix_length > 128 {
return Err(Error::InvalidPrefixLength);
}
let mask = if prefix_length == 0 {
u128::MAX
} else {
(1 << (128 - prefix_length)) - 1
};
let ip: u128 = u128::from(ip);
let mut host_part = ip & mask;
let base_part = ip & !mask;
host_part = host_part.wrapping_add(n);
host_part &= mask;
Ok(Ipv6Addr::from(base_part | host_part))
}
pub fn next_ip_v4(ip: Ipv4Addr, prefix_length: u8, n: u32) -> Result<Ipv4Addr, Error> {
if prefix_length > 32 {
return Err(Error::InvalidPrefixLength);
}
let mask = (1 << (32 - prefix_length)) - 1;
let ip: u32 = u32::from(ip);
let mut host_part = ip & mask;
let base_part = ip & !mask;
host_part = host_part.wrapping_add(n);
if host_part > mask {
host_part &= mask; }
Ok(Ipv4Addr::from(base_part | host_part))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_v4_num_ips() {
assert_eq!(v4_num_ips("192.168.0.0/24").unwrap(), 256);
assert_eq!(v4_num_ips("10.0.0.0/8").unwrap(), 16_777_216);
assert!(v4_num_ips("192.168.0.0/33").is_err());
assert!(v4_num_ips("invalid").is_err());
}
#[test]
fn test_v6_num_ips() {
assert_eq!(
v6_num_ips("2001:db8::/32").unwrap(),
79_228_162_514_264_337_593_543_950_336
);
assert_eq!(v6_num_ips("::/128").unwrap(), 1);
assert!(v6_num_ips("2001:db8::/129").is_err());
assert!(v6_num_ips("invalid").is_err());
}
#[test]
fn test_next_ip_v6() {
let ip = "2001:db8::1".parse::<Ipv6Addr>().unwrap();
let next_ip = next_ip_v6(ip, 32, 10).unwrap();
assert_eq!(next_ip, "2001:db8::b".parse::<Ipv6Addr>().unwrap());
let ip = "2001:db8::ffff:ffff".parse::<Ipv6Addr>().unwrap();
let next_ip = next_ip_v6(ip, 32, 2).unwrap();
assert_eq!(next_ip, "2001:db8::1:0:1".parse::<Ipv6Addr>().unwrap());
let ip = "2001:db8::ffff:ffff".parse::<Ipv6Addr>().unwrap();
let next_ip = next_ip_v6(ip, 32, 100).unwrap();
assert_eq!(next_ip, "2001:db8::1:0:63".parse::<Ipv6Addr>().unwrap());
let ip = "2001:db8::1".parse::<Ipv6Addr>().unwrap();
let next_ip = next_ip_v6(ip, 64, 10).unwrap();
assert_eq!(next_ip, "2001:db8::b".parse::<Ipv6Addr>().unwrap());
}
#[test]
fn test_next_ip_v4() {
let ip = "192.168.1.1".parse::<Ipv4Addr>().unwrap();
let next_ip = next_ip_v4(ip, 24, 10).unwrap();
assert_eq!(next_ip, "192.168.1.11".parse::<Ipv4Addr>().unwrap());
let last_ip = "192.168.1.255".parse::<Ipv4Addr>().unwrap();
let next_ip = next_ip_v4(last_ip, 24, 2).unwrap();
assert_eq!(next_ip, "192.168.1.1".parse::<Ipv4Addr>().unwrap());
let last_ip = "192.168.1.255".parse::<Ipv4Addr>().unwrap();
let next_ip = next_ip_v4(last_ip, 24, 100).unwrap();
assert_eq!(next_ip, "192.168.1.99".parse::<Ipv4Addr>().unwrap());
}
}