use std::{str::FromStr, time::SystemTime};
use anyhow::{Context, Ok};
use ntest::timeout;
use pocketscion::{
network::scion::topology::{ScionAs, ScionTopology},
runtime::PocketScionRuntimeBuilder,
state::SharedPocketScionState,
};
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
use scion_proto::{
address::{IsdAsn, SocketAddr as ScionSocketAddr},
packet::classify_scion_packet,
scmp::{DestinationUnreachableCode, ScmpDestinationUnreachable, ScmpMessage, ScmpMessageBase},
};
use scion_stack::scionstack::ScionStackBuilder;
use snap_tokens::snap_token::dummy_snap_token;
#[tokio::test]
#[timeout(10_000)]
async fn should_receive_scmp_messages() -> anyhow::Result<()> {
let server_ia = IsdAsn::from_str("1-1")?;
let snap_ip_net = "10.2.0.0/24".parse()?;
let mut state = SharedPocketScionState::new(SystemTime::now());
let mut topo = ScionTopology::new();
topo.add_as(ScionAs::new_core(server_ia))?
.add_as(ScionAs::new_core("1-2".parse()?))?
.add_link("1-1#1 core 1-2#1".parse()?)?;
state.set_topology(topo);
let snap_id = {
let server_snap_id = state.add_snap();
state.add_snap_data_plane(
server_snap_id,
server_ia,
vec![snap_ip_net],
ChaCha8Rng::seed_from_u64(1),
);
server_snap_id
};
let ps_rt = PocketScionRuntimeBuilder::new()
.with_system_state(state.into_state())
.start()
.await
.context("starting runtime")?;
let ps_api = ps_rt.api_client();
let all_snaps = ps_api.get_snaps().await.context("error getting snaps")?;
let snap_cp_addr = all_snaps
.snaps
.get(&snap_id)
.context("snap not found")?
.control_plane_api
.clone();
let client_stack = ScionStackBuilder::new(snap_cp_addr)
.with_auth_token(dummy_snap_token())
.build()
.await?;
let client_socket = client_stack.bind(None).await?;
let client_raw = client_stack
.bind_raw(Some(client_socket.local_addr()))
.await?;
let random_message = [0u8; 1];
client_socket
.send_to(
&random_message,
ScionSocketAddr::new("1-2,10.0.0.1".parse()?, 1235), )
.await
.context("error sending client message")?;
let recv = client_raw
.recv()
.await
.context("error receiving client message")?;
let scmp = classify_scion_packet(recv)
.expect("error classifying SCION packet")
.try_into_scmp()
.expect("error converting to SCMP packet")
.message;
assert!(scmp.is_error(), "Expected SCMP error message");
assert!(
matches!(
scmp,
ScmpMessage::DestinationUnreachable(ScmpDestinationUnreachable {
code: DestinationUnreachableCode::AddressUnreachable,
..
})
),
"Expected Destination Unreachable with Address Unreachable code"
);
Ok(())
}