use std::time::{Duration, SystemTime};
use anyhow::{Context, Ok};
use bytes::Bytes;
use chrono::Utc;
use ntest::timeout;
use pocketscion::{
network::scion::topology::{ScionAs, ScionTopology},
runtime::PocketScionRuntimeBuilder,
state::SharedPocketScionState,
};
use scion_proto::{
address::{IsdAsn, SocketAddr},
packet::{ByEndpoint, ScionPacketUdp, classify_scion_packet},
scmp::{DestinationUnreachableCode, ScmpDestinationUnreachable, ScmpMessage, ScmpMessageBase},
};
use scion_stack::{path::manager::traits::PathManager as _, scionstack::ScionStackBuilder};
use snap_tokens::v0::dummy_snap_token;
use test_log::test;
#[test(tokio::test)]
#[timeout(10_000)]
async fn should_receive_scmp_messages() -> anyhow::Result<()> {
scion_sdk_utils::rustls::select_ring_crypto_provider();
let server_ia: IsdAsn = "1-1".parse().unwrap();
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 = state.add_snap(server_ia)?;
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()
.with_endhost_api(snap_cp_addr)
.with_auth_token(dummy_snap_token())
.build()
.await
.expect("build SCION stack");
let client_raw = client_stack
.bind_raw(None)
.await
.expect("bind raw SCION socket");
let client_path_manager = client_stack.create_path_manager();
let (src, dst) = (
client_raw.local_addr(),
"[1-2,10.0.0.1]:12345".parse::<SocketAddr>().unwrap(),
);
let path = client_path_manager
.path_wait(src.isd_asn(), dst.isd_asn(), Utc::now())
.await
.expect("error getting path");
let random_message = Bytes::from_static(b"test message");
let packet = ScionPacketUdp::new(
ByEndpoint {
source: src,
destination: dst,
},
path.data_plane_path,
random_message,
)?;
client_raw
.send(packet.into())
.await
.context("error sending client message")?;
let recv = tokio::time::timeout(Duration::from_secs(1), client_raw.recv())
.await
.context("timeout receiving client message")?
.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(())
}