#![deny(missing_docs)]
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use super::{Error, Result};
use crate::wire::ip::pretty_print_ip_payload;
pub use super::IpProtocol as Protocol;
pub const MIN_MTU: usize = 1280;
pub const ADDR_SIZE: usize = 16;
pub const LINK_LOCAL_ALL_NODES: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1);
pub const LINK_LOCAL_ALL_ROUTERS: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2);
pub const LINK_LOCAL_ALL_MLDV2_ROUTERS: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 0x16);
pub const LINK_LOCAL_ALL_RPL_NODES: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 0x1a);
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum MulticastScope {
InterfaceLocal = 0x1,
LinkLocal = 0x2,
AdminLocal = 0x4,
SiteLocal = 0x5,
OrganizationLocal = 0x8,
Global = 0xE,
Unknown = 0xFF,
}
impl From<u8> for MulticastScope {
fn from(value: u8) -> Self {
match value {
0x1 => Self::InterfaceLocal,
0x2 => Self::LinkLocal,
0x4 => Self::AdminLocal,
0x5 => Self::SiteLocal,
0x8 => Self::OrganizationLocal,
0xE => Self::Global,
_ => Self::Unknown,
}
}
}
pub use core::net::Ipv6Addr as Address;
pub(crate) trait AddressExt {
fn from_bytes(data: &[u8]) -> Address;
fn x_is_unicast(&self) -> bool;
fn is_global_unicast(&self) -> bool;
fn is_link_local(&self) -> bool;
fn x_is_unique_local(&self) -> bool;
fn mask(&self, mask: u8) -> [u8; ADDR_SIZE];
fn solicited_node(&self) -> Address;
fn x_multicast_scope(&self) -> MulticastScope;
fn is_solicited_node_multicast(&self) -> bool;
fn prefix_len(&self) -> Option<u8>;
}
impl AddressExt for Address {
fn from_bytes(data: &[u8]) -> Address {
let mut bytes = [0; ADDR_SIZE];
bytes.copy_from_slice(data);
Address::from(bytes)
}
fn x_is_unicast(&self) -> bool {
!(self.is_multicast() || self.is_unspecified())
}
fn is_global_unicast(&self) -> bool {
(self.octets()[0] >> 5) == 0b001
}
fn is_link_local(&self) -> bool {
self.octets()[0..8] == [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
}
fn x_is_unique_local(&self) -> bool {
(self.octets()[0] & 0b1111_1110) == 0xfc
}
fn mask(&self, mask: u8) -> [u8; ADDR_SIZE] {
assert!(mask <= 128);
let mut bytes = [0u8; ADDR_SIZE];
let idx = (mask as usize) / 8;
let modulus = (mask as usize) % 8;
let octets = self.octets();
let (first, second) = octets.split_at(idx);
bytes[0..idx].copy_from_slice(first);
if idx < ADDR_SIZE {
let part = second[0];
bytes[idx] = part & (!(0xff >> modulus) as u8);
}
bytes
}
fn solicited_node(&self) -> Address {
assert!(self.x_is_unicast());
let o = self.octets();
Address::from([
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, o[13],
o[14], o[15],
])
}
fn x_multicast_scope(&self) -> MulticastScope {
if self.is_multicast() {
return MulticastScope::from(self.octets()[1] & 0b1111);
}
if self.is_link_local() {
MulticastScope::LinkLocal
} else if self.x_is_unique_local() || self.is_global_unicast() {
MulticastScope::Global
} else {
MulticastScope::Unknown
}
}
fn is_solicited_node_multicast(&self) -> bool {
self.octets()[0..13]
== [
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
]
}
fn prefix_len(&self) -> Option<u8> {
let mut ones = true;
let mut prefix_len = 0;
for byte in self.octets() {
let mut mask = 0x80;
for _ in 0..8 {
let one = byte & mask != 0;
if ones {
if one {
prefix_len += 1;
} else {
ones = false;
}
} else if one {
return None;
}
mask >>= 1;
}
}
Some(prefix_len)
}
}
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct Cidr {
address: Address,
prefix_len: u8,
}
impl Cidr {
pub const SOLICITED_NODE_PREFIX: Cidr = Cidr {
address: Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0),
prefix_len: 104,
};
pub const fn new(address: Address, prefix_len: u8) -> Cidr {
assert!(prefix_len <= 128);
Cidr {
address,
prefix_len,
}
}
pub const fn address(&self) -> Address {
self.address
}
pub const fn prefix_len(&self) -> u8 {
self.prefix_len
}
pub fn contains_addr(&self, addr: &Address) -> bool {
if self.prefix_len == 0 {
return true;
}
self.address.mask(self.prefix_len) == addr.mask(self.prefix_len)
}
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(feature = "defmt")]
impl defmt::Format for Cidr {
fn format(&self, f: defmt::Formatter) {
defmt::write!(f, "{}/{=u8}", self.address, self.prefix_len);
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T,
}
mod field {
use crate::wire::field::*;
pub const VER_TC_FLOW: Field = 0..4;
pub const LENGTH: Field = 4..6;
pub const NXT_HDR: usize = 6;
pub const HOP_LIMIT: usize = 7;
pub const SRC_ADDR: Field = 8..24;
pub const DST_ADDR: Field = 24..40;
}
pub const HEADER_LEN: usize = field::DST_ADDR.end;
impl<T: AsRef<[u8]>> Packet<T> {
#[inline]
pub const fn new_unchecked(buffer: T) -> Packet<T> {
Packet { buffer }
}
#[inline]
pub fn new_checked(buffer: T) -> Result<Packet<T>> {
let packet = Self::new_unchecked(buffer);
packet.check_len()?;
Ok(packet)
}
#[inline]
pub fn check_len(&self) -> Result<()> {
let len = self.buffer.as_ref().len();
if len < field::DST_ADDR.end || len < self.total_len() {
Err(Error)
} else {
Ok(())
}
}
#[inline]
pub fn into_inner(self) -> T {
self.buffer
}
#[inline]
pub const fn header_len(&self) -> usize {
field::DST_ADDR.end
}
#[inline]
pub fn version(&self) -> u8 {
let data = self.buffer.as_ref();
data[field::VER_TC_FLOW.start] >> 4
}
#[inline]
pub fn traffic_class(&self) -> u8 {
let data = self.buffer.as_ref();
((NetworkEndian::read_u16(&data[0..2]) & 0x0ff0) >> 4) as u8
}
#[inline]
pub fn flow_label(&self) -> u32 {
let data = self.buffer.as_ref();
NetworkEndian::read_u24(&data[1..4]) & 0x000fffff
}
#[inline]
pub fn payload_len(&self) -> u16 {
let data = self.buffer.as_ref();
NetworkEndian::read_u16(&data[field::LENGTH])
}
#[inline]
pub fn total_len(&self) -> usize {
self.header_len() + self.payload_len() as usize
}
#[inline]
pub fn next_header(&self) -> Protocol {
let data = self.buffer.as_ref();
Protocol::from(data[field::NXT_HDR])
}
#[inline]
pub fn hop_limit(&self) -> u8 {
let data = self.buffer.as_ref();
data[field::HOP_LIMIT]
}
#[inline]
pub fn src_addr(&self) -> Address {
let data = self.buffer.as_ref();
Address::from_bytes(&data[field::SRC_ADDR])
}
#[inline]
pub fn dst_addr(&self) -> Address {
let data = self.buffer.as_ref();
Address::from_bytes(&data[field::DST_ADDR])
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
#[inline]
pub fn payload(&self) -> &'a [u8] {
let data = self.buffer.as_ref();
let range = self.header_len()..self.total_len();
&data[range]
}
}
impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
#[inline]
pub fn set_version(&mut self, value: u8) {
let data = self.buffer.as_mut();
data[0] = (data[0] & 0x0f) | ((value & 0x0f) << 4);
}
#[inline]
pub fn set_traffic_class(&mut self, value: u8) {
let data = self.buffer.as_mut();
data[0] = (data[0] & 0xf0) | ((value & 0xf0) >> 4);
data[1] = (data[1] & 0x0f) | ((value & 0x0f) << 4);
}
#[inline]
pub fn set_flow_label(&mut self, value: u32) {
let data = self.buffer.as_mut();
let raw = (((data[1] & 0xf0) as u32) << 16) | (value & 0x0fffff);
NetworkEndian::write_u24(&mut data[1..4], raw);
}
#[inline]
pub fn set_payload_len(&mut self, value: u16) {
let data = self.buffer.as_mut();
NetworkEndian::write_u16(&mut data[field::LENGTH], value);
}
#[inline]
pub fn set_next_header(&mut self, value: Protocol) {
let data = self.buffer.as_mut();
data[field::NXT_HDR] = value.into();
}
#[inline]
pub fn set_hop_limit(&mut self, value: u8) {
let data = self.buffer.as_mut();
data[field::HOP_LIMIT] = value;
}
#[inline]
pub fn set_src_addr(&mut self, value: Address) {
let data = self.buffer.as_mut();
data[field::SRC_ADDR].copy_from_slice(&value.octets());
}
#[inline]
pub fn set_dst_addr(&mut self, value: Address) {
let data = self.buffer.as_mut();
data[field::DST_ADDR].copy_from_slice(&value.octets());
}
#[inline]
pub fn payload_mut(&mut self) -> &mut [u8] {
let range = self.header_len()..self.total_len();
let data = self.buffer.as_mut();
&mut data[range]
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match Repr::parse(self) {
Ok(repr) => write!(f, "{repr}"),
Err(err) => {
write!(f, "IPv6 ({err})")?;
Ok(())
}
}
}
}
impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
fn as_ref(&self) -> &[u8] {
self.buffer.as_ref()
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Repr {
pub src_addr: Address,
pub dst_addr: Address,
pub next_header: Protocol,
pub payload_len: usize,
pub hop_limit: u8,
}
impl Repr {
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr> {
packet.check_len()?;
if packet.version() != 6 {
return Err(Error);
}
Ok(Repr {
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
next_header: packet.next_header(),
payload_len: packet.payload_len() as usize,
hop_limit: packet.hop_limit(),
})
}
pub const fn buffer_len(&self) -> usize {
field::DST_ADDR.end
}
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
packet.set_version(6);
packet.set_traffic_class(0);
packet.set_flow_label(0);
packet.set_payload_len(self.payload_len as u16);
packet.set_hop_limit(self.hop_limit);
packet.set_next_header(self.next_header);
packet.set_src_addr(self.src_addr);
packet.set_dst_addr(self.dst_addr);
}
}
impl fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
self.src_addr, self.dst_addr, self.next_header, self.hop_limit
)
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for Repr {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(
fmt,
"IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
self.src_addr,
self.dst_addr,
self.next_header,
self.hop_limit
)
}
}
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
let (ip_repr, payload) = match Packet::new_checked(buffer) {
Err(err) => return write!(f, "{indent}({err})"),
Ok(ip_packet) => match Repr::parse(&ip_packet) {
Err(_) => return Ok(()),
Ok(ip_repr) => {
write!(f, "{indent}{ip_repr}")?;
(ip_repr, ip_packet.payload())
}
},
};
pretty_print_ip_payload(f, indent, ip_repr, payload)
}
}
#[cfg(test)]
pub(crate) mod test {
use super::*;
use crate::wire::pretty_print::PrettyPrinter;
#[allow(unused)]
pub(crate) const MOCK_IP_ADDR_1: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
#[allow(unused)]
pub(crate) const MOCK_IP_ADDR_2: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2);
#[allow(unused)]
pub(crate) const MOCK_IP_ADDR_3: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 3);
#[allow(unused)]
pub(crate) const MOCK_IP_ADDR_4: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 4);
#[allow(unused)]
pub(crate) const MOCK_UNSPECIFIED: Address = Address::UNSPECIFIED;
const LINK_LOCAL_ADDR: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
const UNIQUE_LOCAL_ADDR: Address = Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1);
const GLOBAL_UNICAST_ADDR: Address = Address::new(0x2001, 0xdb8, 0x3, 0, 0, 0, 0, 1);
const TEST_SOL_NODE_MCAST_ADDR: Address = Address::new(0xff02, 0, 0, 0, 0, 1, 0xff01, 101);
#[test]
fn test_basic_multicast() {
assert!(!LINK_LOCAL_ALL_ROUTERS.is_unspecified());
assert!(LINK_LOCAL_ALL_ROUTERS.is_multicast());
assert!(!LINK_LOCAL_ALL_ROUTERS.is_link_local());
assert!(!LINK_LOCAL_ALL_ROUTERS.is_loopback());
assert!(!LINK_LOCAL_ALL_ROUTERS.x_is_unique_local());
assert!(!LINK_LOCAL_ALL_ROUTERS.is_global_unicast());
assert!(!LINK_LOCAL_ALL_ROUTERS.is_solicited_node_multicast());
assert!(!LINK_LOCAL_ALL_NODES.is_unspecified());
assert!(LINK_LOCAL_ALL_NODES.is_multicast());
assert!(!LINK_LOCAL_ALL_NODES.is_link_local());
assert!(!LINK_LOCAL_ALL_NODES.is_loopback());
assert!(!LINK_LOCAL_ALL_NODES.x_is_unique_local());
assert!(!LINK_LOCAL_ALL_NODES.is_global_unicast());
assert!(!LINK_LOCAL_ALL_NODES.is_solicited_node_multicast());
}
#[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());
assert!(!LINK_LOCAL_ADDR.x_is_unique_local());
assert!(!LINK_LOCAL_ADDR.is_global_unicast());
assert!(!LINK_LOCAL_ADDR.is_solicited_node_multicast());
}
#[test]
fn test_basic_loopback() {
assert!(!Address::LOCALHOST.is_unspecified());
assert!(!Address::LOCALHOST.is_multicast());
assert!(!Address::LOCALHOST.is_link_local());
assert!(Address::LOCALHOST.is_loopback());
assert!(!Address::LOCALHOST.x_is_unique_local());
assert!(!Address::LOCALHOST.is_global_unicast());
assert!(!Address::LOCALHOST.is_solicited_node_multicast());
}
#[test]
fn test_unique_local() {
assert!(!UNIQUE_LOCAL_ADDR.is_unspecified());
assert!(!UNIQUE_LOCAL_ADDR.is_multicast());
assert!(!UNIQUE_LOCAL_ADDR.is_link_local());
assert!(!UNIQUE_LOCAL_ADDR.is_loopback());
assert!(UNIQUE_LOCAL_ADDR.x_is_unique_local());
assert!(!UNIQUE_LOCAL_ADDR.is_global_unicast());
assert!(!UNIQUE_LOCAL_ADDR.is_solicited_node_multicast());
}
#[test]
fn test_global_unicast() {
assert!(!GLOBAL_UNICAST_ADDR.is_unspecified());
assert!(!GLOBAL_UNICAST_ADDR.is_multicast());
assert!(!GLOBAL_UNICAST_ADDR.is_link_local());
assert!(!GLOBAL_UNICAST_ADDR.is_loopback());
assert!(!GLOBAL_UNICAST_ADDR.x_is_unique_local());
assert!(GLOBAL_UNICAST_ADDR.is_global_unicast());
assert!(!GLOBAL_UNICAST_ADDR.is_solicited_node_multicast());
}
#[test]
fn test_sollicited_node_multicast() {
assert!(!TEST_SOL_NODE_MCAST_ADDR.is_unspecified());
assert!(TEST_SOL_NODE_MCAST_ADDR.is_multicast());
assert!(!TEST_SOL_NODE_MCAST_ADDR.is_link_local());
assert!(!TEST_SOL_NODE_MCAST_ADDR.is_loopback());
assert!(!TEST_SOL_NODE_MCAST_ADDR.x_is_unique_local());
assert!(!TEST_SOL_NODE_MCAST_ADDR.is_global_unicast());
assert!(TEST_SOL_NODE_MCAST_ADDR.is_solicited_node_multicast());
}
#[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, 56);
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, 0x01, 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, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
55,
),
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
56,
),
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
57,
),
(
[
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::from(a), p)) {
assert!(cidr.contains_subnet(&subnet));
}
for subnet in not_subnets
.iter()
.map(|&(a, p)| Cidr::new(Address::from(a), p))
{
assert!(!cidr.contains_subnet(&subnet));
}
let cidr_without_prefix = Cidr::new(LINK_LOCAL_ADDR, 0);
assert!(cidr_without_prefix.contains_addr(&Address::LOCALHOST));
}
#[test]
#[should_panic(expected = "length")]
fn test_from_bytes_too_long() {
let _ = Address::from_bytes(&[0u8; 15]);
}
#[test]
fn test_scope() {
use super::*;
assert_eq!(
Address::new(0xff01, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
MulticastScope::InterfaceLocal
);
assert_eq!(
Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
MulticastScope::LinkLocal
);
assert_eq!(
Address::new(0xff03, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
MulticastScope::Unknown
);
assert_eq!(
Address::new(0xff04, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
MulticastScope::AdminLocal
);
assert_eq!(
Address::new(0xff05, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
MulticastScope::SiteLocal
);
assert_eq!(
Address::new(0xff08, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
MulticastScope::OrganizationLocal
);
assert_eq!(
Address::new(0xff0e, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
MulticastScope::Global
);
assert_eq!(
LINK_LOCAL_ALL_NODES.x_multicast_scope(),
MulticastScope::LinkLocal
);
assert_eq!(
LINK_LOCAL_ADDR.x_multicast_scope(),
MulticastScope::LinkLocal
);
assert_eq!(
GLOBAL_UNICAST_ADDR.x_multicast_scope(),
MulticastScope::Global
);
assert_eq!(
UNIQUE_LOCAL_ADDR.x_multicast_scope(),
MulticastScope::Global
);
}
static REPR_PACKET_BYTES: [u8; 52] = [
0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 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, 0x00, 0x01, 0x00, 0x02, 0x00,
0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
];
static REPR_PAYLOAD_BYTES: [u8; 12] = [
0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
];
const fn packet_repr() -> Repr {
Repr {
src_addr: Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
dst_addr: LINK_LOCAL_ALL_NODES,
next_header: Protocol::Udp,
payload_len: 12,
hop_limit: 64,
}
}
#[test]
fn test_packet_deconstruction() {
let packet = Packet::new_unchecked(&REPR_PACKET_BYTES[..]);
assert_eq!(packet.check_len(), Ok(()));
assert_eq!(packet.version(), 6);
assert_eq!(packet.traffic_class(), 0);
assert_eq!(packet.flow_label(), 0);
assert_eq!(packet.total_len(), 0x34);
assert_eq!(packet.payload_len() as usize, REPR_PAYLOAD_BYTES.len());
assert_eq!(packet.next_header(), Protocol::Udp);
assert_eq!(packet.hop_limit(), 0x40);
assert_eq!(packet.src_addr(), Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
assert_eq!(packet.dst_addr(), LINK_LOCAL_ALL_NODES);
assert_eq!(packet.payload(), &REPR_PAYLOAD_BYTES[..]);
}
#[test]
fn test_packet_construction() {
let mut bytes = [0xff; 52];
let mut packet = Packet::new_unchecked(&mut bytes[..]);
packet.set_version(6);
assert_eq!(packet.version(), 6);
packet.set_traffic_class(0x99);
assert_eq!(packet.version(), 6);
assert_eq!(packet.traffic_class(), 0x99);
packet.set_flow_label(0x54321);
assert_eq!(packet.traffic_class(), 0x99);
assert_eq!(packet.flow_label(), 0x54321);
packet.set_payload_len(0xc);
packet.set_next_header(Protocol::Udp);
packet.set_hop_limit(0xfe);
packet.set_src_addr(LINK_LOCAL_ALL_ROUTERS);
packet.set_dst_addr(LINK_LOCAL_ALL_NODES);
packet
.payload_mut()
.copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
let mut expected_bytes = [
0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let start = expected_bytes.len() - REPR_PAYLOAD_BYTES.len();
expected_bytes[start..].copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
assert_eq!(packet.check_len(), Ok(()));
assert_eq!(&*packet.into_inner(), &expected_bytes[..]);
}
#[test]
fn test_overlong() {
let mut bytes = vec![];
bytes.extend(&REPR_PACKET_BYTES[..]);
bytes.push(0);
assert_eq!(
Packet::new_unchecked(&bytes).payload().len(),
REPR_PAYLOAD_BYTES.len()
);
assert_eq!(
Packet::new_unchecked(&mut bytes).payload_mut().len(),
REPR_PAYLOAD_BYTES.len()
);
}
#[test]
fn test_total_len_overflow() {
let mut bytes = vec![];
bytes.extend(&REPR_PACKET_BYTES[..]);
Packet::new_unchecked(&mut bytes).set_payload_len(0x80);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error);
}
#[test]
fn test_repr_parse_valid() {
let packet = Packet::new_unchecked(&REPR_PACKET_BYTES[..]);
let repr = Repr::parse(&packet).unwrap();
assert_eq!(repr, packet_repr());
}
#[test]
fn test_repr_parse_bad_version() {
let mut bytes = [0; 40];
let mut packet = Packet::new_unchecked(&mut bytes[..]);
packet.set_version(4);
packet.set_payload_len(0);
let packet = Packet::new_unchecked(&*packet.into_inner());
assert_eq!(Repr::parse(&packet), Err(Error));
}
#[test]
fn test_repr_parse_smaller_than_header() {
let mut bytes = [0; 40];
let mut packet = Packet::new_unchecked(&mut bytes[..]);
packet.set_version(6);
packet.set_payload_len(39);
let packet = Packet::new_unchecked(&*packet.into_inner());
assert_eq!(Repr::parse(&packet), Err(Error));
}
#[test]
fn test_repr_parse_smaller_than_payload() {
let mut bytes = [0; 40];
let mut packet = Packet::new_unchecked(&mut bytes[..]);
packet.set_version(6);
packet.set_payload_len(1);
let packet = Packet::new_unchecked(&*packet.into_inner());
assert_eq!(Repr::parse(&packet), Err(Error));
}
#[test]
fn test_basic_repr_emit() {
let repr = packet_repr();
let mut bytes = vec![0xff; repr.buffer_len() + REPR_PAYLOAD_BYTES.len()];
let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit(&mut packet);
packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES);
assert_eq!(&*packet.into_inner(), &REPR_PACKET_BYTES[..]);
}
#[test]
fn test_pretty_print() {
assert_eq!(
format!(
"{}",
PrettyPrinter::<Packet<&'static [u8]>>::new("\n", &&REPR_PACKET_BYTES[..])
),
"\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4"
);
}
}