use std::{fmt::Debug, mem::transmute};
use super::classify::{ClassifiedPacketView, ClassifyError};
use crate::{
address::{addr::ScionAddr, host_addr::WireHostAddrError, socket_addr::ScionSocketAddr},
core::view::{View, ViewConversionError},
header::{layout::ScionHeaderLayout, view::ScionHeaderView},
payload::{ProtocolNumber, scmp::view::ScmpPayloadView, udp::view::UdpDatagramView},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Raw;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Udp;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Scmp;
#[repr(transparent)]
pub struct ScionPacketView<T = Raw>(std::marker::PhantomData<T>, [u8]);
impl<T> ScionPacketView<T> {
#[inline]
pub fn header(&self) -> &ScionHeaderView {
unsafe {
let header_len = ScionHeaderView::from_slice_unchecked(&self.1).header_len() as usize;
ScionHeaderView::from_slice_unchecked(self.1.get_unchecked(..header_len))
}
}
#[inline]
pub fn header_mut(&mut self) -> &mut ScionHeaderView {
unsafe {
let header_len = ScionHeaderView::from_slice_unchecked(&self.1).header_len() as usize;
ScionHeaderView::from_mut_slice_unchecked(self.1.get_unchecked_mut(..header_len))
}
}
#[inline]
pub fn payload(&self) -> &[u8] {
unsafe {
let header = self.header();
let header_len = header.header_len() as usize;
let payload_len = header.payload_len() as usize;
let tail_len = self.1.len().saturating_sub(header_len);
let truncated_payload_len = std::cmp::min(payload_len, tail_len);
self.1
.get_unchecked(header_len..header_len + truncated_payload_len)
}
}
#[inline]
pub fn src_scion_addr(&self) -> Result<ScionAddr, WireHostAddrError> {
let host = self.header().src_host_addr()?.scion_host_addr()?;
Ok(ScionAddr::new(self.header().src_ia(), host))
}
#[inline]
pub fn dst_scion_addr(&self) -> Result<ScionAddr, WireHostAddrError> {
let host = self.header().dst_host_addr()?.scion_host_addr()?;
Ok(ScionAddr::new(self.header().dst_ia(), host))
}
}
pub type ScionRawPacketView = ScionPacketView<Raw>;
impl View for ScionRawPacketView {
#[inline]
fn has_required_size(buf: &[u8]) -> Result<usize, ViewConversionError> {
let layout = ScionHeaderLayout::try_from_slice(buf)?;
let packet_len = std::cmp::min(layout.header_len + layout.payload_len, buf.len());
Ok(packet_len)
}
#[inline]
unsafe fn from_slice_unchecked(buf: &[u8]) -> &Self {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn from_mut_slice_unchecked(buf: &mut [u8]) -> &mut Self {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn from_boxed_unchecked(buf: Box<[u8]>) -> Box<Self> {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
&mut self.1
}
#[inline]
fn as_slice_boxed(self: Box<Self>) -> Box<[u8]> {
unsafe { std::mem::transmute(self) }
}
#[inline]
fn as_slice(&self) -> &[u8] {
&self.1
}
}
impl Debug for ScionRawPacketView {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScionRawPacketView")
.field("header", &self.header())
.field("payload_len", &self.payload().len())
.finish()
}
}
impl ScionRawPacketView {
#[inline]
pub fn payload_mut(&mut self) -> &mut [u8] {
unsafe {
let header = self.header();
let header_len = header.header_len() as usize;
let payload_len = header.payload_len() as usize;
let tail_len = self.1.len().saturating_sub(header_len);
let truncated_payload_len = std::cmp::min(payload_len, tail_len);
self.1
.get_unchecked_mut(header_len..header_len + truncated_payload_len)
}
}
#[inline]
pub fn try_classify(&self) -> Result<ClassifiedPacketView<'_>, ClassifyError> {
match self.header().next_header() {
ProtocolNumber::Udp => {
let udp =
ScionUdpPacketView::try_from_raw(self).map_err(ClassifyError::MalformedUdp)?;
Ok(ClassifiedPacketView::Udp(udp))
}
ProtocolNumber::Scmp => {
let scmp = ScionScmpPacketView::try_from_raw(self)
.map_err(ClassifyError::MalformedScmp)?;
Ok(ClassifiedPacketView::Scmp(scmp))
}
_ => Ok(ClassifiedPacketView::Other(self)),
}
}
#[inline]
pub fn try_as_udp(&self) -> Result<&ScionUdpPacketView, ViewConversionError> {
if self.header().next_header() != ProtocolNumber::Udp {
return Err(ViewConversionError::Other("next header not UDP"));
}
ScionUdpPacketView::try_from_raw(self)
}
#[inline]
pub fn try_as_udp_mut(&mut self) -> Result<&mut ScionUdpPacketView, ViewConversionError> {
if self.header().next_header() != ProtocolNumber::Udp {
return Err(ViewConversionError::Other("next header not UDP"));
}
ScionUdpPacketView::try_from_raw_mut(self)
}
#[inline]
pub fn try_into_udp(self: Box<Self>) -> Result<Box<ScionUdpPacketView>, ViewConversionError> {
if self.header().next_header() != ProtocolNumber::Udp {
return Err(ViewConversionError::Other("next header not UDP"));
}
ScionUdpPacketView::try_from_raw_owned(self)
}
#[inline]
pub fn try_as_scmp(&self) -> Result<&ScionScmpPacketView, ViewConversionError> {
if self.header().next_header() != ProtocolNumber::Scmp {
return Err(ViewConversionError::Other("next header not SCMP"));
}
ScionScmpPacketView::try_from_raw(self)
}
#[inline]
pub fn try_as_scmp_mut(&mut self) -> Result<&mut ScionScmpPacketView, ViewConversionError> {
if self.header().next_header() != ProtocolNumber::Scmp {
return Err(ViewConversionError::Other("next header not SCMP"));
}
ScionScmpPacketView::try_from_raw_mut(self)
}
#[inline]
pub fn try_into_scmp(self: Box<Self>) -> Result<Box<ScionScmpPacketView>, ViewConversionError> {
if self.header().next_header() != ProtocolNumber::Scmp {
return Err(ViewConversionError::Other("next header not SCMP"));
}
ScionScmpPacketView::try_from_raw_owned(self)
}
}
pub type ScionUdpPacketView = ScionPacketView<Udp>;
impl View for ScionUdpPacketView {
#[inline]
fn has_required_size(buf: &[u8]) -> Result<usize, ViewConversionError> {
let packet_len = ScionRawPacketView::has_required_size(buf)?;
let view = unsafe { ScionRawPacketView::from_slice_unchecked(buf) };
_ = UdpDatagramView::has_required_size(view.payload())?;
Ok(packet_len)
}
#[inline]
unsafe fn from_slice_unchecked(buf: &[u8]) -> &Self {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn from_mut_slice_unchecked(buf: &mut [u8]) -> &mut Self {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn from_boxed_unchecked(buf: Box<[u8]>) -> Box<Self> {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
&mut self.1
}
#[inline]
fn as_slice_boxed(self: Box<Self>) -> Box<[u8]> {
unsafe { transmute(self) }
}
#[inline]
fn as_slice(&self) -> &[u8] {
&self.1
}
}
impl Debug for ScionUdpPacketView {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScionUdpPacketView")
.field("header", &self.header())
.field("datagram", &self.udp())
.finish()
}
}
impl ScionUdpPacketView {
#[inline]
pub fn try_from_raw(raw: &ScionRawPacketView) -> Result<&Self, ViewConversionError> {
if raw.header().next_header() != ProtocolNumber::Udp {
return Err(ViewConversionError::Other("next header not UDP"));
}
let (view, _) = ScionUdpPacketView::try_from_slice(raw.as_slice())?;
Ok(view)
}
#[inline]
pub fn try_from_raw_mut(
raw: &mut ScionRawPacketView,
) -> Result<&mut Self, ViewConversionError> {
if raw.header().next_header() != ProtocolNumber::Udp {
return Err(ViewConversionError::Other("next header not UDP"));
}
let (view, _) = {
unsafe { ScionUdpPacketView::try_from_mut_slice(raw.as_slice_mut())? }
};
Ok(view)
}
#[inline]
pub fn try_from_raw_owned(
raw: Box<ScionRawPacketView>,
) -> Result<Box<Self>, ViewConversionError> {
if raw.header().next_header() != ProtocolNumber::Udp {
return Err(ViewConversionError::Other("next header not UDP"));
}
ScionUdpPacketView::try_from_boxed(raw.as_slice_boxed())
}
#[inline]
pub fn as_raw(&self) -> &ScionRawPacketView {
unsafe { ScionRawPacketView::from_slice_unchecked(self.as_slice()) }
}
#[inline]
pub fn as_raw_mut(&mut self) -> &mut ScionRawPacketView {
unsafe { ScionRawPacketView::from_mut_slice_unchecked(self.as_slice_mut()) }
}
#[inline]
pub fn into_raw(self: Box<Self>) -> Box<ScionRawPacketView> {
unsafe { ScionRawPacketView::from_boxed_unchecked(self.as_slice_boxed()) }
}
#[inline]
pub fn udp(&self) -> &UdpDatagramView {
let (view, _) = UdpDatagramView::try_from_slice(self.payload())
.expect("udp payload is not large enough for a UDP header");
view
}
#[inline]
pub fn src_socket_addr(&self) -> Result<ScionSocketAddr, WireHostAddrError> {
let src_port = self.udp().src_port();
let isd_asn = self.header().src_ia();
let scion_addr = self.header().src_host_addr()?.scion_host_addr()?;
Ok(ScionSocketAddr::new(isd_asn, scion_addr, src_port))
}
#[inline]
pub fn dst_socket_addr(&self) -> Result<ScionSocketAddr, WireHostAddrError> {
let dst_port = self.udp().dst_port();
let isd_asn = self.header().dst_ia();
let scion_addr = self.header().dst_host_addr()?.scion_host_addr()?;
Ok(ScionSocketAddr::new(isd_asn, scion_addr, dst_port))
}
}
impl<'a> TryFrom<&'a ScionRawPacketView> for &'a ScionUdpPacketView {
type Error = ViewConversionError;
#[inline]
fn try_from(value: &'a ScionRawPacketView) -> Result<Self, Self::Error> {
ScionUdpPacketView::try_from_raw(value)
}
}
impl<'a> TryFrom<&'a mut ScionRawPacketView> for &'a mut ScionUdpPacketView {
type Error = ViewConversionError;
#[inline]
fn try_from(value: &'a mut ScionRawPacketView) -> Result<Self, Self::Error> {
ScionUdpPacketView::try_from_raw_mut(value)
}
}
impl TryFrom<Box<ScionRawPacketView>> for Box<ScionUdpPacketView> {
type Error = ViewConversionError;
#[inline]
fn try_from(value: Box<ScionRawPacketView>) -> Result<Self, Self::Error> {
ScionUdpPacketView::try_from_raw_owned(value)
}
}
impl<'a> From<&'a ScionUdpPacketView> for &'a ScionRawPacketView {
#[inline]
fn from(value: &'a ScionUdpPacketView) -> Self {
value.as_raw()
}
}
impl<'a> From<&'a mut ScionUdpPacketView> for &'a mut ScionRawPacketView {
#[inline]
fn from(value: &'a mut ScionUdpPacketView) -> Self {
value.as_raw_mut()
}
}
impl From<Box<ScionUdpPacketView>> for Box<ScionRawPacketView> {
#[inline]
fn from(value: Box<ScionUdpPacketView>) -> Self {
value.into_raw()
}
}
pub type ScionScmpPacketView = ScionPacketView<Scmp>;
impl View for ScionScmpPacketView {
#[inline]
fn has_required_size(buf: &[u8]) -> Result<usize, ViewConversionError> {
let packet_len = ScionRawPacketView::has_required_size(buf)?;
let view = unsafe { ScionRawPacketView::from_slice_unchecked(buf) };
_ = ScmpPayloadView::has_required_size(view.payload())?;
Ok(packet_len)
}
#[inline]
unsafe fn from_slice_unchecked(buf: &[u8]) -> &Self {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn from_mut_slice_unchecked(buf: &mut [u8]) -> &mut Self {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn from_boxed_unchecked(buf: Box<[u8]>) -> Box<Self> {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
&mut self.1
}
#[inline]
fn as_slice_boxed(self: Box<Self>) -> Box<[u8]> {
unsafe { transmute(self) }
}
#[inline]
fn as_slice(&self) -> &[u8] {
&self.1
}
}
impl Debug for ScionScmpPacketView {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScionScmpPacketView")
.field("header", &self.header())
.field("message", &self.scmp())
.finish()
}
}
impl<'a> ScionScmpPacketView {
#[inline]
pub fn try_from_raw(raw: &'a ScionRawPacketView) -> Result<&'a Self, ViewConversionError> {
let (view, _) = ScionScmpPacketView::try_from_slice(raw.as_slice())?;
Ok(view)
}
#[inline]
pub fn try_from_raw_mut(
raw: &'a mut ScionRawPacketView,
) -> Result<&'a mut Self, ViewConversionError> {
let (view, _) = {
unsafe { ScionScmpPacketView::try_from_mut_slice(raw.as_slice_mut())? }
};
Ok(view)
}
#[inline]
pub fn try_from_raw_owned(
raw: Box<ScionRawPacketView>,
) -> Result<Box<Self>, ViewConversionError> {
ScionScmpPacketView::try_from_boxed(raw.as_slice_boxed())
}
#[inline]
pub fn as_raw(&self) -> &ScionRawPacketView {
unsafe { ScionRawPacketView::from_slice_unchecked(self.as_slice()) }
}
#[inline]
pub unsafe fn as_raw_mut(&mut self) -> &mut ScionRawPacketView {
unsafe { ScionRawPacketView::from_mut_slice_unchecked(self.as_slice_mut()) }
}
#[inline]
pub fn into_raw(self: Box<Self>) -> Box<ScionRawPacketView> {
unsafe { ScionRawPacketView::from_boxed_unchecked(self.as_slice_boxed()) }
}
#[inline]
pub fn scmp(&self) -> &ScmpPayloadView {
let (view, _) = ScmpPayloadView::try_from_slice(self.payload())
.expect("scmp payload is not large enough for a SCMP header");
view
}
}
impl<'a> TryFrom<&'a ScionRawPacketView> for &'a ScionScmpPacketView {
type Error = ViewConversionError;
#[inline]
fn try_from(value: &'a ScionRawPacketView) -> Result<Self, Self::Error> {
ScionScmpPacketView::try_from_raw(value)
}
}
impl<'a> TryFrom<&'a mut ScionRawPacketView> for &'a mut ScionScmpPacketView {
type Error = ViewConversionError;
#[inline]
fn try_from(value: &'a mut ScionRawPacketView) -> Result<Self, Self::Error> {
ScionScmpPacketView::try_from_raw_mut(value)
}
}
impl TryFrom<Box<ScionRawPacketView>> for Box<ScionScmpPacketView> {
type Error = ViewConversionError;
#[inline]
fn try_from(value: Box<ScionRawPacketView>) -> Result<Self, Self::Error> {
ScionScmpPacketView::try_from_raw_owned(value)
}
}
impl<'a> From<&'a ScionScmpPacketView> for &'a ScionRawPacketView {
#[inline]
fn from(value: &'a ScionScmpPacketView) -> Self {
value.as_raw()
}
}
impl From<Box<ScionScmpPacketView>> for Box<ScionRawPacketView> {
#[inline]
fn from(value: Box<ScionScmpPacketView>) -> Self {
value.into_raw()
}
}