use crate::inet::{
ip, ipv4::IpV4Address, unspecified::Unspecified, ExplicitCongestionNotification, IpAddress,
SocketAddress, SocketAddressV4,
};
use core::{fmt, net};
use s2n_codec::zerocopy::U16;
const IPV6_LEN: usize = 128 / 8;
define_inet_type!(
pub struct IpV6Address {
octets: [u8; IPV6_LEN],
}
);
impl IpV6Address {
pub const UNSPECIFIED: Self = Self {
octets: [0; IPV6_LEN],
};
#[inline]
pub const fn segments(&self) -> [u16; 8] {
let octets = &self.octets;
[
u16::from_be_bytes([octets[0], octets[1]]),
u16::from_be_bytes([octets[2], octets[3]]),
u16::from_be_bytes([octets[4], octets[5]]),
u16::from_be_bytes([octets[6], octets[7]]),
u16::from_be_bytes([octets[8], octets[9]]),
u16::from_be_bytes([octets[10], octets[11]]),
u16::from_be_bytes([octets[12], octets[13]]),
u16::from_be_bytes([octets[14], octets[15]]),
]
}
#[inline]
pub const fn unmap(self) -> IpAddress {
match self.segments() {
[0, 0, 0, 0, 0, 0, 0, 0] => IpAddress::Ipv6(self),
[0, 0, 0, 0, 0, 0, 0, 1] => IpAddress::Ipv6(self),
[0, 0, 0, 0, 0, 0, ab, cd]
| [0, 0, 0, 0, 0, 0xffff, ab, cd]
| [0x64, 0xff9b, 0, 0, 0, 0, ab, cd] => {
let [a, b] = u16::to_be_bytes(ab);
let [c, d] = u16::to_be_bytes(cd);
IpAddress::Ipv4(IpV4Address {
octets: [a, b, c, d],
})
}
_ => IpAddress::Ipv6(self),
}
}
#[inline]
pub const fn unicast_scope(self) -> Option<ip::UnicastScope> {
use ip::UnicastScope::*;
if let IpAddress::Ipv4(ip) = self.unmap() {
return ip.unicast_scope();
}
match self.segments() {
[0, 0, 0, 0, 0, 0, 0, 0] => None,
[0, 0, 0, 0, 0, 0, 0, 1] => Some(Loopback),
[0x0100, 0, 0, 0, ..] => None,
[0x2001, 0x1, 0, 0, 0, 0, 0, 0x1] => Some(Global),
[0x2001, 0x1, 0, 0, 0, 0, 0, 0x2] => Some(Global),
[0x2001, 0x0..=0x01ff, ..] => None,
[0x2001, 0x0200, 0, ..] => None,
[0x2001, 0xdb8, ..] => None,
[0xfc00..=0xfdff, ..] => {
Some(Private)
}
[0xfe80..=0xfebf, ..] => Some(LinkLocal),
[0xff00..=0xffff, ..] => None,
_ => Some(Global),
}
}
#[inline]
pub fn with_port(self, port: u16) -> SocketAddressV6 {
SocketAddressV6 {
ip: self,
port: port.into(),
}
}
}
impl fmt::Debug for IpV6Address {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "IPv6Address({self})")
}
}
impl fmt::Display for IpV6Address {
#[allow(clippy::many_single_char_names)]
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.segments() {
[0, 0, 0, 0, 0, 0, 0, 0] => write!(fmt, "::"),
[0, 0, 0, 0, 0, 0, 0, 1] => write!(fmt, "::1"),
[0, 0, 0, 0, 0, 0, g, h] => write!(
fmt,
"::{}.{}.{}.{}",
(g >> 8) as u8,
g as u8,
(h >> 8) as u8,
h as u8
),
[0, 0, 0, 0, 0, 0xffff, g, h] => write!(
fmt,
"::ffff:{}.{}.{}.{}",
(g >> 8) as u8,
g as u8,
(h >> 8) as u8,
h as u8
),
[a, b, c, d, e, f, g, h] => {
write!(fmt, "{a:x}:{b:x}:{c:x}:{d:x}:{e:x}:{f:x}:{g:x}:{h:x}")
}
}
}
}
impl Unspecified for IpV6Address {
#[inline]
fn is_unspecified(&self) -> bool {
Self::UNSPECIFIED.eq(self)
}
}
test_inet_snapshot!(ipv6, ipv6_snapshot_test, IpV6Address);
define_inet_type!(
pub struct SocketAddressV6 {
ip: IpV6Address,
port: U16,
}
);
impl SocketAddressV6 {
pub const UNSPECIFIED: Self = Self {
ip: IpV6Address::UNSPECIFIED,
port: U16::ZERO,
};
#[inline]
pub const fn ip(&self) -> &IpV6Address {
&self.ip
}
#[inline]
pub fn port(&self) -> u16 {
self.port.into()
}
#[inline]
pub fn set_port(&mut self, port: u16) {
self.port.set(port)
}
#[inline]
pub fn unmap(self) -> SocketAddress {
match self.ip.unmap() {
IpAddress::Ipv4(addr) => SocketAddressV4::new(addr, self.port).into(),
IpAddress::Ipv6(_) => self.into(),
}
}
#[inline]
pub const fn unicast_scope(&self) -> Option<ip::UnicastScope> {
self.ip.unicast_scope()
}
}
impl fmt::Debug for SocketAddressV6 {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "SocketAddressV6({self})")
}
}
impl fmt::Display for SocketAddressV6 {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "[{}]:{:?}", self.ip, self.port)
}
}
impl Unspecified for SocketAddressV6 {
#[inline]
fn is_unspecified(&self) -> bool {
Self::UNSPECIFIED.eq(self)
}
}
impl From<net::Ipv6Addr> for IpV6Address {
fn from(address: net::Ipv6Addr) -> Self {
(&address).into()
}
}
impl From<&net::Ipv6Addr> for IpV6Address {
fn from(address: &net::Ipv6Addr) -> Self {
address.octets().into()
}
}
impl From<IpV6Address> for net::Ipv6Addr {
fn from(address: IpV6Address) -> Self {
address.octets.into()
}
}
impl From<net::SocketAddrV6> for SocketAddressV6 {
fn from(address: net::SocketAddrV6) -> Self {
let ip = address.ip().into();
let port = address.port().into();
Self { ip, port }
}
}
impl From<(net::Ipv6Addr, u16)> for SocketAddressV6 {
fn from((ip, port): (net::Ipv6Addr, u16)) -> Self {
Self::new(ip, port)
}
}
impl From<SocketAddressV6> for net::SocketAddrV6 {
fn from(address: SocketAddressV6) -> Self {
let ip = address.ip.into();
let port = address.port.into();
Self::new(ip, port, 0, 0)
}
}
impl From<&SocketAddressV6> for net::SocketAddrV6 {
fn from(address: &SocketAddressV6) -> Self {
let ip = address.ip.into();
let port = address.port.into();
Self::new(ip, port, 0, 0)
}
}
impl From<SocketAddressV6> for net::SocketAddr {
fn from(address: SocketAddressV6) -> Self {
let addr: net::SocketAddrV6 = address.into();
addr.into()
}
}
impl From<&SocketAddressV6> for net::SocketAddr {
fn from(address: &SocketAddressV6) -> Self {
let addr: net::SocketAddrV6 = address.into();
addr.into()
}
}
test_inet_snapshot!(socket_v6, socket_v6_snapshot_test, SocketAddressV6);
impl From<[u8; IPV6_LEN]> for IpV6Address {
#[inline]
fn from(octets: [u8; IPV6_LEN]) -> Self {
Self { octets }
}
}
impl From<[u16; IPV6_LEN / 2]> for IpV6Address {
#[inline]
fn from(octets: [u16; IPV6_LEN / 2]) -> Self {
macro_rules! convert {
($($segment:ident),*) => {{
let [$($segment),*] = octets;
$(
let $segment = u16::to_be_bytes($segment);
)*
Self {
octets: [
$(
$segment[0],
$segment[1],
)*
]
}
}}
}
convert!(a, b, c, d, e, f, g, h)
}
}
impl From<IpV6Address> for [u8; IPV6_LEN] {
#[inline]
fn from(v: IpV6Address) -> Self {
v.octets
}
}
impl From<IpV6Address> for [u16; IPV6_LEN / 2] {
#[inline]
fn from(v: IpV6Address) -> Self {
v.segments()
}
}
define_inet_type!(
pub struct Header {
vtcfl: Vtcfl,
payload_len: U16,
next_header: ip::Protocol,
hop_limit: u8,
source: IpV6Address,
destination: IpV6Address,
}
);
impl fmt::Debug for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ipv6::Header")
.field("version", &self.vtcfl.version())
.field("dscp", &self.vtcfl.dscp())
.field("ecn", &self.vtcfl.ecn())
.field(
"flow_label",
&format_args!("0x{:05x}", self.vtcfl.flow_label()),
)
.field("payload_len", &self.payload_len)
.field("next_header", &self.next_header)
.field("hop_limit", &self.hop_limit)
.field("source", &self.source)
.field("destination", &self.destination)
.finish()
}
}
impl Header {
#[inline]
pub fn swap(&mut self) {
core::mem::swap(&mut self.source, &mut self.destination)
}
#[inline]
pub const fn vtcfl(&self) -> &Vtcfl {
&self.vtcfl
}
#[inline]
pub fn vtcfl_mut(&mut self) -> &mut Vtcfl {
&mut self.vtcfl
}
#[inline]
pub const fn payload_len(&self) -> &U16 {
&self.payload_len
}
#[inline]
pub fn payload_len_mut(&mut self) -> &mut U16 {
&mut self.payload_len
}
#[inline]
pub const fn next_header(&self) -> &ip::Protocol {
&self.next_header
}
#[inline]
pub fn next_header_mut(&mut self) -> &mut ip::Protocol {
&mut self.next_header
}
#[inline]
pub const fn hop_limit(&self) -> &u8 {
&self.hop_limit
}
#[inline]
pub fn hop_limit_mut(&mut self) -> &mut u8 {
&mut self.hop_limit
}
#[inline]
pub const fn source(&self) -> &IpV6Address {
&self.source
}
#[inline]
pub fn source_mut(&mut self) -> &mut IpV6Address {
&mut self.source
}
#[inline]
pub const fn destination(&self) -> &IpV6Address {
&self.destination
}
#[inline]
pub fn destination_mut(&mut self) -> &mut IpV6Address {
&mut self.destination
}
}
define_inet_type!(
pub struct Vtcfl {
octets: [u8; 4],
}
);
impl fmt::Debug for Vtcfl {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ipv6::Vtf")
.field("version", &self.version())
.field("dscp", &self.dscp())
.field("ecn", &self.ecn())
.field("flow_label", &format_args!("0x{:05x}", self.flow_label()))
.finish()
}
}
impl Vtcfl {
#[inline]
pub const fn version(&self) -> u8 {
self.octets[0] >> 4
}
#[inline]
pub fn set_version(&mut self, version: u8) -> &mut Self {
self.octets[0] = (version << 4) | self.octets[0] & 0x0F;
self
}
#[inline]
pub fn dscp(&self) -> u8 {
let value = (self.octets[0] << 4) | (self.octets[1] >> 4);
value >> 2
}
#[inline]
pub fn set_dscp(&mut self, value: u8) -> &mut Self {
let value = value << 2;
self.octets[0] = self.octets[0] & 0xF0 | (value >> 4);
self.octets[1] = (value << 4) | self.octets[1] & 0b11_1111;
self
}
#[inline]
pub fn ecn(&self) -> ExplicitCongestionNotification {
ExplicitCongestionNotification::new((self.octets[1] >> 4) & 0b11)
}
#[inline]
pub fn set_ecn(&mut self, ecn: ExplicitCongestionNotification) -> &mut Self {
self.octets[1] = (self.octets[1] & !(0b11 << 4)) | ((ecn as u8) << 4);
self
}
#[inline]
pub const fn flow_label(&self) -> u32 {
u32::from_be_bytes([0, self.octets[1] & 0x0F, self.octets[2], self.octets[3]])
}
#[inline]
pub fn set_flow_label(&mut self, flow_label: u32) -> &mut Self {
let bytes = flow_label.to_be_bytes();
self.octets[1] = self.octets[1] & 0xF0 | bytes[1] & 0x0F;
self.octets[2] = bytes[2];
self.octets[3] = bytes[3];
self
}
}
#[cfg(any(test, feature = "std"))]
mod std_conversion {
use super::*;
use std::net;
impl net::ToSocketAddrs for SocketAddressV6 {
type Iter = std::iter::Once<net::SocketAddr>;
fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
let ip = self.ip.into();
let port = self.port.into();
let addr = net::SocketAddrV6::new(ip, port, 0, 0);
Ok(std::iter::once(addr.into()))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use bolero::{check, generator::*};
use s2n_codec::{DecoderBuffer, DecoderBufferMut};
#[test]
#[cfg_attr(kani, kani::proof, kani::unwind(17), kani::solver(kissat))]
fn scope_test() {
let g = produce::<[u8; 16]>().map_gen(IpV6Address::from);
check!().with_generator(g).cloned().for_each(|subject| {
use ip::UnicastScope::*;
if let IpAddress::Ipv4(ipv4) = subject.unmap() {
assert_eq!(ipv4.unicast_scope(), subject.unicast_scope());
return;
}
let expected = std::net::Ipv6Addr::from(subject);
let network = ip_network::Ipv6Network::from(expected);
match subject.unicast_scope() {
Some(Global) => {
assert!(network.is_global() || network.is_unicast_site_local());
}
Some(Private) => {
assert!(network.is_unique_local());
}
Some(Loopback) => {
assert!(expected.is_loopback());
}
Some(LinkLocal) => {
assert!(network.is_unicast_link_local());
}
None => {
assert!(
expected.is_multicast()
|| expected.is_unspecified()
|| subject.segments()[0] == 0x0100
|| subject.segments()[0] == 0x2001
);
}
}
})
}
#[test]
#[cfg_attr(miri, ignore)]
fn snapshot_test() {
let mut buffer = vec![0u8; core::mem::size_of::<Header>()];
for (idx, byte) in buffer.iter_mut().enumerate() {
*byte = idx as u8;
}
let decoder = DecoderBuffer::new(&buffer);
let (header, _) = decoder.decode::<&Header>().unwrap();
insta::assert_debug_snapshot!("snapshot_test", header);
buffer.fill(255);
let decoder = DecoderBuffer::new(&buffer);
let (header, _) = decoder.decode::<&Header>().unwrap();
insta::assert_debug_snapshot!("snapshot_filled_test", header);
}
#[test]
#[cfg_attr(kani, kani::proof, kani::unwind(17), kani::solver(kissat))]
fn header_getter_setter_test() {
check!().with_type::<Header>().for_each(|expected| {
let mut buffer = [255u8; core::mem::size_of::<Header>()];
let decoder = DecoderBufferMut::new(&mut buffer);
let (header, _) = decoder.decode::<&mut Header>().unwrap();
{
header
.vtcfl_mut()
.set_version(expected.vtcfl().version())
.set_dscp(expected.vtcfl().dscp())
.set_ecn(expected.vtcfl().ecn())
.set_flow_label(expected.vtcfl().flow_label());
*header.hop_limit_mut() = *expected.hop_limit();
*header.next_header_mut() = *expected.next_header();
header.payload_len_mut().set(expected.payload_len().get());
*header.source_mut() = *expected.source();
*header.destination_mut() = *expected.destination();
}
let decoder = DecoderBuffer::new(&buffer);
let (actual, _) = decoder.decode::<&Header>().unwrap();
{
assert_eq!(expected.vtcfl().version(), expected.vtcfl().version());
assert_eq!(expected.vtcfl().dscp(), expected.vtcfl().dscp());
assert_eq!(expected.vtcfl().ecn(), expected.vtcfl().ecn());
assert_eq!(expected.vtcfl().flow_label(), expected.vtcfl().flow_label());
assert_eq!(
expected.vtcfl(),
actual.vtcfl(),
"\nexpected: {:?}\n actual: {:?}",
expected.as_bytes(),
actual.as_bytes()
);
assert_eq!(expected.hop_limit(), actual.hop_limit());
assert_eq!(expected.next_header(), actual.next_header());
assert_eq!(expected.payload_len(), actual.payload_len());
assert_eq!(expected.source(), actual.source());
assert_eq!(expected.destination(), actual.destination());
assert_eq!(
expected,
actual,
"\nexpected: {:?}\n actual: {:?}",
expected.as_bytes(),
actual.as_bytes()
);
}
})
}
#[test]
fn header_round_trip_test() {
check!().for_each(|buffer| {
s2n_codec::assert_codec_round_trip_bytes!(Header, buffer);
});
}
}