renet/src/wire/ipv6.rs

414 lines
15 KiB
Rust

use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
pub use super::IpProtocol as Protocol;
/// A sixteen-octet IPv6 address.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
pub struct Address(pub [u8; 16]);
impl Address {
/// An unspecified address.
pub const UNSPECIFIED: Address = Address([0x00; 16]);
/// Link local all routers multicast address.
pub const LINK_LOCAL_ALL_NODES: Address =
Address([0xff, 0x02, 0x00, 0x0, 0x00, 0x00, 0x00, 0x0,
0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x1]);
/// Link local all nodes multicast address.
pub const LINK_LOCAL_ALL_ROUTERS: Address =
Address([0xff, 0x02, 0x00, 0x0, 0x00, 0x00, 0x00, 0x0,
0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x2]);
/// Loopback address.
pub const LOOPBACK: Address =
Address([0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x0,
0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x1]);
/// Construct an IPv6 address from parts.
pub fn new(a0: u16, a1: u16, a2: u16, a3: u16,
a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
let mut addr = [0u8; 16];
NetworkEndian::write_u16(&mut addr[0..2], a0);
NetworkEndian::write_u16(&mut addr[2..4], a1);
NetworkEndian::write_u16(&mut addr[4..6], a2);
NetworkEndian::write_u16(&mut addr[6..8], a3);
NetworkEndian::write_u16(&mut addr[8..10], a4);
NetworkEndian::write_u16(&mut addr[10..12], a5);
NetworkEndian::write_u16(&mut addr[12..14], a6);
NetworkEndian::write_u16(&mut addr[14..16], a7);
Address(addr)
}
/// Construct an IPv6 address from a sequence of octets, in big-endian.
///
/// # Panics
/// The function panics if `data` is not sixteen octets long.
pub fn from_bytes(data: &[u8]) -> Address {
let mut bytes = [0; 16];
bytes.copy_from_slice(data);
Address(bytes)
}
/// Construct an IPv6 address from a sequence of words, in big-endian.
///
/// # Panics
/// The function panics if `data` is not 8 words long.
pub fn from_parts(data: &[u16]) -> Address {
assert!(data.len() >= 8);
let mut bytes = [0; 16];
for word_idx in 0..8 {
let byte_idx = word_idx * 2;
NetworkEndian::write_u16(&mut bytes[byte_idx..(byte_idx + 2)], data[word_idx]);
}
Address(bytes)
}
/// Write a IPv6 address to the given slice.
///
/// # Panics
/// The function panics if `data` is not 8 words long.
pub fn write_parts(&self, data: &mut [u16]) {
assert!(data.len() >= 8);
for i in 0..8 {
let byte_idx = i * 2;
data[i] = NetworkEndian::read_u16(&self.0[byte_idx..(byte_idx + 2)]);
}
}
/// Return an IPv6 address as a sequence of octets, in big-endian.
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
/// Query whether the IPv6 address is an unicast address.
pub fn is_unicast(&self) -> bool {
!(self.is_multicast() || self.is_unspecified())
}
/// Query whether the IPv6 address is a multicast address.
pub fn is_multicast(&self) -> bool {
self.0[0] == 0xff
}
/// Query whether the IPv6 address is the "unspecified" address.
pub fn is_unspecified(&self) -> bool {
self.0 == [0x00; 16]
}
/// Query whether the IPv6 address is in the "link-local" range.
pub fn is_link_local(&self) -> bool {
self.0[0..8] == [0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00]
}
/// Query whether the IPv6 address is the "loopback" address.
pub fn is_loopback(&self) -> bool {
*self == Self::LOOPBACK
}
/// Helper function used to mask an addres given a prefix.
///
/// # Panics
/// This function panics if `mask` is greater than 128.
pub(super) fn mask(&self, mask: u8) -> [u8; 16] {
assert!(mask <= 128);
let mut bytes = [0u8; 16];
let idx = (mask as usize) / 8;
let modulus = (mask as usize) % 8;
let (first, second) = self.0.split_at(idx);
bytes[0..idx].copy_from_slice(&first);
if idx < 16 {
let part = second[0];
bytes[idx] = part & (!(0xff >> modulus) as u8);
}
bytes
}
}
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
enum State {
Head,
HeadBody,
Tail,
TailBody
}
let mut words = [0u16; 8];
self.write_parts(&mut words);
let mut state = State::Head;
for word in words.iter() {
state = match (*word, &state) {
// Once a u16 equal to zero write a double colon and
// skip to the next non-zero u16.
(0, &State::Head) | (0, &State::HeadBody) => {
write!(f, "::")?;
State::Tail
},
// Continue iterating without writing any characters until
// we hit anothing non-zero value.
(0, &State::Tail) => State::Tail,
// When the state is Head or Tail write a u16 in hexadecimal
// without the leading colon if the value is not 0.
(_, &State::Head) => {
write!(f, "{:x}", word)?;
State::HeadBody
},
(_, &State::Tail) => {
write!(f, "{:x}", word)?;
State::TailBody
},
// Write the u16 with a leading colon when parsing a value
// that isn't the first in a section
(_, &State::HeadBody) | (_, &State::TailBody) => {
write!(f, ":{:x}", word)?;
state
}
}
}
Ok(())
}
}
/// A specification of an IPv6 CIDR block, containing an address and a variable-length
/// subnet masking prefix length.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Cidr {
address: Address,
prefix_len: u8,
}
impl Cidr {
/// Create an IPv6 CIDR block from the given address and prefix length.
///
/// # Panics
/// This function panics if the prefix length is larger than 128.
pub fn new(address: Address, prefix_len: u8) -> Cidr {
assert!(prefix_len <= 128);
Cidr { address, prefix_len }
}
/// Return the address of this IPv6 CIDR block.
pub fn address(&self) -> Address {
self.address
}
/// Return the prefix length of this IPv6 CIDR block.
pub fn prefix_len(&self) -> u8 {
self.prefix_len
}
/// Query whether the subnetwork described by this IPv6 CIDR block contains
/// the given address.
pub fn contains_addr(&self, addr: &Address) -> bool {
// right shift by 128 is not legal
if self.prefix_len == 0 { return true }
let shift = 128 - self.prefix_len;
self.address.mask(shift) == addr.mask(shift)
}
/// Query whether the subnetwork described by this IPV6 CIDR block contains
/// the subnetwork described by the given IPv6 CIDR block.
pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
self.prefix_len <= subnet.prefix_len && self.contains_addr(&subnet.address)
}
}
impl fmt::Display for Cidr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}/{}", self.address, self.prefix_len)
}
}
#[cfg(test)]
mod test {
use super::{Address, Cidr};
static LINK_LOCAL_ADDR: Address = Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01]);
#[test]
fn test_basic_multicast() {
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_unspecified());
assert!(Address::LINK_LOCAL_ALL_ROUTERS.is_multicast());
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_link_local());
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_loopback());
assert!(!Address::LINK_LOCAL_ALL_NODES.is_unspecified());
assert!(Address::LINK_LOCAL_ALL_NODES.is_multicast());
assert!(!Address::LINK_LOCAL_ALL_NODES.is_link_local());
assert!(!Address::LINK_LOCAL_ALL_NODES.is_loopback());
}
#[test]
fn test_basic_link_local() {
assert!(!LINK_LOCAL_ADDR.is_unspecified());
assert!(!LINK_LOCAL_ADDR.is_multicast());
assert!(LINK_LOCAL_ADDR.is_link_local());
assert!(!LINK_LOCAL_ADDR.is_loopback());
}
#[test]
fn test_basic_loopback() {
assert!(!Address::LOOPBACK.is_unspecified());
assert!(!Address::LOOPBACK.is_multicast());
assert!(!Address::LOOPBACK.is_link_local());
assert!(Address::LOOPBACK.is_loopback());
}
#[test]
fn test_address_format() {
assert_eq!("ff02::1",
format!("{}", Address::LINK_LOCAL_ALL_NODES));
assert_eq!("fe80::1",
format!("{}", LINK_LOCAL_ADDR));
assert_eq!("fe80::7f00:0:1",
format!("{}", Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001)));
}
#[test]
fn test_new() {
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
Address::LINK_LOCAL_ALL_NODES);
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2),
Address::LINK_LOCAL_ALL_ROUTERS);
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1),
Address::LOOPBACK);
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0),
Address::UNSPECIFIED);
assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
LINK_LOCAL_ADDR);
}
#[test]
fn test_from_parts() {
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]),
Address::LINK_LOCAL_ALL_NODES);
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]),
Address::LINK_LOCAL_ALL_ROUTERS);
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]),
Address::LOOPBACK);
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]),
Address::UNSPECIFIED);
assert_eq!(Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]),
LINK_LOCAL_ADDR);
}
#[test]
fn test_write_parts() {
let mut bytes = [0u16; 8];
{
Address::LOOPBACK.write_parts(&mut bytes);
assert_eq!(Address::LOOPBACK, Address::from_parts(&bytes));
}
{
Address::LINK_LOCAL_ALL_ROUTERS.write_parts(&mut bytes);
assert_eq!(Address::LINK_LOCAL_ALL_ROUTERS, Address::from_parts(&bytes));
}
{
LINK_LOCAL_ADDR.write_parts(&mut bytes);
assert_eq!(LINK_LOCAL_ADDR, Address::from_parts(&bytes));
}
}
#[test]
fn test_mask() {
let addr = Address::new(0x0123, 0x4567, 0x89ab, 0, 0, 0, 0, 1);
assert_eq!(addr.mask(11), [0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(addr.mask(15), [0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(addr.mask(26), [0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(addr.mask(128), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
assert_eq!(addr.mask(127), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn test_cidr() {
let cidr = Cidr::new(LINK_LOCAL_ADDR, 64);
let inside_subnet = [
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]
];
let outside_subnet = [
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]
];
let subnets = [
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
65),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
128),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78],
96)
];
let not_subnets = [
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
63),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
64),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
65),
([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
128)
];
for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) {
assert!(cidr.contains_addr(&addr));
}
for addr in outside_subnet.iter().map(|a| Address::from_bytes(a)) {
assert!(!cidr.contains_addr(&addr));
}
for subnet in subnets.iter().map(
|&(a, p)| Cidr::new(Address(a), p)) {
assert!(cidr.contains_subnet(&subnet));
}
for subnet in not_subnets.iter().map(
|&(a, p)| Cidr::new(Address(a), p)) {
assert!(!cidr.contains_subnet(&subnet));
}
let cidr_without_prefix = Cidr::new(LINK_LOCAL_ADDR, 0);
assert!(cidr_without_prefix.contains_addr(&Address::LOOPBACK));
}
#[test]
#[should_panic(expected = "destination and source slices have different lengths")]
fn from_bytes_too_long() {
let _ = Address::from_bytes(&[0u8; 15]);
}
#[test]
#[should_panic(expected = "data.len() >= 8")]
fn from_parts_too_long() {
let _ = Address::from_parts(&[0u16; 7]);
}
}