use crate::{
address::addr::ScionAddr,
core::{
convert::{FromView, TryFromView},
encode::{EncodeError, InvalidStructureError, WireEncode},
layout::Layout,
view::ViewConversionError,
write::unchecked_bit_range_be_write,
},
dataplane_path::{model::DpPath, types::PathType},
header::{
layout::{AddressHeaderLayout, CommonHeaderLayout, ScionHeaderLayout},
view::ScionHeaderView,
},
payload::ProtocolNumber,
scion::{
address::host_addr::{WireHostAddr, WireHostAddrType},
identifier::isd_asn::IsdAsn,
},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ScionPacketHeader {
pub common: CommonHeader,
pub address: AddressHeader,
pub path: DpPath,
}
impl ScionPacketHeader {
#[inline]
fn size_units(&self) -> u8 {
(self.required_size() / 4) as u8
}
}
impl ScionPacketHeader {
#[inline]
pub fn required_size(&self) -> usize {
CommonHeaderLayout::SIZE_BYTES + self.address.required_size() + self.path.required_size()
}
#[inline]
pub fn wire_valid(&self) -> Result<(), InvalidStructureError> {
let required_size = self.required_size();
if !required_size.is_multiple_of(4) {
return Err(InvalidStructureError::from(
"header size must be a multiple of 4 bytes",
));
}
if required_size > ScionHeaderLayout::MAX_SIZE_BYTES {
return Err(InvalidStructureError::from(
"header size exceeds maximum encodeable value of 1020 bytes",
));
}
self.common.valid()?;
self.address.wire_valid()?;
self.path.wire_valid()?;
Ok(())
}
#[inline]
pub unsafe fn encode_unchecked(&self, buf: &mut [u8], payload_size: u16) -> usize {
unsafe {
use CommonHeaderLayout as CHL;
self.common.encode_unchecked(
buf,
self.size_units(),
self.path.path_type(),
self.address.dst_addr_type(),
self.address.src_addr_type(),
payload_size,
);
let offset = CHL::SIZE_BYTES;
let address_buf = buf.split_at_mut_unchecked(offset).1;
self.address.encode_unchecked(address_buf);
let offset = offset + self.address.required_size();
let path_buf = buf.split_at_mut_unchecked(offset).1;
self.path.encode_unchecked(path_buf);
}
self.required_size()
}
#[inline]
pub fn try_encode(&self, buf: &mut [u8], payload_size: u16) -> Result<usize, EncodeError> {
self.wire_valid()?;
let required_size = self.required_size();
if buf.len() < required_size {
return Err(EncodeError::BufferTooSmall(required_size));
}
Ok(unsafe { self.encode_unchecked(buf, payload_size) })
}
}
impl TryFromView for ScionPacketHeader {
type ViewType = ScionHeaderView;
#[inline]
fn try_from_view(view: &Self::ViewType) -> Result<Self, ViewConversionError> {
Ok(ScionPacketHeader {
common: CommonHeader::from_view(view),
address: AddressHeader::try_from_view(view)?,
path: DpPath::from_view(&view.path()),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CommonHeader {
pub traffic_class: u8,
pub flow_id: u32,
pub next_header: ProtocolNumber,
}
impl CommonHeader {
#[inline]
pub fn valid(&self) -> Result<(), InvalidStructureError> {
use CommonHeaderLayout as CHL;
if self.flow_id > CHL::FLOW_ID_RNG.max_uint() as u32 {
return Err("flow_id exceeds maximum encodeable value".into());
}
Ok(())
}
const VERSION: u8 = 0;
#[inline]
pub unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
header_len_units: u8,
path_type: PathType,
dst_addr_type: WireHostAddrType,
src_addr_type: WireHostAddrType,
payload_size: u16,
) -> usize {
unsafe {
use CommonHeaderLayout as CHL;
unchecked_bit_range_be_write(buf, CHL::VERSION_RNG, Self::VERSION);
unchecked_bit_range_be_write(buf, CHL::TRAFFIC_CLASS_RNG, self.traffic_class);
unchecked_bit_range_be_write(buf, CHL::FLOW_ID_RNG, self.flow_id);
unchecked_bit_range_be_write::<u8>(buf, CHL::NEXT_HEADER_RNG, self.next_header.into());
unchecked_bit_range_be_write(buf, CHL::HEADER_LEN_RNG, header_len_units);
unchecked_bit_range_be_write(buf, CHL::PAYLOAD_LEN_RNG, payload_size);
unchecked_bit_range_be_write::<u8>(buf, CHL::PATH_TYPE_RNG, path_type.into());
let dst_addr_info: u8 = dst_addr_type.into();
unchecked_bit_range_be_write::<u8>(buf, CHL::DST_ADDR_INFO_RNG, dst_addr_info);
let src_addr_info: u8 = src_addr_type.into();
unchecked_bit_range_be_write::<u8>(buf, CHL::SRC_ADDR_INFO_RNG, src_addr_info);
unchecked_bit_range_be_write(buf, CHL::RSV_RNG, 0u16); }
CommonHeaderLayout::SIZE_BYTES
}
}
impl FromView for CommonHeader {
type ViewType = ScionHeaderView;
#[inline]
fn from_view(view: &Self::ViewType) -> Self {
debug_assert!(view.version() == Self::VERSION, "Unsupported SCION version");
CommonHeader {
traffic_class: view.traffic_class(),
flow_id: view.flow_id(),
next_header: view.next_header(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AddressHeader {
pub dst_ia: IsdAsn,
pub src_ia: IsdAsn,
pub dst_host_addr: WireHostAddr,
pub src_host_addr: WireHostAddr,
}
impl AddressHeader {
#[inline]
pub fn new(src: ScionAddr, dst: ScionAddr) -> Self {
AddressHeader {
dst_ia: dst.isd_asn(),
src_ia: src.isd_asn(),
dst_host_addr: dst.host().into(),
src_host_addr: src.host().into(),
}
}
#[inline]
pub fn dst_addr_type(&self) -> WireHostAddrType {
self.dst_host_addr.addr_type()
}
#[inline]
pub fn src_addr_type(&self) -> WireHostAddrType {
self.src_host_addr.addr_type()
}
}
impl WireEncode for AddressHeader {
#[inline]
fn required_size(&self) -> usize {
AddressHeaderLayout::new(
self.dst_host_addr.required_size() as u8,
self.src_host_addr.required_size() as u8,
)
.size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
self.dst_host_addr.wire_valid()?;
self.src_host_addr.wire_valid()?;
Ok(())
}
#[inline]
unsafe fn encode_unchecked(&self, buf: &mut [u8]) -> usize {
unsafe {
use AddressHeaderLayout as AHL;
unchecked_bit_range_be_write(buf, AHL::DST_ISD_RNG, self.dst_ia.isd().0);
unchecked_bit_range_be_write(buf, AHL::DST_AS_RNG, self.dst_ia.asn().0);
unchecked_bit_range_be_write(buf, AHL::SRC_ISD_RNG, self.src_ia.isd().0);
unchecked_bit_range_be_write(buf, AHL::SRC_AS_RNG, self.src_ia.asn().0);
let layout = AddressHeaderLayout::new(
self.src_host_addr.required_size() as u8,
self.dst_host_addr.required_size() as u8,
);
{
let dst_host_buf =
buf.get_unchecked_mut(layout.dst_host_addr_range().aligned_byte_range());
self.dst_host_addr.encode_unchecked(dst_host_buf);
}
{
let src_host_buf =
buf.get_unchecked_mut(layout.src_host_addr_range().aligned_byte_range());
self.src_host_addr.encode_unchecked(src_host_buf);
}
}
self.required_size()
}
}
impl TryFromView for AddressHeader {
type ViewType = ScionHeaderView;
#[inline]
fn try_from_view(view: &Self::ViewType) -> Result<Self, ViewConversionError> {
Ok(AddressHeader {
dst_ia: view.dst_ia(),
src_ia: view.src_ia(),
dst_host_addr: view
.dst_host_addr()
.map_err(|_| ViewConversionError::Other("invalid dst_host_addr"))?,
src_host_addr: view
.src_host_addr()
.map_err(|_| ViewConversionError::Other("invalid src_host_addr"))?,
})
}
}
#[cfg(feature = "proptest")]
pub mod ptest {
use ::proptest::prelude::*;
use super::*;
use crate::dataplane_path::model::DpPath;
#[derive(Debug, Clone, Default)]
pub struct ArbitraryScionPacketHeaderParams {
pub dst_host_addr: <WireHostAddr as Arbitrary>::Parameters,
pub src_host_addr: <WireHostAddr as Arbitrary>::Parameters,
pub path: <DpPath as Arbitrary>::Parameters,
}
impl Arbitrary for ScionPacketHeader {
type Parameters = ArbitraryScionPacketHeaderParams;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
let traffic_class = any::<u8>();
let flow_id = 0u32..=0xF_FFFFu32;
let next_header = any::<u8>();
let dst_ia = any::<IsdAsn>();
let src_ia = any::<IsdAsn>();
let dst_host_addr = WireHostAddr::arbitrary_with(params.dst_host_addr);
let src_host_addr = WireHostAddr::arbitrary_with(params.src_host_addr);
let path = DpPath::arbitrary_with(params.path);
(
traffic_class,
flow_id,
next_header,
dst_ia,
src_ia,
dst_host_addr,
src_host_addr,
path,
)
.prop_map(
|(
traffic_class,
flow_id,
next_header,
dst_ia,
src_ia,
dst_host_addr,
src_host_addr,
path,
)| {
Self {
common: CommonHeader {
traffic_class,
flow_id,
next_header: next_header.into(),
},
address: AddressHeader {
dst_ia,
src_ia,
dst_host_addr,
src_host_addr,
},
path,
}
},
)
.boxed()
}
}
}