use scion_proto::{
address::{Asn, Isd, IsdAsn},
path::test_builder::{TestPathContext, TestRoutingLinkType},
};
use crate::network::scion::{
routing::AsRoutingLinkType,
topology::{ScionAs, ScionLink, ScionLinkType, ScionTopology},
};
pub trait TestPathContextTopologyExt {
fn build_topology(&self) -> ScionTopology;
}
impl TestPathContextTopologyExt for TestPathContext {
fn build_topology(&self) -> ScionTopology {
let mut topology = ScionTopology::new();
let src_as = self.src_address.isd_asn();
let dst_as = self.dst_address.isd_asn();
let mut using_isd = src_as.isd().to_u16();
let mut as_counter = src_as.asn().to_u64();
let mut previous_egress: Option<(IsdAsn, u16, bool)> = None;
let mut after_segment_change = false;
let mut has_changed_isd = false;
let Some(final_hop) = self.test_segments.iter().flat_map(|s| &s.hop_fields).last() else {
assert_eq!(
src_as, dst_as,
"If path is empty, src and dst AS must be the same"
);
let other_as = IsdAsn::new(Isd::new(using_isd), Asn::new(as_counter + 1));
topology
.add_as(ScionAs::new_core(src_as))
.unwrap()
.add_as(ScionAs::new_core(other_as))
.unwrap()
.add_link(ScionLink::new(src_as, 1, ScionLinkType::Core, other_as, 1).unwrap())
.unwrap();
return topology;
};
for (seg_idx, segment) in self.test_segments.iter().enumerate() {
for hop in &segment.hop_fields {
if after_segment_change {
if hop.egress_link_type == Some(TestRoutingLinkType::LinkToCore)
&& dst_as.isd() != src_as.isd()
{
assert!(
segment.hop_fields.len() >= 3,
"Need at least three core hops to change ISDs"
);
using_isd = dst_as.isd().to_u16();
has_changed_isd = true;
}
let (isd, ..) = previous_egress
.take()
.expect("Previous egress should be set");
previous_egress = Some((isd, hop.egress_if, hop.egress_interface_down));
after_segment_change = false;
continue;
}
after_segment_change = hop.segment_change_next;
let curr_as = if std::ptr::eq(hop, final_hop) {
dst_as } else {
let mut next = IsdAsn::new(Isd::new(using_isd), Asn::new(as_counter));
if next == dst_as {
as_counter += 1;
next = IsdAsn::new(Isd::new(using_isd), Asn::new(as_counter));
}
as_counter += 1;
next
};
let scion_as = match segment.uplink_type {
TestRoutingLinkType::LinkToCore => ScionAs::new_core(curr_as),
_ => {
match hop.segment_change_next {
false => ScionAs::new(curr_as),
true => {
let next_segment = self
.test_segments
.get(seg_idx + 1)
.expect("Next segment should exist on segment change");
if next_segment.uplink_type == TestRoutingLinkType::LinkToCore {
ScionAs::new_core(curr_as)
} else {
ScionAs::new(curr_as)
}
}
}
}
};
topology
.add_as(scion_as.with_forwarding_key(hop.forwarding_key))
.expect("Should not fail to add AS");
let prev_as =
previous_egress.replace((curr_as, hop.egress_if, hop.egress_interface_down));
let Some((prev_as, prev_egress, link_down)) = prev_as else {
continue;
};
let link_type = match hop.ingress_link_type {
Some(link_type) => {
match link_type {
TestRoutingLinkType::LinkToCore => ScionLinkType::Core,
TestRoutingLinkType::LinkToParent => ScionLinkType::Parent,
TestRoutingLinkType::LinkToChild => ScionLinkType::Child,
TestRoutingLinkType::LinkToPeer => ScionLinkType::Peer,
}
}
None => continue, };
let mut link =
ScionLink::new(prev_as, prev_egress, link_type, curr_as, hop.ingress_if)
.expect("Should not fail to create links");
link.set_is_up(!link_down);
topology
.add_link(link)
.expect("Should not fail to add link");
}
}
if dst_as.isd() != src_as.isd() && !has_changed_isd {
panic!(
"If dst_ia is set, and ISD is not the same as start, the path must have at least one core segment with 3 hops to change ISD"
);
}
if !topology.as_map.values().any(|as_entry| as_entry.is_core()) {
let core_ia = IsdAsn::new(
Isd::new(using_isd),
Asn::new(as_counter.saturating_add(100)),
);
let core_as = ScionAs::new_core(core_ia);
topology.add_as(core_as).expect("Failed to add core AS");
let src_link = ScionLink::new(src_as, 50, ScionLinkType::Child, core_ia, 50)
.expect("Failed to create link");
let dst_link = ScionLink::new(dst_as, 51, ScionLinkType::Child, core_ia, 51)
.expect("Failed to create link");
topology.add_link(src_link).expect("Failed to add link");
topology.add_link(dst_link).expect("Failed to add link");
}
topology
}
}
impl From<TestRoutingLinkType> for AsRoutingLinkType {
fn from(value: TestRoutingLinkType) -> Self {
match value {
TestRoutingLinkType::LinkToCore => AsRoutingLinkType::LinkToCore,
TestRoutingLinkType::LinkToParent => AsRoutingLinkType::LinkToParent,
TestRoutingLinkType::LinkToChild => AsRoutingLinkType::LinkToChild,
TestRoutingLinkType::LinkToPeer => AsRoutingLinkType::LinkToPeer,
}
}
}