mod interdomain_samplers;
mod route_samplers;
mod selectors;
mod topology_samplers;
mod weight_samplers;
pub use interdomain_samplers::*;
pub use route_samplers::*;
pub use selectors::*;
pub use topology_samplers::*;
pub use weight_samplers::*;
use std::collections::{BTreeMap, BTreeSet};
use crate::{
bgp::BgpSessionType,
event::EventQueue,
network::Network,
ospf::OspfImpl,
route_map::{RouteMapBuilder, RouteMapDirection},
types::{NetworkError, Prefix, RouterId, ASN},
};
use itertools::Itertools;
pub trait NetworkBuilder<P, Q, Ospf: OspfImpl> {
fn build_ibgp_full_mesh(&mut self) -> Result<(), NetworkError>;
fn build_ibgp_full_mesh_in_as(&mut self, asn: impl Into<ASN>) -> Result<(), NetworkError>;
fn build_ibgp_route_reflection<S: RouterSelector>(
&mut self,
route_reflectors: S,
) -> Result<BTreeMap<ASN, BTreeSet<RouterId>>, NetworkError>;
fn build_ibgp_route_reflection_in_as<S: RouterSelector>(
&mut self,
asn: impl Into<ASN>,
route_reflectors: S,
) -> Result<BTreeSet<RouterId>, NetworkError>;
fn build_ebgp_sessions(&mut self) -> Result<(), NetworkError>;
fn build_link_weights<S: WeightSampler>(&mut self, link_weight: S) -> Result<(), NetworkError>;
fn build_link_weights_in_as<S: WeightSampler>(
&mut self,
asn: impl Into<ASN>,
link_weight: S,
) -> Result<(), NetworkError>;
fn build_external_routers<S: RouterSelector>(
&mut self,
asn: impl Into<ASN>,
external_asn: impl Into<ASN>,
connected_to: S,
) -> Result<Vec<RouterId>, NetworkError>;
fn build_advertisements<S: RouteSampler>(
&mut self,
prefix: P,
preferences: S,
origin_as: impl Into<ASN>,
) -> Result<Vec<(RouterId, usize)>, NetworkError>;
fn build_topology<S: TopologySampler>(
&mut self,
asn: impl Into<ASN>,
topology: S,
) -> Result<Vec<RouterId>, NetworkError>;
fn build_gao_rexford<S: AsLevelSampler>(
&mut self,
topology: S,
) -> Result<BTreeMap<usize, BTreeSet<ASN>>, NetworkError>;
fn build_connected_graph(&mut self) -> Result<(), NetworkError>;
fn build_connected_graph_in_as(&mut self, asn: impl Into<ASN>) -> Result<(), NetworkError>;
}
impl<P: Prefix, Q: EventQueue<P>, Ospf: OspfImpl> NetworkBuilder<P, Q, Ospf>
for Network<P, Q, Ospf>
{
fn build_ibgp_full_mesh(&mut self) -> Result<(), NetworkError> {
let sessions = self
.ospf
.domains()
.iter()
.flat_map(|(_, d)| d.indices().tuple_combinations())
.map(|(a, b)| (a, b, Some(false)))
.collect::<Vec<_>>();
self.set_bgp_session_from(sessions)
}
fn build_ibgp_full_mesh_in_as(&mut self, asn: impl Into<ASN>) -> Result<(), NetworkError> {
let asn = asn.into();
let sessions = self
.ospf
.domain(asn)
.into_iter()
.flat_map(|d| d.indices().tuple_combinations())
.map(|(a, b)| (a, b, Some(false)))
.collect::<Vec<_>>();
self.set_bgp_session_from(sessions)
}
fn build_ibgp_route_reflection<S: RouterSelector>(
&mut self,
mut route_reflectors: S,
) -> Result<BTreeMap<ASN, BTreeSet<RouterId>>, NetworkError> {
let domains: BTreeMap<ASN, Vec<RouterId>> = self
.ospf
.routers
.iter()
.map(|(r, asn)| (*asn, *r))
.into_group_map()
.into_iter()
.collect();
let mut sessions = Vec::new();
let mut all_route_reflectors = BTreeMap::new();
for (asn, routers) in domains {
if routers.len() <= 1 {
continue;
}
all_route_reflectors.insert(
asn,
_ibgp_route_reflection_in_as(
self,
asn,
routers,
&mut sessions,
&mut route_reflectors,
),
);
}
self.set_bgp_session_from(sessions)?;
Ok(all_route_reflectors)
}
fn build_ibgp_route_reflection_in_as<S: RouterSelector>(
&mut self,
asn: impl Into<ASN>,
mut route_reflectors: S,
) -> Result<BTreeSet<RouterId>, NetworkError> {
let asn = asn.into();
let routers = self.indices_in_as(asn).collect::<Vec<_>>();
let mut sessions = Vec::new();
if routers.len() <= 1 {
return Ok(BTreeSet::new());
}
let route_reflectors =
_ibgp_route_reflection_in_as(self, asn, routers, &mut sessions, &mut route_reflectors);
self.set_bgp_session_from(sessions)?;
Ok(route_reflectors)
}
fn build_ebgp_sessions(&mut self) -> Result<(), NetworkError> {
let sessions = self
.ospf
.external_edges()
.map(|e| (e.int, e.ext, Some(false)))
.filter(|(a, b, _)| a.index() <= b.index())
.collect::<Vec<_>>();
self.set_bgp_session_from(sessions)
}
fn build_link_weights<S: WeightSampler>(
&mut self,
mut link_weight: S,
) -> Result<(), NetworkError> {
let mut weights = Vec::new();
for (d_asn, d) in self.ospf.domains().iter() {
weights.extend(
d.internal_edges()
.map(|e| (e.src, e.dst))
.sorted()
.map(|(src, dst)| (src, dst, link_weight.sample(self, *d_asn, src, dst))),
)
}
self.set_link_weights_from(weights)
}
fn build_link_weights_in_as<S: WeightSampler>(
&mut self,
asn: impl Into<ASN>,
mut link_weight: S,
) -> Result<(), NetworkError> {
let asn = asn.into();
let mut weights = Vec::new();
if let Ok(d) = self.ospf.domain(asn) {
weights.extend(
d.internal_edges()
.map(|e| (e.src, e.dst))
.sorted()
.map(|(src, dst)| (src, dst, link_weight.sample(self, asn, src, dst))),
)
}
self.set_link_weights_from(weights)
}
fn build_external_routers<S: RouterSelector>(
&mut self,
asn: impl Into<ASN>,
external_asn: impl Into<ASN>,
mut connected_to: S,
) -> Result<Vec<RouterId>, NetworkError> {
let asn = asn.into();
let external_asn = external_asn.into();
let old_skip_queue = self.skip_queue;
self.skip_queue = false;
let mut new_links = Vec::new();
let new_borders = connected_to.select(self, asn).collect::<Vec<_>>();
let new_routers = new_borders
.into_iter()
.map(|neighbor| {
let neighbor_name = self.get_router(neighbor)?.name().to_owned();
let router_id = self._prepare_node();
let name = format!("{}_ext_{}", neighbor_name, router_id.index());
let asn = self.next_unused_asn(external_asn);
self._add_router_with_router_id(router_id, name, asn);
new_links.push((router_id, neighbor));
Ok(router_id)
})
.collect::<Result<Vec<RouterId>, NetworkError>>()?;
self.add_links_from(new_links)?;
self.skip_queue = old_skip_queue;
Ok(new_routers)
}
fn build_advertisements<S: RouteSampler>(
&mut self,
prefix: P,
mut preferences: S,
origin_asn: impl Into<ASN>,
) -> Result<Vec<(RouterId, usize)>, NetworkError> {
let origin_asn = origin_asn.into();
let mut prefs: Vec<_> = preferences.sample(self).into_iter().collect();
prefs.sort_by_key(|(_, l)| *l);
let old_skip_queue = self.skip_queue;
self.skip_queue = false;
for (router, as_path_len) in prefs.iter().copied() {
let router_as = self.get_router(router)?.asn();
let as_path =
std::iter::repeat_n(router_as, as_path_len).chain(std::iter::once(origin_asn));
self.advertise_route(router, prefix, as_path, None, None)?;
}
self.skip_queue = old_skip_queue;
Ok(prefs)
}
fn build_topology<S: TopologySampler>(
&mut self,
asn: impl Into<ASN>,
mut topology: S,
) -> Result<Vec<RouterId>, NetworkError> {
let asn = asn.into();
let n = topology.num_nodes();
let routers = (0..n)
.map(|_| {
let router_id = self._prepare_node();
let name = format!("R{}", router_id.index());
self._add_router_with_router_id(router_id, name, asn);
router_id
})
.collect::<Vec<_>>();
let links = topology
.sample()
.into_iter()
.map(|(a, b)| (routers[a], routers[b]));
self.add_links_from(links)?;
Ok(routers)
}
fn build_gao_rexford<S: AsLevelSampler>(
&mut self,
mut topology: S,
) -> Result<BTreeMap<usize, BTreeSet<ASN>>, NetworkError> {
let levels = topology
.sample(self)
.into_iter()
.collect::<BTreeMap<ASN, usize>>();
let mut result: BTreeMap<_, BTreeSet<_>> = BTreeMap::new();
for (&asn, &level) in levels.iter() {
result.entry(level).or_default().insert(asn);
let edges = self
.ospf_network()
.domain(asn)
.map(|x| x.external_edges())
.unwrap_or_default();
let edges = edges.filter(|e| {
self.get_router(e.int)
.ok()
.and_then(|r| r.bgp.get_session_type(e.ext))
== Some(BgpSessionType::EBgp)
});
let edges = edges
.filter_map(|e| self.get_router(e.ext).ok().map(|r| (e.int, e.ext, r.asn())))
.collect::<Vec<_>>();
for (r, neighbor, neighbor_asn) in edges {
let Some(neighbor_level) = levels.get(&neighbor_asn).copied() else {
continue;
};
let kind = GaoRexfordPeerType::from_levels(level, neighbor_level);
let in_rm = RouteMapBuilder::new()
.order(10)
.allow()
.set_community(kind.community(asn))
.set_local_pref(kind.local_pref())
.exit()
.build();
let out_rms = match kind {
GaoRexfordPeerType::Customer => vec![],
GaoRexfordPeerType::Peer | GaoRexfordPeerType::Provider => vec![
RouteMapBuilder::new()
.order(10)
.deny()
.match_community(GaoRexfordPeerType::Peer.community(asn))
.build(),
RouteMapBuilder::new()
.order(20)
.deny()
.match_community(GaoRexfordPeerType::Provider.community(asn))
.build(),
RouteMapBuilder::new().order(30).allow().build(),
],
};
self.set_bgp_route_map(r, neighbor, RouteMapDirection::Incoming, in_rm)?;
for out_rm in out_rms {
self.set_bgp_route_map(r, neighbor, RouteMapDirection::Outgoing, out_rm)?;
}
}
}
Ok(result)
}
fn build_connected_graph(&mut self) -> Result<(), NetworkError> {
for asn in self.ases() {
self.build_connected_graph_in_as(asn)?;
}
Ok(())
}
fn build_connected_graph_in_as(&mut self, asn: impl Into<ASN>) -> Result<(), NetworkError> {
let asn = asn.into();
#[cfg(feature = "rand")]
use rand::prelude::*;
#[cfg(feature = "rand")]
let mut rng = thread_rng();
let g = self.ospf.domain(asn).map(|d| d.graph()).unwrap_or_default();
if g.node_count() < 2 {
return Ok(());
}
let mut nodes_missing: BTreeSet<RouterId> = g.node_indices().collect();
let mut components: Vec<Vec<RouterId>> = Vec::new();
while let Some(r) = nodes_missing.pop_first() {
let mut current_component = vec![r];
let mut to_explore = vec![r];
while let Some(r) = to_explore.pop() {
for x in g.neighbors(r) {
if nodes_missing.remove(&x) {
current_component.push(x);
to_explore.push(x);
}
}
}
#[cfg(feature = "rand")]
current_component.shuffle(&mut rng);
components.push(current_component);
}
#[cfg(feature = "rand")]
components.shuffle(&mut rng);
let mut main_component = components.pop().unwrap();
let mut links = Vec::new();
for (idx, mut component) in components.into_iter().enumerate() {
links.push((*component.last().unwrap(), main_component[idx]));
main_component.append(&mut component);
}
self.add_links_from(links)?;
Ok(())
}
}
fn _ibgp_route_reflection_in_as<P: Prefix, Q, Ospf: OspfImpl, S: RouterSelector>(
net: &Network<P, Q, Ospf>,
asn: ASN,
routers: Vec<RouterId>,
sessions: &mut Vec<(RouterId, RouterId, Option<bool>)>,
selector: &mut S,
) -> BTreeSet<RouterId> {
if routers.len() <= 1 {
return Default::default();
}
let route_reflectors: BTreeSet<RouterId> = selector.select(net, asn).collect();
let route_reflectors: BTreeSet<RouterId> = route_reflectors
.intersection(&routers.iter().copied().collect())
.copied()
.collect();
let mut indices = net.indices_in_as(asn).collect::<Vec<_>>();
indices.sort();
for src in indices.iter().copied() {
for dst in indices.iter().copied() {
if src.index() <= dst.index() {
continue;
}
let src_is_rr = route_reflectors.contains(&src);
let dst_is_rr = route_reflectors.contains(&dst);
match (src_is_rr, dst_is_rr) {
(true, true) => sessions.push((src, dst, Some(false))),
(true, false) => sessions.push((src, dst, Some(true))),
(false, true) => sessions.push((dst, src, Some(true))),
(false, false) => sessions.push((src, dst, None)),
}
}
}
route_reflectors
}