Remove policy scripts
Rhai made a decision about a route cost one to three microseconds, some two hundred times what the same costs in Rust. That was bearable where a function is asked once for an UPDATE, and is not where every route is weighed against every peer, as it is when routes are passed on. What is wanted of a policy (mark routes by where they came from, mark what is announced, let communities say who is told what) needs no language; it is to be configuration, done natively. Kept from that work: route selection goes by a LOCAL_PREF that policy set on a route from an external peer, and the lab's configuration can be added to by a phase. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
cbf0df1eef
commit
9367c38907
48 files changed
+21
-3435
No files matched your search
@@ -1,84 +0,0 @@
|
||||
//! The operator's policy script, asked about routes with whatever
|
||||
//! attributes a peer can send. The example script (testdata/policy.rhai)
|
||||
//! touches most of what a script can; whatever the route, a function
|
||||
//! either answers or fails, and never panics. What it changes must still
|
||||
//! be attributes that can be written.
|
||||
#![no_main]
|
||||
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use bgp_packet::message::{Message, PathAttribute, UpdateMessage};
|
||||
use bgp_policy::{Direction, Hook, Peer, Policy, Subject};
|
||||
use bgp_primitives::ip_prefix::IpPrefix;
|
||||
use bgp_primitives::parser::ParserContext;
|
||||
use bytes::BytesMut;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
static HOOKS: LazyLock<(Arc<Policy>, Vec<Hook>)> = LazyLock::new(|| {
|
||||
let policy = Policy::compile(include_str!("../../testdata/policy.rhai")).unwrap();
|
||||
let hooks = ["from_peer", "to_peer"]
|
||||
.into_iter()
|
||||
.map(|name| policy.hook(name).unwrap())
|
||||
.collect();
|
||||
(policy, hooks)
|
||||
});
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let Some((selector, attributes)) = data.split_first() else {
|
||||
return;
|
||||
};
|
||||
let ctx = ParserContext {
|
||||
four_octet_asn: true,
|
||||
ibgp: false,
|
||||
add_path: Vec::new(),
|
||||
};
|
||||
// An UPDATE with no routes withdrawn, these attributes, and one route.
|
||||
let mut body = vec![0, 0];
|
||||
let attributes = &attributes[..attributes.len().min(4000)];
|
||||
body.extend_from_slice(&(attributes.len() as u16).to_be_bytes());
|
||||
body.extend_from_slice(attributes);
|
||||
body.extend_from_slice(&[24, 203, 0, 113]);
|
||||
let Ok(decoded) = UpdateMessage::decode(&ctx, &body) else {
|
||||
return;
|
||||
};
|
||||
let attributes = decoded.update.path_attributes;
|
||||
let prefix = IpPrefix::try_from("203.0.113.0/24").unwrap();
|
||||
let roles = ["", "customer", "peer", "provider", "route_server"];
|
||||
let peer = Peer::new(
|
||||
"backup-transit",
|
||||
std::net::Ipv4Addr::new(192, 0, 2, 2).into(),
|
||||
64602,
|
||||
65000,
|
||||
roles[usize::from(selector >> 1) % roles.len()],
|
||||
);
|
||||
let (hook, direction) = if selector & 1 == 0 {
|
||||
(&HOOKS.1[0], Direction::Import)
|
||||
} else {
|
||||
(&HOOKS.1[1], Direction::Export)
|
||||
};
|
||||
let subject = Subject {
|
||||
prefix: &prefix,
|
||||
attributes: &attributes,
|
||||
next_hop: None,
|
||||
direction,
|
||||
};
|
||||
let Ok(verdict) = hook.run(&peer, &subject) else {
|
||||
return;
|
||||
};
|
||||
if let Some(changed) = verdict.attributes {
|
||||
let changed: Vec<PathAttribute> = changed;
|
||||
let update = UpdateMessage {
|
||||
withdrawn_routes: vec![],
|
||||
path_attributes: changed,
|
||||
announced_routes: vec![],
|
||||
};
|
||||
let mut wire = BytesMut::new();
|
||||
// Too long to send is a thing that can be; anything else is not.
|
||||
if let Err(e) = Message::Update(update).to_wire(&ctx, &mut wire) {
|
||||
assert!(
|
||||
matches!(e, bgp_primitives::parser::EncodeError::TooLong { .. }),
|
||||
"{e}"
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user