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
@@ -12,7 +12,7 @@ use bgp_rpki::RpkiConfig;
|
||||
|
||||
use crate::error::{ConfigError, ImportProblem, OriginateProblem, PeerProblem};
|
||||
use crate::family::FamilySet;
|
||||
use crate::policy::{ImportPolicy, PrefixSet};
|
||||
use crate::policy::{ImportPolicy, PeerRole, PrefixSet};
|
||||
use crate::proto::config::{self as proto, BgpConfig, OriginatedRoute, Peer};
|
||||
|
||||
const DEFAULT_BGP_PORT: u16 = 179;
|
||||
@@ -64,6 +64,8 @@ pub struct PeerConfig {
|
||||
pub add_path: Option<AddPathSendRecv>,
|
||||
/// Which of the peer's routes are taken.
|
||||
pub import: ImportPolicy,
|
||||
/// What the peer is to us, if that was said.
|
||||
pub role: Option<PeerRole>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -225,6 +227,16 @@ fn parse_peer(peer: Peer) -> Result<PeerConfig, PeerProblem> {
|
||||
None => ImportPolicy::default(),
|
||||
};
|
||||
|
||||
let role = match peer.role.enum_value() {
|
||||
Ok(proto::Role::ROLE_UNSPECIFIED) => None,
|
||||
Ok(proto::Role::PROVIDER) => Some(PeerRole::Provider),
|
||||
Ok(proto::Role::CUSTOMER) => Some(PeerRole::Customer),
|
||||
Ok(proto::Role::PEER) => Some(PeerRole::Peer),
|
||||
Ok(proto::Role::ROUTE_SERVER) => Some(PeerRole::RouteServer),
|
||||
Ok(proto::Role::ROUTE_SERVER_CLIENT) => Some(PeerRole::RouteServerClient),
|
||||
Err(_) => return Err(PeerProblem::Role),
|
||||
};
|
||||
|
||||
Ok(PeerConfig {
|
||||
// A peer without a name goes by its address.
|
||||
name: if peer.name.is_empty() {
|
||||
@@ -239,6 +251,7 @@ fn parse_peer(peer: Peer) -> Result<PeerConfig, PeerProblem> {
|
||||
families,
|
||||
add_path,
|
||||
import,
|
||||
role,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -323,6 +336,7 @@ fn parse_import(import: &proto::ImportPolicy) -> Result<ImportPolicy, ImportProb
|
||||
)?,
|
||||
accept_any_first_as: import.accept_any_first_as,
|
||||
accept_rpki_invalid: import.accept_rpki_invalid,
|
||||
accept_aspa_invalid: import.accept_aspa_invalid,
|
||||
max_as_path_length: match import.max_as_path_length {
|
||||
0 => defaults.max_as_path_length,
|
||||
length => u16::try_from(length).map_err(|_| ImportProblem::PathLength(length))?,
|
||||
@@ -737,6 +751,25 @@ mod tests {
|
||||
);
|
||||
|
||||
// A peer can be exempt.
|
||||
// A peer's paths are verified only if it is said what the peer is.
|
||||
let role_of = |body: &str| {
|
||||
parse(&format!(
|
||||
"{BASE}peers {{ endpoint {{ address: \"192.0.2.2\" }} remote_asn: 1 {body} }}"
|
||||
))
|
||||
.map(|config| config.peers[0].role)
|
||||
};
|
||||
assert_eq!(role_of("").unwrap(), None);
|
||||
assert_eq!(role_of("role: CUSTOMER").unwrap(), Some(PeerRole::Customer));
|
||||
assert_eq!(
|
||||
role_of("role: ROUTE_SERVER").unwrap(),
|
||||
Some(PeerRole::RouteServer)
|
||||
);
|
||||
assert!(!import_of("").unwrap().accept_aspa_invalid);
|
||||
assert!(
|
||||
import_of("import { accept_aspa_invalid: true }")
|
||||
.unwrap()
|
||||
.accept_aspa_invalid
|
||||
);
|
||||
assert!(!import_of("").unwrap().accept_rpki_invalid);
|
||||
assert!(
|
||||
import_of("import { accept_rpki_invalid: true }")
|
||||
|
||||
Reference in new issue
Block a user