Skip to content

Commit

Permalink
feat: use an enum for dataplane paths
Browse files Browse the repository at this point in the history
  • Loading branch information
jpcsmith committed Dec 1, 2023
1 parent f0d63d0 commit 05a9c5b
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 130 deletions.
50 changes: 23 additions & 27 deletions crates/scion-proto/src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ mod address_header;
pub use address_header::{AddressHeader, RawHostAddress};

mod path_header;
pub use path_header::PathHeader;
pub use path_header::DataplanePath;

/// Instances of an object associated with both a source and destination endpoint.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand All @@ -36,47 +36,41 @@ pub struct ScionPacket {
/// Source and destination addresses.
pub address_header: AddressHeader,
/// The path to the destination, when necessary.
pub path_header: Option<PathHeader>,
pub path_header: DataplanePath,
/// The packet payload.
pub payload: Bytes,
}

impl<T> WireDecode<T> for ScionPacket
where
T: Buf,
{
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)?;

let path_context = (common_header.path_type, header_data.remaining());
let path_header = match PathHeader::decode_with_context(&mut header_data, path_context) {
Err(DecodeError::EmptyPath) => None,
other => Some(other?),
};

// The path header consumes all the remaining header bytes
debug_assert_eq!(header_data.remaining(), 0);

if data.remaining() < common_header.payload_size() {
return Err(DecodeError::PacketEmptyOrTruncated);
// 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 {
common_header,
address_header,
path_header,
payload,
})
}

let payload = data.copy_to_bytes(common_header.payload_size());

Ok(Self {
common_header,
address_header,
path_header,
payload,
})
}
}

Expand All @@ -89,6 +83,8 @@ pub enum DecodeError {
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}")]
Expand Down
5 changes: 1 addition & 4 deletions crates/scion-proto/src/packet/address_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ pub struct AddressHeader {
pub host: ByEndpoint<MaybeEncoded<Host, (AddressInfo, RawHostAddress)>>,
}

impl<T> WireDecodeWithContext<T> for AddressHeader
where
T: Buf,
{
impl<T: Buf> WireDecodeWithContext<T> for AddressHeader {
type Error = DecodeError;
type Context = ByEndpoint<AddressInfo>;

Expand Down
15 changes: 6 additions & 9 deletions crates/scion-proto/src/packet/common_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub struct CommonHeader {
///
/// Recognized path types are decoded whereas unrecognized path types are provided
/// in their wire format.
pub path_type: MaybeEncoded<PathType, u8>,
pub path_type: PathType,

/// The source and destination host address type and length.
///
Expand Down Expand Up @@ -187,10 +187,7 @@ impl From<ByEndpoint<AddressInfo>> for u8 {
}
}

impl<T> WireDecode<T> for CommonHeader
where
T: Buf,
{
impl<T: Buf> WireDecode<T> for CommonHeader {
type Error = DecodeError;

fn decode(data: &mut T) -> Result<Self, Self::Error> {
Expand Down Expand Up @@ -261,7 +258,7 @@ impl WireEncode for CommonHeader {
buffer.put_u8(self.next_header);
buffer.put_u8(self.header_length_factor.get());
buffer.put_u16(self.payload_length);
buffer.put_u8(self.path_type.into_encoded());
buffer.put_u8(self.path_type.into());
buffer.put_u8(self.address_info.into());
buffer.put_u16(self.reserved);

Expand All @@ -284,7 +281,7 @@ mod tests {
next_header: 17,
header_length_factor: NonZeroU8::new(9).unwrap(),
payload_length: 0,
path_type: MaybeEncoded::Decoded(PathType::Empty),
path_type: PathType::Empty,
address_info: ByEndpoint {
destination: AddressInfo::from_host_type(HostType::Ipv4).unwrap(),
source: AddressInfo::from_host_type(HostType::Ipv4).unwrap(),
Expand Down Expand Up @@ -326,7 +323,7 @@ mod tests {
let (mut expected_encoded, header) = base_header();

let header = CommonHeader {
path_type: MaybeEncoded::Encoded(5),
path_type: PathType::Other(5),
..header
};
expected_encoded[8] = 0x05;
Expand Down Expand Up @@ -390,7 +387,7 @@ mod tests {
assert_eq!(
CommonHeader::decode(&mut data.as_slice()).expect("must successfully decode"),
CommonHeader {
path_type: MaybeEncoded::Encoded(5),
path_type: PathType::Other(5),
..expected
}
);
Expand Down
84 changes: 41 additions & 43 deletions crates/scion-proto/src/packet/path_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use bytes::{Buf, Bytes};
use super::DecodeError;
use crate::{
path::standard::StandardPath,
wire_encoding::{MaybeEncoded, WireDecodeWithContext},
wire_encoding::{WireDecode, WireDecodeWithContext},
};

/// SCION path types that may be encountered in a packet
Expand All @@ -12,7 +12,7 @@ use crate::{
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum PathType {
/// The empty path type.
Empty = 0,
Empty,
/// The standard SCION path type.
Scion,
/// One-hop paths between neighbouring border routers.
Expand All @@ -21,34 +21,32 @@ pub enum PathType {
Epic,
/// Experimental Colibri path type.
Colibri,
/// Other, unrecognised path types
Other(u8),
}

impl From<PathType> for u8 {
fn from(value: PathType) -> Self {
value as u8
}
}

impl TryFrom<u8> for PathType {
type Error = UnsupportedPathType;

fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Empty),
1 => Ok(Self::Scion),
2 => Ok(Self::OneHop),
3 => Ok(Self::Epic),
4 => Ok(Self::Colibri),
_ => Err(UnsupportedPathType(value)),
PathType::Empty => 0,
PathType::Scion => 1,
PathType::OneHop => 2,
PathType::Epic => 3,
PathType::Colibri => 4,
PathType::Other(value) => value,
}
}
}

impl From<u8> for MaybeEncoded<PathType, u8> {
impl From<u8> for PathType {
fn from(value: u8) -> Self {
match PathType::try_from(value) {
Ok(path_type) => MaybeEncoded::Decoded(path_type),
Err(UnsupportedPathType(raw)) => MaybeEncoded::Encoded(raw),
match value {
0 => Self::Empty,
1 => Self::Scion,
2 => Self::OneHop,
3 => Self::Epic,
4 => Self::Colibri,
value => Self::Other(value),
}
}
}
Expand All @@ -57,43 +55,43 @@ impl From<u8> for MaybeEncoded<PathType, u8> {
#[error("unsupported path type {0}")]
pub struct UnsupportedPathType(pub u8);

/// Path header found in a SCION packet
pub enum PathHeader {
/// Dataplane path found in a SCION packet
#[derive(Debug, Clone, PartialEq)]
pub enum DataplanePath {
/// The empty path type, used for intra-AS hops
EmptyPath,
/// The standard SCION path header.
Standard(StandardPath),
/// The raw bytes of an unsupported path header type.
Unsupported(Bytes),
Unsupported { path_type: PathType, bytes: Bytes },
}

impl From<StandardPath> for PathHeader {
impl From<StandardPath> for DataplanePath {
fn from(value: StandardPath) -> Self {
PathHeader::Standard(value)
DataplanePath::Standard(value)
}
}

impl<T> WireDecodeWithContext<T> for PathHeader
where
T: Buf,
{
impl WireDecodeWithContext<Bytes> for DataplanePath {
type Error = DecodeError;
type Context = (MaybeEncoded<PathType, u8>, usize);
type Context = (PathType, usize);

fn decode_with_context(
data: &mut T,
type_and_length: (MaybeEncoded<PathType, u8>, usize),
data: &mut Bytes,
(path_type, length_hint): Self::Context,
) -> Result<Self, Self::Error> {
let (path_type, path_length) = type_and_length;
if data.remaining() < path_length {
return Err(DecodeError::PacketEmptyOrTruncated);
}

match path_type {
MaybeEncoded::Decoded(PathType::Empty) => Err(Self::Error::EmptyPath),
MaybeEncoded::Decoded(PathType::Scion) => {
Ok(StandardPath::decode_with_context(data, path_length)?.into())
}
MaybeEncoded::Decoded(_) | MaybeEncoded::Encoded(_) => {
Ok(PathHeader::Unsupported(data.copy_to_bytes(path_length)))
PathType::Empty => Ok(DataplanePath::EmptyPath),
PathType::Scion => Ok(StandardPath::decode(data)?.into()),
other => {
if data.remaining() < length_hint {
Err(Self::Error::PacketEmptyOrTruncated)
} else {
Ok(DataplanePath::Unsupported {
path_type: other,
bytes: data.split_to(length_hint),
})
}
}
}
}
Expand Down
15 changes: 10 additions & 5 deletions crates/scion-proto/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ use bytes::Bytes;
use scion_grpc::daemon::v1 as daemon_grpc;
use tracing::warn;

use crate::{address::IsdAsn, packet::ByEndpoint};
use crate::{
address::IsdAsn,
packet::{ByEndpoint, DataplanePath},
wire_encoding::WireDecode,
};

pub mod error;
pub use error::{DataplanePathErrorKind, PathParseError, PathParseErrorKind};
Expand All @@ -23,7 +27,7 @@ use self::standard::StandardPath;
#[derive(Debug, Clone, PartialEq)]
pub struct Path {
/// The raw bytes to be added as the path header to SCION dataplane packets
dataplane_path: StandardPath,
dataplane_path: DataplanePath,
/// The underlay address (IP + port) of the next hop; i.e., the local border router
underlay_next_hop: SocketAddr,
/// The ISD-ASN where the path starts and ends
Expand All @@ -38,12 +42,13 @@ impl Path {
mut value: daemon_grpc::Path,
isd_asn: ByEndpoint<IsdAsn>,
) -> Result<Self, PathParseError> {
let dataplane_path = Bytes::from(std::mem::take(&mut value.raw));
let mut dataplane_path = Bytes::from(std::mem::take(&mut value.raw));
if dataplane_path.is_empty() {
return Err(PathParseErrorKind::EmptyRaw.into());
};
let dataplane_path = StandardPath::decode_from_buffer(dataplane_path)
.map_err(|_| PathParseError::from(PathParseErrorKind::InvalidRaw))?;
let dataplane_path = StandardPath::decode(&mut dataplane_path)
.map_err(|_| PathParseError::from(PathParseErrorKind::InvalidRaw))?
.into();

let underlay_next_hop = match &value.interface {
Some(daemon_grpc::Interface {
Expand Down
Loading

0 comments on commit 05a9c5b

Please sign in to comment.