use std::{collections::BTreeMap, net::SocketAddr, sync::Arc};
use ipnet::IpNet;
use scion_proto::packet::{ScionPacketRaw, classify_scion_packet};
use sciparse::{core::view::View, packet::view::ScionPacketView};
use snap_dataplane::dispatcher::Dispatcher;
use crate::network::local::receivers::Receiver;
pub struct RouterSocket<D> {
socket: tokio::net::UdpSocket,
snap_data_plane_interfaces: BTreeMap<String, SocketAddr>,
snap_data_plane_excludes: Vec<IpNet>,
dispatcher: Arc<D>,
}
impl<D> RouterSocket<D> {
pub async fn new(
socket: tokio::net::UdpSocket,
snap_data_plane_interfaces: BTreeMap<String, SocketAddr>,
snap_data_plane_excludes: Vec<IpNet>,
dispatcher: Arc<D>,
) -> std::io::Result<Self> {
Ok(Self {
socket,
snap_data_plane_interfaces,
snap_data_plane_excludes,
dispatcher,
})
}
pub fn addr(&self) -> SocketAddr {
self.socket.local_addr().expect("socket should be bound")
}
}
impl<D: Dispatcher> RouterSocket<D> {
pub async fn run(&self) -> std::io::Result<()> {
let mut buf = vec![0u8; 65536]; loop {
match self.socket.recv_from(&mut buf).await {
Ok((size, src)) => {
let view = match ScionPacketView::from_slice(&buf[..size]) {
Ok((view, _)) => view,
Err(e) => {
tracing::error!(error = ?e, ?src, "Failed to parse SCION packet");
continue;
}
};
self.dispatcher.try_dispatch(view);
}
Err(e) => {
tracing::error!(error = %e, "Failed to receive packet");
}
}
}
}
}
pub struct SharedRouterSocket<D: Dispatcher>(Arc<RouterSocket<D>>);
impl<D: Dispatcher> SharedRouterSocket<D> {
pub fn new(router_socket: RouterSocket<D>) -> Self {
Self(Arc::new(router_socket))
}
pub async fn run(&self) -> std::io::Result<()> {
self.0.run().await
}
}
impl<D: Dispatcher> Clone for SharedRouterSocket<D> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<D: Dispatcher> Receiver for SharedRouterSocket<D> {
fn receive_packet(&self, packet: ScionPacketRaw) {
let classified_packet = match classify_scion_packet(packet) {
Ok(classification) => classification,
Err(e) => {
tracing::error!(error = %e, "Failed to classify SCION packet");
return;
}
};
let dst_addr = match classified_packet.destination() {
Some(addr) => addr,
None => {
tracing::error!("Could not extract destination address from SCION packet");
return;
}
};
let dst_addr = match dst_addr.local_address() {
Some(addr) => addr,
None => {
tracing::error!("Svc address not supported");
return;
}
};
let forward_to = if let Some(snap_internal_addr) =
self.0.snap_data_plane_interfaces.values().next()
&& !self
.0
.snap_data_plane_excludes
.iter()
.any(|net| net.contains(&dst_addr.ip()))
{
*snap_internal_addr
} else {
dst_addr
};
let src_addr = self.0.socket.local_addr().expect("no fail");
tracing::debug!(?dst_addr, ?src_addr, "Router socket dispatching packet");
let raw = classified_packet.encode_to_vec();
if let Err(e) = self.0.socket.try_send_to(&raw, forward_to) {
tracing::error!(error = %e, %dst_addr, "Failed to send packet");
}
}
}