Import rules: preference and communities for some routes only
The import policy did the same to every route of a session. Now `rules` can do more to some of them: those with certain communities, those for certain prefixes, or both. Each rule can set the local preference and put communities on; each route takes the first rule that holds for it. Rules see a route as the session's own statements leave it, with our communities taken off and the session's marks put on. So a router can act on where another router of ours learned a route, by the mark that router put on it, and a customer's single prefix can be marked for a prepend. Routes of one UPDATE that different rules hold for go into the table with attribute sets of their own. An UPDATE that no rule touches is taken as before, without its attributes being written out again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4d13336874
commit
4e945b278f
9 files changed
+481
-70
No files matched your search
@@ -16,8 +16,8 @@ use bgp_packet::message::{
|
||||
use bgp_primitives::ip_prefix::IpPrefix;
|
||||
use bgp_server::config::PeerConfig;
|
||||
use bgp_server::family::FamilySet;
|
||||
use bgp_server::marks::Scrub;
|
||||
use bgp_server::policy::{ImportPolicy, PrefixSet};
|
||||
use bgp_server::marks::{Marks, Patterns, Scrub};
|
||||
use bgp_server::policy::{ImportPolicy, ImportRule, PrefixSet};
|
||||
use bgp_server::session::DialState;
|
||||
use eyre::Result;
|
||||
use tokio::net::TcpListener;
|
||||
@@ -434,3 +434,79 @@ async fn announce_with(
|
||||
}))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Rules do to some routes what the rest of the policy does to all: by
|
||||
/// their communities, as the session has marked them, or by their prefix,
|
||||
/// even when they came in one UPDATE with others. The first rule that
|
||||
/// holds for a route is the one applied.
|
||||
#[tokio::test]
|
||||
async fn test_rules_apply_to_the_routes_they_hold_for() -> Result<()> {
|
||||
let mut within = PrefixSet::default();
|
||||
within.allow(prefix("193.0.0.0/21"), 24);
|
||||
let rule = |communities: &[&str], prefixes: Option<PrefixSet>, local_pref, add: &[&str]| {
|
||||
let strings = |texts: &[&str]| texts.iter().map(|t| t.to_string()).collect::<Vec<_>>();
|
||||
ImportRule {
|
||||
communities: Patterns::parse(&strings(communities)).unwrap(),
|
||||
prefixes,
|
||||
local_pref,
|
||||
marks: Marks::parse(&[], &[], &strings(add)).unwrap(),
|
||||
}
|
||||
};
|
||||
let policy = ImportPolicy {
|
||||
// The session marks where its routes were learned.
|
||||
marks: Marks::parse(&[], &[], &["1299:101:1".to_string()]).unwrap(),
|
||||
rules: vec![
|
||||
rule(&["3356:3"], None, Some(50), &[]),
|
||||
rule(&[], Some(within), None, &["1299:31:10"]),
|
||||
rule(&["1299:101:*"], None, Some(120), &[]),
|
||||
],
|
||||
..default_policy()
|
||||
};
|
||||
let (server, _listener) = server_with(policy).await?;
|
||||
let mut peer = established(&server).await?;
|
||||
announce_with(
|
||||
&mut peer,
|
||||
&["194.0.0.0/16"],
|
||||
vec![PathAttribute::Communities(CommunitiesPathAttribute(vec![
|
||||
(3356, 3),
|
||||
]))],
|
||||
)
|
||||
.await?;
|
||||
announce_with(&mut peer, &["193.0.0.0/22", "195.0.0.0/16"], vec![]).await?;
|
||||
table_after(&server, 3, 0).await?;
|
||||
|
||||
let kept = |wanted: &str| {
|
||||
let routes = server.received_routes();
|
||||
let route = routes
|
||||
.iter()
|
||||
.find(|route| route.prefix.to_string() == wanted)
|
||||
.unwrap_or_else(|| panic!("{wanted} is in the table"));
|
||||
let local_pref = route
|
||||
.attributes
|
||||
.iter()
|
||||
.find_map(|attribute| match attribute {
|
||||
PathAttribute::LocalPref(local_pref) => Some(local_pref.0),
|
||||
_ => None,
|
||||
});
|
||||
let large = route
|
||||
.attributes
|
||||
.iter()
|
||||
.find_map(|attribute| match attribute {
|
||||
PathAttribute::LargeCommunities(large) => Some(large.communities.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_default();
|
||||
(local_pref, large)
|
||||
};
|
||||
let learned = large(1299, 101, 1);
|
||||
// By its community: the first rule, and only that one.
|
||||
assert_eq!(kept("194.0.0.0/16"), (Some(50), vec![learned.clone()]));
|
||||
// By its prefix, apart from the route it came with.
|
||||
assert_eq!(
|
||||
kept("193.0.0.0/22"),
|
||||
(None, vec![learned.clone(), large(1299, 31, 10)])
|
||||
);
|
||||
// By the mark the session put on.
|
||||
assert_eq!(kept("195.0.0.0/16"), (Some(120), vec![learned]));
|
||||
Ok(())
|
||||
}
|
||||
Reference in new issue
Block a user