use std::{
cmp::Ordering,
collections::{hash_map::Entry, BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet},
};
use itertools::{EitherOrBoth, Itertools};
use maplit::btreemap;
use ordered_float::NotNan;
use serde::{Deserialize, Serialize};
use serde_with::{As, Same};
use crate::{
ospf::{LinkWeight, OspfArea},
types::RouterId,
};
use super::{LinkType, Lsa, LsaData, LsaHeader, LsaKey, LsaType, RouterLsaLink, MAX_AGE, MAX_SEQ};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OspfRib {
router_id: RouterId,
#[serde(with = "As::<Vec<(Same, Same)>>")]
pub(super) areas: BTreeMap<OspfArea, AreaDataStructure>,
#[serde(with = "As::<Vec<(Same, Same)>>")]
pub(super) external_lsas: HashMap<LsaKey, Lsa>,
recompute_as_external: HashSet<RouterId>,
#[serde(with = "As::<Vec<(Same, Same)>>")]
pub(super) rib: HashMap<RouterId, OspfRibEntry>,
}
impl OspfRib {
pub(super) fn new(router_id: RouterId) -> Self {
Self {
router_id,
areas: Default::default(),
rib: [(router_id, OspfRibEntry::empty(router_id))]
.into_iter()
.collect(),
external_lsas: Default::default(),
recompute_as_external: HashSet::new(),
}
}
pub fn num_areas(&self) -> usize {
self.areas.len()
}
pub fn areas(&self) -> impl Iterator<Item = OspfArea> + '_ {
self.areas.keys().copied()
}
pub fn in_area(&self, area: OspfArea) -> bool {
self.areas.contains_key(&area)
}
pub(super) fn insert_area(&mut self, area: OspfArea) {
self.get_or_insert_area(area);
}
fn get_or_insert_area(&mut self, area: OspfArea) -> &mut AreaDataStructure {
self.areas
.entry(area)
.or_insert_with(|| AreaDataStructure::new(self.router_id, area))
}
pub(super) fn remove_area(&mut self, area: OspfArea) {
if let Some(area) = self.areas.remove(&area) {
debug_assert!(area.get_router_lsa(self.router_id).unwrap().1.is_empty())
}
}
pub fn get_lsa_list(&self, area: Option<OspfArea>) -> Option<&HashMap<LsaKey, Lsa>> {
if let Some(area) = area {
self.areas.get(&area).map(|ds| &ds.lsa_list)
} else {
Some(&self.external_lsas)
}
}
pub fn get_spt(&self, area: OspfArea) -> Option<&HashMap<RouterId, SptNode>> {
self.areas.get(&area).map(|ds| &ds.spt)
}
pub fn get_rib_entry(&self, target: RouterId) -> Option<OspfRibEntry> {
OspfRibEntry::from_paths(
self.areas
.iter()
.filter_map(|(a, ds)| ds.spt.get(&target).map(|n| (*a, n))),
)
}
pub fn get_rib(&self) -> &HashMap<RouterId, OspfRibEntry> {
&self.rib
}
pub(crate) fn get_summary_list(&self, area: OspfArea) -> HashMap<LsaKey, Lsa> {
self.areas
.get(&area)
.into_iter()
.flat_map(|ds| ds.lsa_list.iter())
.chain(self.external_lsas.iter())
.filter(|(_, lsa)| !lsa.is_max_age())
.map(|(key, lsa)| (*key, lsa.clone()))
.collect()
}
pub fn get_lsa(&self, key: impl Into<LsaKey>, area: Option<OspfArea>) -> Option<&Lsa> {
let key = key.into();
if key.is_external() {
self.external_lsas.get(&key)
} else {
area.and_then(|area| self.areas.get(&area))
.and_then(|ds| ds.lsa_list.get(&key))
}
}
pub fn get_router_lsa(
&self,
router_id: RouterId,
area: OspfArea,
) -> Option<(&LsaHeader, &Vec<RouterLsaLink>)> {
self.areas
.get(&area)
.and_then(|ds| ds.get_router_lsa(router_id))
}
pub(super) fn insert(&mut self, lsa: Lsa, area: Option<OspfArea>) {
if lsa.is_external() {
let target = lsa.target();
self.external_lsas.insert(lsa.key(), lsa);
self.recompute_as_external.insert(target);
} else {
self.get_or_insert_area(area.unwrap()).insert(lsa);
}
}
pub(super) fn remove(&mut self, key: impl Into<LsaKey>, area: Option<OspfArea>) {
let key = key.into();
if key.is_external() {
let target = key.target();
self.external_lsas.remove(&key);
self.recompute_as_external.insert(target);
} else {
self.get_or_insert_area(area.unwrap()).remove(key);
}
}
pub(super) fn set_max_seq_and_age(
&mut self,
key: impl Into<LsaKey>,
area: Option<OspfArea>,
) -> Option<&Lsa> {
let key = key.into();
if key.is_external() {
let target = key.target.unwrap();
if let Some(e) = self.external_lsas.get_mut(&key) {
e.header.seq = MAX_SEQ;
e.header.age = MAX_AGE;
}
self.recompute_as_external.insert(target);
self.external_lsas.get(&key)
} else {
self.get_or_insert_area(area.unwrap())
.set_max_seq_and_age(key)
}
}
pub(super) fn set_seq(
&mut self,
key: impl Into<LsaKey>,
area: Option<OspfArea>,
target: u32,
) -> Option<&Lsa> {
let key = key.into();
if key.is_external() {
if let Some(e) = self.external_lsas.get_mut(&key) {
e.header.seq = target;
}
self.external_lsas.get(&key)
} else {
self.get_or_insert_area(area.unwrap()).set_seq(key, target)
}
}
pub(super) fn remove_unreachable_lsas(&mut self) {
self.areas
.values_mut()
.for_each(|ds| ds.remove_unreachable_lsas());
}
pub(super) fn update_local_lsa(
&mut self,
neighbor: RouterId,
area: OspfArea,
weight: Option<LinkWeight>,
) -> Option<(&Lsa, Option<Lsa>)> {
self.get_or_insert_area(area)
.update_local_lsa(neighbor, weight)
}
pub(super) fn update_external_lsa(
&mut self,
neighbor: RouterId,
weight: Option<LinkWeight>,
) -> Option<(&Lsa, Option<Option<Lsa>>)> {
let router = self.router_id;
let target = Some(neighbor);
let key = LsaKey {
lsa_type: LsaType::External,
router,
target,
};
let Some(old_lsa) = self.external_lsas.get(&key) else {
let Some(weight) = weight else {
return None;
};
let data = LsaData::External(NotNan::new(weight).unwrap());
let lsa = Lsa {
header: LsaHeader {
lsa_type: key.lsa_type,
router,
target,
seq: 0,
age: 0,
},
data,
};
self.insert(lsa, None);
return Some((self.external_lsas.get(&key).unwrap(), None));
};
let Some(weight) = weight else {
let mut lsa = old_lsa.clone();
lsa.header.age = MAX_AGE;
self.insert(lsa, None);
return Some((self.external_lsas.get(&key).unwrap(), Some(None)));
};
let data = LsaData::External(NotNan::new(weight).unwrap());
if old_lsa.data == data {
return None;
}
if old_lsa.header.seq < MAX_SEQ - 1 {
let lsa = Lsa {
header: LsaHeader {
seq: old_lsa.header.seq + 1,
..old_lsa.header
},
data,
};
self.insert(lsa, None);
return Some((self.external_lsas.get(&key).unwrap(), None));
}
let old_lsa = self.set_max_seq_and_age(key, None).unwrap();
let new_lsa = Lsa {
header: LsaHeader {
lsa_type: LsaType::External,
router,
target,
seq: 0,
age: 0,
},
data,
};
Some((old_lsa, Some(Some(new_lsa))))
}
#[allow(clippy::type_complexity)]
pub(super) fn update_summary_lsas(
&mut self,
) -> BTreeMap<OspfArea, (Vec<Lsa>, Vec<(LsaKey, Option<Lsa>)>)> {
let mut result = BTreeMap::new();
if self.areas.contains_key(&OspfArea::BACKBONE) && self.areas.len() > 1 {
for (area, area_ds) in self.areas.iter_mut() {
let (redistribute, track_max_age) = area_ds.redistribute_into(&self.rib);
result.insert(*area, (redistribute, track_max_age));
}
} else {
for (area, area_ds) in self.areas.iter_mut() {
let (redistribute, track_max_age) =
area_ds.redistribute_from_paths(Default::default());
result.insert(*area, (redistribute, track_max_age));
}
}
result
}
pub(super) fn update_routing_table(&mut self) -> bool {
let mut changed = false;
for adv in self.areas.values_mut() {
changed |= adv.update_spt();
}
changed |= self.areas.is_empty();
if changed {
let mut rib: HashMap<RouterId, OspfRibEntry> =
[(self.router_id, OspfRibEntry::empty(self.router_id))]
.into_iter()
.collect();
for (area, area_ds) in self.areas.iter() {
for path in area_ds.spt.values() {
match rib.entry(path.router_id) {
Entry::Occupied(mut e) => {
e.get_mut().update(path, *area);
}
Entry::Vacant(e) => {
e.insert(OspfRibEntry::new(path, *area));
}
}
}
}
self.rib = rib;
}
if changed || !self.recompute_as_external.is_empty() {
self.compute_as_external_routes(changed);
self.recompute_as_external.clear();
changed = true;
}
changed
}
fn compute_as_external_routes(&mut self, all: bool) {
if !all {
self.rib
.retain(|k, _| !self.recompute_as_external.contains(k));
}
for lsa in self.external_lsas.values() {
if !(all || self.recompute_as_external.contains(&lsa.target())) {
continue;
}
let Some(path) = compute_as_external_route(self.router_id, lsa, &self.rib) else {
continue;
};
match self.rib.entry(path.router_id) {
Entry::Vacant(e) => {
e.insert(path);
}
Entry::Occupied(mut e) => {
*e.get_mut() += path;
}
}
}
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct OspfRibEntry {
pub router_id: RouterId,
pub fibs: BTreeSet<RouterId>,
pub cost: NotNan<LinkWeight>,
pub inter_area: bool,
#[serde(with = "As::<Vec<(Same, Same)>>")]
pub keys: BTreeMap<Option<OspfArea>, LsaKey>,
}
impl std::fmt::Debug for OspfRibEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"OspfRibEntry({} --> {}, cost {} {} from {})",
self.router_id.index(),
if self.fibs.is_empty() {
"XX".to_string()
} else {
self.fibs.iter().map(|x| x.index()).join(" || ")
},
self.cost,
if self.inter_area { "R" } else { "I" },
self.keys
.keys()
.map(|x| x.map(|x| x.to_string()).unwrap_or("External".to_string()))
.join(" & ")
)
}
}
impl PartialEq for OspfRibEntry {
fn eq(&self, other: &Self) -> bool {
(
self.router_id,
&self.fibs,
self.cost,
self.inter_area,
self.keys.keys().collect::<Vec<_>>(),
) == (
other.router_id,
&other.fibs,
other.cost,
other.inter_area,
other.keys.keys().collect::<Vec<_>>(),
)
}
}
impl OspfRibEntry {
pub(crate) fn empty(router_id: RouterId) -> Self {
Self {
router_id,
fibs: Default::default(),
cost: NotNan::default(),
inter_area: false,
keys: Default::default(),
}
}
pub(crate) fn new(path: &SptNode, area: OspfArea) -> Self {
Self {
router_id: path.router_id,
fibs: path.fibs.clone(),
cost: path.cost,
inter_area: path.inter_area,
keys: btreemap! {Some(area) => path.key},
}
}
fn from_paths<'a>(paths: impl IntoIterator<Item = (OspfArea, &'a SptNode)>) -> Option<Self> {
let mut paths = paths.into_iter();
let (first_area, first_path) = paths.next()?;
let mut rib = Self::new(first_path, first_area);
for (area, path) in paths {
rib.update(path, area)
}
Some(rib)
}
pub(crate) fn update(&mut self, path: &SptNode, area: OspfArea) {
match (self.inter_area, self.cost).cmp(&(path.inter_area, path.cost)) {
Ordering::Less => {}
Ordering::Equal => {
self.fibs.extend(path.fibs.iter().copied());
self.keys.insert(Some(area), path.key);
}
Ordering::Greater => {
self.fibs.clone_from(&path.fibs);
self.cost = path.cost;
self.inter_area = path.inter_area;
self.keys = btreemap! {Some(area) => path.key};
}
}
}
}
impl std::ops::AddAssign for OspfRibEntry {
fn add_assign(&mut self, rhs: Self) {
match (self.inter_area, self.cost).cmp(&(rhs.inter_area, rhs.cost)) {
Ordering::Less => {}
Ordering::Equal => {
self.fibs.extend(rhs.fibs);
self.keys.extend(rhs.keys);
}
Ordering::Greater => {
*self = rhs;
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct AreaDataStructure {
router_id: RouterId,
area: OspfArea,
#[serde(with = "As::<Vec<(Same, Same)>>")]
lsa_list: HashMap<LsaKey, Lsa>,
transit_capability: bool,
#[serde(with = "As::<Vec<(Same, Same)>>")]
spt: HashMap<RouterId, SptNode>,
recompute_intra_area: bool,
recompute_inter_area: BTreeSet<RouterId>,
#[serde(with = "As::<Vec<(Same, Same)>>")]
redistributed_paths: BTreeMap<RouterId, NotNan<LinkWeight>>,
}
impl PartialEq for AreaDataStructure {
fn eq(&self, other: &Self) -> bool {
(self.router_id, self.area, &self.lsa_list) == (other.router_id, other.area, &self.lsa_list)
}
}
impl AreaDataStructure {
fn new(router_id: RouterId, area: OspfArea) -> Self {
let mut s = Self {
router_id,
area,
lsa_list: HashMap::new(),
transit_capability: Default::default(),
spt: HashMap::from_iter([(router_id, SptNode::new(router_id))]),
redistributed_paths: Default::default(),
recompute_intra_area: false,
recompute_inter_area: Default::default(),
};
let self_lsa = Lsa {
header: LsaHeader {
lsa_type: LsaType::Router,
router: router_id,
target: None,
seq: 0,
age: 0,
},
data: LsaData::Router(Vec::new()),
};
s.lsa_list.insert(self_lsa.key(), self_lsa);
s
}
pub(super) fn from_raw(
router_id: RouterId,
area: OspfArea,
lsa_list: HashMap<LsaKey, Lsa>,
spt: HashMap<RouterId, SptNode>,
redistributed_paths: BTreeMap<RouterId, NotNan<LinkWeight>>,
) -> Self {
let mut s = Self {
router_id,
area,
lsa_list,
transit_capability: Default::default(),
spt,
redistributed_paths,
recompute_intra_area: false,
recompute_inter_area: Default::default(),
};
if s.lsa_list.is_empty() {
let self_lsa = Lsa {
header: LsaHeader {
lsa_type: LsaType::Router,
router: router_id,
target: None,
seq: 0,
age: 0,
},
data: LsaData::Router(Vec::new()),
};
s.lsa_list.insert(self_lsa.key(), self_lsa);
}
if s.spt.is_empty() {
s.spt = HashMap::from_iter([(router_id, SptNode::new(router_id))]);
}
s
}
#[allow(clippy::type_complexity)]
pub(super) fn into_raw(
self,
) -> (
HashMap<LsaKey, Lsa>,
HashMap<RouterId, SptNode>,
BTreeMap<RouterId, NotNan<LinkWeight>>,
) {
(self.lsa_list, self.spt, self.redistributed_paths)
}
fn get_router_lsa(&self, router_id: RouterId) -> Option<(&LsaHeader, &Vec<RouterLsaLink>)> {
let key = LsaKey::router(router_id);
self.lsa_list.get(&key).and_then(|x| match &x.data {
LsaData::Router(r) => Some((&x.header, r)),
_ => None,
})
}
fn update_local_lsa(
&mut self,
neighbor: RouterId,
weight: Option<LinkWeight>,
) -> Option<(&Lsa, Option<Lsa>)> {
let router_id = self.router_id;
let (header, links) = self.get_router_lsa(router_id).unwrap();
let key = header.key();
let header = *header;
let mut links = links.clone();
if let Some(new_weight) = weight {
if let Some(pos) = links.iter().position(|l| l.target == neighbor) {
if links[pos].weight == new_weight {
return None;
}
links[pos].weight = NotNan::new(new_weight).unwrap();
} else {
links.push(RouterLsaLink {
link_type: LinkType::PointToPoint,
target: neighbor,
weight: NotNan::new(new_weight).unwrap(),
})
}
} else if let Some(pos) = links.iter().position(|l| l.target == neighbor) {
links.remove(pos);
} else {
return None;
}
let mut lsa = Lsa {
header,
data: LsaData::Router(links),
};
if lsa.header.seq < MAX_SEQ - 1 {
lsa.header.seq += 1;
self.insert(lsa);
Some((self.lsa_list.get(&key).unwrap(), None))
} else {
let old_lsa = self.set_max_seq_and_age(lsa.key()).unwrap();
lsa.header.seq = 0;
lsa.header.age = 0;
Some((old_lsa, Some(lsa)))
}
}
fn set_max_seq_and_age(&mut self, key: impl Into<LsaKey>) -> Option<&Lsa> {
let key = key.into();
if let Some(e) = self.lsa_list.get_mut(&key) {
e.header.seq = MAX_SEQ;
e.header.age = MAX_AGE;
}
if key.is_router() {
self.recompute_intra_area = true;
} else if key.is_summary() {
self.recompute_inter_area.insert(key.target());
} else if key.is_external() {
unreachable!()
}
self.lsa_list.get(&key)
}
fn set_seq(&mut self, key: impl Into<LsaKey>, target: u32) -> Option<&Lsa> {
let key = key.into();
if let Some(e) = self.lsa_list.get_mut(&key) {
e.header.seq = target;
}
self.lsa_list.get(&key)
}
fn insert(&mut self, lsa: Lsa) {
let key = lsa.key();
self.lsa_list.insert(key, lsa);
if key.is_router() {
self.recompute_intra_area = true;
} else if key.is_summary() {
self.recompute_inter_area.insert(key.target());
} else if key.is_external() {
unreachable!()
}
}
fn remove(&mut self, key: impl Into<LsaKey>) {
let key = key.into();
self.lsa_list.remove(&key);
if key.is_router() {
self.recompute_intra_area = true;
} else if key.is_summary() {
self.recompute_inter_area.insert(key.target());
} else if key.is_external() {
unreachable!()
}
}
fn update_spt(&mut self) -> bool {
let mut modified = false;
if self.recompute_intra_area {
self.spt.clear();
self.spt = compute_intra_area_routes(self.router_id, &self.lsa_list);
self.recompute_intra_area = false;
modified = true;
}
if modified || !self.recompute_inter_area.is_empty() {
self.compute_inter_area_routes(modified);
self.recompute_inter_area.clear();
modified = true;
}
modified
}
fn compute_inter_area_routes(&mut self, recompute_all: bool) {
let mut new_paths: HashMap<RouterId, SptNode> = HashMap::new();
if !recompute_all {
for r in self.recompute_inter_area.iter() {
if let Entry::Occupied(e) = self.spt.entry(*r) {
if e.get().inter_area {
e.remove();
}
}
}
}
for lsa in self.lsa_list.values() {
let target = lsa.target();
if !(recompute_all || self.recompute_inter_area.contains(&target)) {
continue;
}
let Some(path) = compute_inter_area_route(self.router_id, lsa, &self.spt) else {
continue;
};
match new_paths.entry(target) {
Entry::Occupied(mut e) => {
*e.get_mut() += path;
}
Entry::Vacant(e) => {
e.insert(path);
}
}
}
self.spt.extend(new_paths);
}
fn redistribute_into(
&mut self,
rib: &HashMap<RouterId, OspfRibEntry>,
) -> (Vec<Lsa>, Vec<(LsaKey, Option<Lsa>)>) {
let neighbors: BTreeSet<RouterId> = self
.get_router_lsa(self.router_id)
.into_iter()
.flat_map(|(_, l)| l)
.map(|l| l.target)
.collect();
if neighbors.is_empty() {
return (Vec::new(), Vec::new());
}
let redistributed_paths = rib
.values()
.filter(|path| is_path_redistributed(self.router_id, self.area, &neighbors, path))
.map(|path| (path.router_id, path.cost))
.collect();
self.redistribute_from_paths(redistributed_paths)
}
fn redistribute_from_paths(
&mut self,
redistributed_paths: BTreeMap<RouterId, NotNan<LinkWeight>>,
) -> (Vec<Lsa>, Vec<(LsaKey, Option<Lsa>)>) {
let mut redistribute = Vec::new();
let mut track_max_age = Vec::new();
for m in redistributed_paths
.iter()
.merge_join_by(self.redistributed_paths.iter(), |(a, _), (b, _)| a.cmp(b))
{
match m {
EitherOrBoth::Both((&r, &new), (_, &old)) if old != new => {
let key = LsaKey::summary(self.router_id, r);
let lsa = self.lsa_list.get_mut(&key).expect("Key must exist");
if lsa.is_max_age() {
let mut new_lsa = lsa.clone();
new_lsa.data = LsaData::Summary(new);
new_lsa.header.seq = 0;
new_lsa.header.age = 0;
track_max_age.push((key, Some(new_lsa)));
} else if lsa.header.seq >= MAX_SEQ - 1 {
lsa.header.seq = MAX_SEQ;
lsa.header.age = MAX_AGE;
redistribute.push(lsa.clone());
let mut new_lsa = lsa.clone();
new_lsa.data = LsaData::Summary(new);
new_lsa.header.seq = 0;
new_lsa.header.age = 0;
track_max_age.push((key, Some(new_lsa)));
} else {
lsa.header.seq += 1;
lsa.data = LsaData::Summary(new);
redistribute.push(lsa.clone());
}
}
EitherOrBoth::Both(_, _) => {
}
EitherOrBoth::Left((&r, &new)) => {
let lsa = Lsa {
header: LsaHeader {
lsa_type: LsaType::Summary,
router: self.router_id,
target: Some(r),
seq: 0,
age: 0,
},
data: LsaData::Summary(new),
};
let key = lsa.key();
match self.lsa_list.entry(key) {
Entry::Occupied(e) => {
debug_assert!(e.get().is_max_age());
track_max_age.push((key, Some(lsa)));
}
Entry::Vacant(e) => {
e.insert(lsa.clone());
redistribute.push(lsa);
}
}
}
EitherOrBoth::Right((&r, &old)) => {
let key = LsaKey::summary(self.router_id, r);
let seq = self
.lsa_list
.get(&key)
.map(|x| x.header.seq)
.unwrap_or(MAX_SEQ);
let lsa = Lsa {
header: LsaHeader {
lsa_type: key.lsa_type,
router: key.router,
target: key.target,
seq,
age: MAX_AGE,
},
data: LsaData::Summary(old),
};
self.lsa_list.insert(key, lsa.clone());
redistribute.push(lsa);
track_max_age.push((key, None));
}
}
}
self.redistributed_paths = redistributed_paths;
(redistribute, track_max_age)
}
fn remove_unreachable_lsas(&mut self) {
self.lsa_list.retain(|k, _| {
k.router == self.router_id
|| self
.spt
.get(&k.router)
.map(|path| !path.inter_area)
.unwrap_or(false)
})
}
}
pub(crate) fn compute_intra_area_routes(
root: RouterId,
lsa_list: &HashMap<LsaKey, Lsa>,
) -> HashMap<RouterId, SptNode> {
let get_router_lsa = |r| {
let key = LsaKey::router(r);
lsa_list
.get(&key)
.and_then(|lsa| lsa.data.router().map(|links| (&lsa.header, links)))
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct HeapEntry {
node: RouterId,
parent: RouterId,
cost: NotNan<LinkWeight>,
}
impl PartialOrd for HeapEntry {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for HeapEntry {
fn cmp(&self, other: &Self) -> Ordering {
other.cost.cmp(&self.cost)
}
}
let mut spt = HashMap::new();
let mut visit_next = BinaryHeap::new();
spt.insert(root, SptNode::new(root));
visit_next.extend(
get_router_lsa(root)
.into_iter()
.filter(|(h, _)| !h.is_max_age())
.flat_map(|(_, l)| l)
.filter(|l| l.is_p2p())
.map(|l| HeapEntry {
node: l.target,
parent: root,
cost: l.weight,
}),
);
while let Some(HeapEntry { node, parent, cost }) = visit_next.pop() {
let mut from_fibs = spt.get(&parent).expect("not yet visited").fibs.clone();
if from_fibs.is_empty() {
debug_assert!(parent == root);
from_fibs.insert(node);
}
match spt.entry(node) {
Entry::Occupied(mut e) => {
let e = e.get_mut();
match cost.cmp(&e.cost) {
Ordering::Less => unreachable!("Negative link weights are not allowed!"),
Ordering::Equal => e.fibs.extend(from_fibs),
Ordering::Greater => {}
}
}
Entry::Vacant(e) => {
e.insert(SptNode::from(node, cost, from_fibs));
visit_next.extend(
get_router_lsa(node)
.into_iter()
.filter(|(h, _)| !h.is_max_age())
.flat_map(|(_, l)| l)
.filter(|l| l.is_p2p())
.map(|l| HeapEntry {
node: l.target,
parent: node,
cost: cost + l.weight,
}),
);
}
}
}
spt
}
pub(crate) fn compute_inter_area_route(
router_id: RouterId,
lsa: &Lsa,
spt: &HashMap<RouterId, SptNode>,
) -> Option<SptNode> {
let weight = lsa.data.summary()?;
let target = lsa.target();
if lsa.header.is_max_age() {
return None;
}
if lsa.header.router == router_id {
return None;
}
let adv_node = spt.get(&lsa.header.router)?;
if let Some(current_path) = spt.get(&target) {
if !current_path.inter_area {
return None;
}
}
let path = SptNode {
router_id: target,
key: lsa.key(),
fibs: adv_node.fibs.clone(),
cost: adv_node.cost + weight,
inter_area: true,
};
Some(path)
}
pub(crate) fn compute_as_external_route(
router_id: RouterId,
lsa: &Lsa,
rib: &HashMap<RouterId, OspfRibEntry>,
) -> Option<OspfRibEntry> {
let weight = lsa.data.external()?;
let target = lsa.target();
if lsa.header.is_max_age() {
return None;
}
let adv_rib = rib.get(&lsa.header.router)?;
let mut path = OspfRibEntry {
router_id: target,
keys: btreemap! {None => lsa.key()},
fibs: adv_rib.fibs.clone(),
cost: adv_rib.cost + weight,
inter_area: adv_rib.inter_area,
};
if lsa.header.router == router_id {
debug_assert!(path.fibs.is_empty());
path.fibs.insert(target);
}
Some(path)
}
pub(crate) fn is_path_redistributed(
router_id: RouterId,
area: OspfArea,
neighbors: &BTreeSet<RouterId>,
path: &OspfRibEntry,
) -> bool {
if path.router_id == router_id {
return false;
}
if path.keys.values().any(|key| key.is_external()) {
return false;
}
if path.keys.contains_key(&Some(area)) {
return false;
}
if path.fibs.iter().any(|nh| neighbors.contains(nh)) {
return false;
}
if path.cost.is_infinite() {
return false;
}
if area.is_backbone() && path.inter_area {
return false;
}
true
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SptNode {
pub router_id: RouterId,
pub key: LsaKey,
pub fibs: BTreeSet<RouterId>,
pub cost: NotNan<LinkWeight>,
pub inter_area: bool,
}
impl std::ops::AddAssign for SptNode {
fn add_assign(&mut self, rhs: Self) {
match (self.inter_area, self.cost).cmp(&(rhs.inter_area, rhs.cost)) {
Ordering::Less => {
}
Ordering::Equal => {
self.fibs.extend(rhs.fibs);
}
Ordering::Greater => {
*self = rhs;
}
}
}
}
impl SptNode {
pub fn new(router_id: RouterId) -> Self {
Self {
router_id,
key: LsaKey::router(router_id),
fibs: Default::default(),
cost: Default::default(),
inter_area: false,
}
}
pub fn from(router_id: RouterId, cost: NotNan<LinkWeight>, fibs: BTreeSet<RouterId>) -> Self {
Self {
router_id,
key: LsaKey::router(router_id),
fibs,
cost,
inter_area: false,
}
}
}
impl<'n, P: crate::types::Prefix, Q> crate::formatter::NetworkFormatter<'n, P, Q, super::LocalOspf>
for OspfRib
{
fn fmt(&self, net: &'n crate::network::Network<P, Q, super::LocalOspf>) -> String {
let external_lsas = format!(
"external LSAs: {{\n {}\n}}",
self.external_lsas
.iter()
.sorted_by_key(|(k, _)| *k)
.map(|(_, lsa)| lsa.fmt(net))
.join("\n ")
);
self.areas
.values()
.map(|ds| ds.fmt(net))
.chain(std::iter::once(external_lsas))
.join("\n")
}
}
impl<'n, P: crate::types::Prefix, Q> crate::formatter::NetworkFormatter<'n, P, Q, super::LocalOspf>
for AreaDataStructure
{
fn fmt(&self, net: &'n crate::network::Network<P, Q, super::LocalOspf>) -> String {
format!(
"{} LSAs: {{\n {}\n}}",
self.area,
self.lsa_list
.iter()
.sorted_by_key(|(k, _)| *k)
.map(|(_, lsa)| lsa.fmt(net))
.join("\n ")
)
}
}