use std::net::IpAddr;
use sciparse::{
core::view::{View, ViewConversionError},
dataplane_path::types::PathType,
packet::view::ScionPacketView,
};
use thiserror::Error;
pub fn inbound_datagram_check<'a>(
datagram: &'a [u8],
expected_ip: IpAddr,
) -> Result<&'a ScionPacketView, PacketPolicyError<'a>> {
let (view, _) = ScionPacketView::try_from_slice(datagram)
.map_err(|e| PacketPolicyError::MalformedPacket(datagram, e))?;
let src_ip = view
.header()
.src_host_addr()
.ok()
.and_then(|w| w.ip())
.ok_or(PacketPolicyError::InvalidSourceAddress(view))?;
if src_ip != expected_ip {
return Err(PacketPolicyError::InvalidSourceAddress(view));
}
match view.header().path_type() {
PathType::Scion | PathType::Empty => {}
pt => return Err(PacketPolicyError::InvalidPathType(view, pt)),
}
Ok(view)
}
#[derive(Error)]
pub enum PacketPolicyError<'a> {
#[error("malformed packet: {1}")]
MalformedPacket(&'a [u8], ViewConversionError),
#[error("packet with invalid path type: {1:?}")]
InvalidPathType(&'a ScionPacketView, PathType),
#[error("packet does not have a valid source address")]
InvalidSourceAddress(&'a ScionPacketView),
}
impl std::fmt::Debug for PacketPolicyError<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PacketPolicyError::MalformedPacket(_, e) => {
f.debug_tuple("MalformedPacket").field(e).finish()
}
PacketPolicyError::InvalidPathType(_, pt) => {
f.debug_tuple("InvalidPathType").field(pt).finish()
}
PacketPolicyError::InvalidSourceAddress(_) => {
f.debug_tuple("InvalidSourceAddress").finish()
}
}
}
}
#[cfg(test)]
mod tests {
use std::net::{Ipv4Addr, Ipv6Addr};
use sciparse::{
address::addr::ScionAddr,
core::{encode::WireEncode, view::View},
dataplane_path::{model::DpPath, standard::model::StandardPath},
identifier::{asn::Asn, isd::Isd, isd_asn::IsdAsn},
packet::model::ScionRawPacket,
util::ToValue,
};
use test_log::test;
use super::*;
fn standard_path() -> DpPath {
DpPath::Standard(StandardPath::arbitrary_value(0))
}
fn example_source_addrs() -> Vec<ScionAddr> {
let ia = IsdAsn::new(Isd(1), Asn::new(0xff00_0000_0110));
vec![
ScionAddr::new(ia, Ipv4Addr::new(127, 0, 0, 1).into()),
ScionAddr::new(
ia,
Ipv6Addr::new(
0x2001, 0x0db8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7334,
)
.into(),
),
]
}
fn get_valid_packet(source: ScionAddr, dp_path: DpPath) -> Vec<u8> {
let packet = ScionRawPacket::new(
source,
example_source_addrs()[0],
dp_path,
sciparse::payload::ProtocolNumber::Udp,
b"my SCION packet".to_vec(),
);
packet.try_encode_to_vec().unwrap()
}
#[test]
fn inbound_datagram_valid_packet_accepted() {
let source_addrs = example_source_addrs();
let packet = get_valid_packet(source_addrs[0], standard_path());
let res = inbound_datagram_check(&packet, source_addrs[0].ip().unwrap());
assert!(res.is_ok());
assert_eq!(&packet[..], res.unwrap().as_slice());
}
#[test]
fn inbound_datagram_invalid_packet_rejected() {
let source_addrs = example_source_addrs();
let invalid_packet: &[u8; 4] = &[1, 2, 3, 4];
let res = inbound_datagram_check(invalid_packet, source_addrs[0].ip().unwrap());
assert!(matches!(res, Err(PacketPolicyError::MalformedPacket(..))));
}
#[test]
fn inbound_datagram_invalid_source_addr_rejected() {
let source_addrs = example_source_addrs();
let packet = get_valid_packet(source_addrs[0], standard_path());
let wrong_source = Ipv4Addr::new(127, 0, 0, 2);
let res = inbound_datagram_check(&packet, wrong_source.into());
assert!(matches!(
res,
Err(PacketPolicyError::InvalidSourceAddress(_))
));
}
}