use std::collections::{HashMap, HashSet};
use crate::{
dataplane_path::standard::types::HopFieldMac,
identifier::isd_asn::IsdAsn,
segment::{AsEntry, HopEntry, PeerEntry, SegmentHopField, UnsignedPathSegment},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Link {
#[allow(dead_code)]
a: IsdAsn,
a_ifid: u16,
b: IsdAsn,
b_ifid: u16,
peer: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Graph {
ases: HashSet<IsdAsn>,
links: HashMap<IsdAsn, HashMap<u16, Link>>,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum GraphError {
#[error("AS {0} not in graph")]
AsNotInGraph(IsdAsn),
#[error("cannot create empty segment")]
EmptySegment,
#[error("unknown interface id {0}")]
UnknownIfId(u16),
}
pub const DEFAULT_HOP_EXPIRATION: u8 = 63;
impl Graph {
pub fn new() -> Self {
Self {
ases: HashSet::new(),
links: HashMap::new(),
}
}
pub fn add_node(&mut self, ia: IsdAsn) {
self.ases.insert(ia);
}
pub fn add_link(
&mut self,
a: IsdAsn,
a_if: u16,
b: IsdAsn,
b_if: u16,
peer: bool,
) -> Result<(), GraphError> {
self.add_node(a);
self.add_node(b);
self.links
.entry(a)
.or_default()
.entry(a_if)
.or_insert(Link {
a,
a_ifid: a_if,
b,
b_ifid: b_if,
peer,
});
self.links
.entry(b)
.or_default()
.entry(b_if)
.or_insert(Link {
a: b,
a_ifid: b_if,
b: a,
b_ifid: a_if,
peer,
});
Ok(())
}
pub fn delete_interface(&mut self, ia: IsdAsn, if_id: u16) -> Result<(), GraphError> {
self.links
.get_mut(&ia)
.and_then(|links| links.remove(&if_id))
.ok_or(GraphError::UnknownIfId(if_id))?;
Ok(())
}
pub fn remove_link(
&mut self,
a: IsdAsn,
a_if: u16,
b: IsdAsn,
b_if: u16,
) -> Result<(), GraphError> {
self.links
.get_mut(&a)
.and_then(|links| links.remove(&a_if))
.ok_or(GraphError::UnknownIfId(a_if))?;
self.links
.get_mut(&b)
.and_then(|links| links.remove(&b_if))
.ok_or(GraphError::UnknownIfId(b_if))?;
Ok(())
}
pub fn beacon(
&self,
start_ia: IsdAsn,
egress_ifs: &[u16],
) -> Result<UnsignedPathSegment, GraphError> {
if egress_ifs.is_empty() {
return Err(GraphError::EmptySegment);
}
let mut as_entries = Vec::new();
let mut curr_ia = start_ia;
let mut ingress = 0u16;
for (i, egress) in egress_ifs.iter().chain(std::iter::once(&0)).enumerate() {
let exp_time = DEFAULT_HOP_EXPIRATION;
let (next_ia, next_ingress) = if i < egress_ifs.len() {
let out_link = self
.links
.get(&curr_ia)
.ok_or(GraphError::AsNotInGraph(curr_ia))?
.get(egress)
.ok_or(GraphError::UnknownIfId(*egress))?;
(out_link.b, out_link.b_ifid)
} else {
(IsdAsn::from(0u64), 0)
};
let mut peer_entries = Vec::new();
if let Some(links) = self.links.get(&curr_ia) {
for (_, link) in links.iter() {
if !link.peer {
continue;
}
peer_entries.push(PeerEntry {
peer: link.b,
peer_interface: link.b_ifid,
peer_mtu: 1280,
hop_field: SegmentHopField {
expiration_units: exp_time,
cons_ingress: link.a_ifid,
cons_egress: *egress,
mac: HopFieldMac::zero(),
},
});
}
}
let as_entry = AsEntry {
local: curr_ia,
next: next_ia,
mtu: 2000,
hop_entry: HopEntry {
ingress_mtu: 1280,
hop_field: SegmentHopField {
expiration_units: exp_time,
cons_ingress: ingress,
cons_egress: *egress,
mac: HopFieldMac::zero(),
},
},
peer_entries,
extensions: Vec::new(),
unsigned_extensions: Vec::new(),
};
as_entries.push(as_entry);
if i < egress_ifs.len() {
ingress = next_ingress;
curr_ia = next_ia;
}
}
Ok(UnsignedPathSegment::new(0, 42, as_entries))
}
}
pub fn default_graph() -> Result<Graph, GraphError> {
let mut g = Graph::new();
let ia110: IsdAsn = "1-ff00:0:110".parse().unwrap();
let ia120: IsdAsn = "1-ff00:0:120".parse().unwrap();
let ia130: IsdAsn = "1-ff00:0:130".parse().unwrap();
let ia210: IsdAsn = "2-ff00:0:210".parse().unwrap();
let ia220: IsdAsn = "2-ff00:0:220".parse().unwrap();
let ia111: IsdAsn = "1-ff00:0:111".parse().unwrap();
let ia112: IsdAsn = "1-ff00:0:112".parse().unwrap();
let ia121: IsdAsn = "1-ff00:0:121".parse().unwrap();
let ia122: IsdAsn = "1-ff00:0:122".parse().unwrap();
let ia131: IsdAsn = "1-ff00:0:131".parse().unwrap();
let ia132: IsdAsn = "1-ff00:0:132".parse().unwrap();
let ia133: IsdAsn = "1-ff00:0:133".parse().unwrap();
let ia211: IsdAsn = "2-ff00:0:211".parse().unwrap();
let ia212: IsdAsn = "2-ff00:0:212".parse().unwrap();
let ia221: IsdAsn = "2-ff00:0:221".parse().unwrap();
let ia222: IsdAsn = "2-ff00:0:222".parse().unwrap();
g.add_link(ia110, 1, ia120, 6, false)?;
g.add_link(ia110, 2, ia130, 104, false)?;
g.add_link(ia110, 3, ia210, 453, false)?;
g.add_link(ia120, 1, ia130, 105, false)?;
g.add_link(ia120, 2, ia220, 501, false)?;
g.add_link(ia120, 3, ia220, 502, false)?;
g.add_link(ia210, 450, ia220, 503, false)?;
g.add_link(ia120, 4, ia121, 3, false)?;
g.add_link(ia120, 5, ia111, 104, false)?;
g.add_link(ia130, 111, ia131, 479, false)?;
g.add_link(ia130, 112, ia111, 105, false)?;
g.add_link(ia130, 113, ia112, 495, false)?;
g.add_link(ia111, 103, ia112, 494, false)?;
g.add_link(ia121, 2, ia122, 2, false)?;
g.add_link(ia131, 478, ia132, 2, false)?;
g.add_link(ia132, 1, ia133, 2, false)?;
g.add_link(ia210, 451, ia211, 7, false)?;
g.add_link(ia210, 452, ia211, 8, false)?;
g.add_link(ia220, 500, ia221, 2, false)?;
g.add_link(ia211, 2, ia212, 201, false)?;
g.add_link(ia211, 3, ia212, 200, false)?;
g.add_link(ia211, 4, ia222, 301, false)?;
g.add_link(ia221, 1, ia222, 302, false)?;
g.add_link(ia111, 100, ia121, 4, true)?;
g.add_link(ia111, 101, ia211, 5, true)?;
g.add_link(ia111, 102, ia211, 6, true)?;
g.add_link(ia121, 1, ia131, 480, true)?;
g.add_link(ia122, 1, ia133, 1, true)?;
g.add_link(ia211, 1, ia221, 3, true)?;
Ok(g)
}