pub mod global;
mod iterator;
pub mod local;
pub use iterator::*;
use petgraph::{prelude::StableGraph, Directed};
use std::collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use serde_with::{As, Same};
use crate::{
event::Event,
formatter::NetworkFormatter,
forwarding_state::{ForwardingState, TO_DST},
network::Network,
router::Router,
types::{
DeviceError, IndexType, NetworkError, NetworkErrorOption, Prefix, PrefixMap, RouterId,
SimplePrefix, ASN,
},
};
use global::GlobalOspfCoordinator;
use local::OspfEvent;
pub use global::GlobalOspf;
pub use local::LocalOspf;
use self::global::GlobalOspfProcess;
pub type LinkWeight = f64;
pub const DEFAULT_LINK_WEIGHT: LinkWeight = 100.0;
pub const EXTERNAL_LINK_WEIGHT: LinkWeight = 0.0;
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)]
pub struct OspfArea(pub(crate) u32);
impl std::fmt::Display for OspfArea {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_backbone() {
f.write_str("Backbone")
} else {
write!(f, "Area {}", self.0)
}
}
}
impl std::fmt::Debug for OspfArea {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_backbone() {
f.write_str("backbone")
} else {
write!(f, "area{}", self.0)
}
}
}
impl OspfArea {
pub const BACKBONE: OspfArea = OspfArea(0);
pub const fn backbone() -> Self {
OspfArea(0)
}
pub const fn is_backbone(&self) -> bool {
self.0 == 0
}
pub const fn num(&self) -> u32 {
self.0
}
}
impl From<u32> for OspfArea {
fn from(x: u32) -> Self {
OspfArea(x)
}
}
impl From<u64> for OspfArea {
fn from(x: u64) -> Self {
Self(x as u32)
}
}
impl From<usize> for OspfArea {
fn from(x: usize) -> Self {
Self(x as u32)
}
}
impl From<i32> for OspfArea {
fn from(x: i32) -> Self {
OspfArea(x as u32)
}
}
impl From<i64> for OspfArea {
fn from(x: i64) -> Self {
Self(x as u32)
}
}
impl From<isize> for OspfArea {
fn from(x: isize) -> Self {
Self(x as u32)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OspfDomain<Ospf = GlobalOspfCoordinator> {
asn: ASN,
#[serde(with = "As::<Vec<(Same, Same)>>")]
pub(crate) external_links: HashMap<RouterId, HashSet<RouterId>>,
#[serde(with = "As::<Vec<(Same, Vec<(Same, Same)>)>>")]
pub(crate) links: HashMap<RouterId, HashMap<RouterId, (LinkWeight, OspfArea)>>,
pub(crate) coordinator: Ospf,
}
impl<Ospf> PartialEq for OspfDomain<Ospf> {
fn eq(&self, other: &Self) -> bool {
self.links == other.links && self.external_links == other.external_links
}
}
impl<Ospf> OspfDomain<Ospf>
where
Ospf: OspfCoordinator,
{
fn new(asn: &ASN) -> Self {
let asn = *asn;
Self {
asn,
external_links: Default::default(),
links: Default::default(),
coordinator: Ospf::new(asn),
}
}
pub fn has_edge(&self, a: RouterId, b: RouterId) -> bool {
self.links
.get(&a)
.map(|x| x.contains_key(&b))
.unwrap_or(false)
|| self
.external_links
.get(&a)
.map(|x| x.contains(&b))
.unwrap_or(false)
}
pub(crate) fn swap_coordinator<Ospf2: OspfCoordinator>(self) -> (OspfDomain<Ospf2>, Ospf) {
(
OspfDomain {
asn: self.asn,
external_links: self.external_links,
links: self.links,
coordinator: Ospf2::new(self.asn),
},
self.coordinator,
)
}
fn is_internal(&self, id: RouterId) -> bool {
self.links.contains_key(&id)
}
fn is_external(&self, id: RouterId) -> bool {
!self.is_internal(id)
}
fn add_router(&mut self, id: RouterId) {
self.links.insert(id, Default::default());
self.external_links.insert(id, Default::default());
}
pub(crate) fn reset<P: Prefix, T: Default>(
&mut self,
mut routers: BTreeMap<RouterId, &mut Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError> {
routers
.values_mut()
.for_each(|r| r.ospf = Ospf::Process::new(r.router_id()));
self.coordinator = Ospf::new(self.asn);
let mut deltas = Vec::new();
for (r, ext) in &self.external_links {
for n in ext {
deltas.push(NeighborhoodChange::AddExternalNetwork { int: *r, ext: *n });
}
}
for (r, int) in &self.links {
for (n, (weight, area)) in int {
if *r > *n {
continue;
}
let weight_rev = self.links[n][r].0;
deltas.push(NeighborhoodChange::AddLink {
a: *r,
b: *n,
area: *area,
weight: (*weight, weight_rev),
});
}
}
OspfCoordinator::update(
&mut self.coordinator,
NeighborhoodChange::Batch(deltas),
routers,
&self.links,
&self.external_links,
)
}
pub(crate) fn add_links_from<P: Prefix, T: Default, I>(
&mut self,
links: I,
routers: BTreeMap<RouterId, &mut Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError>
where
I: IntoIterator<Item = (RouterId, RouterId)>,
{
let mut deltas = Vec::new();
for (a, b) in links {
assert!(self.is_internal(a));
if self.is_internal(b) {
let area = OspfArea::BACKBONE;
let weight = DEFAULT_LINK_WEIGHT;
match self.links.entry(a).or_default().entry(b) {
Entry::Occupied(_) => {
deltas.push(NeighborhoodChange::Weight {
src: a,
dst: b,
old: LinkWeight::INFINITY,
new: weight,
area,
});
deltas.push(NeighborhoodChange::Weight {
src: b,
dst: a,
old: LinkWeight::INFINITY,
new: weight,
area,
});
}
Entry::Vacant(e) => {
e.insert((weight, area));
self.links.entry(b).or_default().insert(a, (weight, area));
deltas.push(NeighborhoodChange::AddLink {
a,
b,
area,
weight: (weight, weight),
})
}
}
} else {
self.external_links.entry(a).or_default().insert(b);
deltas.push(NeighborhoodChange::AddExternalNetwork { int: a, ext: b });
}
}
OspfCoordinator::update(
&mut self.coordinator,
NeighborhoodChange::Batch(deltas),
routers,
&self.links,
&self.external_links,
)
}
pub(crate) fn set_weight<P: Prefix, T: Default>(
&mut self,
src: RouterId,
dst: RouterId,
weight: LinkWeight,
routers: BTreeMap<RouterId, &mut Router<P, Ospf::Process>>,
) -> Result<(Vec<Event<P, T>>, LinkWeight), NetworkError> {
let (w, a) = self
.links
.get_mut(&src)
.or_router_not_found(src)?
.get_mut(&dst)
.or_link_not_found(src, dst)?;
let area = *a;
let old_weight = *w;
*w = weight;
let events = OspfCoordinator::update(
&mut self.coordinator,
NeighborhoodChange::Weight {
src,
dst,
old: old_weight,
new: weight,
area,
},
routers,
&self.links,
&self.external_links,
)?;
Ok((events, old_weight))
}
pub(crate) fn set_link_weights_from<P: Prefix, T: Default, I>(
&mut self,
weights: I,
routers: BTreeMap<RouterId, &mut Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError>
where
I: IntoIterator<Item = (RouterId, RouterId, LinkWeight)>,
{
let mut deltas = Vec::new();
for (src, dst, weight) in weights.into_iter() {
let (w, a) = self
.links
.get_mut(&src)
.or_router_not_found(src)?
.get_mut(&dst)
.or_link_not_found(src, dst)?;
let area = *a;
let old_weight = *w;
*w = weight;
deltas.push(NeighborhoodChange::Weight {
src,
dst,
old: old_weight,
new: weight,
area,
})
}
let events = OspfCoordinator::update(
&mut self.coordinator,
NeighborhoodChange::Batch(deltas),
routers,
&self.links,
&self.external_links,
)?;
Ok(events)
}
pub fn indices(&self) -> DomainIndices<'_> {
DomainIndices(self.links.keys())
}
pub fn graph(&self) -> StableGraph<(), (LinkWeight, OspfArea), Directed, IndexType> {
let mut g = StableGraph::new();
let routers: BTreeSet<_> = self.links.keys().copied().collect();
let Some(max_router_id) = routers.last().copied() else {
return g;
};
while g.add_node(()) < max_router_id {}
for r in (0..max_router_id.index() as u32).map(RouterId::from) {
if !routers.contains(&r) {
g.remove_node(r);
}
}
for (r, neighbors) in &self.links {
for (neighbor, (weight, area)) in neighbors {
g.add_edge(*r, *neighbor, (*weight, *area));
}
}
g
}
pub fn get_weight(&self, a: RouterId, b: RouterId) -> LinkWeight {
self.links
.get(&a)
.and_then(|x| x.get(&b))
.and_then(|(w, _)| w.is_finite().then_some(*w))
.or_else(|| {
self.external_links
.get(&a)
.and_then(|x| x.contains(&b).then_some(EXTERNAL_LINK_WEIGHT))
})
.or_else(|| {
self.external_links
.get(&b)
.and_then(|x| x.contains(&a).then_some(EXTERNAL_LINK_WEIGHT))
})
.unwrap_or(LinkWeight::INFINITY)
}
pub(crate) fn set_area<P: Prefix, T: Default>(
&mut self,
a: RouterId,
b: RouterId,
area: OspfArea,
routers: BTreeMap<RouterId, &mut Router<P, Ospf::Process>>,
) -> Result<(Vec<Event<P, T>>, OspfArea), NetworkError> {
let (w_a_b, aa) = self
.links
.get_mut(&a)
.or_router_not_found(a)?
.get_mut(&b)
.or_link_not_found(a, b)?;
let w_a_b = *w_a_b;
let old_area = *aa;
*aa = area;
let (w_b_a, aa) = self
.links
.get_mut(&b)
.or_router_not_found(b)?
.get_mut(&a)
.or_link_not_found(b, a)?;
*aa = area;
let w_b_a = *w_b_a;
let events = OspfCoordinator::update(
&mut self.coordinator,
NeighborhoodChange::Area {
a,
b,
old: old_area,
new: area,
weight: (w_a_b, w_b_a),
},
routers,
&self.links,
&self.external_links,
)?;
Ok((events, old_area))
}
pub fn get_area(&self, a: RouterId, b: RouterId) -> Option<OspfArea> {
self.links.get(&a).and_then(|x| x.get(&b)).map(|(_, a)| *a)
}
pub(crate) fn remove_link<P: Prefix, T: Default>(
&mut self,
a: RouterId,
b: RouterId,
routers: BTreeMap<RouterId, &mut Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError> {
assert!(self.is_internal(a));
let update = if self.is_internal(b) {
let (w_a_b, area) = self
.links
.get_mut(&a)
.or_router_not_found(a)?
.remove(&b)
.or_link_not_found(a, b)?;
let (w_b_a, _) = self
.links
.get_mut(&b)
.or_router_not_found(b)?
.remove(&a)
.or_link_not_found(b, a)?;
NeighborhoodChange::RemoveLink {
a,
b,
area,
weight: (w_a_b, w_b_a),
}
} else {
let (int, ext) = (a, b);
self.external_links
.get_mut(&int)
.or_router_not_found(int)?
.take(&ext)
.or_link_not_found(int, ext)?;
NeighborhoodChange::RemoveExternalNetwork { int, ext }
};
OspfCoordinator::update(
&mut self.coordinator,
update,
routers,
&self.links,
&self.external_links,
)
}
pub(crate) fn remove_router<P: Prefix, T: Default>(
&mut self,
r: RouterId,
routers: BTreeMap<RouterId, &mut Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError> {
let mut deltas = Vec::new();
if self.is_external(r) {
for (int, x) in self.external_links.iter_mut() {
if x.remove(&r) {
deltas.push(NeighborhoodChange::RemoveExternalNetwork { int: *int, ext: r })
}
}
} else {
for (b, (w_r_b, area)) in self.links.remove(&r).or_router_not_found(r)? {
let (w_b_r, _) = self
.links
.get_mut(&b)
.or_router_not_found(b)?
.remove(&r)
.or_link_not_found(b, r)?;
deltas.push(NeighborhoodChange::RemoveLink {
a: r,
b,
area,
weight: (w_r_b, w_b_r),
})
}
for ext in self.external_links.remove(&r).or_router_not_found(r)? {
deltas.push(NeighborhoodChange::RemoveExternalNetwork { int: r, ext })
}
}
OspfCoordinator::update(
&mut self.coordinator,
NeighborhoodChange::Batch(deltas),
routers,
&self.links,
&self.external_links,
)
}
pub fn is_reachable<P: Prefix>(
&self,
a: RouterId,
b: RouterId,
routers: &BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> bool {
if self.is_external(a) || !routers.contains_key(&a) || !routers.contains_key(&b) {
return false;
}
if self.is_external(b) {
return self.external_links.get(&a).unwrap().contains(&b);
}
let is_reachable = |src, dst| {
routers
.get(&src)
.map(|r| r.ospf.is_reachable(dst))
.unwrap_or(false)
};
is_reachable(a, b) && is_reachable(b, a)
}
pub fn coordinator(&self) -> &Ospf {
&self.coordinator
}
pub fn internal_edges(&self) -> InternalEdges<'_> {
InternalEdges {
outer: vec![self.links.iter()],
inner: None,
}
}
pub fn external_edges(&self) -> ExternalEdges<'_> {
ExternalEdges {
outer: vec![self.external_links.iter()],
inner: None,
}
}
pub fn edges(&self) -> Edges<'_> {
Edges {
int: self.internal_edges(),
ext: self.external_edges(),
}
}
fn internal_neighbors(&self, r: RouterId) -> InternalEdges<'_> {
self.is_internal(r)
.then(|| InternalEdges {
outer: Vec::new(),
inner: self.links.get(&r).map(|n| (r, n.iter())),
})
.unwrap_or_default()
}
fn external_neighbors(&self, r: RouterId) -> ExternalEdges<'_> {
self.is_internal(r)
.then(|| ExternalEdges {
outer: Vec::new(),
inner: self.external_links.get(&r).map(|n| (r, n.iter())),
})
.unwrap_or_default()
}
fn neighbors(&self, r: RouterId) -> Edges<'_> {
self.is_internal(r)
.then(|| Edges {
int: self.internal_neighbors(r),
ext: self.external_neighbors(r),
})
.unwrap_or_default()
}
}
#[derive(Debug, Clone, Default)]
pub struct DomainIndices<'a>(
std::collections::hash_map::Keys<'a, RouterId, HashMap<RouterId, (LinkWeight, OspfArea)>>,
);
impl Iterator for DomainIndices<'_> {
type Item = RouterId;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().copied()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(bound(
serialize = "Ospf: serde::Serialize",
deserialize = "Ospf: serde::Deserialize<'de>"
))]
pub struct OspfNetwork<Ospf = GlobalOspfCoordinator> {
#[serde(with = "As::<Vec<(Same, Same)>>")]
pub(crate) domains: BTreeMap<ASN, OspfDomain<Ospf>>,
#[serde(with = "As::<Vec<(Same, Same)>>")]
pub(crate) routers: BTreeMap<RouterId, ASN>,
}
impl<Ospf> Default for OspfNetwork<Ospf> {
fn default() -> Self {
Self {
domains: BTreeMap::new(),
routers: BTreeMap::new(),
}
}
}
impl<Ospf> OspfNetwork<Ospf>
where
Ospf: OspfCoordinator,
{
pub fn has_edge(&self, a: RouterId, b: RouterId) -> bool {
self.routers
.get(&a)
.and_then(|asn| self.domains.get(asn))
.map(|x| x.has_edge(a, b))
.unwrap_or(false)
}
fn split<'a, P: Prefix>(
&self,
asn: ASN,
routers: &'a mut BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> BTreeMap<RouterId, &'a mut Router<P, Ospf::Process>> {
routers
.iter_mut()
.map(|(router_id, device)| {
let backup_asn = device.asn();
(
router_id,
device,
self.routers.get(router_id).copied().unwrap_or(backup_asn),
)
})
.filter(|(_, _, d_asn)| *d_asn == asn)
.map(|(r, device, _)| (*r, device))
.collect()
}
fn split_all<'a, P: Prefix>(
&self,
routers: &'a mut BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> BTreeMap<ASN, BTreeMap<RouterId, &'a mut Router<P, Ospf::Process>>> {
let mut result: BTreeMap<_, BTreeMap<_, _>> = BTreeMap::new();
for (router_id, device) in routers.iter_mut() {
let asn = self
.routers
.get(router_id)
.copied()
.unwrap_or_else(|| device.asn());
result.entry(asn).or_default().insert(*router_id, device);
}
result
}
pub(crate) fn reset<P: Prefix, T: Default>(
&mut self,
routers: &mut BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError> {
let mut events = Vec::new();
let mut routers = self.split_all(routers);
for (asn, domain) in &mut self.domains {
let routers = routers.remove(asn).unwrap_or_default();
events.extend(domain.reset(routers)?);
}
Ok(events)
}
pub(crate) fn add_router(&mut self, id: RouterId, asn: ASN) {
let old_asn = self.routers.insert(id, asn);
if let Some(old_asn) = old_asn {
assert_eq!(
old_asn, asn,
"Added a router that already exists in a different ASN"
);
}
self.domains
.entry(asn)
.or_insert_with_key(OspfDomain::new)
.add_router(id);
}
pub(crate) fn add_link<P: Prefix, T: Default>(
&mut self,
a: RouterId,
b: RouterId,
routers: &mut BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError> {
self.add_links_from([(a, b)], routers)
}
pub(crate) fn add_links_from<P: Prefix, T: Default, I>(
&mut self,
links: I,
routers: &mut BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError>
where
I: IntoIterator<Item = (RouterId, RouterId)>,
{
let mut domain_links: HashMap<ASN, Vec<(RouterId, RouterId)>> = HashMap::new();
for (a, b) in links.into_iter() {
let a_asn = self
.routers
.get(&a)
.ok_or(NetworkError::DeviceNotFound(a))?;
let b_asn = self
.routers
.get(&b)
.ok_or(NetworkError::DeviceNotFound(b))?;
domain_links.entry(*a_asn).or_default().push((a, b));
if a_asn != b_asn {
domain_links.entry(*b_asn).or_default().push((b, a))
}
}
Ok(domain_links
.into_iter()
.map(|(asn, links)| {
let routers = self.split(asn, routers);
self.domains
.entry(asn)
.or_insert_with_key(OspfDomain::new)
.add_links_from(links, routers)
})
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect())
}
pub(crate) fn set_weight<P: Prefix, T: Default>(
&mut self,
src: RouterId,
dst: RouterId,
weight: LinkWeight,
routers: &mut BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> Result<(Vec<Event<P, T>>, LinkWeight), NetworkError> {
let asn = self
.routers
.get(&src)
.ok_or(NetworkError::DeviceNotFound(src))?;
let routers = self.split(*asn, routers);
self.domains
.entry(*asn)
.or_insert_with_key(OspfDomain::new)
.set_weight(src, dst, weight, routers)
}
pub(crate) fn set_link_weights_from<P: Prefix, T: Default, I>(
&mut self,
weights: I,
routers: &mut BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError>
where
I: IntoIterator<Item = (RouterId, RouterId, LinkWeight)>,
{
let mut domain_weights: HashMap<ASN, Vec<(RouterId, RouterId, LinkWeight)>> =
HashMap::new();
for (src, dst, weight) in weights {
let asn = self
.routers
.get(&src)
.ok_or(NetworkError::DeviceNotFound(src))?;
domain_weights
.entry(*asn)
.or_default()
.push((src, dst, weight));
}
Ok(domain_weights
.into_iter()
.map(|(asn, weights)| {
let routers = self.split(asn, routers);
self.domains
.entry(asn)
.or_insert_with_key(OspfDomain::new)
.set_link_weights_from(weights, routers)
})
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect())
}
pub fn get_weight(&self, a: RouterId, b: RouterId) -> LinkWeight {
self.routers
.get(&a)
.and_then(|asn| self.domains.get(asn))
.map(|x| x.get_weight(a, b))
.unwrap_or(LinkWeight::INFINITY)
}
pub(crate) fn set_area<P: Prefix, T: Default>(
&mut self,
a: RouterId,
b: RouterId,
area: OspfArea,
routers: &mut BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> Result<(Vec<Event<P, T>>, OspfArea), NetworkError> {
let asn = self
.routers
.get(&a)
.ok_or(NetworkError::DeviceNotFound(a))?;
let routers = self.split(*asn, routers);
self.domains
.entry(*asn)
.or_insert_with_key(OspfDomain::new)
.set_area(a, b, area, routers)
}
pub fn get_area(&self, a: RouterId, b: RouterId) -> Option<OspfArea> {
self.routers
.get(&a)
.and_then(|asn| self.domains.get(asn))
.and_then(|x| x.get_area(a, b))
}
pub(crate) fn remove_link<P: Prefix, T: Default>(
&mut self,
a: RouterId,
b: RouterId,
routers: &mut BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError> {
let a_asn = self
.routers
.get(&a)
.ok_or(NetworkError::DeviceNotFound(a))?;
let b_asn = self
.routers
.get(&b)
.ok_or(NetworkError::DeviceNotFound(b))?;
let a_routers = self.split(*a_asn, routers);
let mut events = self
.domains
.entry(*a_asn)
.or_insert_with_key(OspfDomain::new)
.remove_link(a, b, a_routers)?;
if a_asn != b_asn {
let b_routers = self.split(*b_asn, routers);
events.extend(
self.domains
.entry(*b_asn)
.or_insert_with_key(OspfDomain::new)
.remove_link(b, a, b_routers)?,
);
}
Ok(events)
}
pub(crate) fn remove_router<P: Prefix, T: Default>(
&mut self,
r: RouterId,
routers: &mut BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> Result<Vec<Event<P, T>>, NetworkError> {
let mut routers = self.split_all(routers);
let events = self
.domains
.iter_mut()
.filter_map(|(asn, domain)| routers.remove(asn).map(|routers| (asn, domain, routers)))
.map(|(_, domain, routers)| domain.remove_router(r, routers))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect();
self.domains.retain(|_, d| !d.links.is_empty());
self.routers.remove(&r);
Ok(events)
}
pub fn is_reachable<P: Prefix>(
&self,
a: RouterId,
b: RouterId,
routers: &BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> bool {
let Some(a_asn) = self.routers.get(&a) else {
return false;
};
let Some(b_asn) = self.routers.get(&a) else {
return false;
};
let reachable_a_asn = self
.domains
.get(a_asn)
.map(|x| x.is_reachable(a, b, routers))
.unwrap_or(false);
let reachable_b_asn = if a_asn == b_asn {
reachable_a_asn
} else {
self.domains
.get(b_asn)
.map(|x| x.is_reachable(a, b, routers))
.unwrap_or(false)
};
reachable_a_asn && reachable_b_asn
}
pub(crate) fn get_forwarding_state<P: Prefix>(
&self,
routers: &BTreeMap<RouterId, Router<P, Ospf::Process>>,
) -> (
ForwardingState<SimplePrefix>,
HashMap<RouterId, SimplePrefix>,
) {
let n = self.routers.len();
let mut lut: HashMap<RouterId, SimplePrefix> = HashMap::with_capacity(n);
let mut state: HashMap<RouterId, <SimplePrefix as Prefix>::Map<Vec<RouterId>>> =
HashMap::with_capacity(n);
let mut reversed: HashMap<RouterId, <SimplePrefix as Prefix>::Map<HashSet<RouterId>>> =
HashMap::with_capacity(n);
for dst in self.routers.keys() {
let p: SimplePrefix = dst.index().into();
lut.insert(*dst, p);
for src in self.routers.keys() {
if src == dst {
state.entry(*dst).or_default().insert(p, vec![*TO_DST]);
reversed
.entry(*TO_DST)
.or_default()
.get_mut_or_default(p)
.insert(*dst);
} else {
let r = routers.get(src).expect("iterating over keys");
let nhs = r.ospf.get(*dst);
for nh in nhs.iter() {
reversed
.entry(*nh)
.or_default()
.get_mut_or_default(p)
.insert(*src);
}
state.entry(*src).or_default().insert(p, nhs.to_vec());
}
}
}
(ForwardingState::from_raw(state, reversed), lut)
}
pub fn domains(&self) -> &BTreeMap<ASN, OspfDomain<Ospf>> {
&self.domains
}
pub fn domain(&self, asn: impl Into<ASN>) -> Result<&OspfDomain<Ospf>, NetworkError> {
let asn = asn.into();
self.domains.get(&asn).ok_or(NetworkError::UnknownAS(asn))
}
pub fn get_coordinator(&self, asn: ASN) -> Option<&Ospf> {
self.domains.get(&asn).map(|x| &x.coordinator)
}
pub fn internal_edges(&self) -> InternalEdges<'_> {
InternalEdges {
outer: self.domains.values().map(|x| x.links.iter()).collect(),
inner: None,
}
}
pub fn external_edges(&self) -> ExternalEdges<'_> {
ExternalEdges {
outer: self
.domains
.values()
.map(|x| x.external_links.iter())
.collect(),
inner: None,
}
}
pub fn edges(&self) -> Edges<'_> {
Edges {
int: self.internal_edges(),
ext: self.external_edges(),
}
}
pub fn internal_neighbors(&self, r: RouterId) -> InternalEdges<'_> {
self.routers
.get(&r)
.and_then(|asn| self.domains.get(asn))
.map(|d| d.internal_neighbors(r))
.unwrap_or_default()
}
pub fn external_neighbors(&self, r: RouterId) -> ExternalEdges<'_> {
self.routers
.get(&r)
.and_then(|asn| self.domains.get(asn))
.map(|d| d.external_neighbors(r))
.unwrap_or_default()
}
pub fn neighbors(&self, r: RouterId) -> Edges<'_> {
self.routers
.get(&r)
.and_then(|asn| self.domains.get(asn))
.map(|d| d.neighbors(r))
.unwrap_or_default()
}
}
pub trait OspfImpl {
type Coordinator: OspfCoordinator<Process = Self::Process>;
type Process: OspfProcess;
fn into_global(
coordinators: (Self::Coordinator, &mut GlobalOspfCoordinator),
processes: HashMap<RouterId, (Self::Process, &mut GlobalOspfProcess)>,
) -> Result<(), NetworkError>;
fn from_global(
coordinators: (&mut Self::Coordinator, GlobalOspfCoordinator),
processes: HashMap<RouterId, (&mut Self::Process, GlobalOspfProcess)>,
) -> Result<(), NetworkError>;
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum NeighborhoodChange {
AddLink {
a: RouterId,
b: RouterId,
area: OspfArea,
weight: (LinkWeight, LinkWeight),
},
Area {
a: RouterId,
b: RouterId,
old: OspfArea,
new: OspfArea,
weight: (LinkWeight, LinkWeight),
},
Weight {
src: RouterId,
dst: RouterId,
old: LinkWeight,
new: LinkWeight,
area: OspfArea,
},
RemoveLink {
a: RouterId,
b: RouterId,
area: OspfArea,
weight: (LinkWeight, LinkWeight),
},
AddExternalNetwork {
int: RouterId,
ext: RouterId,
},
RemoveExternalNetwork {
int: RouterId,
ext: RouterId,
},
Batch(Vec<NeighborhoodChange>),
}
pub trait OspfCoordinator: std::fmt::Debug + Clone + for<'de> Deserialize<'de> + Serialize {
type Process: OspfProcess;
fn new(asn: ASN) -> Self;
fn update<P: Prefix, T: Default>(
&mut self,
delta: NeighborhoodChange,
routers: BTreeMap<RouterId, &mut Router<P, Self::Process>>,
links: &HashMap<RouterId, HashMap<RouterId, (LinkWeight, OspfArea)>>,
external_links: &HashMap<RouterId, HashSet<RouterId>>,
) -> Result<Vec<Event<P, T>>, NetworkError>;
}
pub trait OspfProcess:
std::fmt::Debug + PartialEq + Clone + for<'de> Deserialize<'de> + Serialize + Send + Sync + 'static
{
fn new(router_id: RouterId) -> Self;
fn get_table(&self) -> &BTreeMap<RouterId, (Vec<RouterId>, LinkWeight)>;
fn get_neighbors(&self) -> &BTreeMap<RouterId, LinkWeight>;
fn handle_event<P: Prefix, T: Default>(
&mut self,
src: RouterId,
area: OspfArea,
event: OspfEvent,
) -> Result<(bool, Vec<Event<P, T>>), DeviceError>;
fn get(&self, target: impl Into<IgpTarget>) -> &[RouterId] {
let target = target.into();
match target {
IgpTarget::Neighbor(dst) => match self.get_neighbors().get_key_value(&dst) {
Some((dst, _)) => std::slice::from_ref(dst),
None => Default::default(),
},
IgpTarget::Ospf(dst) => self
.get_table()
.get(&dst)
.map(|(nhs, _)| nhs.as_slice())
.unwrap_or_default(),
_ => Default::default(),
}
}
fn get_cost(&self, dst: RouterId) -> Option<LinkWeight> {
self.get_table().get(&dst).map(|(_, w)| *w)
}
fn get_nhs_cost(&self, dst: RouterId) -> Option<(&[RouterId], LinkWeight)> {
self.get_table()
.get(&dst)
.map(|(nhs, w)| (nhs.as_slice(), *w))
}
fn is_reachable(&self, dst: RouterId) -> bool {
self.get_table()
.get(&dst)
.map(|(_, w)| w)
.or_else(|| self.get_neighbors().get(&dst))
.map(|w| w.is_finite())
.unwrap_or(false)
}
fn is_neighbor(&self, dst: RouterId) -> bool {
self.get_neighbors().contains_key(&dst)
}
fn is_waiting_for_timeout(&self) -> bool;
fn trigger_timeout<P: Prefix, T: Default>(
&mut self,
) -> Result<(bool, Vec<Event<P, T>>), DeviceError>;
fn remove_unreachable_lsas(&mut self) {}
fn fmt<P, Q, Ospf>(&self, net: &Network<P, Q, Ospf>) -> String
where
P: Prefix,
Ospf: OspfImpl<Process = Self>,
{
let table = self.get_table();
format!(
"OspfRib: {{\n{}\n}}",
table
.iter()
.map(|(r, (nhs, cost))| format!(
" {}: {} (cost: {cost})",
r.fmt(net),
nhs.iter().map(|r| r.fmt(net)).join(" || ")
))
.map(|s| if s.is_empty() { "XX".to_string() } else { s })
.join("\n")
)
}
}
impl<P: Prefix, Q: crate::event::EventQueue<P>, Ospf: OspfImpl> Network<P, Q, Ospf> {
pub(crate) fn swap_ospf<F, Ospf2>(
mut self,
mut convert: F,
) -> Result<Network<P, Q, Ospf2>, NetworkError>
where
Ospf2: OspfImpl,
F: FnMut(
Ospf::Coordinator,
HashMap<RouterId, Ospf::Process>,
&mut Ospf2::Coordinator,
HashMap<RouterId, &mut Ospf2::Process>,
) -> Result<(), NetworkError>,
{
crate::interactive::InteractiveNetwork::simulate(&mut self)?;
let mut old_processes: HashMap<ASN, HashMap<_, _>> = HashMap::new();
let mut domain_routers: HashMap<ASN, HashMap<_, _>> = HashMap::new();
for (router_id, r) in self.routers {
let asn = r.asn();
let (new_r, old_p) = r.swap_ospf();
old_processes
.entry(asn)
.or_default()
.insert(router_id, old_p);
domain_routers
.entry(asn)
.or_default()
.insert(router_id, new_r);
}
let mut domains = BTreeMap::new();
for (asn, domain) in self.ospf.domains.into_iter() {
let (mut ospf_domain, old_coordinator) = domain.swap_coordinator();
let Some(old_processes) = old_processes.remove(&asn) else {
for (r, neighbors) in ospf_domain.external_links.clone() {
for n in neighbors {
let routers = domain_routers
.get_mut(&asn)
.unwrap()
.iter_mut()
.map(|(r, d)| (*r, d))
.collect::<BTreeMap<_, _>>();
let events = OspfCoordinator::update::<P, ()>(
&mut ospf_domain.coordinator,
NeighborhoodChange::AddExternalNetwork { int: r, ext: n },
routers,
&ospf_domain.links,
&ospf_domain.external_links,
)?;
assert!(
events.is_empty(),
"External routers will not trigger any OSPF events"
);
}
}
let links = ospf_domain.links.clone();
for (&a, neighbors) in &links {
for (&b, &(weight, area)) in neighbors {
let routers = domain_routers
.get_mut(&asn)
.unwrap()
.iter_mut()
.map(|(r, d)| (*r, d))
.collect::<BTreeMap<_, _>>();
let weight_rev = links[&b][&a].0;
let events = OspfCoordinator::update::<P, ()>(
&mut ospf_domain.coordinator,
NeighborhoodChange::AddLink {
a,
b,
area,
weight: (weight, weight_rev),
},
routers,
&ospf_domain.links,
&ospf_domain.external_links,
)?;
assert!(
events.is_empty(),
"External routers will not trigger any OSPF events"
);
}
}
domains.insert(asn, ospf_domain);
continue; };
let new_processes = domain_routers
.get_mut(&asn)
.unwrap()
.values_mut()
.map(|r| (r.router_id(), &mut r.ospf))
.collect();
convert(
old_coordinator,
old_processes,
&mut ospf_domain.coordinator,
new_processes,
)?;
domains.insert(asn, ospf_domain);
}
let ospf = OspfNetwork {
domains,
routers: self.ospf.routers,
};
let routers = domain_routers.into_values().flatten().collect();
Ok(Network {
net: self.net,
ospf,
routers,
bgp_sessions: self.bgp_sessions,
known_prefixes: self.known_prefixes,
stop_after: self.stop_after,
queue: self.queue,
skip_queue: self.skip_queue,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IgpTarget {
Neighbor(RouterId),
Ospf(RouterId),
Drop,
}
impl From<RouterId> for IgpTarget {
fn from(value: RouterId) -> Self {
Self::Ospf(value)
}
}
impl<'n, P: Prefix, Q, Ospf: OspfImpl> NetworkFormatter<'n, P, Q, Ospf> for IgpTarget {
fn fmt(&self, net: &'n crate::network::Network<P, Q, Ospf>) -> String {
match self {
IgpTarget::Neighbor(r) => format!("{} (neighbor)", r.fmt(net)),
IgpTarget::Ospf(r) => r.fmt(net).to_string(),
IgpTarget::Drop => "drop".to_string(),
}
}
}