use crate::{
core::{
convert::{FromView, ToModel},
encode::InvalidStructureError,
layout::Layout as _,
view::{View, ViewConversionError},
write::unchecked_bit_range_be_write,
},
header::model::AddressHeader,
identifier::isd_asn::IsdAsn,
packet::view::ScionRawPacketView,
payload::{
ProtocolNumber,
encode::PayloadEncode,
scmp::{
layout::{
ScmpDestinationUnreachableLayout, ScmpEchoReplyLayout, ScmpEchoRequestLayout,
ScmpExternalInterfaceDownLayout, ScmpInternalConnectivityDownLayout,
ScmpPacketTooBigLayout, ScmpParameterProblemLayout, ScmpTracerouteReplyLayout,
ScmpTracerouteRequestLayout, ScmpUnknownMessageLayout,
},
types::{ScmpDestinationUnreachableCode, ScmpMessageType, ScmpParameterProblemCode},
view::{
ScmpDestinationUnreachableMessageView, ScmpEchoReplyMessageView,
ScmpEchoRequestMessageView, ScmpExternalInterfaceDownMessageView,
ScmpInternalConnectivityDownMessageView, ScmpMessageView,
ScmpPacketTooBigMessageView, ScmpParameterProblemMessageView, ScmpPayloadView,
ScmpTracerouteReplyMessageView, ScmpTracerouteRequestMessageView,
ScmpUnknownMessageView,
},
},
udp::view::UdpDatagramView,
},
scion::checksum::ChecksumDigest,
};
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum ScmpMessage {
DestinationUnreachable(ScmpDestinationUnreachable),
PacketTooBig(ScmpPacketTooBig),
ParameterProblem(ScmpParameterProblem),
ExternalInterfaceDown(ScmpExternalInterfaceDown),
InternalConnectivityDown(ScmpInternalConnectivityDown),
EchoRequest(ScmpEchoRequest),
EchoReply(ScmpEchoReply),
TracerouteRequest(ScmpTracerouteRequest),
TracerouteReply(ScmpTracerouteReply),
Unknown(ScmpMessageUnknown),
}
impl ScmpMessage {
#[inline]
pub fn try_from_slice(bytes: &[u8]) -> Result<(Self, &[u8]), ViewConversionError> {
let (view, rest) = ScmpPayloadView::try_from_slice(bytes)?;
Ok((Self::from_view(&view.message()), rest))
}
pub fn from_view(view: &ScmpMessageView) -> Self {
match view {
ScmpMessageView::DestinationUnreachable(view) => {
Self::DestinationUnreachable(ScmpDestinationUnreachable::from_view(view))
}
ScmpMessageView::PacketTooBig(view) => {
Self::PacketTooBig(ScmpPacketTooBig::from_view(view))
}
ScmpMessageView::ParameterProblem(view) => {
Self::ParameterProblem(ScmpParameterProblem::from_view(view))
}
ScmpMessageView::ExternalInterfaceDown(view) => {
Self::ExternalInterfaceDown(ScmpExternalInterfaceDown::from_view(view))
}
ScmpMessageView::InternalConnectivityDown(view) => {
Self::InternalConnectivityDown(ScmpInternalConnectivityDown::from_view(view))
}
ScmpMessageView::EchoRequest(view) => {
Self::EchoRequest(ScmpEchoRequest::from_view(view))
}
ScmpMessageView::EchoReply(view) => Self::EchoReply(ScmpEchoReply::from_view(view)),
ScmpMessageView::TracerouteRequest(view) => {
Self::TracerouteRequest(ScmpTracerouteRequest::from_view(view))
}
ScmpMessageView::TracerouteReply(view) => {
Self::TracerouteReply(ScmpTracerouteReply::from_view(view))
}
ScmpMessageView::Unknown(view) => Self::Unknown(ScmpMessageUnknown::from_view(view)),
}
}
}
impl ScmpMessage {
#[inline]
pub const fn message_type(&self) -> ScmpMessageType {
match self {
Self::DestinationUnreachable(_) => ScmpMessageType::DestinationUnreachable,
Self::PacketTooBig(_) => ScmpMessageType::PacketTooBig,
Self::ParameterProblem(_) => ScmpMessageType::ParameterProblem,
Self::ExternalInterfaceDown(_) => ScmpMessageType::ExternalInterfaceDown,
Self::InternalConnectivityDown(_) => ScmpMessageType::InternalConnectivityDown,
Self::EchoRequest(_) => ScmpMessageType::EchoRequest,
Self::EchoReply(_) => ScmpMessageType::EchoReply,
Self::TracerouteRequest(_) => ScmpMessageType::TracerouteRequest,
Self::TracerouteReply(_) => ScmpMessageType::TracerouteReply,
Self::Unknown(msg) => ScmpMessageType::Unknown(msg.message_type),
}
}
#[inline]
pub fn dst_port(&self) -> Option<u16> {
let udp_src_port = |offending_packet: &[u8]| {
let (inner, _) = ScionRawPacketView::try_from_slice(offending_packet).ok()?;
if inner.header().next_header() != ProtocolNumber::Udp {
return None;
}
let (udp, _) = UdpDatagramView::try_from_slice(inner.payload()).ok()?;
Some(udp.src_port())
};
match self {
ScmpMessage::EchoRequest(v) => Some(v.identifier),
ScmpMessage::EchoReply(v) => Some(v.identifier),
ScmpMessage::TracerouteRequest(v) => Some(v.identifier),
ScmpMessage::TracerouteReply(v) => Some(v.identifier),
ScmpMessage::DestinationUnreachable(v) => udp_src_port(&v.offending_packet),
ScmpMessage::PacketTooBig(v) => udp_src_port(&v.offending_packet),
ScmpMessage::ParameterProblem(v) => udp_src_port(&v.offending_packet),
ScmpMessage::ExternalInterfaceDown(v) => udp_src_port(&v.offending_packet),
ScmpMessage::InternalConnectivityDown(v) => udp_src_port(&v.offending_packet),
ScmpMessage::Unknown(_) => None,
}
}
#[inline]
pub fn try_into_informational_message(self) -> Result<ScmpInformationalMessage, ScmpMessage> {
match self {
Self::EchoRequest(x) => Ok(ScmpInformationalMessage::EchoRequest(x)),
Self::EchoReply(x) => Ok(ScmpInformationalMessage::EchoReply(x)),
Self::TracerouteRequest(x) => Ok(ScmpInformationalMessage::TracerouteRequest(x)),
Self::TracerouteReply(x) => Ok(ScmpInformationalMessage::TracerouteReply(x)),
_ => Err(self),
}
}
#[inline]
pub fn try_into_error_message(self) -> Result<ScmpErrorMessage, ScmpMessage> {
match self {
Self::DestinationUnreachable(x) => Ok(ScmpErrorMessage::DestinationUnreachable(x)),
Self::PacketTooBig(x) => Ok(ScmpErrorMessage::PacketTooBig(x)),
Self::ParameterProblem(x) => Ok(ScmpErrorMessage::ParameterProblem(x)),
Self::ExternalInterfaceDown(x) => Ok(ScmpErrorMessage::ExternalInterfaceDown(x)),
Self::InternalConnectivityDown(x) => Ok(ScmpErrorMessage::InternalConnectivityDown(x)),
_ => Err(self),
}
}
}
impl PayloadEncode for &ScmpMessage {
#[inline]
fn required_size(&self, header_and_extensions_size: usize) -> usize {
match *self {
ScmpMessage::DestinationUnreachable(x) => x.required_size(header_and_extensions_size),
ScmpMessage::PacketTooBig(x) => x.required_size(header_and_extensions_size),
ScmpMessage::ParameterProblem(x) => x.required_size(header_and_extensions_size),
ScmpMessage::ExternalInterfaceDown(x) => x.required_size(header_and_extensions_size),
ScmpMessage::InternalConnectivityDown(x) => x.required_size(header_and_extensions_size),
ScmpMessage::EchoRequest(x) => x.required_size(header_and_extensions_size),
ScmpMessage::EchoReply(x) => x.required_size(header_and_extensions_size),
ScmpMessage::TracerouteRequest(x) => x.required_size(header_and_extensions_size),
ScmpMessage::TracerouteReply(x) => x.required_size(header_and_extensions_size),
ScmpMessage::Unknown(x) => x.required_size(header_and_extensions_size),
}
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
match *self {
ScmpMessage::DestinationUnreachable(x) => x.wire_valid(),
ScmpMessage::PacketTooBig(x) => x.wire_valid(),
ScmpMessage::ParameterProblem(x) => x.wire_valid(),
ScmpMessage::ExternalInterfaceDown(x) => x.wire_valid(),
ScmpMessage::InternalConnectivityDown(x) => x.wire_valid(),
ScmpMessage::EchoRequest(x) => x.wire_valid(),
ScmpMessage::EchoReply(x) => x.wire_valid(),
ScmpMessage::TracerouteRequest(x) => x.wire_valid(),
ScmpMessage::TracerouteReply(x) => x.wire_valid(),
ScmpMessage::Unknown(x) => x.wire_valid(),
}
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
unsafe {
match *self {
ScmpMessage::DestinationUnreachable(x) => {
x.encode_unchecked(buf, address_header, header_and_extensions_size)
}
ScmpMessage::PacketTooBig(x) => {
x.encode_unchecked(buf, address_header, header_and_extensions_size)
}
ScmpMessage::ParameterProblem(x) => {
x.encode_unchecked(buf, address_header, header_and_extensions_size)
}
ScmpMessage::ExternalInterfaceDown(x) => {
x.encode_unchecked(buf, address_header, header_and_extensions_size)
}
ScmpMessage::InternalConnectivityDown(x) => {
x.encode_unchecked(buf, address_header, header_and_extensions_size)
}
ScmpMessage::EchoRequest(x) => {
x.encode_unchecked(buf, address_header, header_and_extensions_size)
}
ScmpMessage::EchoReply(x) => {
x.encode_unchecked(buf, address_header, header_and_extensions_size)
}
ScmpMessage::TracerouteRequest(x) => {
x.encode_unchecked(buf, address_header, header_and_extensions_size)
}
ScmpMessage::TracerouteReply(x) => {
x.encode_unchecked(buf, address_header, header_and_extensions_size)
}
ScmpMessage::Unknown(x) => {
x.encode_unchecked(buf, address_header, header_and_extensions_size)
}
}
}
}
}
impl PayloadEncode for ScmpMessage {
#[inline]
fn required_size(&self, header_and_extensions_size: usize) -> usize {
(&self).required_size(header_and_extensions_size)
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
(&self).wire_valid()
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
unsafe { (&self).encode_unchecked(buf, address_header, header_and_extensions_size) }
}
}
impl From<ScmpErrorMessage> for ScmpMessage {
#[inline]
fn from(value: ScmpErrorMessage) -> Self {
match value {
ScmpErrorMessage::DestinationUnreachable(x) => Self::DestinationUnreachable(x),
ScmpErrorMessage::PacketTooBig(x) => Self::PacketTooBig(x),
ScmpErrorMessage::ParameterProblem(x) => Self::ParameterProblem(x),
ScmpErrorMessage::ExternalInterfaceDown(x) => Self::ExternalInterfaceDown(x),
ScmpErrorMessage::InternalConnectivityDown(x) => Self::InternalConnectivityDown(x),
}
}
}
impl From<ScmpInformationalMessage> for ScmpMessage {
#[inline]
fn from(value: ScmpInformationalMessage) -> Self {
match value {
ScmpInformationalMessage::EchoRequest(x) => Self::EchoRequest(x),
ScmpInformationalMessage::EchoReply(x) => Self::EchoReply(x),
ScmpInformationalMessage::TracerouteRequest(x) => Self::TracerouteRequest(x),
ScmpInformationalMessage::TracerouteReply(x) => Self::TracerouteReply(x),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum ScmpErrorMessage {
DestinationUnreachable(ScmpDestinationUnreachable),
PacketTooBig(ScmpPacketTooBig),
ParameterProblem(ScmpParameterProblem),
ExternalInterfaceDown(ScmpExternalInterfaceDown),
InternalConnectivityDown(ScmpInternalConnectivityDown),
}
impl ScmpErrorMessage {
#[inline]
pub const fn message_type(&self) -> ScmpMessageType {
match self {
Self::DestinationUnreachable(_) => ScmpMessageType::DestinationUnreachable,
Self::PacketTooBig(_) => ScmpMessageType::PacketTooBig,
Self::ParameterProblem(_) => ScmpMessageType::ParameterProblem,
Self::ExternalInterfaceDown(_) => ScmpMessageType::ExternalInterfaceDown,
Self::InternalConnectivityDown(_) => ScmpMessageType::InternalConnectivityDown,
}
}
}
impl PayloadEncode for ScmpErrorMessage {
#[inline]
fn required_size(&self, header_and_extensions_size: usize) -> usize {
match self {
Self::DestinationUnreachable(m) => m.required_size(header_and_extensions_size),
Self::PacketTooBig(m) => m.required_size(header_and_extensions_size),
Self::ParameterProblem(m) => m.required_size(header_and_extensions_size),
Self::ExternalInterfaceDown(m) => m.required_size(header_and_extensions_size),
Self::InternalConnectivityDown(m) => m.required_size(header_and_extensions_size),
}
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
match self {
Self::DestinationUnreachable(x) => x.wire_valid(),
Self::PacketTooBig(x) => x.wire_valid(),
Self::ParameterProblem(x) => x.wire_valid(),
Self::ExternalInterfaceDown(x) => x.wire_valid(),
Self::InternalConnectivityDown(x) => x.wire_valid(),
}
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
unsafe {
match self {
Self::DestinationUnreachable(m) => {
m.encode_unchecked(buf, address_header, header_and_extensions_size)
}
Self::PacketTooBig(m) => {
m.encode_unchecked(buf, address_header, header_and_extensions_size)
}
Self::ParameterProblem(m) => {
m.encode_unchecked(buf, address_header, header_and_extensions_size)
}
Self::ExternalInterfaceDown(m) => {
m.encode_unchecked(buf, address_header, header_and_extensions_size)
}
Self::InternalConnectivityDown(m) => {
m.encode_unchecked(buf, address_header, header_and_extensions_size)
}
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum ScmpInformationalMessage {
EchoRequest(ScmpEchoRequest),
EchoReply(ScmpEchoReply),
TracerouteRequest(ScmpTracerouteRequest),
TracerouteReply(ScmpTracerouteReply),
}
impl ScmpInformationalMessage {
#[inline]
pub const fn message_type(&self) -> ScmpMessageType {
match self {
Self::EchoRequest(_) => ScmpMessageType::EchoRequest,
Self::EchoReply(_) => ScmpMessageType::EchoReply,
Self::TracerouteRequest(_) => ScmpMessageType::TracerouteRequest,
Self::TracerouteReply(_) => ScmpMessageType::TracerouteReply,
}
}
}
impl PayloadEncode for ScmpInformationalMessage {
#[inline]
fn required_size(&self, header_and_extensions_size: usize) -> usize {
match self {
Self::EchoRequest(m) => m.required_size(header_and_extensions_size),
Self::EchoReply(m) => m.required_size(header_and_extensions_size),
Self::TracerouteRequest(m) => m.required_size(header_and_extensions_size),
Self::TracerouteReply(m) => m.required_size(header_and_extensions_size),
}
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
match self {
Self::EchoRequest(x) => x.wire_valid(),
Self::EchoReply(x) => x.wire_valid(),
Self::TracerouteRequest(x) => x.wire_valid(),
Self::TracerouteReply(x) => x.wire_valid(),
}
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
unsafe {
match self {
Self::EchoRequest(m) => {
m.encode_unchecked(buf, address_header, header_and_extensions_size)
}
Self::EchoReply(m) => {
m.encode_unchecked(buf, address_header, header_and_extensions_size)
}
Self::TracerouteRequest(m) => {
m.encode_unchecked(buf, address_header, header_and_extensions_size)
}
Self::TracerouteReply(m) => {
m.encode_unchecked(buf, address_header, header_and_extensions_size)
}
}
}
}
}
macro_rules! error_message {
(
$(#[$outer:meta])*
pub struct $name:ident : $message_type:ident {
$($(#[$doc:meta])* $vis:vis $field:ident : $type:ty,)*
}
) => {
$(#[$outer])*
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "proptest", derive(proptest_derive::Arbitrary))]
pub struct $name {
$($(#[$doc])* $vis $field: $type,)*
/// The (truncated) packet that triggered the error.
offending_packet: Vec<u8>,
}
impl ::core::fmt::Debug for $name {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
let mut d = f.debug_struct(stringify!($name));
$(d.field(stringify!($field), &self.$field);)*
d.field(
"offending_packet",
&format_offending_packet(&self.offending_packet),
);
d.finish()
}
}
impl $name {
#[inline]
pub fn new($($field: $type,)* offending_packet: Vec<u8>) -> Self {
Self{
$($field,)*
offending_packet,
}
}
#[inline]
pub fn get_offending_packet(&self) -> &[u8] {
&self.offending_packet
}
#[inline]
pub fn set_offending_packet(&mut self, offending_packet: Vec<u8>) {
self.offending_packet = offending_packet;
}
}
impl From<$name> for ScmpErrorMessage {
#[inline]
fn from(value: $name) -> Self {
Self::$message_type(value)
}
}
impl From<$name> for ScmpMessage {
#[inline]
fn from(value: $name) -> Self {
Self::$message_type(value)
}
}
};
}
#[inline]
fn format_offending_packet(offending_packet: &[u8]) -> String {
format!("<{} bytes>", offending_packet.len())
}
error_message!(
pub struct ScmpDestinationUnreachable: DestinationUnreachable {
pub code: ScmpDestinationUnreachableCode,
}
);
impl FromView for ScmpDestinationUnreachable {
type ViewType = ScmpDestinationUnreachableMessageView;
#[inline]
fn from_view(view: &ScmpDestinationUnreachableMessageView) -> Self {
Self {
code: view.code(),
offending_packet: view.offending_packet().to_vec(),
}
}
}
impl ToModel for ScmpDestinationUnreachableMessageView {
type ModelType = ScmpDestinationUnreachable;
}
impl PayloadEncode for ScmpDestinationUnreachable {
#[inline]
fn required_size(&self, header_and_extensions_size: usize) -> usize {
ScmpDestinationUnreachableLayout::from_offending_packet_length(
self.offending_packet.len(),
header_and_extensions_size,
)
.size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
Ok(())
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
use ScmpDestinationUnreachableLayout as L;
unsafe {
unchecked_bit_range_be_write::<u8>(
buf,
L::TYPE_RNG,
ScmpMessageType::DestinationUnreachable.into(),
);
unchecked_bit_range_be_write::<u8>(buf, L::CODE_RNG, self.code.into());
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, 0u16);
unchecked_bit_range_be_write(buf, L::RESERVED_RNG, 0u32);
let l = L::from_offending_packet_length(
self.offending_packet.len(),
header_and_extensions_size,
);
let message_length = l.size_bytes();
let range = l.offending_packet_rng().aligned_byte_range();
let offending_packet_len = range.end - range.start;
buf.get_unchecked_mut(range)
.copy_from_slice(&self.offending_packet[..offending_packet_len]);
let checksum = ChecksumDigest::with_pseudoheader(
address_header,
ProtocolNumber::Scmp.into(),
&buf[0..message_length],
)
.checksum();
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, checksum);
message_length
}
}
}
error_message!(
pub struct ScmpPacketTooBig: PacketTooBig {
pub mtu: u16,
}
);
impl FromView for ScmpPacketTooBig {
type ViewType = ScmpPacketTooBigMessageView;
#[inline]
fn from_view(view: &ScmpPacketTooBigMessageView) -> Self {
Self {
mtu: view.mtu(),
offending_packet: view.offending_packet().to_vec(),
}
}
}
impl ToModel for ScmpPacketTooBigMessageView {
type ModelType = ScmpPacketTooBig;
}
impl PayloadEncode for ScmpPacketTooBig {
#[inline]
fn required_size(&self, header_and_extensions_size: usize) -> usize {
ScmpPacketTooBigLayout::from_offending_packet_length(
self.offending_packet.len(),
header_and_extensions_size,
)
.size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
Ok(())
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
use ScmpPacketTooBigLayout as L;
unsafe {
unchecked_bit_range_be_write::<u8>(
buf,
L::TYPE_RNG,
ScmpMessageType::PacketTooBig.into(),
);
unchecked_bit_range_be_write::<u8>(buf, L::CODE_RNG, 0u8);
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, 0u16);
unchecked_bit_range_be_write::<u16>(buf, L::RESERVED_RNG, 0u16);
unchecked_bit_range_be_write::<u16>(buf, L::MTU_RNG, self.mtu);
let l = L::from_offending_packet_length(
self.offending_packet.len(),
header_and_extensions_size,
);
let message_length = l.size_bytes();
let range = l.offending_packet_rng().aligned_byte_range();
let offending_packet_len = range.end - range.start;
buf.get_unchecked_mut(range)
.copy_from_slice(&self.offending_packet[..offending_packet_len]);
let checksum = ChecksumDigest::with_pseudoheader(
address_header,
ProtocolNumber::Scmp.into(),
&buf[0..message_length],
)
.checksum();
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, checksum);
message_length
}
}
}
error_message!(
pub struct ScmpParameterProblem: ParameterProblem {
pub code: ScmpParameterProblemCode,
pub pointer: u16,
}
);
impl FromView for ScmpParameterProblem {
type ViewType = ScmpParameterProblemMessageView;
#[inline]
fn from_view(view: &ScmpParameterProblemMessageView) -> Self {
Self {
code: view.code(),
pointer: view.pointer(),
offending_packet: view.offending_packet().to_vec(),
}
}
}
impl ToModel for ScmpParameterProblemMessageView {
type ModelType = ScmpParameterProblem;
}
impl PayloadEncode for ScmpParameterProblem {
#[inline]
fn required_size(&self, header_and_extensions_size: usize) -> usize {
ScmpParameterProblemLayout::from_offending_packet_length(
self.offending_packet.len(),
header_and_extensions_size,
)
.size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
Ok(())
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
use ScmpParameterProblemLayout as L;
unsafe {
unchecked_bit_range_be_write::<u8>(
buf,
L::TYPE_RNG,
ScmpMessageType::ParameterProblem.into(),
);
unchecked_bit_range_be_write::<u8>(buf, L::CODE_RNG, self.code.into());
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, 0u16);
unchecked_bit_range_be_write::<u16>(buf, L::RESERVED_RNG, 0u16);
unchecked_bit_range_be_write::<u16>(buf, L::POINTER_RNG, self.pointer);
let l = L::from_offending_packet_length(
self.offending_packet.len(),
header_and_extensions_size,
);
let message_length = l.size_bytes();
let range = l.offending_packet_rng().aligned_byte_range();
let offending_packet_len = range.end - range.start;
buf.get_unchecked_mut(range)
.copy_from_slice(&self.offending_packet[..offending_packet_len]);
let checksum = ChecksumDigest::with_pseudoheader(
address_header,
ProtocolNumber::Scmp.into(),
&buf[0..message_length],
)
.checksum();
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, checksum);
message_length
}
}
}
error_message!(
pub struct ScmpExternalInterfaceDown: ExternalInterfaceDown {
pub isd_asn: IsdAsn,
pub interface_id: u16,
}
);
impl FromView for ScmpExternalInterfaceDown {
type ViewType = ScmpExternalInterfaceDownMessageView;
#[inline]
fn from_view(view: &ScmpExternalInterfaceDownMessageView) -> Self {
Self {
isd_asn: view.isd_asn(),
interface_id: view.interface_id() as u16,
offending_packet: view.offending_packet().to_vec(),
}
}
}
impl ToModel for ScmpExternalInterfaceDownMessageView {
type ModelType = ScmpExternalInterfaceDown;
}
impl PayloadEncode for ScmpExternalInterfaceDown {
#[inline]
fn required_size(&self, header_and_extensions_size: usize) -> usize {
ScmpExternalInterfaceDownLayout::from_offending_packet_length(
self.offending_packet.len(),
header_and_extensions_size,
)
.size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
Ok(())
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
use ScmpExternalInterfaceDownLayout as L;
unsafe {
unchecked_bit_range_be_write::<u8>(
buf,
L::TYPE_RNG,
ScmpMessageType::ExternalInterfaceDown.into(),
);
unchecked_bit_range_be_write::<u8>(buf, L::CODE_RNG, 0u8);
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, 0u16);
unchecked_bit_range_be_write::<u64>(buf, L::ISD_AS_RNG, self.isd_asn.to_u64());
unchecked_bit_range_be_write::<u64>(buf, L::INTERFACE_ID_RNG, self.interface_id as u64);
let l = L::from_offending_packet_length(
self.offending_packet.len(),
header_and_extensions_size,
);
let message_length = l.size_bytes();
let range = l.offending_packet_rng().aligned_byte_range();
let offending_packet_len = range.end - range.start;
buf.get_unchecked_mut(range)
.copy_from_slice(&self.offending_packet[..offending_packet_len]);
let checksum = ChecksumDigest::with_pseudoheader(
address_header,
ProtocolNumber::Scmp.into(),
&buf[0..message_length],
)
.checksum();
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, checksum);
message_length
}
}
}
error_message!(
pub struct ScmpInternalConnectivityDown: InternalConnectivityDown {
pub isd_asn: IsdAsn,
pub ingress_interface_id: u16,
pub egress_interface_id: u16,
}
);
impl FromView for ScmpInternalConnectivityDown {
type ViewType = ScmpInternalConnectivityDownMessageView;
#[inline]
fn from_view(view: &ScmpInternalConnectivityDownMessageView) -> Self {
Self {
isd_asn: view.isd_asn(),
ingress_interface_id: view.ingress_interface_id() as u16,
egress_interface_id: view.egress_interface_id() as u16,
offending_packet: view.offending_packet().to_vec(),
}
}
}
impl ToModel for ScmpInternalConnectivityDownMessageView {
type ModelType = ScmpInternalConnectivityDown;
}
impl PayloadEncode for ScmpInternalConnectivityDown {
#[inline]
fn required_size(&self, header_and_extensions_size: usize) -> usize {
ScmpInternalConnectivityDownLayout::from_offending_packet_length(
self.offending_packet.len(),
header_and_extensions_size,
)
.size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
Ok(())
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
use ScmpInternalConnectivityDownLayout as L;
unsafe {
unchecked_bit_range_be_write::<u8>(
buf,
L::TYPE_RNG,
ScmpMessageType::InternalConnectivityDown.into(),
);
unchecked_bit_range_be_write::<u8>(buf, L::CODE_RNG, 0u8);
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, 0u16);
unchecked_bit_range_be_write::<u64>(buf, L::ISD_AS_RNG, self.isd_asn.to_u64());
unchecked_bit_range_be_write::<u64>(
buf,
L::INGRESS_INTERFACE_ID_RNG,
self.ingress_interface_id as u64,
);
unchecked_bit_range_be_write::<u64>(
buf,
L::EGRESS_INTERFACE_ID_RNG,
self.egress_interface_id as u64,
);
let l = L::from_offending_packet_length(
self.offending_packet.len(),
header_and_extensions_size,
);
let message_length = l.size_bytes();
let range = l.offending_packet_rng().aligned_byte_range();
let offending_packet_len = range.end - range.start;
buf.get_unchecked_mut(range)
.copy_from_slice(&self.offending_packet[..offending_packet_len]);
let checksum = ChecksumDigest::with_pseudoheader(
address_header,
ProtocolNumber::Scmp.into(),
&buf[0..message_length],
)
.checksum();
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, checksum);
message_length
}
}
}
macro_rules! informational_message {
(
$(#[$outer:meta])*
$message_type:ident => pub struct $name:ident {$($(#[$doc:meta])* $vis:vis $field:ident : $type:ty,)*}
) => {
$(#[$outer])*
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "proptest", derive(proptest_derive::Arbitrary))]
pub struct $name {
/// A 16-bit identifier to aid matching replies with requests.
pub identifier: u16,
pub sequence_number: u16,
$($(#[$doc])* $vis $field: $type,)*
}
impl $name {
#[inline]
pub fn new(identifier: u16, sequence_number: u16, $($field: $type,)*) -> Self {
Self {
identifier,
sequence_number,
$($field,)*
}
}
}
impl From<$name> for ScmpInformationalMessage {
#[inline]
fn from(value: $name) -> Self {
Self::$message_type(value)
}
}
impl From<$name> for ScmpMessage {
#[inline]
fn from(value: $name) -> Self {
Self::$message_type(value)
}
}
};
}
informational_message!(
EchoRequest => pub struct ScmpEchoRequest {
pub data: Vec<u8>,
}
);
impl FromView for ScmpEchoRequest {
type ViewType = ScmpEchoRequestMessageView;
#[inline]
fn from_view(view: &ScmpEchoRequestMessageView) -> Self {
Self {
identifier: view.identifier(),
sequence_number: view.sequence_number(),
data: view.data().to_vec(),
}
}
}
impl ToModel for ScmpEchoRequestMessageView {
type ModelType = ScmpEchoRequest;
}
impl PayloadEncode for ScmpEchoRequest {
#[inline]
fn required_size(&self, _header_and_extensions_size: usize) -> usize {
ScmpEchoRequestLayout::from_data_length(self.data.len()).size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
Ok(())
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
use ScmpEchoRequestLayout as L;
unsafe {
unchecked_bit_range_be_write::<u8>(
buf,
L::TYPE_RNG,
ScmpMessageType::EchoRequest.into(),
);
unchecked_bit_range_be_write::<u8>(buf, L::CODE_RNG, 0u8);
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, 0u16);
unchecked_bit_range_be_write::<u16>(buf, L::IDENTIFIER_RNG, self.identifier);
unchecked_bit_range_be_write::<u16>(buf, L::SEQUENCE_NUMBER_RNG, self.sequence_number);
let layout = L::from_data_length(self.data.len());
let range = layout.data_rng().aligned_byte_range();
let included_len = range.end - range.start;
buf.get_unchecked_mut(range)
.copy_from_slice(&self.data[..included_len]);
let checksum = ChecksumDigest::with_pseudoheader(
address_header,
ProtocolNumber::Scmp.into(),
&buf[0..self.required_size(header_and_extensions_size)],
)
.checksum();
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, checksum);
self.required_size(header_and_extensions_size)
}
}
}
informational_message!(
EchoReply => pub struct ScmpEchoReply {
pub data: Vec<u8>,
}
);
impl FromView for ScmpEchoReply {
type ViewType = ScmpEchoReplyMessageView;
#[inline]
fn from_view(view: &ScmpEchoReplyMessageView) -> Self {
Self {
identifier: view.identifier(),
sequence_number: view.sequence_number(),
data: view.data().to_vec(),
}
}
}
impl ToModel for ScmpEchoReplyMessageView {
type ModelType = ScmpEchoReply;
}
impl PayloadEncode for ScmpEchoReply {
#[inline]
fn required_size(&self, _header_and_extensions_size: usize) -> usize {
ScmpEchoReplyLayout::from_data_length(self.data.len()).size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
Ok(())
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
use ScmpEchoReplyLayout as L;
unsafe {
unchecked_bit_range_be_write::<u8>(buf, L::TYPE_RNG, ScmpMessageType::EchoReply.into());
unchecked_bit_range_be_write::<u8>(buf, L::CODE_RNG, 0u8);
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, 0u16);
unchecked_bit_range_be_write::<u16>(buf, L::IDENTIFIER_RNG, self.identifier);
unchecked_bit_range_be_write::<u16>(buf, L::SEQUENCE_NUMBER_RNG, self.sequence_number);
let layout = L::from_data_length(self.data.len());
let range = layout.data_rng().aligned_byte_range();
let included_len = range.end - range.start;
buf.get_unchecked_mut(range)
.copy_from_slice(&self.data[..included_len]);
let checksum = ChecksumDigest::with_pseudoheader(
address_header,
ProtocolNumber::Scmp.into(),
&buf[0..self.required_size(header_and_extensions_size)],
)
.checksum();
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, checksum);
self.required_size(header_and_extensions_size)
}
}
}
informational_message!(
TracerouteRequest => pub struct ScmpTracerouteRequest {}
);
impl FromView for ScmpTracerouteRequest {
type ViewType = ScmpTracerouteRequestMessageView;
#[inline]
fn from_view(view: &ScmpTracerouteRequestMessageView) -> Self {
Self {
identifier: view.identifier(),
sequence_number: view.sequence_number(),
}
}
}
impl ToModel for ScmpTracerouteRequestMessageView {
type ModelType = ScmpTracerouteRequest;
}
impl PayloadEncode for ScmpTracerouteRequest {
#[inline]
fn required_size(&self, _header_and_extensions_size: usize) -> usize {
ScmpTracerouteRequestLayout.size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
Ok(())
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
use ScmpTracerouteRequestLayout as L;
unsafe {
unchecked_bit_range_be_write::<u8>(
buf,
L::TYPE_RNG,
ScmpMessageType::TracerouteRequest.into(),
);
unchecked_bit_range_be_write::<u8>(buf, L::CODE_RNG, 0u8);
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, 0u16);
unchecked_bit_range_be_write::<u16>(buf, L::IDENTIFIER_RNG, self.identifier);
unchecked_bit_range_be_write::<u16>(buf, L::SEQUENCE_NUMBER_RNG, self.sequence_number);
unchecked_bit_range_be_write::<u64>(buf, L::ISD_AS_RNG, 0u64);
unchecked_bit_range_be_write::<u64>(buf, L::INTERFACE_ID_RNG, 0u64);
let checksum = ChecksumDigest::with_pseudoheader(
address_header,
ProtocolNumber::Scmp.into(),
&buf[0..self.required_size(header_and_extensions_size)],
)
.checksum();
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, checksum);
self.required_size(header_and_extensions_size)
}
}
}
informational_message!(
TracerouteReply => pub struct ScmpTracerouteReply {
pub isd_asn: IsdAsn,
pub interface_id: u16,
}
);
impl FromView for ScmpTracerouteReply {
type ViewType = ScmpTracerouteReplyMessageView;
#[inline]
fn from_view(view: &ScmpTracerouteReplyMessageView) -> Self {
Self {
identifier: view.identifier(),
sequence_number: view.sequence_number(),
isd_asn: view.isd_asn(),
interface_id: view.interface_id() as u16,
}
}
}
impl ToModel for ScmpTracerouteReplyMessageView {
type ModelType = ScmpTracerouteReply;
}
impl PayloadEncode for ScmpTracerouteReply {
#[inline]
fn required_size(&self, _header_and_extensions_size: usize) -> usize {
ScmpTracerouteReplyLayout.size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
Ok(())
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
use ScmpTracerouteReplyLayout as L;
unsafe {
unchecked_bit_range_be_write::<u8>(
buf,
L::TYPE_RNG,
ScmpMessageType::TracerouteReply.into(),
);
unchecked_bit_range_be_write::<u8>(buf, L::CODE_RNG, 0u8);
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, 0u16);
unchecked_bit_range_be_write::<u16>(buf, L::IDENTIFIER_RNG, self.identifier);
unchecked_bit_range_be_write::<u16>(buf, L::SEQUENCE_NUMBER_RNG, self.sequence_number);
unchecked_bit_range_be_write::<u64>(buf, L::ISD_AS_RNG, self.isd_asn.to_u64());
unchecked_bit_range_be_write::<u64>(buf, L::INTERFACE_ID_RNG, self.interface_id as u64);
let checksum = ChecksumDigest::with_pseudoheader(
address_header,
ProtocolNumber::Scmp.into(),
&buf[0..self.required_size(header_and_extensions_size)],
)
.checksum();
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, checksum);
self.required_size(header_and_extensions_size)
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct ScmpMessageUnknown {
pub message_type: u8,
pub code: u8,
pub message_specific_data: Vec<u8>,
}
impl ScmpMessageUnknown {
#[inline]
pub fn new(message_type: u8, code: u8, payload: Vec<u8>) -> Self {
Self {
message_type,
code,
message_specific_data: payload,
}
}
}
impl FromView for ScmpMessageUnknown {
type ViewType = ScmpUnknownMessageView;
#[inline]
fn from_view(view: &ScmpUnknownMessageView) -> Self {
Self {
message_type: view.message_type(),
code: view.code(),
message_specific_data: view.message_specific_data().to_vec(),
}
}
}
impl ToModel for ScmpUnknownMessageView {
type ModelType = ScmpMessageUnknown;
}
impl PayloadEncode for ScmpMessageUnknown {
#[inline]
fn required_size(&self, _header_and_extensions_size: usize) -> usize {
ScmpUnknownMessageLayout::from_message_specific_data_length(
self.message_specific_data.len(),
)
.size_bytes()
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
Ok(())
}
#[inline]
unsafe fn encode_unchecked(
&self,
buf: &mut [u8],
address_header: &AddressHeader,
header_and_extensions_size: usize,
) -> usize {
use ScmpUnknownMessageLayout as L;
unsafe {
unchecked_bit_range_be_write::<u8>(buf, L::TYPE_RNG, self.message_type);
unchecked_bit_range_be_write::<u8>(buf, L::CODE_RNG, self.code);
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, 0u16);
let layout = ScmpUnknownMessageLayout::from_message_specific_data_length(
self.message_specific_data.len(),
);
let range = layout.message_specific_data_rng().aligned_byte_range();
let data_len = range.end - range.start;
buf.get_unchecked_mut(range)
.copy_from_slice(&self.message_specific_data[..data_len]);
let checksum = ChecksumDigest::with_pseudoheader(
address_header,
ProtocolNumber::Scmp.into(),
&buf[0..self.required_size(header_and_extensions_size)],
)
.checksum();
unchecked_bit_range_be_write::<u16>(buf, L::CHECKSUM_RNG, checksum);
self.required_size(header_and_extensions_size)
}
}
}
impl From<ScmpMessageUnknown> for ScmpMessage {
#[inline]
fn from(value: ScmpMessageUnknown) -> Self {
Self::Unknown(value)
}
}
#[cfg(feature = "proptest")]
pub mod ptest {
use ::proptest::prelude::*;
use super::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ArbitraryScmpMessageParams {
pub destination_unreachable: u32,
pub packet_too_big: u32,
pub parameter_problem: u32,
pub external_interface_down: u32,
pub internal_connectivity_down: u32,
pub echo_request: u32,
pub echo_reply: u32,
pub traceroute_request: u32,
pub traceroute_reply: u32,
pub unknown: u32,
}
impl Default for ArbitraryScmpMessageParams {
fn default() -> Self {
Self {
destination_unreachable: 1,
packet_too_big: 1,
parameter_problem: 1,
external_interface_down: 1,
internal_connectivity_down: 1,
echo_request: 1,
echo_reply: 1,
traceroute_request: 1,
traceroute_reply: 1,
unknown: 1,
}
}
}
impl Arbitrary for ScmpMessage {
type Parameters = ArbitraryScmpMessageParams;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
prop_oneof![
params.destination_unreachable =>
any::<ScmpDestinationUnreachable>()
.prop_map(ScmpMessage::DestinationUnreachable),
params.packet_too_big =>
any::<ScmpPacketTooBig>()
.prop_map(ScmpMessage::PacketTooBig),
params.parameter_problem =>
any::<ScmpParameterProblem>()
.prop_map(ScmpMessage::ParameterProblem),
params.external_interface_down =>
any::<ScmpExternalInterfaceDown>()
.prop_map(ScmpMessage::ExternalInterfaceDown),
params.internal_connectivity_down =>
any::<ScmpInternalConnectivityDown>()
.prop_map(ScmpMessage::InternalConnectivityDown),
params.echo_request =>
any::<ScmpEchoRequest>()
.prop_map(ScmpMessage::EchoRequest),
params.echo_reply =>
any::<ScmpEchoReply>()
.prop_map(ScmpMessage::EchoReply),
params.traceroute_request =>
any::<ScmpTracerouteRequest>()
.prop_map(ScmpMessage::TracerouteRequest),
params.traceroute_reply =>
any::<ScmpTracerouteReply>()
.prop_map(ScmpMessage::TracerouteReply),
params.unknown =>
any::<ScmpMessageUnknown>()
.prop_map(ScmpMessage::Unknown),
]
.boxed()
}
}
impl Arbitrary for ScmpMessageUnknown {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
any::<u8>(),
any::<u8>(),
::proptest::collection::vec(any::<u8>(), 0..1200),
)
.prop_map(|(message_type, code, message_specific_data)| {
let mut message_type = message_type;
while !matches!(
ScmpMessageType::from(message_type),
ScmpMessageType::Unknown(_)
) {
message_type = message_type.wrapping_add(1);
}
Self {
message_type,
code,
message_specific_data,
}
})
.boxed()
}
}
}