use std::{net::Ipv4Addr, sync::Arc, time::SystemTime};
use bytes::{BufMut, Bytes, BytesMut};
use pocketscion::{
io_config::SharedPocketScionIoConfig, runtime::PocketScionRuntimeBuilder,
state::SharedPocketScionState,
};
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
use scion_proto::{
address::ScionAddr,
packet,
packet::{ByEndpoint, FlowId, ScionPacketRaw},
path::{DataPlanePath, encoded::EncodedStandardPath},
wire_encoding::{WireDecode, WireEncodeVec},
};
use scion_stack::snap_tunnel::SnapTunnel;
use snap_control::client::{ControlPlaneApi, CrpcSnapControlClient};
use snap_tokens::snap_token::dummy_snap_token;
use test_log::test;
#[test(tokio::test)]
async fn snap_tunnel_simple_echo_test() {
scion_sdk_utils::test::install_rustls_crypto_provider();
let mut pstate = SharedPocketScionState::new(SystemTime::now());
let io_config = SharedPocketScionIoConfig::new();
let isd_as = "1-ff00:0:110".parse().unwrap();
let tunnel_addr = "127.0.0.1:9000".parse().unwrap();
let snap_id = pstate.add_snap();
let dp_id = pstate.add_snap_data_plane(
snap_id,
isd_as,
vec!["10.0.0.0/16".parse().unwrap()],
ChaCha8Rng::seed_from_u64(42),
);
let cp_api = std::net::SocketAddr::from((Ipv4Addr::LOCALHOST, 9002));
io_config.set_snap_control_addr(snap_id, cp_api);
io_config.set_snap_data_plane_addr(dp_id, tunnel_addr);
let mut pocketscion_runtime = PocketScionRuntimeBuilder::new()
.with_system_state(pstate.into_state())
.with_io_config(io_config.into_state())
.with_mgmt_listen_addr("127.0.0.1:9001".parse().expect("no fail"))
.start()
.await
.expect("starting pocketscion");
pocketscion_runtime
.wait_for_ready()
.await
.expect("pocketscion ready");
simple_echo().await;
pocketscion_runtime.stop_and_join().await;
}
fn build_scion_packet(source_addr: ScionAddr, dest_addr: ScionAddr, payload: &Bytes) -> Vec<u8> {
let endpoints = ByEndpoint {
source: source_addr,
destination: dest_addr,
};
let mut path_raw = BytesMut::with_capacity(36);
path_raw.put_u32(0x0000_2000);
path_raw.put_slice(&[0_u8; 32]);
let dp_path =
DataPlanePath::Standard(EncodedStandardPath::decode(&mut path_raw.freeze()).unwrap());
let packet = ScionPacketRaw::new(
endpoints,
dp_path,
payload.clone(),
0,
FlowId::new(0).unwrap(),
)
.unwrap();
packet.encode_to_bytes_vec().concat()
}
async fn send_and_receive_echo(tun: &SnapTunnel, payload: Bytes) {
let packet = build_scion_packet(
tun.assigned_addresses()[0].into(),
tun.assigned_addresses()[0].into(),
&payload,
);
tun.send_datagram(packet.clone().into()).unwrap();
let rdata = tun.read_datagram().await.unwrap();
assert!(packet.len() == rdata.len());
let received_packet = packet::ScionPacketRaw::decode(&mut rdata.clone()).unwrap();
assert!(received_packet.payload == payload);
assert!(
received_packet.headers.address.destination().unwrap()
== tun.assigned_addresses()[0].into()
);
}
async fn simple_echo() {
let cp_api = url::Url::parse("http://127.0.0.1:9002").expect("parse URL");
let mut client =
CrpcSnapControlClient::new(&cp_api).expect("create SNAP control plane API client");
client.use_token_source(Arc::new(dummy_snap_token()));
let session_grants = client
.create_data_plane_sessions()
.await
.expect("create SNAP data plane sessions");
assert!(session_grants.len() == 1);
let session_grant = session_grants
.first()
.expect("get first SNAP data plane session grant");
let tun = SnapTunnel::new(session_grant, Arc::new(client), vec![], None)
.await
.unwrap();
send_and_receive_echo(&tun, Bytes::from_static(b"my SCION packet")).await;
}