Skip to content

Commit

Permalink
feat: add other headers
Browse files Browse the repository at this point in the history
  • Loading branch information
jpcsmith committed Oct 27, 2023
1 parent 9cd7a3d commit 4ff71f9
Show file tree
Hide file tree
Showing 5 changed files with 426 additions and 215 deletions.
243 changes: 47 additions & 196 deletions crates/scion/src/packet.rs
Original file line number Diff line number Diff line change
@@ -1,110 +1,48 @@
//! SCION packet module.
//!

use std::{
convert::Infallible,
net::{Ipv4Addr, Ipv6Addr},
};

// TODO(jsmith): Document the module.
// This is the format for the scion packet on the wire. The goal is to enable using this in
// other libraries where they may want to do their own stuff with SCION packets and, as such,
// this should be permissible.
use bytes::{Buf, Bytes};

mod common_header;
pub use common_header::{AddressInfo, CommonHeader, FlowId, Version};

use crate::address::{Host, HostType, IsdAsn, ServiceAddress};

pub trait WireDecode<T>
where
T: Buf,
Self: Sized,
{
type Error;

fn decode(data: &mut T) -> Result<Self, Self::Error>;
}
mod address_header;
pub use address_header::{AddressHeader, RawHostAddress};

mod path_header;
pub use path_header::{
HopFieldIndex,
InfoFieldIndex,
PathHeader,
PathMetaHeader,
PathMetaReserved,
SegmentLength,
};

pub trait WireDecodeWithContext<T, U>
where
T: Buf,
Self: Sized,
{
type Error;
mod wire_encoding;
use wire_encoding::{bounded_uint, DecodeError, MaybeEncoded, WireDecode, WireDecodeWithContext};

fn decode_with_context(data: &mut T, context: U) -> Result<Self, Self::Error>;
/// Instances of an object associated with both a source and destination endpoint.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ByEndpoint<T> {
destination: T,
source: T,
}

/// Represents a Scion packet with no assumptions made on the fields
/// A SCION network packet.
#[allow(unused)]
pub struct Scion<T> {
pub struct Scion {
/// Metadata about the remaining headers and payload.
common_header: CommonHeader,
/// Source and destination addresses.
address_header: AddressHeader,
/// The path to the destination, when necessary.
path_header: Option<PathHeader>,
extensions_header: Option<()>,

payload: T,
}

pub enum PathHeader {
Standard(StandardPathHeader),
}

pub struct StandardPathHeader {
pub meta_header: PathMetaHeader,
pub hop_fields: Bytes,
}

impl<T> WireDecodeWithContext<T, usize> for StandardPathHeader
where
T: Buf + Into<Bytes>,
{
type Error = Infallible;

/// SOmething
fn decode_with_context(data: &mut T, path_length: usize) -> Result<Self, Self::Error> {
let mut path_data = data.take(path_length);
assert_eq!(path_data.remaining(), path_length);

Ok(Self {
meta_header: PathMetaHeader::decode(&mut path_data)?,
hop_fields: path_data.copy_to_bytes(path_data.remaining()),
})
}
}

pub struct PathMetaHeader {
pub current_info_field: u8,
pub current_hop_field: u8,
pub reserved: u8,
pub segment_length: [u8; 3],
}

impl<T: Buf> WireDecode<T> for PathMetaHeader {
type Error = Infallible;

fn decode(data: &mut T) -> Result<Self, Self::Error> {
const BIT_LENGTH: usize = 6;
const MASK: u32 = 0b11_1111;

let fields = data.get_u32();

Ok(Self {
current_info_field: (fields >> (5 * BIT_LENGTH)) as u8,
current_hop_field: (fields >> (4 * BIT_LENGTH) & MASK) as u8,
reserved: (fields >> (3 * BIT_LENGTH) & MASK) as u8,
segment_length: [
(fields >> (2 * BIT_LENGTH) & MASK) as u8,
(fields >> BIT_LENGTH & MASK) as u8,
(fields & MASK) as u8,
],
})
}
/// The packet payload.
payload: Bytes,
}

impl<T> WireDecode<T> for Scion<T>
impl<T> WireDecode<T> for Scion
where
T: Buf,
{
Expand All @@ -114,118 +52,31 @@ where
let initial_length = data.remaining();

let common_header = CommonHeader::decode(data)?;
let address_header = AddressHeader::decode_with_context(data, common_header.address_info);
let address_header = AddressHeader::decode_with_context(data, common_header.address_info)?;

let consumed = initial_length - data.remaining();
// let path_length = common_header.header_length() - consumed;

todo!()
}
}

#[repr(u8)]
#[derive(Debug, PartialEq, Eq)]
pub enum PathType {
Empty = 0,
Scion,
OneHop,
Epic,
Colibri,
}

#[derive(Debug, thiserror::Error)]
#[error("Unsupported path type {0}")]
pub struct UnsupportedPathType(pub 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)),
let path_length = common_header
.header_length()
.expect("decoded headers always have a length")
- consumed;

let path_header =
match PathHeader::decode_with_context(data, (common_header.path_type, path_length)) {
Err(DecodeError::EmptyPath) => None,
other => Some(other?),
};

if data.remaining() < common_header.payload_size() {
return Err(DecodeError::PacketTruncated);
}
}
}

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

#[derive(Debug, thiserror::Error, PartialEq, Eq)]
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),
}

#[derive(Debug, PartialEq, Eq)]
pub enum MaybeEncoded<T, U> {
Decoded(T),
Encoded(U),
}

pub type RawHostAddress = [u8; AddressInfo::MAX_ADDRESS_BYTES];

pub struct AddressHeader {
pub ia: ByEndpoint<IsdAsn>,
pub host: ByEndpoint<MaybeEncoded<Host, RawHostAddress>>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ByEndpoint<T> {
destination: T,
source: T,
}

impl<T> WireDecodeWithContext<T, ByEndpoint<AddressInfo>> for AddressHeader
where
T: Buf,
{
type Error = Infallible;
let payload = data.copy_to_bytes(common_header.payload_size());

fn decode_with_context(
data: &mut T,
context: ByEndpoint<AddressInfo>,
) -> Result<Self, Self::Error> {
Ok(Self {
ia: ByEndpoint {
destination: IsdAsn(data.get_u64()),
source: IsdAsn(data.get_u64()),
},
host: ByEndpoint {
destination: maybe_decode_host(data, context.destination),
source: maybe_decode_host(data, context.source),
},
Ok(Scion {
common_header,
address_header,
path_header,
payload,
})
}
}

fn maybe_decode_host<T>(data: &mut T, info: AddressInfo) -> MaybeEncoded<Host, RawHostAddress>
where
T: Buf,
{
match info.host_type() {
MaybeEncoded::Decoded(host_type) => MaybeEncoded::Decoded(match host_type {
HostType::None => unreachable!("AddressInfo never returns None host type"),
HostType::Ipv4 => Host::Ip(Ipv4Addr::from(data.get_u32()).into()),
HostType::Ipv6 => Host::Ip(Ipv6Addr::from(data.get_u128()).into()),
HostType::Svc => Host::Svc(ServiceAddress(data.get_u16())),
}),
MaybeEncoded::Encoded(_) => {
let mut host_address: RawHostAddress = [0u8; AddressInfo::MAX_ADDRESS_BYTES];

assert!(info.address_length() <= AddressInfo::MAX_ADDRESS_BYTES);
data.copy_to_slice(&mut host_address[..info.address_length()]);

MaybeEncoded::Encoded(host_address)
}
}
}
80 changes: 80 additions & 0 deletions crates/scion/src/packet/address_header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use std::net::{Ipv4Addr, Ipv6Addr};

use bytes::Buf;

use super::{AddressInfo, ByEndpoint, DecodeError, MaybeEncoded, WireDecodeWithContext};
use crate::address::{Host, HostType, IsdAsn, ServiceAddress};

/// The bytes of an encoded host address.
///
/// The length of the host address is stored in an associated [`AddressInfo`] instance.
pub type RawHostAddress = [u8; AddressInfo::MAX_ADDRESS_BYTES];

/// The address header of a SCION packet.
///
/// It contains the SCION ISD-AS number and host address of the destination and
/// source endpoints.
pub struct AddressHeader {
/// The ISD-AS numbers of the source and destination hosts.
pub ia: ByEndpoint<IsdAsn>,
/// The host addresses of the source and destination.
pub host: ByEndpoint<MaybeEncoded<Host, RawHostAddress>>,
}

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

fn decode_with_context(
data: &mut T,
context: ByEndpoint<AddressInfo>,
) -> Result<Self, Self::Error> {
const IA_BYTE_LENGTH: usize = 8;

if data.remaining() < 2 * IA_BYTE_LENGTH {
return Err(Self::Error::PacketTruncated);
}

Ok(Self {
ia: ByEndpoint {
destination: IsdAsn(data.get_u64()),
source: IsdAsn(data.get_u64()),
},
host: ByEndpoint {
destination: maybe_decode_host(data, context.destination)?,
source: maybe_decode_host(data, context.source)?,
},
})
}
}

fn maybe_decode_host<T>(
data: &mut T,
info: AddressInfo,
) -> Result<MaybeEncoded<Host, RawHostAddress>, DecodeError>
where
T: Buf,
{
if data.remaining() < info.address_length() {
return Err(DecodeError::PacketTruncated);
}

Ok(match info.host_type() {
MaybeEncoded::Decoded(host_type) => MaybeEncoded::Decoded(match host_type {
HostType::None => unreachable!("AddressInfo never returns None host type"),
HostType::Ipv4 => Host::Ip(Ipv4Addr::from(data.get_u32()).into()),
HostType::Ipv6 => Host::Ip(Ipv6Addr::from(data.get_u128()).into()),
HostType::Svc => Host::Svc(ServiceAddress(data.get_u16())),
}),
MaybeEncoded::Encoded(_) => {
let mut host_address: RawHostAddress = [0u8; AddressInfo::MAX_ADDRESS_BYTES];

assert!(info.address_length() <= AddressInfo::MAX_ADDRESS_BYTES);
data.copy_to_slice(&mut host_address[..info.address_length()]);

MaybeEncoded::Encoded(host_address)
}
})
}
Loading

0 comments on commit 4ff71f9

Please sign in to comment.