Files
ipnetwork/src/ipv6.rs

211 lines
6.3 KiB
Rust

use std::cmp;
use std::fmt;
use std::net::Ipv6Addr;
use std::str::FromStr;
use common::{IpNetworkError, cidr_parts, parse_prefix};
const IPV6_BITS: u8 = 128;
const IPV6_SEGMENT_BITS: u8 = 16;
#[derive(Debug,Clone,Copy,Hash,PartialEq,Eq)]
pub struct Ipv6Network {
addr: Ipv6Addr,
prefix: u8,
}
impl Ipv6Network {
/// Constructs a new `Ipv6Network` from any `Ipv6Addr` and a prefix denoting the network size.
/// If the prefix is larger than 128 this will return an `IpNetworkError::InvalidPrefix`.
pub fn new(addr: Ipv6Addr, prefix: u8) -> Result<Ipv6Network, IpNetworkError> {
if prefix > IPV6_BITS {
Err(IpNetworkError::InvalidPrefix)
} else {
Ok(Ipv6Network {
addr: addr,
prefix: prefix,
})
}
}
pub fn from_cidr(cidr: &str) -> Result<Ipv6Network, IpNetworkError> {
let (addr_str, prefix_str) = try!(cidr_parts(cidr));
let addr = try!(Ipv6Addr::from_str(addr_str)
.map_err(|_| IpNetworkError::InvalidAddr(format!("{}", addr_str))));
let prefix = try!(parse_prefix(prefix_str, IPV6_BITS));
Self::new(addr, prefix)
}
pub fn ip(&self) -> Ipv6Addr {
self.addr
}
pub fn prefix(&self) -> u8 {
self.prefix
}
/// Returns the mask for this `Ipv6Network`.
/// That means the `prefix` most significant bits will be 1 and the rest 0
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use ipnetwork::Ipv6Network;
///
/// let net = Ipv6Network::from_cidr("ff01::0/32").unwrap();
/// assert_eq!(net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0));
/// ```
pub fn mask(&self) -> Ipv6Addr {
// Ipv6Addr::from is only implemented for [u8; 16]
let mut segments = [0; 16];
for (i, segment) in segments.iter_mut().enumerate() {
let bits_remaining = self.prefix.saturating_sub(i as u8 * 8);
let set_bits = cmp::min(bits_remaining, 8);
*segment = !(0xff as u16 >> set_bits) as u8;
}
Ipv6Addr::from(segments)
}
/// Checks if a given `Ipv6Addr` is in this `Ipv6Network`
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use ipnetwork::Ipv6Network;
///
/// let net = Ipv6Network::from_cidr("ff01::0/32").unwrap();
/// assert!(net.contains(Ipv6Addr::new(0xff01, 0, 0, 0, 0, 0, 0, 0x1)));
/// assert!(!net.contains(Ipv6Addr::new(0xffff, 0, 0, 0, 0, 0, 0, 0x1)));
/// ```
pub fn contains(&self, ip: Ipv6Addr) -> bool {
let a = self.addr.segments();
let b = ip.segments();
let addrs = Iterator::zip(a.iter(), b.iter());
self.mask().segments().iter().zip(addrs).all(|(mask, (a, b))|
a & mask == b & mask
)
}
}
impl fmt::Display for Ipv6Network {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}/{}", self.ip(), self.prefix())
}
}
/// Converts a `Ipv6Addr` network mask into a prefix.
/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
let mask = mask.segments();
let mut mask_iter = mask.into_iter();
// Count the number of set bits from the start of the address
let mut prefix = 0;
for &segment in &mut mask_iter {
if segment == 0xffff {
prefix += IPV6_SEGMENT_BITS;
} else if segment == 0 {
// Prefix finishes on a segment boundary
break;
} else {
let prefix_bits = (!segment).leading_zeros() as u8;
// Check that the remainder of the bits are all unset
if segment << prefix_bits != 0 {
return Err(IpNetworkError::InvalidPrefix);
}
prefix += prefix_bits;
break;
}
}
// Now check all the remaining bits are unset
for &segment in mask_iter {
if segment != 0 {
return Err(IpNetworkError::InvalidPrefix);
}
}
Ok(prefix)
}
#[cfg(test)]
mod test {
use std::net::Ipv6Addr;
use super::*;
#[test]
fn create_v6() {
let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 24).unwrap();
assert_eq!(cidr.prefix(), 24);
}
#[test]
fn create_v6_invalid_prefix() {
let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 129);
assert!(cidr.is_err());
}
#[test]
fn parse_v6() {
let cidr = Ipv6Network::from_cidr("::1/0").unwrap();
assert_eq!(cidr.ip(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
assert_eq!(cidr.prefix(), 0);
}
#[test]
fn parse_v6_2() {
let cidr = Ipv6Network::from_cidr("FF01:0:0:17:0:0:0:2/64").unwrap();
assert_eq!(cidr.ip(), Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2));
assert_eq!(cidr.prefix(), 64);
}
#[test]
fn parse_v6_fail_addr() {
let cidr = Ipv6Network::from_cidr("2001::1::/8");
assert!(cidr.is_err());
}
#[test]
fn parse_v6_fail_prefix() {
let cidr = Ipv6Network::from_cidr("::1/129");
assert!(cidr.is_err());
}
#[test]
fn mask_v6() {
let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 40).unwrap();
let mask = cidr.mask();
assert_eq!(mask, Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0, 0, 0, 0, 0));
}
#[test]
fn contains_v6() {
let cidr = Ipv6Network::new(Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2), 65).unwrap();
let ip = Ipv6Addr::new(0xff01, 0, 0, 0x17, 0x7fff, 0, 0, 0x2);
assert!(cidr.contains(ip));
}
#[test]
fn not_contains_v6() {
let cidr = Ipv6Network::new(Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2), 65).unwrap();
let ip = Ipv6Addr::new(0xff01, 0, 0, 0x17, 0xffff, 0, 0, 0x2);
assert!(!cidr.contains(ip));
}
#[test]
fn v6_mask_to_prefix() {
let mask = Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0, 0, 0, 0, 0);
let prefix = ipv6_mask_to_prefix(mask).unwrap();
assert_eq!(prefix, 48);
}
#[test]
fn invalid_v6_mask_to_prefix() {
let mask = Ipv6Addr::new(0, 0, 0xffff, 0xffff, 0, 0, 0, 0);
let prefix = ipv6_mask_to_prefix(mask);
assert!(prefix.is_err());
}
}