Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SCION SCMP packets and improved packet handling #113

Merged
merged 3 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions crates/scion-proto/src/address/scion_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::net::{Ipv4Addr, Ipv6Addr};

use super::{error::AddressKind, AddressParseError, HostAddr, IsdAsn, ServiceAddr};
use crate::packet::AddressInfo;

/// A SCION network address.
///
Expand All @@ -29,7 +30,7 @@ impl ScionAddr {
}
}

/// Returns the host associated with this socket address.
/// Returns the host associated with this SCION address.
pub const fn host(&self) -> HostAddr {
match self {
ScionAddr::V4(addr) => HostAddr::V4(*addr.host()),
Expand Down Expand Up @@ -60,6 +61,15 @@ impl ScionAddr {
ScionAddr::Svc(addr) => addr.set_isd_asn(new_isd_asn),
}
}

/// Returns the address info corresponding to the SCION address's address type.
pub const fn address_info(&self) -> AddressInfo {
match self {
ScionAddr::V4(_) => AddressInfo::IPV4,
ScionAddr::V6(_) => AddressInfo::IPV6,
ScionAddr::Svc(_) => AddressInfo::SERVICE,
}
}
}

impl AsRef<IsdAsn> for ScionAddr {
Expand Down Expand Up @@ -138,7 +148,7 @@ macro_rules! scion_address {
Self { isd_asn, host }
}

/// Returns the host associated with this socket address.
/// Returns the host associated with this SCION address.
pub const fn host(&self) -> &$host_type {
&self.host
}
Expand Down
9 changes: 9 additions & 0 deletions crates/scion-proto/src/address/socket_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ impl SocketAddr {
}
}

/// Returns the SCION address associated with this socket address.
pub fn scion_address(&self) -> ScionAddr {
match self {
SocketAddr::V4(addr) => ScionAddr::V4(*addr.scion_addr()),
SocketAddr::V6(addr) => ScionAddr::V6(*addr.scion_addr()),
SocketAddr::Svc(addr) => ScionAddr::Svc(*addr.scion_addr()),
}
}

/// Returns the host address associated with this socket address.
pub fn host(&self) -> HostAddr {
match self {
Expand Down
39 changes: 24 additions & 15 deletions crates/scion-proto/src/datagram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
use bytes::{Buf, BufMut, Bytes, BytesMut};

use crate::{
packet::{AddressHeader, ByEndpoint, ChecksumDigest, InadequateBufferSize},
packet::{
self,
AddressHeader,
ByEndpoint,
ChecksumDigest,
InadequateBufferSize,
MessageChecksum,
},
wire_encoding::{WireDecode, WireEncodeVec},
};

Expand All @@ -14,6 +21,9 @@ pub enum UdpDecodeError {
DatagramEmptyOrTruncated,
#[error("next-header value of SCION header is not correct")]
WrongProtocolNumber(u8),
/// An error when decoding the SCION packet.
#[error(transparent)]
PackedDecodeError(#[from] packet::DecodeError),
}

#[allow(missing_docs)]
Expand All @@ -29,7 +39,7 @@ pub enum UdpEncodeError {
/// the [RFC].
///
/// [RFC]: https://www.ietf.org/archive/id/draft-dekater-scion-dataplane-00.html
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Default, PartialEq, Clone)]
pub struct UdpMessage {
/// The source and destination ports
pub port: ByEndpoint<u16>,
Expand Down Expand Up @@ -65,9 +75,19 @@ impl UdpMessage {
};
Ok(datagram)
}
}

impl MessageChecksum for UdpMessage {
fn checksum(&self) -> u16 {
self.checksum
}

fn set_checksum(&mut self, address_header: &AddressHeader) {
self.checksum = 0;
self.checksum = self.calculate_checksum(address_header);
}

/// Compute the checksum for this datagram using the provided address header.
pub fn calculate_checksum(&self, address_header: &AddressHeader) -> u16 {
fn calculate_checksum(&self, address_header: &AddressHeader) -> u16 {
ChecksumDigest::with_pseudoheader(address_header, Self::PROTOCOL_NUMBER, self.length.into())
.add_u16(self.port.source)
.add_u16(self.port.destination)
Expand All @@ -76,17 +96,6 @@ impl UdpMessage {
.add_slice(&self.payload)
.checksum()
}

/// Returns true if the checksum successfully verifies, otherwise false.
pub fn verify_checksum(&self, address_header: &AddressHeader) -> bool {
self.calculate_checksum(address_header) == 0
}

/// Clears then sets the checksum to the value returned by [`Self::calculate_checksum()`].
pub fn set_checksum(&mut self, address_header: &AddressHeader) {
self.checksum = 0;
self.checksum = self.calculate_checksum(address_header);
}
}

impl WireEncodeVec<2> for UdpMessage {
Expand Down
5 changes: 4 additions & 1 deletion crates/scion-proto/src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ pub use headers::{
pub mod raw;
pub use raw::ScionPacketRaw;

pub mod scmp;
pub use scmp::ScionPacketScmp;

pub mod udp;
pub use udp::ScionPacketUdp;

mod checksum;
pub use checksum::ChecksumDigest;
pub use checksum::{ChecksumDigest, MessageChecksum};
17 changes: 17 additions & 0 deletions crates/scion-proto/src/packet/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ use crate::{
wire_encoding::{MaybeEncoded, WireEncode},
};

/// Trait implemented by all higher-layer messages that use 2-byte checksums.
pub trait MessageChecksum {
/// Returns the currently stored checksum of the message.
fn checksum(&self) -> u16;

/// Clears then sets the checksum to the value returned by [`Self::calculate_checksum()`].
fn set_checksum(&mut self, address_header: &AddressHeader);

/// Compute the checksum for this message using the provided address header.
fn calculate_checksum(&self, address_header: &AddressHeader) -> u16;

/// Returns true if the checksum successfully verifies, otherwise false.
fn verify_checksum(&self, address_header: &AddressHeader) -> bool {
self.calculate_checksum(address_header) == 0
}
}

/// Incrementally computes the 16-bit checksum for upper layer protocols.
///
/// A new, empty digest can be created with [`ChecksumDigest::new()`], or
Expand Down
23 changes: 21 additions & 2 deletions crates/scion-proto/src/packet/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,35 @@ impl From<DataplanePathErrorKind> for DecodeError {
}
}

/// Errors raised when failing to encode a [`super::ScionPacketRaw`] or [`super::ScionPacketUdp`].
#[allow(missing_docs)]
/// Errors raised when failing to encode a [`super::ScionPacketRaw`], [`super::ScionPacketScmp`], or
/// [`super::ScionPacketUdp`].
#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone, Copy)]
pub enum EncodeError {
/// The payload is too large to be properly encoded in a SCION packet.
#[error("packet payload is too large")]
PayloadTooLarge,
/// The overall header is too large.
///
/// This is most likely due to a too long path.
#[error("packet header is too large")]
HeaderTooLarge,
}

/// Errors raised when creating a [`super::ScionPacketScmp`].
#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone, Copy)]
pub enum ScmpEncodeError {
/// Some SCMP messages (notably the [`ScmpTracerouteRequest`][crate::scmp::ScmpTracerouteRequest])
/// require a specific path type.
#[error("the provided path type is not appropriate for this type of packet")]
InappropriatePathType,
/// A provided parameter is out of range.
#[error("a provided parameter is out of range")]
ParameterOutOfRange,
/// A general [`EncodeError`] occurred.
#[error("encoding error")]
GeneralEncodeError(#[from] EncodeError),
}

/// 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
Expand Down
40 changes: 27 additions & 13 deletions crates/scion-proto/src/packet/headers.rs
jpcsmith marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ pub use address_header::{AddressHeader, RawHostAddress};

use super::{EncodeError, InadequateBufferSize};
use crate::{
address::SocketAddr,
address::{ScionAddr, SocketAddr},
path::{DataplanePath, Path},
wire_encoding::WireEncode,
};

/// SCION packet headers.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct ScionHeaders {
/// Metadata about the remaining headers and payload.
pub common: CommonHeader,
Expand All @@ -28,13 +28,14 @@ pub struct ScionHeaders {
}

impl ScionHeaders {
/// Creates a new [`ScionHeaders`] object given the source and destination [`SocketAddr`],
/// the [`Path`], the next-header value, and the payload length
/// Creates a new [`ScionHeaders`] object given the source and destination [`ScionAddr`],
/// the [`Path`], the next-header value, and the payload length.
pub fn new(
endhosts: &ByEndpoint<SocketAddr>,
endhosts: &ByEndpoint<ScionAddr>,
path: &Path,
next_header: u8,
payload_length: usize,
flow_id: FlowId,
) -> Result<Self, EncodeError> {
let address_header = AddressHeader::from(*endhosts);

Expand All @@ -51,14 +52,14 @@ impl ScionHeaders {
let common_header = CommonHeader {
version: Version::default(),
traffic_class: 0,
flow_id: Self::simple_flow_id(endhosts),
flow_id,
next_header,
header_length_factor,
payload_length: payload_length
.try_into()
.map_err(|_| EncodeError::PayloadTooLarge)?,
path_type: path.dataplane_path.path_type(),
address_info: endhosts.map(SocketAddr::address_info),
address_info: endhosts.map(ScionAddr::address_info),
reserved: 0,
};

Expand All @@ -69,11 +70,24 @@ impl ScionHeaders {
})
}

// TODO(mlegner): More sophisticated flow ID?
/// Simple flow ID containing the XOR of source and destination port with a prepended 1
/// to prevent a value of 0.
fn simple_flow_id(endhosts: &ByEndpoint<SocketAddr>) -> FlowId {
(0x1_0000 | (endhosts.source.port() ^ endhosts.destination.port()) as u32).into()
/// Creates a new [`ScionHeaders`] object given the source and destination [`SocketAddr`],
/// the [`Path`], the next-header value, and the payload length.
///
/// This is equivalent to [`ScionHeaders::new`] but uses [`FlowId::new_from_ports`] to set the
/// `flow_id`.
pub fn new_with_ports(
endhosts: &ByEndpoint<SocketAddr>,
path: &Path,
next_header: u8,
payload_length: usize,
) -> Result<Self, EncodeError> {
Self::new(
&endhosts.map(SocketAddr::scion_address),
path,
next_header,
payload_length,
FlowId::new_from_ports(&endhosts.map(SocketAddr::port)),
)
}
}

Expand Down Expand Up @@ -160,7 +174,7 @@ mod tests {
source: SocketAddr::from_str("[1-1,10.0.0.1]:10001").unwrap(),
destination: SocketAddr::from_str("[1-2,10.0.0.2]:10002").unwrap(),
};
let headers = ScionHeaders::new(
let headers = ScionHeaders::new_with_ports(
&endpoints,
&Path::empty(endpoints.map(SocketAddr::isd_asn)),
0,
Expand Down
17 changes: 17 additions & 0 deletions crates/scion-proto/src/packet/headers/common_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,23 @@ wire_encoding::bounded_uint! {
pub struct FlowId(u32 : 20);
}

// TODO(mlegner): Figure out better way of setting the flow ID for various cases.

impl Default for FlowId {
/// Set the flow ID to a dummy value of 1 (0 is disallowed by the specification).
fn default() -> Self {
FlowId::new_unchecked(1)
}
}

impl FlowId {
/// Simple flow ID containing the XOR of source and destination port with a prepended 1
/// to prevent a (disallowed) value of 0.
pub fn new_from_ports(ports: &ByEndpoint<u16>) -> Self {
(0x1_0000 | (ports.source ^ ports.destination) as u32).into()
}
}

wire_encoding::bounded_uint!(
/// An AddressInfo instance describes the type and length of a host address in
/// the SCION common header.
Expand Down
4 changes: 2 additions & 2 deletions crates/scion-proto/src/packet/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
};

/// A SCION network packet.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct ScionPacketRaw {
/// Packet headers
pub headers: ScionHeaders,
Expand All @@ -34,7 +34,7 @@ impl ScionPacketRaw {
payload: Bytes,
next_header: u8,
) -> Result<Self, EncodeError> {
let headers = ScionHeaders::new(endhosts, path, next_header, payload.len())?;
let headers = ScionHeaders::new_with_ports(endhosts, path, next_header, payload.len())?;

Ok(Self { headers, payload })
}
Expand Down
Loading