use crate::{
bgp::{BgpRoute, BgpSessionType, BgpState, BgpStateRef, Community},
config::{NetworkConfig, RouteMapEdit},
event::{BasicEventQueue, Event, EventQueue},
forwarding_state::ForwardingState,
interactive::InteractiveNetwork,
ospf::{
global::GlobalOspf, LinkWeight, LocalOspf, OspfArea, OspfImpl, OspfNetwork, OspfProcess,
},
route_map::{RouteMap, RouteMapDirection},
router::{Router, StaticRoute},
types::{
IntoIpv4Prefix, Ipv4Prefix, NetworkError, NetworkErrorOption, PhysicalNetwork, Prefix,
PrefixSet, RouterId, SimplePrefix, ASN,
},
};
#[cfg(test)]
use crate::formatter::NetworkFormatter;
use log::*;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
iter::FusedIterator,
};
static DEFAULT_STOP_AFTER: usize = 1_000_000;
pub const DEFAULT_INTERNAL_ASN: ASN = ASN(65500);
#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
#[serde(bound(
serialize = "Q: serde::Serialize",
deserialize = "P: for<'a> serde::Deserialize<'a>, Q: for<'a> serde::Deserialize<'a>"
))]
pub struct Network<
P: Prefix = SimplePrefix,
Q = BasicEventQueue<SimplePrefix>,
Ospf: OspfImpl = GlobalOspf,
> {
pub(crate) net: PhysicalNetwork,
pub(crate) ospf: OspfNetwork<Ospf::Coordinator>,
pub(crate) routers: BTreeMap<RouterId, Router<P, Ospf::Process>>,
#[serde_as(as = "Vec<(_, _)>")]
pub(crate) bgp_sessions: BTreeMap<(RouterId, RouterId), Option<bool>>,
pub(crate) known_prefixes: P::Set,
pub(crate) stop_after: Option<usize>,
pub(crate) queue: Q,
pub(crate) skip_queue: bool,
}
impl<P: Prefix, Q: Clone, Ospf: OspfImpl> Clone for Network<P, Q, Ospf> {
fn clone(&self) -> Self {
log::debug!("Cloning the network!");
Self {
net: self.net.clone(),
ospf: self.ospf.clone(),
routers: self.routers.clone(),
bgp_sessions: self.bgp_sessions.clone(),
known_prefixes: self.known_prefixes.clone(),
stop_after: self.stop_after,
queue: self.queue.clone(),
skip_queue: self.skip_queue,
}
}
}
impl<P: Prefix, Ospf: OspfImpl> Default for Network<P, BasicEventQueue<P>, Ospf> {
fn default() -> Self {
Self::new(BasicEventQueue::new())
}
}
impl<P: Prefix, Q, Ospf: OspfImpl> Network<P, Q, Ospf> {
pub fn new(queue: Q) -> Self {
Self {
net: PhysicalNetwork::default(),
ospf: OspfNetwork::default(),
routers: BTreeMap::new(),
bgp_sessions: BTreeMap::new(),
known_prefixes: Default::default(),
stop_after: Some(DEFAULT_STOP_AFTER),
queue,
skip_queue: false,
}
}
pub fn add_router(&mut self, name: impl Into<String>, asn: impl Into<ASN>) -> RouterId {
let router_id = self._prepare_node();
self._add_router_with_router_id(router_id, name, asn.into());
router_id
}
pub fn set_asn(&mut self, router_id: RouterId, asn: impl Into<ASN>) -> Result<ASN, NetworkError>
where
Q: EventQueue<P>,
{
let old_skip = self.skip_queue;
let old_stop_after = self.stop_after;
self.skip_queue = false;
self.stop_after = Some(10_000_000);
self.do_queue_maybe_skip()?;
let sessions = self
.get_router(router_id)?
.bgp
.get_sessions()
.iter()
.filter_map(|(n, _)| {
match (
self.bgp_sessions.get(&(router_id, *n)).copied().flatten(),
self.bgp_sessions.get(&(*n, router_id)).copied().flatten(),
) {
(Some(_), None) | (None, Some(_)) | (Some(true), Some(true)) => {
unreachable!("Inconsistent BGP session state.")
}
(Some(true), Some(false)) => Some((router_id, *n, true)),
(Some(false), Some(true)) => Some((*n, router_id, true)),
(Some(false), Some(false)) => Some((router_id, *n, false)),
(None, None) => None,
}
})
.collect::<Vec<_>>();
self.set_bgp_session_from(sessions.iter().map(|(a, b, _)| (*a, *b, None)))?;
let links = self
.ospf
.neighbors(router_id)
.map(|e| (e.src(), e.dst()))
.collect::<Vec<_>>();
self.ospf
.remove_router::<P, ()>(router_id, &mut self.routers)?;
let new_asn = asn.into();
let old_asn = self
.routers
.get_mut(&router_id)
.expect("already checked")
.set_asn(new_asn);
self.ospf.add_router(router_id, new_asn);
self.ospf
.add_links_from::<P, (), _>(links, &mut self.routers)?;
let events = self.ospf.reset(&mut self.routers)?;
self.enqueue_events(events);
self.do_queue_maybe_skip()?;
self.set_bgp_session_from(sessions.into_iter().map(|(a, b, t)| (a, b, Some(t))))?;
self.skip_queue = old_skip;
self.stop_after = old_stop_after;
Ok(old_asn)
}
pub(crate) fn _add_router_with_router_id(
&mut self,
router_id: RouterId,
name: impl Into<String>,
asn: impl Into<ASN>,
) {
let asn = asn.into();
let new_router = Router::new(name.into(), router_id, asn);
let router_id = new_router.router_id();
self.routers.insert(router_id, new_router);
self.ospf.add_router(router_id, asn);
}
pub(crate) fn _prepare_node(&mut self) -> RouterId {
self.net.add_node(())
}
pub fn set_router_name(
&mut self,
router: RouterId,
name: impl Into<String>,
) -> Result<(), NetworkError> {
self.routers
.get_mut(&router)
.ok_or(NetworkError::DeviceNotFound(router))?
.set_name(name.into());
Ok(())
}
pub fn get_forwarding_state(&self) -> ForwardingState<P> {
ForwardingState::from_net(self)
}
pub fn get_bgp_state(&self, prefix: P) -> BgpStateRef<'_, P> {
BgpStateRef::from_net(self, prefix)
}
pub fn get_bgp_state_owned(&self, prefix: P) -> BgpState<P> {
BgpState::from_net(self, prefix)
}
pub fn ospf_network(&self) -> &OspfNetwork<Ospf::Coordinator> {
&self.ospf
}
pub fn get_ospf_forwarding_state(
&self,
) -> (
ForwardingState<SimplePrefix>,
HashMap<RouterId, SimplePrefix>,
) {
self.ospf.get_forwarding_state(&self.routers)
}
pub fn indices(&self) -> Indices<'_, P, Ospf::Process> {
Indices {
i: self.routers.keys(),
}
}
pub fn indices_in_as(&self, asn: ASN) -> IndicesInAs<'_> {
IndicesInAs {
i: self.ospf.routers.iter(),
asn,
}
}
pub fn routers(
&self,
) -> std::collections::btree_map::Values<'_, RouterId, Router<P, Ospf::Process>> {
self.routers.values()
}
pub fn ases(&self) -> BTreeSet<ASN> {
self.ospf.domains.keys().copied().collect()
}
pub fn routers_in_as(&self, asn: ASN) -> RoutersInAs<'_, P, Ospf::Process> {
RoutersInAs {
i: self.routers.values(),
asn,
}
}
pub(crate) fn routers_mut(
&mut self,
) -> std::collections::btree_map::ValuesMut<'_, RouterId, Router<P, Ospf::Process>> {
self.routers.values_mut()
}
pub fn num_routers(&self) -> usize {
self.routers.len()
}
pub fn get_router(&self, id: RouterId) -> Result<&Router<P, Ospf::Process>, NetworkError> {
self.routers
.get(&id)
.ok_or(NetworkError::DeviceNotFound(id))
}
pub(crate) fn get_router_mut(
&mut self,
id: RouterId,
) -> Result<&mut Router<P, Ospf::Process>, NetworkError> {
self.routers
.get_mut(&id)
.ok_or(NetworkError::DeviceNotFound(id))
}
pub fn get_router_id(&self, name: impl AsRef<str>) -> Result<RouterId, NetworkError> {
self.routers
.iter()
.filter(|(_, r)| r.name() == name.as_ref())
.map(|(id, _)| *id)
.next()
.ok_or_else(|| NetworkError::DeviceNameNotFound(name.as_ref().to_string()))
}
#[deprecated(
since = "0.20.0",
note = "Use functions provided by the `OspfNetwork` instead."
)]
pub fn get_topology(&self) -> &PhysicalNetwork {
&self.net
}
pub fn get_known_prefixes(&self) -> impl Iterator<Item = &P> {
self.known_prefixes.iter()
}
pub fn set_msg_limit(&mut self, stop_after: Option<usize>) {
self.stop_after = stop_after;
}
#[deprecated(
since = "0.20.0",
note = "Use functions provided by the `OspfNetwork` instead."
)]
pub fn get_link_weight(
&self,
source: RouterId,
target: RouterId,
) -> Result<LinkWeight, NetworkError> {
self.net
.find_edge(source, target)
.ok_or(NetworkError::LinkNotFound(source, target))?;
Ok(self.ospf.get_weight(source, target))
}
#[deprecated(
since = "0.20.0",
note = "Use functions provided by the `OspfNetwork` instead."
)]
pub fn get_ospf_area(
&self,
source: RouterId,
target: RouterId,
) -> Result<OspfArea, NetworkError> {
self.net
.find_edge(source, target)
.ok_or(NetworkError::LinkNotFound(source, target))?;
self.ospf
.get_area(source, target)
.ok_or(NetworkError::LinkNotFound(source, target))
}
}
impl<P: Prefix, Q: EventQueue<P>, Ospf: OspfImpl> Network<P, Q, Ospf> {
pub fn swap_queue<QA>(self, mut queue: QA) -> Network<P, QA, Ospf>
where
QA: EventQueue<P>,
{
queue.update_params(&self.routers, &self.net);
Network {
net: self.net,
ospf: self.ospf,
routers: self.routers,
bgp_sessions: self.bgp_sessions,
known_prefixes: self.known_prefixes,
stop_after: self.stop_after,
queue,
skip_queue: self.skip_queue,
}
}
pub fn add_link(&mut self, a: RouterId, b: RouterId) -> Result<(), NetworkError> {
if !self.net.contains_edge(a, b) {
self.net.add_edge(a, b, ());
let events = self.ospf.add_link(a, b, &mut self.routers)?;
self.enqueue_events(events);
self.refresh_bgp_sessions()?;
self.do_queue_maybe_skip()?;
}
Ok(())
}
pub fn add_links_from<I>(&mut self, links: I) -> Result<(), NetworkError>
where
I: IntoIterator<Item = (RouterId, RouterId)>,
{
let links = links
.into_iter()
.filter(|(a, b)| !self.net.contains_edge(*a, *b))
.map(|(a, b)| {
if a.index() < b.index() {
(a, b)
} else {
(b, a)
}
})
.collect::<HashSet<_>>();
for (a, b) in links.iter() {
self.net.add_edge(*a, *b, ());
}
let events = self.ospf.add_links_from(links, &mut self.routers)?;
self.enqueue_events(events);
self.refresh_bgp_sessions()?;
self.do_queue_maybe_skip()?;
Ok(())
}
pub fn set_bgp_session(
&mut self,
source: RouterId,
target: RouterId,
ty: Option<bool>,
) -> Result<(), NetworkError> {
self._set_bgp_session(source, target, ty)?;
self.refresh_bgp_sessions()?;
self.do_queue_maybe_skip()
}
pub fn set_bgp_session_from<I>(&mut self, sessions: I) -> Result<(), NetworkError>
where
I: IntoIterator<Item = (RouterId, RouterId, Option<bool>)>,
{
for (source, target, ty) in sessions.into_iter() {
self._set_bgp_session(source, target, ty)?;
}
self.refresh_bgp_sessions()?;
self.do_queue_maybe_skip()
}
pub fn set_link_weight(
&mut self,
source: RouterId,
target: RouterId,
weight: LinkWeight,
) -> Result<LinkWeight, NetworkError> {
self.net
.find_edge(source, target)
.ok_or(NetworkError::LinkNotFound(source, target))?;
let (events, old_weight) =
self.ospf
.set_weight(source, target, weight, &mut self.routers)?;
self.enqueue_events(events);
self.refresh_bgp_sessions()?;
self.do_queue_maybe_skip()?;
Ok(old_weight)
}
pub fn set_link_weights_from<I>(&mut self, weights: I) -> Result<(), NetworkError>
where
I: IntoIterator<Item = (RouterId, RouterId, LinkWeight)>,
{
let weights = weights.into_iter().collect::<Vec<_>>();
for (source, target, _) in weights.iter() {
if self.net.find_edge(*source, *target).is_none() {
return Err(NetworkError::LinkNotFound(*source, *target));
}
}
let events = self
.ospf
.set_link_weights_from(weights, &mut self.routers)?;
self.enqueue_events(events);
self.refresh_bgp_sessions()?;
self.do_queue_maybe_skip()?;
Ok(())
}
pub fn set_ospf_area(
&mut self,
source: RouterId,
target: RouterId,
area: impl Into<OspfArea>,
) -> Result<OspfArea, NetworkError> {
self.net
.find_edge(source, target)
.ok_or(NetworkError::LinkNotFound(source, target))?;
let (events, old_area) =
self.ospf
.set_area(source, target, area.into(), &mut self.routers)?;
self.enqueue_events(events);
self.refresh_bgp_sessions()?;
self.do_queue_maybe_skip()?;
Ok(old_area)
}
pub fn set_bgp_route_map(
&mut self,
router: RouterId,
neighbor: RouterId,
direction: RouteMapDirection,
route_map: RouteMap<P>,
) -> Result<Option<RouteMap<P>>, NetworkError> {
let (old_map, events) = self
.get_router_mut(router)?
.bgp
.set_route_map(neighbor, direction, route_map)?;
self.enqueue_events(events);
self.do_queue_maybe_skip()?;
Ok(old_map)
}
pub fn remove_bgp_route_map(
&mut self,
router: RouterId,
neighbor: RouterId,
direction: RouteMapDirection,
order: i16,
) -> Result<Option<RouteMap<P>>, NetworkError> {
let (old_map, events) = self
.get_router_mut(router)?
.bgp
.remove_route_map(neighbor, direction, order)?;
self.enqueue_events(events);
self.do_queue_maybe_skip()?;
Ok(old_map)
}
pub fn batch_update_route_maps(
&mut self,
router: RouterId,
updates: &[RouteMapEdit<P>],
) -> Result<(), NetworkError> {
let events = self
.get_router_mut(router)?
.bgp
.batch_update_route_maps(updates)?;
self.enqueue_events(events);
self.do_queue_maybe_skip()?;
Ok(())
}
pub fn set_static_route(
&mut self,
router: RouterId,
prefix: P,
route: Option<StaticRoute>,
) -> Result<Option<StaticRoute>, NetworkError> {
Ok(self.get_router_mut(router)?.sr.set(prefix, route))
}
pub fn set_load_balancing(
&mut self,
router: RouterId,
do_load_balancing: bool,
) -> Result<bool, NetworkError> {
let old_val = self
.get_router_mut(router)?
.set_load_balancing(do_load_balancing);
Ok(old_val)
}
pub fn advertise_route<A, C>(
&mut self,
source: RouterId,
prefix: impl Into<P>,
as_path: A,
med: Option<u32>,
community: C,
) -> Result<(), NetworkError>
where
A: IntoIterator,
A::Item: Into<ASN>,
C: IntoIterator<Item = Community>,
{
let prefix: P = prefix.into();
let as_path: Vec<ASN> = as_path.into_iter().map(|x| x.into()).collect();
let community = community.into_iter().collect();
debug!(
"Advertise {} on {}",
prefix,
self.get_router(source)?.name()
);
self.known_prefixes.insert(prefix);
let events = self
.routers
.get_mut(&source)
.or_router_not_found(source)?
.bgp
.advertise_route(
prefix,
Some(BgpRoute {
prefix,
as_path,
next_hop: source,
local_pref: None,
med,
community,
originator_id: None,
cluster_list: Default::default(),
}),
)?;
self.enqueue_events(events);
self.do_queue_maybe_skip()
}
pub fn withdraw_route(
&mut self,
source: RouterId,
prefix: impl Into<P>,
) -> Result<(), NetworkError> {
let prefix: P = prefix.into();
debug!("Withdraw {} on {}", prefix, self.get_router(source)?.name());
let events = self
.routers
.get_mut(&source)
.or_router_not_found(source)?
.bgp
.advertise_route(prefix, None)?;
self.enqueue_events(events);
self.do_queue_maybe_skip()
}
pub fn remove_link(
&mut self,
router_a: RouterId,
router_b: RouterId,
) -> Result<(), NetworkError> {
debug!(
"Remove link: {} -- {}",
self.get_router(router_a)?.name(),
self.get_router(router_b)?.name()
);
self.net.remove_edge(
self.net
.find_edge(router_a, router_b)
.ok_or(NetworkError::LinkNotFound(router_a, router_b))?,
);
let events = self
.ospf
.remove_link(router_a, router_b, &mut self.routers)?;
self.enqueue_events(events);
self.refresh_bgp_sessions()?;
self.do_queue_maybe_skip()?;
Ok(())
}
pub fn remove_router(&mut self, router: RouterId) -> Result<(), NetworkError> {
let old_skip = self.skip_queue;
let old_stop_after = self.stop_after;
self.skip_queue = false;
self.stop_after = None;
self.do_queue_maybe_skip()?;
let bgp_neighbors = self
.bgp_sessions
.keys()
.filter_map(|(a, b)| {
if *a == router {
Some(*b)
} else if *b == router {
Some(*a)
} else {
None
}
})
.collect::<Vec<_>>();
let events = self.ospf.remove_router(router, &mut self.routers)?;
self.enqueue_events(events);
self.refresh_bgp_sessions()?;
for neighbor in bgp_neighbors {
self.set_bgp_session(router, neighbor, None)?;
}
self.routers.remove(&router);
self.net.remove_node(router);
self.do_queue_maybe_skip()?;
self.skip_queue = old_skip;
self.stop_after = old_stop_after;
Ok(())
}
pub fn get_bgp_sessions(&self) -> Vec<(RouterId, RouterId, BgpSessionType, bool)> {
self.bgp_sessions
.iter()
.filter_map(|((src, dst), ty)| ty.map(|ty| (*src, *dst, ty)))
.filter_map(|(src, dst, is_client)| {
let reverse_is_client = self
.bgp_sessions
.get(&(dst, src))
.copied()
.flatten()
.unwrap_or(false);
let src_asn = self.routers.get(&src)?.asn();
let dst_asn = self.routers.get(&dst)?.asn();
let reachable = self.ospf.is_reachable(src, dst, &self.routers);
if src_asn == dst_asn {
if is_client {
Some((src, dst, BgpSessionType::IBgpClient, reachable))
} else if reverse_is_client {
None
} else if src.index() <= dst.index() {
Some((src, dst, BgpSessionType::IBgpPeer, reachable))
} else {
None
}
} else if src.index() <= dst.index() {
Some((src, dst, BgpSessionType::EBgp, reachable))
} else {
None
}
})
.collect()
}
pub(crate) fn next_unused_asn(&self, start_asn: ASN) -> ASN {
for asn in ((start_asn.0)..).map(ASN) {
if !self.ospf.domains.contains_key(&asn) {
return asn;
}
}
panic!("Used too many AS numbers...")
}
fn _set_bgp_session(
&mut self,
source: RouterId,
target: RouterId,
ty: Option<bool>,
) -> Result<(), NetworkError> {
self.bgp_sessions.insert((source, target), ty);
self.bgp_sessions
.insert((target, source), ty.map(|_| false));
Ok(())
}
pub(crate) fn refresh_bgp_sessions(&mut self) -> Result<(), NetworkError> {
let effective_sessions: Vec<_> = self
.bgp_sessions
.iter()
.map(|((source, target), ty)| {
(
*source,
*target,
self.ospf
.is_reachable(*source, *target, &self.routers)
.then_some(*ty)
.flatten(),
)
})
.collect();
for (source, target, target_is_client) in effective_sessions {
let (target_name, target_asn) = self
.routers
.get(&target)
.map(|x| (x.name().to_string(), x.asn()))
.unwrap_or(("?".to_string(), ASN(0)));
let info = target_is_client.map(|x| (target_asn, x));
let Some(r) = self.routers.get_mut(&source) else {
continue;
};
if r.bgp.get_session_info(target) != info && source < target {
let action = if info.is_some() {
"established"
} else {
"broke down"
};
log::debug!(
"BGP session between {} and {target_name} {action}!",
r.name(),
);
}
let events = r.bgp.set_session(target, info)?.1;
self.enqueue_events(events);
}
Ok(())
}
pub(crate) fn do_queue_maybe_skip(&mut self) -> Result<(), NetworkError> {
self.queue.update_params(&self.routers, &self.net);
if self.skip_queue {
return Ok(());
}
self.simulate()
}
#[inline(always)]
pub(crate) fn enqueue_events(&mut self, events: Vec<Event<P, Q::Priority>>) {
self.queue.push_many(events, &self.routers, &self.net)
}
}
impl<P: Prefix, Q: EventQueue<P>> Network<P, Q, GlobalOspf> {
pub fn into_local_ospf(self) -> Result<Network<P, Q, LocalOspf>, NetworkError> {
Network::<P, Q, LocalOspf>::from_global_ospf(self)
}
}
impl<P: Prefix, Q: EventQueue<P>, Ospf: OspfImpl> Network<P, Q, Ospf> {
pub fn from_global_ospf(net: Network<P, Q, GlobalOspf>) -> Result<Self, NetworkError> {
net.swap_ospf(|global_c, mut global_p, c, p| {
let coordinators = (c, global_c);
let processes = p
.into_iter()
.map(|(r, p)| (r, (p, global_p.remove(&r).unwrap())))
.collect();
Ospf::from_global(coordinators, processes)
})
}
pub fn into_global_ospf(self) -> Result<Network<P, Q, GlobalOspf>, NetworkError> {
self.swap_ospf(|c, p, global_c, mut global_p| {
let coordinators = (c, global_c);
let processes = p
.into_iter()
.map(|(r, p)| (r, (p, global_p.remove(&r).unwrap())))
.collect();
Ospf::into_global(coordinators, processes)
})
}
#[allow(clippy::result_large_err)]
pub fn into_ipv4_prefix<QA>(self, mut queue: QA) -> Result<Network<Ipv4Prefix, QA, Ospf>, Self>
where
QA: EventQueue<Ipv4Prefix>,
{
if !self.queue.is_empty() {
return Err(self);
}
let routers = self
.routers
.into_iter()
.map(|(id, r)| (id, r.into_ipv4_prefix()))
.collect();
queue.update_params(&routers, &self.net);
Ok(Network {
net: self.net,
ospf: self.ospf,
routers,
queue,
bgp_sessions: self.bgp_sessions,
known_prefixes: self
.known_prefixes
.into_iter()
.map(Prefix::into_ipv4_prefix)
.collect(),
stop_after: self.stop_after,
skip_queue: self.skip_queue,
})
}
}
impl<P, Q, Ospf> Network<P, Q, Ospf>
where
P: Prefix,
Q: EventQueue<P> + PartialEq,
Ospf: OspfImpl,
{
pub fn weak_eq(&self, other: &Self) -> bool {
if self.queue != other.queue {
#[cfg(test)]
{
eprintln!("Queues don't match.");
eprintln!("self: {} events enqueued", self.queue.len());
eprintln!("other: {} events enqueued", other.queue.len());
}
return false;
}
if self.indices().collect::<HashSet<_>>() != other.indices().collect::<HashSet<_>>() {
#[cfg(test)]
eprintln!("Router indices don't match!");
return false;
}
for r in self.indices() {
let self_r = self.get_router(r).unwrap();
let other_r = other.get_router(r).unwrap();
if self_r.ospf.get_table() != other_r.ospf.get_table() {
#[cfg(test)]
{
let self_table = self_r.ospf.get_table().iter().collect::<BTreeMap<_, _>>();
let other_table = other_r.ospf.get_table().iter().collect::<BTreeMap<_, _>>();
eprintln!(
"OSPF table of {} (and {}) don't match!",
self_r.name(),
other_r.name()
);
eprintln!(
"{}",
pretty_assertions::Comparison::new(&self_table, &other_table)
);
}
return false;
}
}
if self.get_forwarding_state() != other.get_forwarding_state() {
#[cfg(test)]
{
eprintln!("Forwarding state doesn't match!");
eprintln!("\nself:");
eprintln!("{}", self.get_forwarding_state().fmt_multiline(self));
eprintln!("\nother:");
eprintln!("{}", other.get_forwarding_state().fmt_multiline(other));
}
return false;
}
for id in self.indices() {
if !self
.get_router(id)
.unwrap()
.bgp
.compare_table(&other.get_router(id).unwrap().bgp)
{
#[cfg(test)]
{
eprintln!(
"Routing Tables of {} (and {}) don't match!",
id.fmt(self),
id.fmt(other)
);
}
return false;
}
}
true
}
}
impl<P, Q, Ospf> PartialEq for Network<P, Q, Ospf>
where
P: Prefix,
Q: EventQueue<P> + PartialEq,
Ospf: OspfImpl,
{
fn eq(&self, other: &Self) -> bool {
if self.routers != other.routers {
return false;
}
if self.queue != other.queue {
return false;
}
if self.get_config() != other.get_config() {
return false;
}
let self_ns = HashSet::<RouterId>::from_iter(self.net.node_indices());
let other_ns = HashSet::<RouterId>::from_iter(other.net.node_indices());
if self_ns != other_ns {
return false;
}
true
}
}
#[derive(Debug)]
pub struct Indices<'a, P: Prefix, Ospf> {
i: std::collections::btree_map::Keys<'a, RouterId, Router<P, Ospf>>,
}
impl<P: Prefix, Ospf> Iterator for Indices<'_, P, Ospf> {
type Item = RouterId;
fn next(&mut self) -> Option<Self::Item> {
self.i.next().copied()
}
}
impl<P: Prefix, Ospf> FusedIterator for Indices<'_, P, Ospf> {}
impl<P: Prefix, Ospf> Indices<'_, P, Ospf> {
pub fn detach(self) -> std::vec::IntoIter<RouterId> {
self.collect::<Vec<RouterId>>().into_iter()
}
}
#[derive(Debug)]
pub struct IndicesInAs<'a> {
i: std::collections::btree_map::Iter<'a, RouterId, ASN>,
asn: ASN,
}
impl Iterator for IndicesInAs<'_> {
type Item = RouterId;
fn next(&mut self) -> Option<Self::Item> {
for (id, asn) in self.i.by_ref() {
if *asn == self.asn {
return Some(*id);
}
}
None
}
}
impl IndicesInAs<'_> {
pub fn detach(self) -> std::vec::IntoIter<RouterId> {
self.collect::<Vec<RouterId>>().into_iter()
}
}
#[derive(Debug)]
pub struct RoutersInAs<'a, P: Prefix, Ospf> {
i: std::collections::btree_map::Values<'a, RouterId, Router<P, Ospf>>,
asn: ASN,
}
impl<'a, P: Prefix, Ospf> Iterator for RoutersInAs<'a, P, Ospf> {
type Item = &'a Router<P, Ospf>;
fn next(&mut self) -> Option<Self::Item> {
self.i.by_ref().find(|&r| r.asn() == self.asn)
}
}