Skip to content

Commit

Permalink
refactor(packet): clean up module structure
Browse files Browse the repository at this point in the history
  • Loading branch information
mlegner committed Dec 5, 2023
1 parent 9700382 commit 4dd0dc6
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 165 deletions.
165 changes: 6 additions & 159 deletions crates/scion-proto/src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,14 @@
//!
//! For paths useable in a SCION packet, see the [path module][`crate::path`].

use bytes::{Buf, Bytes};

use crate::{
address::{Extract, SocketAddr},
path::{DataplanePathErrorKind, Path},
wire_encoding::{WireDecode, WireDecodeWithContext, WireEncode, WireEncodeVec},
};
pub mod error;
pub use error::{DecodeError, EncodeError, InadequateBufferSize};

pub mod headers;
pub use headers::{
AddressHeader,
AddressInfo,
ByEndpoint,
CommonHeader,
DataplanePath,
FlowId,
Expand All @@ -25,160 +21,11 @@ pub use headers::{
Version,
};

pub mod raw;
pub use raw::ScionPacketRaw;

pub mod udp;
pub use udp::ScionPacketUdp;

mod checksum;
pub use checksum::ChecksumDigest;

/// Instances of an object associated with both a source and destination endpoint.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub struct ByEndpoint<T> {
pub destination: T,
pub source: T,
}

impl<T: Clone> ByEndpoint<T> {
/// Create a new instance where both the source and destination have the same value.
pub fn with_cloned(source_and_destination: T) -> Self {
Self {
destination: source_and_destination.clone(),
source: source_and_destination,
}
}
}

impl<T> ByEndpoint<T> {
pub fn map<U, F>(&self, function: F) -> ByEndpoint<U>
where
F: Fn(&T) -> U,
{
ByEndpoint {
destination: function(&self.destination),
source: function(&self.source),
}
}
}

impl<T, U> Extract<ByEndpoint<U>> for ByEndpoint<T>
where
T: Extract<U>,
{
fn extract(&self) -> ByEndpoint<U> {
self.map(|e| e.extract())
}
}

/// A SCION network packet.
#[derive(Debug, Clone)]
pub struct ScionPacket {
/// Packet headers
pub headers: ScionHeaders,
/// The packet payload.
pub payload: Bytes,
}

impl ScionPacket {
pub fn new(
endhosts: &ByEndpoint<SocketAddr>,
path: &Path,
payload: Bytes,
next_header: u8,
) -> Result<Self, EncodeError> {
let headers = ScionHeaders::new(endhosts, path, next_header, payload.len())?;

Ok(Self { headers, payload })
}
}

impl WireEncodeVec for &ScionPacket {
type Error = InadequateBufferSize;

fn encode_with(&self, buffer: &mut bytes::BytesMut) -> Result<Vec<Bytes>, Self::Error> {
let mut bytes = self.headers.encode_with(buffer)?;
bytes.push(self.payload.clone());
Ok(bytes)
}

fn total_length(&self) -> usize {
self.headers.encoded_length() + self.payload.len()
}
}

impl<T: Buf> WireDecode<T> for ScionPacket {
type Error = DecodeError;

fn decode(data: &mut T) -> Result<Self, Self::Error> {
let common_header = CommonHeader::decode(data)?;

// Limit the data for headers to the length specified by the common header
let mut header_data = data.take(common_header.remaining_header_length());
let address_header =
AddressHeader::decode_with_context(&mut header_data, common_header.address_info)?;

// The path requires a Bytes, if we were already parsing a Bytes, then this is just an
// Arc increment, otherwise we copy the bytes needed for the path.
let mut path_bytes = header_data.copy_to_bytes(header_data.remaining());
let context = (common_header.path_type, path_bytes.len());
let path_header = DataplanePath::decode_with_context(&mut path_bytes, context)?;

if path_bytes.has_remaining() {
Err(DecodeError::InconsistentPathLength)
} else if data.remaining() < common_header.payload_size() {
Err(DecodeError::PacketEmptyOrTruncated)
} else {
let payload = data.copy_to_bytes(common_header.payload_size());
Ok(Self {
headers: ScionHeaders {
common: common_header,
address: address_header,
path: path_header,
},
payload,
})
}
}
}

/// Errors raised when failing to decode a [`ScionPacket`] or its constituents.
#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone, Copy)]
pub enum DecodeError {
#[error("cannot decode packet with unsupported header version {0:?}")]
UnsupportedVersion(Version),
#[error("header length factor is inconsistent with the SCION specification: {0}")]
InvalidHeaderLength(u8),
#[error("the provided bytes did not include the full packet")]
PacketEmptyOrTruncated,
#[error("the path type and length do not correspond")]
InconsistentPathLength,
#[error("attempted to decode the empty path type")]
EmptyPath,
#[error("invalid path header: {0}")]
InvalidPath(DataplanePathErrorKind),
}

impl From<DataplanePathErrorKind> for DecodeError {
fn from(value: DataplanePathErrorKind) -> Self {
Self::InvalidPath(value)
}
}

/// Errors raised when failing to encode a [`ScionPacket`].
#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone, Copy)]
pub enum EncodeError {
#[error("packet payload is too large")]
PayloadTooLarge,
#[error("packet header is too large")]
HeaderTooLarge,
#[error("header is not properly aligned")]
MisalignedHeader,
}

/// Raised if the buffer does not have sufficient capacity for encoding the SCION headers.
///
/// As the headers can be a maximum of 1020 bytes in length, it is advisable to have at
/// least that amount of remaining space for encoding a [`ScionPacket`] (the payload is not
/// written to the buffer).
#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone, Copy, Default)]
#[error("the provided buffer did not have sufficient size")]
pub struct InadequateBufferSize;
45 changes: 45 additions & 0 deletions crates/scion-proto/src/packet/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use super::Version;
use crate::path::DataplanePathErrorKind;

/// Errors raised when failing to decode a [`ScionPacket`] or its constituents.
#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone, Copy)]
pub enum DecodeError {
#[error("cannot decode packet with unsupported header version {0:?}")]
UnsupportedVersion(Version),
#[error("header length factor is inconsistent with the SCION specification: {0}")]
InvalidHeaderLength(u8),
#[error("the provided bytes did not include the full packet")]
PacketEmptyOrTruncated,
#[error("the path type and length do not correspond")]
InconsistentPathLength,
#[error("attempted to decode the empty path type")]
EmptyPath,
#[error("invalid path header: {0}")]
InvalidPath(DataplanePathErrorKind),
}

impl From<DataplanePathErrorKind> for DecodeError {
fn from(value: DataplanePathErrorKind) -> Self {
Self::InvalidPath(value)
}
}

/// Errors raised when failing to encode a [`ScionPacket`].
#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone, Copy)]
pub enum EncodeError {
#[error("packet payload is too large")]
PayloadTooLarge,
#[error("packet header is too large")]
HeaderTooLarge,
#[error("header is not properly aligned")]
MisalignedHeader,
}

/// Raised if the buffer does not have sufficient capacity for encoding the SCION headers.
///
/// As the headers can be a maximum of 1020 bytes in length, it is advisable to have at
/// least that amount of remaining space for encoding a [`ScionPacket`] (the payload is not
/// written to the buffer).
#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone, Copy, Default)]
#[error("the provided buffer did not have sufficient size")]
pub struct InadequateBufferSize;
40 changes: 39 additions & 1 deletion crates/scion-proto/src/packet/headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub use address_header::{AddressHeader, RawHostAddress};
mod path_header;
pub use path_header::DataplanePath;

use super::{ByEndpoint, EncodeError, InadequateBufferSize};
use super::{EncodeError, InadequateBufferSize};
use crate::{
address::{Extract, SocketAddr},
path::Path,
Expand Down Expand Up @@ -69,3 +69,41 @@ impl WireEncode for ScionHeaders {
Ok(())
}
}

/// Instances of an object associated with both a source and destination endpoint.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub struct ByEndpoint<T> {
pub destination: T,
pub source: T,
}

impl<T: Clone> ByEndpoint<T> {
/// Create a new instance where both the source and destination have the same value.
pub fn with_cloned(source_and_destination: T) -> Self {
Self {
destination: source_and_destination.clone(),
source: source_and_destination,
}
}
}

impl<T> ByEndpoint<T> {
pub fn map<U, F>(&self, function: F) -> ByEndpoint<U>
where
F: Fn(&T) -> U,
{
ByEndpoint {
destination: function(&self.destination),
source: function(&self.source),
}
}
}

impl<T, U> Extract<ByEndpoint<U>> for ByEndpoint<T>
where
T: Extract<U>,
{
fn extract(&self) -> ByEndpoint<U> {
self.map(|e| e.extract())
}
}
88 changes: 88 additions & 0 deletions crates/scion-proto/src/packet/raw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use bytes::{Buf, Bytes};

use super::{
AddressHeader,
ByEndpoint,
CommonHeader,
DataplanePath,
DecodeError,
EncodeError,
InadequateBufferSize,
ScionHeaders,
};
use crate::{
address::SocketAddr,
path::Path,
wire_encoding::{WireDecode, WireDecodeWithContext, WireEncode, WireEncodeVec},
};

/// A SCION network packet.
#[derive(Debug, Clone)]
pub struct ScionPacketRaw {
/// Packet headers
pub headers: ScionHeaders,
/// The packet payload.
pub payload: Bytes,
}

impl ScionPacketRaw {
pub fn new(
endhosts: &ByEndpoint<SocketAddr>,
path: &Path,
payload: Bytes,
next_header: u8,
) -> Result<Self, EncodeError> {
let headers = ScionHeaders::new(endhosts, path, next_header, payload.len())?;

Ok(Self { headers, payload })
}
}

impl WireEncodeVec for &ScionPacketRaw {
type Error = InadequateBufferSize;

fn encode_with(&self, buffer: &mut bytes::BytesMut) -> Result<Vec<Bytes>, Self::Error> {
let mut bytes = self.headers.encode_with(buffer)?;
bytes.push(self.payload.clone());
Ok(bytes)
}

fn total_length(&self) -> usize {
self.headers.encoded_length() + self.payload.len()
}
}

impl<T: Buf> WireDecode<T> for ScionPacketRaw {
type Error = DecodeError;

fn decode(data: &mut T) -> Result<Self, Self::Error> {
let common_header = CommonHeader::decode(data)?;

// Limit the data for headers to the length specified by the common header
let mut header_data = data.take(common_header.remaining_header_length());
let address_header =
AddressHeader::decode_with_context(&mut header_data, common_header.address_info)?;

// The path requires a Bytes, if we were already parsing a Bytes, then this is just an
// Arc increment, otherwise we copy the bytes needed for the path.
let mut path_bytes = header_data.copy_to_bytes(header_data.remaining());
let context = (common_header.path_type, path_bytes.len());
let path_header = DataplanePath::decode_with_context(&mut path_bytes, context)?;

if path_bytes.has_remaining() {
Err(DecodeError::InconsistentPathLength)
} else if data.remaining() < common_header.payload_size() {
Err(DecodeError::PacketEmptyOrTruncated)
} else {
let payload = data.copy_to_bytes(common_header.payload_size());
Ok(Self {
headers: ScionHeaders {
common: common_header,
address: address_header,
path: path_header,
},
payload,
})
}
}
}
6 changes: 3 additions & 3 deletions crates/scion-proto/src/packet/udp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use bytes::Bytes;

use super::{InadequateBufferSize, ScionHeaders, ScionPacket};
use super::{InadequateBufferSize, ScionHeaders, ScionPacketRaw};
use crate::{
address::SocketAddr,
datagram::{UdpDatagram, UdpDecodeError},
Expand Down Expand Up @@ -36,10 +36,10 @@ impl ScionPacketUdp {
}
}

impl TryFrom<ScionPacket> for ScionPacketUdp {
impl TryFrom<ScionPacketRaw> for ScionPacketUdp {
type Error = UdpDecodeError;

fn try_from(mut value: ScionPacket) -> Result<Self, Self::Error> {
fn try_from(mut value: ScionPacketRaw) -> Result<Self, Self::Error> {
if value.headers.common.next_header != UdpDatagram::PROTOCOL_NUMBER {
return Err(UdpDecodeError::WrongProtocolNumber(
value.headers.common.next_header,
Expand Down
Loading

0 comments on commit 4dd0dc6

Please sign in to comment.