Harden import, the kernel task and the RTR client after review
Three independent reviews (hijack resistance, reliability, code health) of the whole tree. This is what they found that could let a bad route in or take the daemon down. Hijacks: - A route had two paths on a two-octet session, AS_PATH and AS4_PATH, read in two places by two rules. A peer could make the origin checked on import differ from the one checked at revalidation, and get an RPKI-invalid route into the table. AS4_PATH is now merged into AS_PATH once, on receipt (RFC 6793 §4.2.3), and nothing else knows of it. - The next hop is judged on import: it must be usable, agreed on (RFC 8950), and by default the peer itself. - Our own originated prefixes are not taken from other networks. - A route for a network the machine is on is never put in the kernel. - Paths with an AS_SET are refused (RFC 9774). - A configured peer gets one session, not one per BGP Identifier. - More IPv6 special-purpose prefixes are bogons. Reliability: - A batch of requests the kernel refused could overflow the netlink socket and end the daemon. The socket has room now, a lost answer or a silent kernel means asking what is there, and neither is fatal. - Working the routes out again within the grace period after a restart no longer drops the routes being kept. - That work can no longer be put off for ever by interface changes. - Peers whose sessions keep failing are slowed down. - Unused attribute sets are swept when they pile up, not only each minute. - Log lines a peer can cause are said once in a while. - An RTR answer is bounded in size and time; data cannot expire between two refreshes. - The shard of a key is taken from a stirred hash: neighbouring bits of the hash proved alike for prefixes in a row. The test that showed it, failing one run in sixteen, is deterministic now. The lab has a phase, hijacks, in which FRR tries the first three. route::apply is a struct with methods instead of nested closures, and the kernel task's loop is shorter. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
fd2248feb5
commit
9ca86ede2f
34 files changed
+1696
-470
No files matched your search
@@ -6,6 +6,7 @@ use std::collections::BTreeSet;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::time::Duration;
|
||||
|
||||
use bgp_integration_tests::default_policy;
|
||||
use bgp_integration_tests::test_cache::TestCache;
|
||||
use bgp_integration_tests::test_peer::TestPeer;
|
||||
use bgp_integration_tests::test_server::TestServer;
|
||||
@@ -154,8 +155,7 @@ fn aspas() -> Vec<(u32, Vec<u32>)> {
|
||||
#[tokio::test]
|
||||
async fn test_a_leaked_route_is_refused_and_the_rest_are_taken() -> Result<()> {
|
||||
let cache = TestCache::start_with(vec![], aspas()).await;
|
||||
let Setup { server, .. } =
|
||||
server_with(&cache, Some(PeerRole::Peer), ImportPolicy::default()).await?;
|
||||
let Setup { server, .. } = server_with(&cache, Some(PeerRole::Peer), default_policy()).await?;
|
||||
let (base, _http) = server.serve_http().await?;
|
||||
let mut peer = established(&server).await?;
|
||||
|
||||
@@ -211,7 +211,7 @@ async fn test_a_route_that_has_crossed_a_peering_is_taken_from_a_provider_only()
|
||||
(None, true),
|
||||
] {
|
||||
let cache = TestCache::start_with(vec![], aspas()).await;
|
||||
let Setup { server, .. } = server_with(&cache, role, ImportPolicy::default()).await?;
|
||||
let Setup { server, .. } = server_with(&cache, role, default_policy()).await?;
|
||||
let mut peer = established(&server).await?;
|
||||
announce(&mut peer, &["8.8.8.0/24"], &[PEER_ASN, 3356, 15169]).await?;
|
||||
let expected = if taken {
|
||||
@@ -232,7 +232,7 @@ async fn test_paths_from_a_route_server_do_not_begin_with_its_as() -> Result<()>
|
||||
let cache = TestCache::start_with(vec![], aspas()).await;
|
||||
let import = ImportPolicy {
|
||||
accept_any_first_as: true,
|
||||
..ImportPolicy::default()
|
||||
..default_policy()
|
||||
};
|
||||
let Setup { server, .. } = server_with(&cache, Some(PeerRole::RouteServer), import).await?;
|
||||
let mut peer = established(&server).await?;
|
||||
@@ -250,8 +250,7 @@ async fn test_paths_from_a_route_server_do_not_begin_with_its_as() -> Result<()>
|
||||
#[tokio::test]
|
||||
async fn test_routes_follow_the_aspas() -> Result<()> {
|
||||
let cache = TestCache::start_with(vec![], aspas()).await;
|
||||
let Setup { server, .. } =
|
||||
server_with(&cache, Some(PeerRole::Peer), ImportPolicy::default()).await?;
|
||||
let Setup { server, .. } = server_with(&cache, Some(PeerRole::Peer), default_policy()).await?;
|
||||
let mut peer = established(&server).await?;
|
||||
|
||||
announce(&mut peer, &["194.0.0.0/24"], &[PEER_ASN, 2121]).await?;
|
||||
@@ -311,7 +310,7 @@ async fn test_a_peer_can_be_exempt_from_path_verification() -> Result<()> {
|
||||
let cache = TestCache::start_with(vec![], aspas()).await;
|
||||
let import = ImportPolicy {
|
||||
accept_aspa_invalid: true,
|
||||
..ImportPolicy::default()
|
||||
..default_policy()
|
||||
};
|
||||
let Setup { server, .. } = server_with(&cache, Some(PeerRole::Peer), import).await?;
|
||||
let (base, _http) = server.serve_http().await?;
|
||||
@@ -337,8 +336,7 @@ async fn test_a_route_is_held_until_origin_and_path_are_both_in_order() -> Resul
|
||||
asn,
|
||||
};
|
||||
let cache = TestCache::start_with(vec![google(13335)], aspas()).await;
|
||||
let Setup { server, .. } =
|
||||
server_with(&cache, Some(PeerRole::Peer), ImportPolicy::default()).await?;
|
||||
let Setup { server, .. } = server_with(&cache, Some(PeerRole::Peer), default_policy()).await?;
|
||||
let mut peer = established(&server).await?;
|
||||
announce(&mut peer, &["8.8.8.0/24"], &[PEER_ASN, 3356, 15169]).await?;
|
||||
table_becomes(&server, &[], 1).await?;
|
||||
|
||||
Reference in new issue
Block a user