use std::error::Error;
use inetnum::addr::Prefix;
use inetnum::asn::Asn;
use roto::{NoCtx, Runtime, TypedFunc, Val, Verdict, library};
use routecore::bgp::aspath::{Hop, HopPath};
use routecore::bgp::nlri::afisafi::IsPrefix;
use routecore::bgp::workshop::route::RouteWorkshop;
mod hidden {
use inetnum::addr::Prefix;
use routecore::bgp::{
nlri::afisafi::{
Ipv4MulticastNlri, Ipv4UnicastNlri, Ipv6MulticastNlri,
Ipv6UnicastNlri,
},
path_attributes::PaMap,
workshop::route::RouteWorkshop,
};
#[allow(dead_code)]
#[derive(Clone)]
pub enum RotondaRoute {
Ipv4Unicast(RouteWorkshop<Ipv4UnicastNlri>),
Ipv6Unicast(RouteWorkshop<Ipv6UnicastNlri>),
Ipv4Multicast(RouteWorkshop<Ipv4MulticastNlri>),
Ipv6Multicast(RouteWorkshop<Ipv6MulticastNlri>),
}
impl RotondaRoute {
pub fn attributes(&self) -> &PaMap {
match self {
RotondaRoute::Ipv4Unicast(n) => n.attributes(),
RotondaRoute::Ipv6Unicast(n) => n.attributes(),
RotondaRoute::Ipv4Multicast(n) => n.attributes(),
RotondaRoute::Ipv6Multicast(n) => n.attributes(),
}
}
}
#[derive(Debug)]
pub struct OutputStream<T>(Vec<T>);
impl<T> Default for OutputStream<T> {
fn default() -> Self {
Self(Vec::default())
}
}
impl<T> OutputStream<T> {
pub fn push(&mut self, value: T) {
self.0.push(value)
}
pub fn drain(&mut self) -> std::vec::Drain<'_, T> {
self.0.drain(..)
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub enum Output {
Custom(u32, u32),
Prefix(Prefix),
}
}
use hidden::*;
type Log = *mut OutputStream<Output>;
type Func =
TypedFunc<NoCtx, fn(Val<Log>, Val<RotondaRoute>) -> Verdict<(), ()>>;
fn main() -> Result<(), Box<dyn Error>> {
let lib = library! {
#[clone] type Route = Val<RotondaRoute>;
impl Val<RotondaRoute> {
fn prefix_matches(rr: Val<RotondaRoute>, to_match: Prefix) -> bool {
let rr_prefix = match &*rr {
RotondaRoute::Ipv4Unicast(n) => n.nlri().prefix(),
RotondaRoute::Ipv6Unicast(n) => n.nlri().prefix(),
RotondaRoute::Ipv4Multicast(n) => n.nlri().prefix(),
RotondaRoute::Ipv6Multicast(n) => n.nlri().prefix(),
};
rr_prefix == to_match
}
fn aspath_origin(rr: Val<RotondaRoute>, to_match: Asn) -> bool {
if let Some(hoppath) = rr.attributes().get::<HopPath>()
&& let Some(Hop::Asn(asn)) = hoppath.origin() {
return *asn == to_match;
}
false
}
}
#[clone] type Log = Val<Log>;
impl Val<Log> {
fn log_prefix(stream: Val<Log>, prefix: Prefix) {
let mut stream = stream;
let stream = unsafe { &mut **stream };
stream.push(Output::Prefix(prefix));
}
fn log_custom(stream: Val<Log>, id: u32, local: u32) {
let mut stream = stream;
let stream = unsafe { &mut **stream };
stream.push(Output::Custom(id, local));
}
}
};
let rt = Runtime::from_lib(lib).unwrap();
let mut compiled = rt
.compile("examples/presentation.roto")
.inspect_err(|e| eprintln!("{e}"))?;
let function = compiled.get_function("rib_in_pre").unwrap();
run_with_prefix(&function, "8.8.8.0/24")?;
run_with_prefix(&function, "100.40.0.0/17")?;
Ok(())
}
fn run_with_prefix(
function: &Func,
prefix: &str,
) -> Result<(), Box<dyn Error>> {
let route =
RotondaRoute::Ipv4Unicast(RouteWorkshop::new(prefix.parse()?));
let mut output = OutputStream::default();
let log = &mut output as *mut _;
let verdict = function.call(Val(log), Val(route));
println!("Input: {prefix}");
println!("Verdict: {verdict:?}");
println!();
println!("Output: ");
for entry in output.drain() {
println!(" - {entry:?}");
}
println!("=====================");
Ok(())
}