diff --git a/crates/fib/src/kernel.rs b/crates/fib/src/kernel.rs index 3275e4d..7e414a6 100644 --- a/crates/fib/src/kernel.rs +++ b/crates/fib/src/kernel.rs @@ -728,7 +728,7 @@ impl Task { Op::Remove { prefix } => format!("remove {prefix}"), }; // Counted always, said out loud once in a while: a table's worth - // of refusals is a table's worth of lines. + // of rejections is a table's worth of lines. ERRORS.inc(); let said = format!("the kernel refused to {what}: {error}"); let before = self.fib.update(|status| { @@ -738,9 +738,9 @@ impl Task { before }); if before == 0 || before.is_power_of_two() { - warn!(refusals = before + 1, "{said}"); + warn!(rejections = before + 1, "{said}"); } else { - debug!(refusals = before + 1, "{said}"); + debug!(rejections = before + 1, "{said}"); } Ok(self.plan.refused_by_kernel(op)) } diff --git a/crates/fib/tests/kernel.rs b/crates/fib/tests/kernel.rs index 2b7b3f4..620e628 100644 --- a/crates/fib/tests/kernel.rs +++ b/crates/fib/tests/kernel.rs @@ -463,7 +463,7 @@ async fn many_routes_are_installed_and_removed() { /// Someone empties the table behind our back (`ip route flush`), and then /// the peers withdraw everything: the kernel refuses every removal, a -/// table's worth of refusals at once. That is more than the socket holds +/// table's worth of rejections at once. That is more than the socket holds /// answers for, and must be no more than a reason to look again. #[tokio::test(flavor = "multi_thread")] #[ignore = "changes the machine's routes; run scripts/kernel-tests.sh"] diff --git a/crates/integration-tests/src/test_server.rs b/crates/integration-tests/src/test_server.rs index 5ae0799..c4c49cd 100644 --- a/crates/integration-tests/src/test_server.rs +++ b/crates/integration-tests/src/test_server.rs @@ -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, /// 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), diff --git a/crates/integration-tests/tests/add_path.rs b/crates/integration-tests/tests/add_path.rs index 37bff79..55b12bb 100644 --- a/crates/integration-tests/tests/add_path.rs +++ b/crates/integration-tests/tests/add_path.rs @@ -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, - originate: &[&str], -) -> Result { +async fn server_offering(add_path: Option, originate: &[&str]) -> Result { let originate: Vec = 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, + offer: Option, ) -> 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, 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, diff --git a/crates/integration-tests/tests/aspa.rs b/crates/integration-tests/tests/aspa.rs index 79f4926..708e512 100644 --- a/crates/integration-tests/tests/aspa.rs +++ b/crates/integration-tests/tests/aspa.rs @@ -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(()) } diff --git a/crates/integration-tests/tests/roles.rs b/crates/integration-tests/tests/roles.rs index 497fd9a..219e792 100644 --- a/crates/integration-tests/tests/roles.rs +++ b/crates/integration-tests/tests/roles.rs @@ -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(()) } diff --git a/crates/integration-tests/tests/rpki.rs b/crates/integration-tests/tests/rpki.rs index 4ef188d..15ca34a 100644 --- a/crates/integration-tests/tests/rpki.rs +++ b/crates/integration-tests/tests/rpki.rs @@ -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(()) } diff --git a/crates/packet/src/open.rs b/crates/packet/src/open.rs index f9fe16b..3b6ab18 100644 --- a/crates/packet/src/open.rs +++ b/crates/packet/src/open.rs @@ -452,7 +452,7 @@ impl Capability { Some(AddPathEntry { afi: Afi(u16::from_be_bytes([entry[0], entry[1]])), safi: Safi(entry[2]), - send_recv: AddPathSendRecv::try_from(entry[3]).ok()?, + send_recv: SendReceive::try_from(entry[3]).ok()?, }) }) .collect(); @@ -589,7 +589,7 @@ impl OrfFamily { for _ in 0..count { let (rest, orf_type) = be_u8(buf)?; let (rest, send_recv) = be_u8(rest)?; - let send_recv = OrfSendRecv::try_from(send_recv) + let send_recv = SendReceive::try_from(send_recv) .map_err(|e| malformed(format!("Failed to parse ORF send/recv: {e}")))?; orfs.push(OutboundRouteFilterEntry { r#type: OrfType(orf_type), @@ -622,7 +622,7 @@ impl OrfFamily { #[derive(Debug, Clone, Serialize, PartialEq, Eq)] pub struct OutboundRouteFilterEntry { pub r#type: OrfType, - pub send_recv: OrfSendRecv, + pub send_recv: SendReceive, } /// An ORF type. Any number is one: most are unassigned or vendor-specific. @@ -647,9 +647,6 @@ pub enum SendReceive { Both = 3, } -pub type OrfSendRecv = SendReceive; -pub type AddPathSendRecv = SendReceive; - impl SendReceive { /// Whether the speaker that advertised this sends. pub fn sends(self) -> bool { @@ -683,7 +680,7 @@ impl TryFrom for SendReceive { pub struct AddPathEntry { pub afi: Afi, pub safi: Safi, - pub send_recv: AddPathSendRecv, + pub send_recv: SendReceive, } impl AddPathEntry { @@ -780,9 +777,8 @@ mod tests { use eyre::Result; use crate::open::{ - AddPathEntry, AddPathSendRecv, Capability, ExtendedNextHopEntry, GracefulRestartEntry, - MultiLabelEntry, OpenMessage, OpenOption, OrfFamily, OrfSendRecv, OrfType, - OutboundRouteFilterEntry, + AddPathEntry, Capability, ExtendedNextHopEntry, GracefulRestartEntry, MultiLabelEntry, + OpenMessage, OpenOption, OrfFamily, OrfType, OutboundRouteFilterEntry, SendReceive, }; use bgp_primitives::constants::{Afi, Safi}; @@ -1005,7 +1001,7 @@ mod tests { entries: vec![AddPathEntry { afi: evpn.0, safi: evpn.1, - send_recv: AddPathSendRecv::Both, + send_recv: SendReceive::Both, }], }, ), @@ -1054,12 +1050,12 @@ mod tests { AddPathEntry { afi: Afi::IPV4, safi: Safi::UNICAST, - send_recv: AddPathSendRecv::Receive, + send_recv: SendReceive::Receive, }, AddPathEntry { afi: Afi::IPV6, safi: Safi::UNICAST, - send_recv: AddPathSendRecv::Both, + send_recv: SendReceive::Both, }, ], } @@ -1119,8 +1115,8 @@ mod tests { assert_eq!( cap, Capability::OutboundRouteFilter(vec![ - family(Afi::IPV4, OrfSendRecv::Both), - family(Afi::IPV6, OrfSendRecv::Receive), + family(Afi::IPV4, SendReceive::Both), + family(Afi::IPV6, SendReceive::Receive), ]) ); let mut out = BytesMut::new(); diff --git a/crates/packet/tests/properties.rs b/crates/packet/tests/properties.rs index c584431..83a5bfa 100644 --- a/crates/packet/tests/properties.rs +++ b/crates/packet/tests/properties.rs @@ -25,9 +25,9 @@ use bgp_packet::message::{ PathAttributeFlags, RouteRefreshMessage, UpdateMessage, }; use bgp_packet::open::{ - AddPathEntry, AddPathSendRecv, BgpRole, BgpSecHeader, Capability, ExtendedNextHopEntry, - GracefulRestartEntry, LongLivedGracefulRestartEntry, MultiLabelEntry, OpenMessage, OpenOption, - OrfFamily, OrfSendRecv, OrfType, OutboundRouteFilterEntry, + AddPathEntry, BgpRole, BgpSecHeader, Capability, ExtendedNextHopEntry, GracefulRestartEntry, + LongLivedGracefulRestartEntry, MultiLabelEntry, OpenMessage, OpenOption, OrfFamily, OrfType, + OutboundRouteFilterEntry, SendReceive, }; use bgp_primitives::constants::{Afi, AfiSafi, IpVersion, Safi}; use bgp_primitives::ip_prefix::{IpPrefix, Nlri}; @@ -377,16 +377,16 @@ fn capability() -> impl Strategy { let orf = ( any::().prop_map(OrfType), prop_oneof![ - Just(OrfSendRecv::Receive), - Just(OrfSendRecv::Send), - Just(OrfSendRecv::Both) + Just(SendReceive::Receive), + Just(SendReceive::Send), + Just(SendReceive::Both) ], ) .prop_map(|(r#type, send_recv)| OutboundRouteFilterEntry { r#type, send_recv }); let add_path = prop_oneof![ - Just(AddPathSendRecv::Receive), - Just(AddPathSendRecv::Send), - Just(AddPathSendRecv::Both) + Just(SendReceive::Receive), + Just(SendReceive::Send), + Just(SendReceive::Both) ]; prop_oneof![ (afi(), safi()).prop_map(|(afi, safi)| Capability::MultiProtocol { afi, safi }), diff --git a/crates/server/src/config.rs b/crates/server/src/config.rs index eeeddb7..8cb424f 100644 --- a/crates/server/src/config.rs +++ b/crates/server/src/config.rs @@ -6,7 +6,7 @@ use std::time::Duration; use bgp_fib::FibConfig; use bgp_packet::community; use bgp_packet::message::OriginPathAttribute; -use bgp_packet::open::AddPathSendRecv; +use bgp_packet::open::SendReceive; use bgp_primitives::constants::{AfiSafi, IpVersion}; use bgp_primitives::ip_prefix::IpPrefix; use bgp_rib::{LocalRoute, LocalRouteAttrs}; @@ -87,7 +87,7 @@ pub struct PeerConfig { pub families: FamilySet, /// The directions ADD-PATH (RFC 7911) is offered to this peer for, on /// every one of `families`. `None` offers it for neither. - pub add_path: Option, + pub add_path: Option, /// Which of the peer's routes are taken. pub import: ImportPolicy, /// What the peer is to us, if that was said. @@ -466,9 +466,9 @@ fn parse_peer(peer: Peer, defaults: &PeerDefaults) -> Result None, - Ok(proto::AddPath::ADD_PATH_RECEIVE) => Some(AddPathSendRecv::Receive), - Ok(proto::AddPath::ADD_PATH_SEND) => Some(AddPathSendRecv::Send), - Ok(proto::AddPath::ADD_PATH_BOTH) => Some(AddPathSendRecv::Both), + Ok(proto::AddPath::ADD_PATH_RECEIVE) => Some(SendReceive::Receive), + Ok(proto::AddPath::ADD_PATH_SEND) => Some(SendReceive::Send), + Ok(proto::AddPath::ADD_PATH_BOTH) => Some(SendReceive::Both), Err(_) => return Err(PeerProblem::AddPath), }; @@ -1019,12 +1019,9 @@ mod tests { assert_eq!(add_path("add_path: ADD_PATH_OFF"), None); assert_eq!( add_path("add_path: ADD_PATH_RECEIVE"), - Some(AddPathSendRecv::Receive) - ); - assert_eq!( - add_path("add_path: ADD_PATH_BOTH"), - Some(AddPathSendRecv::Both) + Some(SendReceive::Receive) ); + assert_eq!(add_path("add_path: ADD_PATH_BOTH"), Some(SendReceive::Both)); } #[test] diff --git a/crates/server/src/connection.rs b/crates/server/src/connection.rs index 33a35e9..7c26bfc 100644 --- a/crates/server/src/connection.rs +++ b/crates/server/src/connection.rs @@ -19,7 +19,7 @@ use bgp_packet::message::RouteRefreshMessage; use bgp_packet::message::{ BGP_HEADER_LEN, BGP_MAX_MSG_LEN, Decoded, Message, PathAttribute, UpdateMessage, }; -use bgp_packet::open::AddPathSendRecv; +use bgp_packet::open::SendReceive; use bgp_packet::update_decode::{UpdateError, merge_as4_path}; use bgp_primitives::constants::{AfiSafi, IpVersion}; use bgp_primitives::ip_prefix::Nlri; @@ -49,7 +49,7 @@ use crate::metrics::{ use crate::panic::contained; use crate::policy::{self, ExportLimits, ExportPolicy, ImportPolicy, PeerRole}; use crate::reach::{Reach, neighbor_at}; -use crate::refusals::{PeerRefusals, RefusalLog}; +use crate::rejections::{PeerRejections, RejectionLog}; use crate::restart::{self, Agreed, GracefulRestartConfig, Leave, Restarts}; use crate::routes::{self, Held}; use crate::rpki::{Checks, Subject, Validator}; @@ -84,7 +84,7 @@ pub struct ServerContext { /// Peers that are being kept down for going over their limits. pub hold_downs: HoldDowns, /// 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, /// The machine's interfaces, where routes are installed in the kernel. @@ -106,7 +106,7 @@ pub struct PeerSettings { /// The address families to offer the peer. pub families: FamilySet, /// The directions to offer the peer ADD-PATH for (RFC 7911). - pub add_path: Option, + pub add_path: Option, /// Which of the peer's routes are taken. pub import: Arc, /// What the peer is to us, if that is known. @@ -262,7 +262,7 @@ fn hold_down_after(session: &Session, result: &Result<(), SessionError>) { if let Some((why, exceeded)) = over_limit { hold_downs.hold(address, session.peer.import.hold_down, why); exceeded - .with_label_values(&[session.refusals.label()]) + .with_label_values(&[session.rejections.label()]) .inc(); } if let (Some(up), Some(_)) = (&session.established, &session.peer.name) @@ -313,7 +313,7 @@ async fn release_routes( } = established; // The peer has no session to count its routes for. // Fails only if the session never set it, which is nothing to do. - let _ = PEER_ROUTES.remove_label_values(&[session.refusals.label()]); + let _ = PEER_ROUTES.remove_label_values(&[session.rejections.label()]); if *session.leave.borrow() == Some(Leave::Restart) { // The daemon is about to go, and its routes with it; the kernel // keeps its own. Taking them out first would only hold the @@ -418,7 +418,7 @@ impl Established { fn import<'a>( &'a mut self, policy: &'a ImportPolicy, - refusals: &'a PeerRefusals, + rejections: &'a PeerRejections, ) -> (routes::Import<'a>, &'a mut Held) { let import = routes::Import { peer: self.rib_peer, @@ -428,7 +428,7 @@ impl Established { .validation .as_mut() .map_or(Checks::NONE, |validation| validation.validator.checks()), - refusals, + rejections, }; (import, &mut self.held) } @@ -652,7 +652,7 @@ struct Session { /// What is configured for the peer. peer: PeerSettings, /// Where what is refused is noted. - refusals: Arc, + rejections: Arc, /// Says when the daemon is leaving, and how. leave: watch::Receiver>, } @@ -690,7 +690,7 @@ impl Session { .unwrap_or_else(|| PeerAddress::from(peer_addr).to_string()); let leave = server.registry.leaving(); let session = Session { - refusals: server.refusals.of_peer(peer_addr.into(), &label), + rejections: server.rejections.of_peer(peer_addr.into(), &label), fsm, frames: FrameReader::new(reader), writer, @@ -1018,7 +1018,7 @@ impl Session { routes: PeerRoutes::new(Arc::clone(&self.server.rib), rib_peer), from: self.route_source(negotiated), counters: Arc::clone(&handle.counters), - routes_metric: PEER_ROUTES.with_label_values(&[self.refusals.label()]), + routes_metric: PEER_ROUTES.with_label_values(&[self.rejections.label()]), _registration: self.server.registry.register(handle), }); if let Some(kept) = kept { @@ -1228,7 +1228,7 @@ impl Session { let Some(established) = &mut self.established else { return Ok(()); }; - let (import, held) = established.import(&self.peer.import, &self.refusals); + let (import, held) = established.import(&self.peer.import, &self.rejections); let routes::Applied { outcome, rejected } = routes::apply(&self.server.rib, &import, held, update, attributes_wire)?; // One line for every UPDATE is too much to log by default: a full @@ -1252,7 +1252,7 @@ impl Session { let Some(established) = &mut self.established else { return vec![]; }; - let (import, held) = established.import(&self.peer.import, &self.refusals); + let (import, held) = established.import(&self.peer.import, &self.rejections); let routes::Applied { outcome, rejected } = routes::revalidate(&self.server.rib, &import, held, suspects); if outcome != Outcome::default() { @@ -1606,7 +1606,7 @@ mod tests { collisions: CollisionTable::new(false), hold_downs: HoldDowns::default(), interfaces: None, - refusals: RefusalLog::default(), + rejections: RejectionLog::default(), rpki: None, restarts: Restarts::new(rib, true, [], 120), }); @@ -1657,7 +1657,7 @@ mod tests { collisions: CollisionTable::new(false), hold_downs: HoldDowns::default(), interfaces: None, - refusals: RefusalLog::default(), + rejections: RejectionLog::default(), rpki: None, restarts: Restarts::new(rib, true, [], 120), }); @@ -1732,7 +1732,7 @@ mod tests { }, policy: &ImportPolicy::permissive(), checks: Checks::NONE, - refusals: &RefusalLog::default().of_peer( + rejections: &RejectionLog::default().of_peer( std::net::IpAddr::from(Ipv4Addr::new(192, 0, 2, 1)).into(), "test", ), diff --git a/crates/server/src/fsm.rs b/crates/server/src/fsm.rs index 981c104..96acaad 100644 --- a/crates/server/src/fsm.rs +++ b/crates/server/src/fsm.rs @@ -17,7 +17,7 @@ use std::time::Duration; use bgp_packet::errors::{BgpError, CeaseSubcode, DecodeError, OpenMsgSubcode}; use bgp_packet::message::{AS_TRANS, BGP4_VERSION, Message, NotificationMessage, UpdateMessage}; use bgp_packet::open::{ - AddPathEntry, AddPathSendRecv, Capability, ExtendedNextHopEntry, OpenMessage, OpenOption, + AddPathEntry, Capability, ExtendedNextHopEntry, OpenMessage, OpenOption, SendReceive, }; use bgp_primitives::constants::{Afi, AfiSafi, Safi}; @@ -69,7 +69,7 @@ pub struct FsmConfig { pub families: FamilySet, /// The directions to offer ADD-PATH (RFC 7911) for, on each of /// `families`; `None` for neither. - pub add_path: Option, + pub add_path: Option, /// What the peer is to us, if that is configured. Our OPEN then says /// what we are to it, and the peer's must not say otherwise (RFC 9234). /// Not for a peer in our own AS. @@ -900,8 +900,7 @@ mod tests { #[test] fn add_path_is_in_use_where_one_side_sends_and_the_other_receives() { use bgp_packet::open::SendReceive::{Both, Receive, Send}; - let negotiated = |ours: Option, - theirs: Vec<(AfiSafi, AddPathSendRecv)>| { + let negotiated = |ours: Option, theirs: Vec<(AfiSafi, SendReceive)>| { let (mut fsm, first) = Fsm::new(FsmConfig { add_path: ours, ..config() diff --git a/crates/server/src/lib.rs b/crates/server/src/lib.rs index addb0bb..3f0617d 100644 --- a/crates/server/src/lib.rs +++ b/crates/server/src/lib.rs @@ -14,7 +14,7 @@ pub mod metrics; mod panic; pub mod policy; mod reach; -pub mod refusals; +pub mod rejections; pub mod restart; mod routes; pub mod rpki; diff --git a/crates/server/src/policy.rs b/crates/server/src/policy.rs index 23fd3fd..7f85d23 100644 --- a/crates/server/src/policy.rs +++ b/crates/server/src/policy.rs @@ -1325,7 +1325,7 @@ mod tests { assert!(out.is_empty()); } - /// The counts of refusals are kept by a reason's place in the list. + /// The counts of rejections are kept by a reason's place in the list. #[test] fn every_reason_is_at_its_own_place_in_the_list() { for (at, rejection) in Rejection::ALL.iter().enumerate() { diff --git a/crates/server/src/reach.rs b/crates/server/src/reach.rs index 88436db..59dc7a5 100644 --- a/crates/server/src/reach.rs +++ b/crates/server/src/reach.rs @@ -214,7 +214,7 @@ mod tests { use super::*; use crate::policy::{ImportPolicy, PrefixSet, Rejection, RouteSource}; - use crate::refusals::RefusalLog; + use crate::rejections::RejectionLog; use crate::routes::{self, Held, Import}; use crate::rpki::Checks; @@ -261,7 +261,7 @@ mod tests { struct Bed { table: RibTable, policy: ImportPolicy, - refusals: RefusalLog, + rejections: RejectionLog, } impl Bed { @@ -269,7 +269,7 @@ mod tests { Self { table: RibTable::new(4), policy: ImportPolicy::permissive(), - refusals: RefusalLog::default(), + rejections: RejectionLog::default(), } } @@ -322,7 +322,7 @@ mod tests { }), ..Checks::NONE }, - refusals: &self.refusals.of_peer(address.into(), "test"), + rejections: &self.rejections.of_peer(address.into(), "test"), }; work(&self.table, &import, &mut peer.held) } @@ -366,7 +366,7 @@ mod tests { bed.announce(&mut peer, &interfaces, "192.0.2.0/24", 1); bed.announce(&mut peer, &interfaces, "192.0.2.128/25", 1); assert_eq!((bed.table.len(), peer.held.len()), (0, 2)); - let refused = bed.refusals.peers(); + let refused = bed.rejections.peers(); assert_eq!(refused[0].1.by_reason(), [(Rejection::OwnNetwork, 2)]); } @@ -387,7 +387,7 @@ mod tests { bed.announce(&mut peer, &interfaces, "203.0.113.0/24", 1); assert_eq!((bed.table.len(), peer.held.len()), (0, 0)); assert!( - bed.refusals.peers().is_empty(), + bed.rejections.peers().is_empty(), "nothing counted as refused" ); @@ -412,7 +412,7 @@ mod tests { assert_eq!(bed.best(route), Some(near.index)); assert_eq!(bed.table.len(), 1, "the other is not in the table at all"); assert_eq!((near.held.len(), far.held.len()), (0, 1)); - let refused = bed.refusals.peers(); + let refused = bed.rejections.peers(); assert_eq!(refused.len(), 1); assert_eq!( refused[0].1.by_reason(), diff --git a/crates/server/src/refusals.rs b/crates/server/src/rejections.rs similarity index 89% rename from crates/server/src/refusals.rs rename to crates/server/src/rejections.rs index d4d04ef..0745c16 100644 --- a/crates/server/src/refusals.rs +++ b/crates/server/src/rejections.rs @@ -2,7 +2,7 @@ //! //! A refused route is not stored, so without this there would be nothing //! to show for it but a number. For each peer the reasons are counted and -//! the latest refusals are kept: enough to see what a peer is doing wrong, +//! the latest rejections are kept: enough to see what a peer is doing wrong, //! or what in our policy is. They are kept by the peer's address and //! outlive the session, since the session a peer was cut off on is the one //! worth looking at. @@ -25,7 +25,7 @@ use crate::sync::IgnorePoison; /// How many of a peer's latest refused routes are kept. pub const RECENT_REFUSALS: usize = 200; -/// How many peers refusals are kept for. More than any configuration has; +/// How many peers rejections are kept for. More than any configuration has; /// it bounds what hosts that are let in without being configured (on a /// test network) can make us keep. const MAX_PEERS: usize = 8192; @@ -36,7 +36,7 @@ const LOG_INTERVAL: Duration = Duration::from_secs(60); /// A route a peer announced that its import policy refused. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct RefusedRoute { +pub struct RejectedRoute { pub prefix: IpPrefix, /// The peer's identifier for the route; zero without ADD-PATH. pub path_id: u32, @@ -55,15 +55,15 @@ pub struct RefusedRoute { pub at: SystemTime, } -/// What the routes of one UPDATE came with, for [`PeerRefusals::note`]. +/// What the routes of one UPDATE came with, for [`PeerRejections::note`]. pub(crate) struct Came<'a> { pub(crate) as_path: &'a Arc<[u32]>, pub(crate) attributes: &'a Arc<[PathAttribute]>, pub(crate) next_hop: Option<&'a NlriNextHop>, } -/// The refusals of one peer. -pub struct PeerRefusals { +/// The rejections of one peer. +pub struct PeerRejections { /// What the peer is called in the log and in metrics: its configured /// name, or its address. label: String, @@ -74,13 +74,13 @@ pub struct PeerRefusals { struct State { /// The latest refused routes, oldest first. - recent: VecDeque, + recent: VecDeque, logged: Option, /// How many were refused since the log last said so. unlogged: u64, } -impl PeerRefusals { +impl PeerRejections { fn new(label: &str) -> Self { Self { label: label.to_string(), @@ -113,7 +113,7 @@ impl PeerRefusals { if state.recent.len() == RECENT_REFUSALS { state.recent.pop_front(); } - state.recent.push_back(RefusedRoute { + state.recent.push_back(RejectedRoute { prefix: route.prefix, path_id: route.path_id, reason, @@ -139,7 +139,7 @@ impl PeerRefusals { reason = reason.as_str(), as_path = ?&came.as_path[..], others_since_last_report = state.unlogged + count - 1, - "Refused a route" + "Rejected a route" ); state.logged = Some(Instant::now()); state.unlogged = 0; @@ -172,25 +172,25 @@ impl PeerRefusals { } /// The latest refused routes, newest first. - pub fn recent(&self) -> Vec { + pub fn recent(&self) -> Vec { self.state.locked().recent.iter().rev().cloned().collect() } } -/// The refusals of every peer, by address. Cheap to clone; clones share +/// The rejections of every peer, by address. Cheap to clone; clones share /// one record. #[derive(Clone, Default)] -pub struct RefusalLog(Arc>>>); +pub struct RejectionLog(Arc>>>); -impl RefusalLog { +impl RejectionLog { /// The record for the peer at `address`, made if there was none. - /// `label` is what to call the peer; see [`PeerRefusals::label`]. - pub(crate) fn of_peer(&self, address: PeerAddress, label: &str) -> Arc { + /// `label` is what to call the peer; see [`PeerRejections::label`]. + pub(crate) fn of_peer(&self, address: PeerAddress, label: &str) -> Arc { let mut peers = self.0.locked(); if let Some(existing) = peers.get(&address) { return Arc::clone(existing); } - let record = Arc::new(PeerRefusals::new(label)); + let record = Arc::new(PeerRejections::new(label)); // Past the bound the record is still made, for the session to // count in, but it is not kept after. if peers.len() < MAX_PEERS { @@ -200,7 +200,7 @@ impl RefusalLog { } /// Every peer that has had a route refused, with its record. - pub fn peers(&self) -> Vec<(PeerAddress, Arc)> { + pub fn peers(&self) -> Vec<(PeerAddress, Arc)> { self.0 .locked() .iter() @@ -225,9 +225,9 @@ mod tests { #[test] fn refusals_are_counted_by_reason_and_the_latest_are_kept() { - let log = RefusalLog::default(); + let log = RejectionLog::default(); let address = PeerAddress::from("192.0.2.1".parse::().unwrap()); - let record = log.of_peer(address, "refusals-test-1"); + let record = log.of_peer(address, "rejections-test-1"); assert!(log.peers().is_empty(), "nothing refused yet"); let path: Arc<[u32]> = vec![3333, 64512].into(); @@ -269,7 +269,7 @@ mod tests { // The peer's next session finds the same record. let again = log.of_peer(address, "another-name"); assert_eq!(again.total(), 3); - assert_eq!(again.label(), "refusals-test-1"); + assert_eq!(again.label(), "rejections-test-1"); assert_eq!(log.peers().len(), 1); } @@ -277,7 +277,7 @@ mod tests { /// amount of memory. #[test] fn only_so_many_refused_routes_are_kept() { - let record = PeerRefusals::new("refusals-test-2"); + let record = PeerRejections::new("rejections-test-2"); let path: Arc<[u32]> = vec![3333].into(); let attributes: Arc<[PathAttribute]> = Vec::new().into(); let came = Came { diff --git a/crates/server/src/routes.rs b/crates/server/src/routes.rs index ce8234e..baa6079 100644 --- a/crates/server/src/routes.rs +++ b/crates/server/src/routes.rs @@ -14,7 +14,7 @@ use bgp_rib::table::{AttrSet, Outcome, PeerIndex, RibTable}; use bgp_rpki::{PathValidity, Validity}; use crate::policy::{self, ImportPolicy, Rejection, RouteSource}; -use crate::refusals::{Came, PeerRefusals}; +use crate::rejections::{Came, PeerRejections}; use crate::rpki::Checks; /// The most routes of one peer that are held aside as invalid. Far more @@ -40,7 +40,7 @@ pub(crate) struct Import<'a> { /// RPKI. pub(crate) checks: Checks<'a>, /// Where what is refused is noted. - pub(crate) refusals: &'a PeerRefusals, + pub(crate) rejections: &'a PeerRejections, } /// A peer's routes that are not in the routing table because the RPKI's @@ -433,7 +433,7 @@ impl Applying<'_> { .get_or_init(|| attributes_of(self.update)), next_hop, }; - self.import.refusals.note(refused, rejection, &came); + self.import.rejections.note(refused, rejection, &came); self.applied.rejected += refused.len(); self.applied.outcome += self.table.withdraw(self.import.peer, refused); } @@ -557,7 +557,7 @@ pub(crate) fn revalidate( applied.rejected += 1; route.attrs.as_path(&mut path); let (attributes, next_hop) = route.attrs.attributes().unwrap_or_default(); - import.refusals.note( + import.rejections.note( std::slice::from_ref(suspect), rejection, &Came { diff --git a/crates/server/src/server.rs b/crates/server/src/server.rs index d487d26..fe9ffb2 100644 --- a/crates/server/src/server.rs +++ b/crates/server/src/server.rs @@ -30,7 +30,7 @@ use crate::metrics::{CONNECTIONS_REFUSED, PEER_MAX_PREFIXES}; use crate::panic; use crate::policy::ExportLimits; use crate::reach; -use crate::refusals::RefusalLog; +use crate::rejections::RejectionLog; use crate::restart::{Leave, Restarts}; use crate::rpki::spawn_revalidator; use crate::session::{ConfiguredPeer, DialState, SessionRegistry}; @@ -87,7 +87,7 @@ pub struct BgpServer { /// Live view of established sessions. pub registry: SessionRegistry, /// 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, /// The task that keeps that data up to date. @@ -163,7 +163,7 @@ impl BgpServer { rib, local_rib, registry: SessionRegistry::with_configured(configured), - refusals: RefusalLog::default(), + rejections: RejectionLog::default(), config, }) } @@ -189,7 +189,7 @@ impl BgpServer { registry: self.registry.clone(), collisions: CollisionTable::new(self.config.collision_detect_established_state), hold_downs: HoldDowns::default(), - refusals: self.refusals.clone(), + rejections: self.rejections.clone(), rpki: self.rpki.clone(), interfaces: self.kernel.as_ref().map(Fib::interfaces), restarts: Arc::clone(&self.restarts), diff --git a/crates/web/src/api.rs b/crates/web/src/api.rs index 0fd2f36..6e90a50 100644 --- a/crates/web/src/api.rs +++ b/crates/web/src/api.rs @@ -1105,7 +1105,7 @@ pub async fn kernel(State(state): State) -> Json { /// 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, + pub recent: Vec, } #[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, } @@ -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, - Query(query): Query, -) -> Json> { - let mut peers: Vec = state - .refusals + Query(query): Query, +) -> Json> { + let mut peers: Vec = 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(), diff --git a/crates/web/src/lib.rs b/crates/web/src/lib.rs index e2b3cb0..292ee89 100644 --- a/crates/web/src/lib.rs +++ b/crates/web/src/lib.rs @@ -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, /// 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, /// What keeps the best routes in the kernel, where that is configured. diff --git a/crates/web/static/statusz.html b/crates/web/static/statusz.html index 90d8249..1a2493e 100644 --- a/crates/web/static/statusz.html +++ b/crates/web/static/statusz.html @@ -113,11 +113,11 @@ from
-

Rejected routes (...)

+

Rejected routes (...)

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.
- +
peerprefixAS pathreasonwhennext hoporiginMEDlocal prefcommunitieslarge communitiesother attributes

Event log

@@ -137,7 +137,7 @@ from 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 = '-'; // ---- helpers ----------------------------------------------------------- @@ -274,7 +274,7 @@ from ${up ? age(p.established_at_unix) : DASH}${up ? (p.four_octet_asn ? 'yes' : 'no') : DASH} ${up ? esc((p.address_families || []).join(', ')) + addPath(p) + (p.extended_next_hop ? ' · IPv4 with IPv6 next hops' : '') + graceful : DASH} ${up ? p.routes + stale : DASH} - ${up ? (p.rejected_routes ? `${p.rejected_routes}` : 0) + (p.held_routes ? ` · ${p.held_routes} held now` : '') : DASH}${detail}`; + ${up ? (p.rejected_routes ? `${p.rejected_routes}` : 0) + (p.held_routes ? ` · ${p.held_routes} held now` : '') : DASH}${detail}`; }).join('')); renderPeerChoices(); } @@ -387,7 +387,7 @@ from 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` : '') - : 'no data: every route is "not found" and none is refused as invalid'; + : 'no data: every route is "not found" and none is rejected as invalid'; setHTML(b, `Cache ${esc(r.cache || '?')}: ${esc(r.state)}` + ` (RTR version ${r.protocol_version}). ${data}.` + (r.updated_at_unix && r.protocol_version < 2 ? '
This cache speaks an RTR version without ASPAs: AS paths are not verified.' : '') + @@ -406,7 +406,7 @@ from row('installed as', `table ${k.table} metric ${k.metric} proto bgp`) + (k.kept ? row('kept from before the restart', `${fmtCount(k.kept)}, not announced again yet`) : '') + (unresolved ? row('not installed', `${unresolved}`) : '') + - (k.refused ? row('refused by the kernel', `${fmtCount(k.refused)}`) : '') + + (k.rejected ? row('rejected by the kernel', `${fmtCount(k.rejected)}`) : '') + (k.last_error ? row('last error', `${esc(k.last_error)} (${fmtCount(k.errors)} errors in all)`) : '') + ''; // Interfaces that are up or have addresses get a row each; the others @@ -428,19 +428,19 @@ from } // 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, 'Nothing a peer announced has been rejected.'); 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, 'Nothing a peer announced has been rejected.'); 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 - ? ` Latest ${REFUSED_SHOWN} shown; the latest ${p.recent.length} as json.` : ''; + const more = p.recent.length > REJECTED_SHOWN + ? ` Latest ${REJECTED_SHOWN} shown; the latest ${p.recent.length} as json.` : ''; return `${esc(p.peer)} ${esc(p.peer_addr)}: ${announcements(p.total)} rejected (${why}).${more}` + - p.recent.slice(0, REFUSED_SHOWN).map(r => { const s = r.summary; return ` + p.recent.slice(0, REJECTED_SHOWN).map(r => { const s = r.summary; return ` ${esc(r.prefix)}${r.path_id ? ` path ${r.path_id}` : ''} ${asPath(r.as_path)}${esc(r.reason)}${age(r.at_unix)} ago ${s.next_hop ? esc(s.next_hop) : DASH}${s.origin ? esc(s.origin) : DASH} @@ -480,7 +480,7 @@ from } 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()]); }