Verify AS paths with ASPAs, over RTR version 2
Origin validation says who may originate a prefix and nothing about the
rest of the path: it does not catch a route leak, nor a hijack that keeps
the right origin at the end of a made-up path. ASPAs, in which a network
lists its providers, do.
bgp-rpki:
- aspa: the ASPAs and the upstream and downstream verification procedures
of draft-ietf-sidrops-aspa-verification-24, tested against the draft's
wording as written.
- rtr: protocol version 2 (draft-ietf-sidrops-8210bis-23) with the ASPA
PDU and the new error codes.
- client: asks for version 2 and falls back to whatever older version the
cache speaks; keeps ASPAs with the VRPs, applied whole or not at all;
publishes which customers' ASPAs changed; tells the cache why it is
being left (Error Report); does not ask one version's serial of another.
Server:
- A peer has a role (provider, customer, peer, route server, route server
client), which decides how its paths are verified. Without one they are
not.
- A route whose path is invalid is refused ("ASPA invalid") and held, like
one with an invalid origin; accept_aspa_invalid exempts a peer.
- When ASPAs change, routes in the table whose path has a changed customer
on it are verified again, and held routes are released when they pass
both checks.
- The revalidator finds a route's peer by index instead of searching a
list for every route.
The status page and API show each peer's role, each route's path validity,
the number of ASPAs and the RTR version in use; bgp_rpki_aspas is on
/metricz.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
bdaf226a16
commit
8086aa994f
35 files changed
+2484
-359
No files matched your search
@@ -23,6 +23,8 @@ use crate::collision::Direction;
|
||||
use crate::family::FamilySet;
|
||||
use crate::fsm::Negotiated;
|
||||
use crate::metrics::SESSIONS_ESTABLISHED;
|
||||
use crate::policy::PeerRole;
|
||||
use crate::rpki::Subject;
|
||||
use crate::sync::IgnorePoisonRw;
|
||||
|
||||
const EVENT_CHANNEL_CAPACITY: usize = 256;
|
||||
@@ -60,6 +62,8 @@ pub struct ConfiguredPeer {
|
||||
pub name: String,
|
||||
pub endpoint: SocketAddr,
|
||||
pub remote_asn: u32,
|
||||
/// What the peer is to us, if that was said.
|
||||
pub role: Option<PeerRole>,
|
||||
}
|
||||
|
||||
/// Counts kept by a session as it goes, for whoever wants to look.
|
||||
@@ -117,6 +121,8 @@ pub struct SessionHandle {
|
||||
pub established_at: SystemTime,
|
||||
/// Whether the peer is in our own AS.
|
||||
pub ibgp: bool,
|
||||
/// What the peer is to us, if that is known.
|
||||
pub role: Option<PeerRole>,
|
||||
/// Whether the peer negotiated RFC 6793 four-octet ASNs. Determines how
|
||||
/// AS_PATH is encoded on the wire for this session.
|
||||
pub four_octet_asn: bool,
|
||||
@@ -134,9 +140,9 @@ pub struct SessionHandle {
|
||||
/// How many routes the peer has, and how many were refused.
|
||||
pub counters: Arc<SessionCounters>,
|
||||
outbound: mpsc::Sender<Message>,
|
||||
/// Where the session is told to look at its routes again, if they are
|
||||
/// subject to route origin validation.
|
||||
revalidate: Option<mpsc::UnboundedSender<Vec<Nlri>>>,
|
||||
/// Which of the RPKI's checks the peer's routes are subject to, if
|
||||
/// any, and where the session is told to look at its routes again.
|
||||
validation: Option<(Subject, mpsc::UnboundedSender<Vec<Nlri>>)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
|
||||
@@ -160,6 +166,7 @@ impl SessionHandle {
|
||||
local_addr,
|
||||
established_at: SystemTime::now(),
|
||||
ibgp: negotiated.ibgp,
|
||||
role: None,
|
||||
four_octet_asn: negotiated.four_octet_asn,
|
||||
families: negotiated.families.clone(),
|
||||
add_path_receive: negotiated.add_path_receive.clone(),
|
||||
@@ -167,29 +174,29 @@ impl SessionHandle {
|
||||
rib_peer,
|
||||
counters: Arc::default(),
|
||||
outbound,
|
||||
revalidate: None,
|
||||
validation: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Marks the session's routes as subject to route origin validation.
|
||||
/// What is sent with [`SessionHandle::revalidate`] arrives at the
|
||||
/// receiver returned.
|
||||
pub(crate) fn validated(&mut self) -> mpsc::UnboundedReceiver<Vec<Nlri>> {
|
||||
/// Marks the session's routes as subject to validation with the
|
||||
/// RPKI's data. What is sent with [`SessionHandle::revalidate`]
|
||||
/// arrives at the receiver returned.
|
||||
pub(crate) fn validate(&mut self, subject: Subject) -> mpsc::UnboundedReceiver<Vec<Nlri>> {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
self.revalidate = Some(tx);
|
||||
self.validation = Some((subject, tx));
|
||||
rx
|
||||
}
|
||||
|
||||
/// Whether the session's routes are subject to route origin
|
||||
/// validation.
|
||||
pub fn is_validated(&self) -> bool {
|
||||
self.revalidate.is_some()
|
||||
/// Which of the RPKI's checks the session's routes are subject to, if
|
||||
/// any.
|
||||
pub fn validated(&self) -> Option<Subject> {
|
||||
self.validation.as_ref().map(|(subject, _)| *subject)
|
||||
}
|
||||
|
||||
/// Tells the session that the validation data has changed. `suspects`
|
||||
/// are routes of its peer's in the table that are now invalid.
|
||||
pub(crate) fn revalidate(&self, suspects: Vec<Nlri>) {
|
||||
if let Some(revalidate) = &self.revalidate {
|
||||
if let Some((_, revalidate)) = &self.validation {
|
||||
// Fails only if the session has just ended.
|
||||
let _ = revalidate.send(suspects);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user