One word for a route that is not taken: rejected
ci / check (push) Canceled after 0s
ci / docs (push) Canceled after 0s
ci / interop (push) Canceled after 0s
ci / fuzz (push) Canceled after 0s

The API, the metrics, the status page and the documentation say a route
is "rejected" (/api/routes/rejected, rejected_routes,
bgp_routes_rejected_total, "Rejected routes"), and the reason type is
Rejection. The code that keeps the record said "refused": RefusalLog,
PeerRefusals, RefusedRoute, RefusalsView, RefusedRouteView, the refusals
module. Those are RejectionLog, PeerRejections, RejectedRoute,
RejectionsView, RejectedRouteView and rejections now, and the log line
says "Rejected a route". Nothing the API or the metrics expose changes.

Refusing stays what it was: a connection turned away at the door
(server::Refusal), a kernel that would not take a route, an OPEN we
would not have.

The two aliases of SendReceive, AddPathSendRecv and OrfSendRecv, are
gone; it is one type.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Rayhaan JaufeerallyandClaude Fable 5.1 committed 2026-10-06 12:10:58 +02:00
1 parent c218ea4d85
commit 23e8b4aadb
21 files changed
+146 -154

No files matched your search

+5 -5
View File
@@ -11,7 +11,7 @@ use bgp_rib::table::{Event, PeerIndex, Reader, RibTable};
use bgp_rpki::Rpki;
use bgp_server::config::{Config, PeerConfig};
use bgp_server::error::ServerError;
use bgp_server::refusals::RefusalLog;
use bgp_server::rejections::RejectionLog;
use bgp_server::restart::Restarts;
use bgp_server::server::BgpServer;
use bgp_server::session::{SessionHandle, SessionRegistry};
@@ -36,7 +36,7 @@ pub struct TestServer {
/// Live view of the server's established sessions.
pub registry: SessionRegistry,
/// What peers announced that was not taken.
pub refusals: RefusalLog,
pub rejections: RejectionLog,
/// The data for route origin validation, if the server validates.
pub rpki: Option<Rpki>,
/// What keeps the best routes in the kernel, if the server does.
@@ -92,7 +92,7 @@ impl TestServer {
let rib = Arc::clone(&bound.rib);
let local_rib = bound.local_rib.clone();
let registry = bound.registry.clone();
let refusals = bound.refusals.clone();
let rejections = bound.rejections.clone();
let rpki = bound.rpki.clone();
let kernel = bound.kernel.clone();
let restarts = Arc::clone(&bound.restarts);
@@ -104,7 +104,7 @@ impl TestServer {
rib,
local_rib,
registry,
refusals,
rejections,
rpki,
kernel,
restarts,
@@ -348,7 +348,7 @@ impl TestServer {
registry: self.registry.clone(),
local_rib: self.local_rib.clone(),
rib: Arc::clone(&self.rib),
refusals: self.refusals.clone(),
rejections: self.rejections.clone(),
rpki: self.rpki.clone(),
kernel: self.kernel.clone(),
restarts: Arc::clone(&self.restarts),
+11 -14
View File
@@ -9,7 +9,7 @@ use bgp_integration_tests::test_peer::TestPeer;
use bgp_integration_tests::test_server::TestServer;
use bgp_integration_tests::{peer_config, prefix, wait_until};
use bgp_packet::message::PathAttribute;
use bgp_packet::open::{AddPathEntry, AddPathSendRecv, Capability, OpenMessage};
use bgp_packet::open::{AddPathEntry, Capability, OpenMessage, SendReceive};
use bgp_primitives::constants::AfiSafi;
use bgp_primitives::ip_prefix::Nlri;
use bgp_rib::{LocalRoute, LocalRouteAttrs};
@@ -24,10 +24,7 @@ const PEER_ASN: u32 = 65001;
/// A server on which connections from this machine are those of a
/// configured peer, with ADD-PATH offered to it as `add_path` says.
async fn server_offering(
add_path: Option<AddPathSendRecv>,
originate: &[&str],
) -> Result<TestServer> {
async fn server_offering(add_path: Option<SendReceive>, originate: &[&str]) -> Result<TestServer> {
let originate: Vec<LocalRoute> = originate
.iter()
.map(|p| LocalRoute::new(prefix(p), LocalRouteAttrs::default()))
@@ -50,7 +47,7 @@ async fn server_offering(
/// IPv4 unicast. Returns the peer and the server's OPEN.
async fn peer_offering(
server: &TestServer,
offer: Option<AddPathSendRecv>,
offer: Option<SendReceive>,
) -> Result<(TestPeer, OpenMessage)> {
let mut capabilities = vec![Capability::FourByteAsn { asn: PEER_ASN }];
capabilities.extend(SUPPORTED.into_iter().map(Capability::multiprotocol));
@@ -71,7 +68,7 @@ async fn peer_offering(
Ok((peer, open))
}
fn add_path_offered(open: &OpenMessage) -> Vec<(AfiSafi, AddPathSendRecv)> {
fn add_path_offered(open: &OpenMessage) -> Vec<(AfiSafi, SendReceive)> {
open.capabilities()
.filter_map(|capability| match capability {
Capability::AddPath { entries } => Some(entries),
@@ -111,14 +108,14 @@ fn held(server: &TestServer) -> Vec<(u32, Vec<u32>, bool)> {
/// of them is chosen, and each is withdrawn on its own.
#[tokio::test]
async fn test_several_routes_for_a_prefix_from_one_peer() -> Result<()> {
let server = server_offering(Some(AddPathSendRecv::Receive), &[]).await?;
let server = server_offering(Some(SendReceive::Receive), &[]).await?;
let (base, _http) = server.serve_http().await?;
let (mut peer, open) = peer_offering(&server, Some(AddPathSendRecv::Send)).await?;
let (mut peer, open) = peer_offering(&server, Some(SendReceive::Send)).await?;
// The server offered to receive, for every family it offers.
assert_eq!(
add_path_offered(&open),
SUPPORTED.map(|family| (family, AddPathSendRecv::Receive))
SUPPORTED.map(|family| (family, SendReceive::Receive))
);
// In use for the one family the peer sends for, and in that direction.
let session = &server.registry.list()[0];
@@ -195,7 +192,7 @@ async fn test_several_routes_for_a_prefix_from_one_peer() -> Result<()> {
#[tokio::test]
async fn test_add_path_is_not_used_unless_both_sides_offer_it() -> Result<()> {
let server = server_offering(None, &["10.100.0.0/16"]).await?;
let (mut peer, open) = peer_offering(&server, Some(AddPathSendRecv::Both)).await?;
let (mut peer, open) = peer_offering(&server, Some(SendReceive::Both)).await?;
assert!(add_path_offered(&open).is_empty());
let session = &server.registry.list()[0];
assert!(session.add_path_receive.is_empty() && session.add_path_send.is_empty());
@@ -209,7 +206,7 @@ async fn test_add_path_is_not_used_unless_both_sides_offer_it() -> Result<()> {
assert_eq!(held(&server), [(0, vec![PEER_ASN], true)]);
// And the other way round: offered by the server, not by the peer.
let server = server_offering(Some(AddPathSendRecv::Both), &["10.100.0.0/16"]).await?;
let server = server_offering(Some(SendReceive::Both), &["10.100.0.0/16"]).await?;
let (mut peer, open) = peer_offering(&server, None).await?;
assert_eq!(add_path_offered(&open).len(), SUPPORTED.len());
let update = peer.recv_update().await?;
@@ -222,8 +219,8 @@ async fn test_add_path_is_not_used_unless_both_sides_offer_it() -> Result<()> {
/// understands them.
#[tokio::test]
async fn test_prefixes_are_sent_with_path_identifiers_when_agreed() -> Result<()> {
let server = server_offering(Some(AddPathSendRecv::Both), &["10.100.0.0/16"]).await?;
let (mut peer, _) = peer_offering(&server, Some(AddPathSendRecv::Receive)).await?;
let server = server_offering(Some(SendReceive::Both), &["10.100.0.0/16"]).await?;
let (mut peer, _) = peer_offering(&server, Some(SendReceive::Receive)).await?;
let session = &server.registry.list()[0];
assert_eq!(
session.add_path_send,
+2 -2
View File
@@ -182,7 +182,7 @@ async fn test_routes_follow_the_aspas() -> Result<()> {
changed.push((2121, vec![1101]));
cache.set_aspas(changed);
server.table_becomes(&[], 2).await?;
let refused = &server.refusals.peers()[0].1;
let refused = &server.rejections.peers()[0].1;
assert_eq!(refused.total(), 2);
assert_eq!(refused.recent()[0].prefix, prefix("194.0.0.0/24"));
assert_eq!(&refused.recent()[0].as_path[..], [PEER_ASN, 2121]);
@@ -246,7 +246,7 @@ async fn test_a_peer_can_be_exempt_from_path_verification() -> Result<()> {
.json()
.await?;
assert_eq!(routes["routes"][0]["aspa"], "invalid");
assert!(server.refusals.peers().is_empty());
assert!(server.rejections.peers().is_empty());
Ok(())
}
+7 -4
View File
@@ -202,8 +202,11 @@ async fn test_a_marked_route_from_a_customer_is_a_leak() -> Result<()> {
marks(&server, 1).await?,
[("194.0.0.0/24".to_string(), None)]
);
wait_until("a route is refused", || !server.refusals.peers().is_empty()).await?;
let refused = &server.refusals.peers()[0].1;
wait_until("a route is refused", || {
!server.rejections.peers().is_empty()
})
.await?;
let refused = &server.rejections.peers()[0].1;
assert_eq!(refused.total(), 1);
assert_eq!(refused.recent()[0].prefix, prefix("8.8.8.0/24"));
assert_eq!(
@@ -238,7 +241,7 @@ async fn test_a_route_marked_by_someone_else_is_a_leak_from_a_peer() -> Result<(
("195.0.0.0/24".to_string(), Some(PEER_ASN)),
]
);
assert_eq!(server.refusals.peers()[0].1.total(), 1);
assert_eq!(server.rejections.peers()[0].1.total(), 1);
Ok(())
}
@@ -288,7 +291,7 @@ async fn test_routes_from_a_provider_are_marked_on_the_way_in() -> Result<()> {
],
"four-octet AS numbers: {four_octet}"
);
assert!(server.refusals.peers().is_empty());
assert!(server.rejections.peers().is_empty());
}
Ok(())
}
+2 -2
View File
@@ -146,7 +146,7 @@ async fn test_routes_follow_the_validation_data() -> Result<()> {
// table is now invalid, and goes.
cache.set(vec![ripe, vrp("8.8.8.0/24", 24, 13335)]);
server.table_becomes(&[], 2).await?;
let refused = &server.refusals.peers()[0].1;
let refused = &server.rejections.peers()[0].1;
assert_eq!(refused.total(), 2);
assert_eq!(refused.recent()[0].prefix, prefix("8.8.8.0/24"));
assert_eq!(&refused.recent()[0].as_path[..], [PEER_ASN, 15169]);
@@ -225,7 +225,7 @@ async fn test_a_peer_can_be_exempt_from_validation() -> Result<()> {
.json()
.await?;
assert_eq!(routes["routes"][0]["rpki"], "invalid");
assert!(server.refusals.peers().is_empty());
assert!(server.rejections.peers().is_empty());
Ok(())
}