One word for a route that is not taken: rejected
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:
1 parent
c218ea4d85
commit
23e8b4aadb
21 files changed
+146
-154
No files matched your search
+10
-10
@@ -1105,7 +1105,7 @@ pub async fn kernel(State(state): State<AppState>) -> Json<KernelView> {
|
||||
|
||||
/// What one peer announced that was refused.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct RefusalsView {
|
||||
pub struct RejectionsView {
|
||||
/// The peer's configured name, or its address if it has none.
|
||||
pub peer: String,
|
||||
pub peer_addr: String,
|
||||
@@ -1114,11 +1114,11 @@ pub struct RefusalsView {
|
||||
/// The same, by reason.
|
||||
pub by_reason: BTreeMap<&'static str, u64>,
|
||||
/// The latest of them, newest first.
|
||||
pub recent: Vec<RefusedRouteView>,
|
||||
pub recent: Vec<RejectedRouteView>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct RefusedRouteView {
|
||||
pub struct RejectedRouteView {
|
||||
pub prefix: String,
|
||||
pub path_id: u32,
|
||||
pub reason: &'static str,
|
||||
@@ -1133,7 +1133,7 @@ pub struct RefusedRouteView {
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct RefusalQuery {
|
||||
pub struct RejectionQuery {
|
||||
/// Only this peer: its configured name, or its address.
|
||||
peer: Option<String>,
|
||||
}
|
||||
@@ -1142,10 +1142,10 @@ pub struct RefusalQuery {
|
||||
/// each peer that had any, how many and why, and the latest of them.
|
||||
pub async fn rejected_routes(
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<RefusalQuery>,
|
||||
) -> Json<Vec<RefusalsView>> {
|
||||
let mut peers: Vec<RefusalsView> = state
|
||||
.refusals
|
||||
Query(query): Query<RejectionQuery>,
|
||||
) -> Json<Vec<RejectionsView>> {
|
||||
let mut peers: Vec<RejectionsView> = state
|
||||
.rejections
|
||||
.peers()
|
||||
.into_iter()
|
||||
.filter(|(address, record)| {
|
||||
@@ -1154,7 +1154,7 @@ pub async fn rejected_routes(
|
||||
.as_deref()
|
||||
.is_none_or(|wanted| record.label() == wanted || address.is_called(wanted))
|
||||
})
|
||||
.map(|(address, record)| RefusalsView {
|
||||
.map(|(address, record)| RejectionsView {
|
||||
peer: record.label().to_string(),
|
||||
peer_addr: address.to_string(),
|
||||
total: record.total(),
|
||||
@@ -1166,7 +1166,7 @@ pub async fn rejected_routes(
|
||||
recent: record
|
||||
.recent()
|
||||
.into_iter()
|
||||
.map(|route| RefusedRouteView {
|
||||
.map(|route| RejectedRouteView {
|
||||
prefix: route.prefix.to_string(),
|
||||
path_id: route.path_id,
|
||||
reason: route.reason.as_str(),
|
||||
|
||||
@@ -20,15 +20,15 @@ use bgp_fib::Fib;
|
||||
use bgp_rib::LocalRib;
|
||||
use bgp_rib::table::RibTable;
|
||||
use bgp_rpki::Rpki;
|
||||
use bgp_server::refusals::RefusalLog;
|
||||
use bgp_server::rejections::RejectionLog;
|
||||
use bgp_server::restart::Restarts;
|
||||
use bgp_server::session::SessionRegistry;
|
||||
use tokio::net::TcpListener;
|
||||
use tracing::info;
|
||||
|
||||
pub use api::{
|
||||
AttrSummary, ConfigView, KernelView, LocalRouteView, PeerView, ReceivedRouteView, RefusalsView,
|
||||
RefusedRouteView, RoutesPage, RpkiView, StatusView,
|
||||
AttrSummary, ConfigView, KernelView, LocalRouteView, PeerView, ReceivedRouteView,
|
||||
RejectedRouteView, RejectionsView, RoutesPage, RpkiView, StatusView,
|
||||
};
|
||||
|
||||
/// Static facts about the router shown in status output.
|
||||
@@ -98,7 +98,7 @@ pub struct AppState {
|
||||
/// The routing table: routes learned from peers and the best of them.
|
||||
pub rib: Arc<RibTable>,
|
||||
/// What peers announced that was not taken.
|
||||
pub refusals: RefusalLog,
|
||||
pub rejections: RejectionLog,
|
||||
/// The data for route origin validation, where that is configured.
|
||||
pub rpki: Option<Rpki>,
|
||||
/// What keeps the best routes in the kernel, where that is configured.
|
||||
|
||||
@@ -113,11 +113,11 @@ from <select id="lookup-peer"><option value="">any peer</option></select>
|
||||
</table>
|
||||
<div class="pager" id="received-pager-below"></div>
|
||||
|
||||
<h1 id="refused">Rejected routes (<span id="refused-count">...</span>)</h1>
|
||||
<h1 id="rejected">Rejected routes (<span id="rejected-count">...</span>)</h1>
|
||||
<div class="dim">Routes that peers announced and their import policies did not take. The counts are of announcements since the daemon started: a route that a peer announces again, and that is rejected again, counts again. The latest rejected routes of each peer are kept.</div>
|
||||
<table class="data">
|
||||
<thead><tr><th>peer</th><th>prefix</th><th>AS path</th><th>reason</th><th>when</th><th>next hop</th><th>origin</th><th>MED</th><th>local pref</th><th>communities</th><th>large communities</th><th>other attributes</th></tr></thead>
|
||||
<tbody id="refused-body"></tbody>
|
||||
<tbody id="rejected-body"></tbody>
|
||||
</table>
|
||||
|
||||
<h1 id="events">Event log</h1>
|
||||
@@ -137,7 +137,7 @@ from <select id="lookup-peer"><option value="">any peer</option></select>
|
||||
|
||||
const $ = id => document.getElementById(id);
|
||||
const esc = s => String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||||
const state = { status: null, peers: [], local: [], received: [], receivedPage: {}, refused: [], rpki: null, kernel: null };
|
||||
const state = { status: null, peers: [], local: [], received: [], receivedPage: {}, rejected: [], rpki: null, kernel: null };
|
||||
const DASH = '<span class="dim">-</span>';
|
||||
|
||||
// ---- helpers -----------------------------------------------------------
|
||||
@@ -274,7 +274,7 @@ from <select id="lookup-peer"><option value="">any peer</option></select>
|
||||
<td>${up ? age(p.established_at_unix) : DASH}</td><td>${up ? (p.four_octet_asn ? 'yes' : 'no') : DASH}</td>
|
||||
<td>${up ? esc((p.address_families || []).join(', ')) + addPath(p) + (p.extended_next_hop ? ' · IPv4 with IPv6 next hops' : '') + graceful : DASH}</td>
|
||||
<td>${up ? p.routes + stale : DASH}</td>
|
||||
<td>${up ? (p.rejected_routes ? `<a class="bad" href="#refused">${p.rejected_routes}</a>` : 0) + (p.held_routes ? ` · <span title="Routes rejected as RPKI or ASPA invalid, or for a next hop that cannot be reached, and kept aside, to be taken if that changes">${p.held_routes} held now</span>` : '') : DASH}</td><td>${detail}</td></tr>`;
|
||||
<td>${up ? (p.rejected_routes ? `<a class="bad" href="#rejected">${p.rejected_routes}</a>` : 0) + (p.held_routes ? ` · <span title="Routes rejected as RPKI or ASPA invalid, or for a next hop that cannot be reached, and kept aside, to be taken if that changes">${p.held_routes} held now</span>` : '') : DASH}</td><td>${detail}</td></tr>`;
|
||||
}).join(''));
|
||||
renderPeerChoices();
|
||||
}
|
||||
@@ -387,7 +387,7 @@ from <select id="lookup-peer"><option value="">any peer</option></select>
|
||||
const data = r.updated_at_unix
|
||||
? `${r.vrps_ipv4} IPv4 and ${r.vrps_ipv6} IPv6 VRPs and ${r.aspas} ASPAs at serial ${r.serial ?? '?'}, confirmed ${age(r.updated_at_unix)} ago` +
|
||||
(r.expires_at_unix ? `, dropped in ${until(r.expires_at_unix)} unless confirmed again` : '')
|
||||
: '<span class="warn">no data: every route is "not found" and none is refused as invalid</span>';
|
||||
: '<span class="warn">no data: every route is "not found" and none is rejected as invalid</span>';
|
||||
setHTML(b, `Cache <span class="mono">${esc(r.cache || '?')}</span>: <span class="${RPKI_STATE_CLASS[r.state] || ''}">${esc(r.state)}</span>` +
|
||||
` <span class="dim">(RTR version ${r.protocol_version})</span>. ${data}.` +
|
||||
(r.updated_at_unix && r.protocol_version < 2 ? '<br><span class="warn">This cache speaks an RTR version without ASPAs: AS paths are not verified.</span>' : '') +
|
||||
@@ -406,7 +406,7 @@ from <select id="lookup-peer"><option value="">any peer</option></select>
|
||||
row('installed as', `<span class="mono">table ${k.table} metric ${k.metric} proto bgp</span>`) +
|
||||
(k.kept ? row('kept from before the restart', `<span class="warn">${fmtCount(k.kept)}</span>, not announced again yet`) : '') +
|
||||
(unresolved ? row('not installed', `<span class="warn">${unresolved}</span>`) : '') +
|
||||
(k.refused ? row('refused by the kernel', `<span class="bad">${fmtCount(k.refused)}</span>`) : '') +
|
||||
(k.rejected ? row('rejected by the kernel', `<span class="bad">${fmtCount(k.rejected)}</span>`) : '') +
|
||||
(k.last_error ? row('last error', `<span class="bad">${esc(k.last_error)}</span> (${fmtCount(k.errors)} errors in all)`) : '') +
|
||||
'</table>';
|
||||
// Interfaces that are up or have addresses get a row each; the others
|
||||
@@ -428,19 +428,19 @@ from <select id="lookup-peer"><option value="">any peer</option></select>
|
||||
}
|
||||
|
||||
// For each peer a line that says how many and why, then its latest few.
|
||||
const REFUSED_SHOWN = 20;
|
||||
const REJECTED_SHOWN = 20;
|
||||
function renderRefused() {
|
||||
const b = $('refused-body');
|
||||
const total = state.refused.reduce((n, p) => n + p.total, 0);
|
||||
const b = $('rejected-body');
|
||||
const total = state.rejected.reduce((n, p) => n + p.total, 0);
|
||||
const announcements = n => `${n} announcement${n === 1 ? '' : 's'}`;
|
||||
$('refused-count').textContent = total ? `${announcements(total)} from ${state.refused.length} peer${state.refused.length === 1 ? '' : 's'}` : '0';
|
||||
if (!state.refused.length) { setHTML(b, '<tr><td colspan="12" class="dim">Nothing a peer announced has been rejected.</td></tr>'); return; }
|
||||
setHTML(b, state.refused.map(p => {
|
||||
$('rejected-count').textContent = total ? `${announcements(total)} from ${state.rejected.length} peer${state.rejected.length === 1 ? '' : 's'}` : '0';
|
||||
if (!state.rejected.length) { setHTML(b, '<tr><td colspan="12" class="dim">Nothing a peer announced has been rejected.</td></tr>'); return; }
|
||||
setHTML(b, state.rejected.map(p => {
|
||||
const why = Object.entries(p.by_reason).sort((a, c) => c[1] - a[1]).map(([reason, n]) => `${n} ${esc(reason)}`).join(', ');
|
||||
const more = p.recent.length > REFUSED_SHOWN
|
||||
? ` <span class="dim">Latest ${REFUSED_SHOWN} shown; <a href="/api/routes/rejected?peer=${encodeURIComponent(p.peer)}">the latest ${p.recent.length} as json</a>.</span>` : '';
|
||||
const more = p.recent.length > REJECTED_SHOWN
|
||||
? ` <span class="dim">Latest ${REJECTED_SHOWN} shown; <a href="/api/routes/rejected?peer=${encodeURIComponent(p.peer)}">the latest ${p.recent.length} as json</a>.</span>` : '';
|
||||
return `<tr><td colspan="12"><b>${esc(p.peer)}</b> <span class="dim">${esc(p.peer_addr)}</span>: <span class="bad">${announcements(p.total)} rejected</span> (${why}).${more}</td></tr>` +
|
||||
p.recent.slice(0, REFUSED_SHOWN).map(r => { const s = r.summary; return `<tr><td></td>
|
||||
p.recent.slice(0, REJECTED_SHOWN).map(r => { const s = r.summary; return `<tr><td></td>
|
||||
<td class="mono">${esc(r.prefix)}${r.path_id ? ` <span class="dim">path ${r.path_id}</span>` : ''}</td>
|
||||
<td class="mono path">${asPath(r.as_path)}</td><td>${esc(r.reason)}</td><td class="nowrap">${age(r.at_unix)} ago</td>
|
||||
<td class="mono">${s.next_hop ? esc(s.next_hop) : DASH}</td><td>${s.origin ? esc(s.origin) : DASH}</td>
|
||||
@@ -480,7 +480,7 @@ from <select id="lookup-peer"><option value="">any peer</option></select>
|
||||
}
|
||||
renderReceived();
|
||||
}
|
||||
async function loadRefused() { state.refused = await getJSON('/api/routes/rejected'); renderRefused(); }
|
||||
async function loadRefused() { state.rejected = await getJSON('/api/routes/rejected'); renderRefused(); }
|
||||
async function loadRpki() { state.rpki = await getJSON('/api/rpki'); renderRpki(); }
|
||||
async function loadKernel() { state.kernel = await getJSON('/api/kernel'); renderKernel(); }
|
||||
async function loadAll() { await Promise.all([loadStatus(), loadPeers(), loadLocal(), loadReceived(), loadRefused(), loadRpki(), loadKernel()]); }
|
||||
|
||||
Reference in new issue
Block a user