feature: BGP server, FSM, Adj-RIB-In actor, and integration tests
- Implement passive-side BGP FSM (OpenSent → OpenConfirm → Established) with hold timer, keepalive timer, and NOTIFICATION on expiry - Add bgp-server crate: config validation, TCP listener accept loop (BgpServerBuilder → BgpServer), connection dispatch - Add bgp-rib crate: Adj-RIB-In actor owning route state with event broadcast for streaming consumers and snapshot for introspection - Wire Adj-RIB-In into FSM: announce/withdraw on UPDATE for both legacy IPv4 NLRI fields and MP_REACH/MP_UNREACH path attributes - Add bgp-integration-tests crate with 15 end-to-end tests covering session establishment, timers, UPDATE handling, and RIB introspection - Fix require_empty! macro bug in open.rs (checked wrong buffer) - Fix FourByteAsn capability parser returning consumed buffer as remainder - Fix tracing subscriber not being installed in TracingArgs::install - Expand docs/RFCs.md with full implementation status across 15 RFCs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
4ba644827d
commit
1ec60255ea
24 files changed
+1685
-26
No files matched your search
@@ -0,0 +1,94 @@
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::str::FromStr;
|
||||
|
||||
use eyre::{Result, bail, eyre};
|
||||
|
||||
use crate::proto::config::BgpConfig;
|
||||
|
||||
const DEFAULT_BGP_PORT: u16 = 179;
|
||||
const DEFAULT_HOLD_TIME_SECS: u16 = 90;
|
||||
|
||||
/// Validated, Rust-native server configuration derived from the proto config.
|
||||
pub struct Config {
|
||||
pub router_id: Ipv4Addr,
|
||||
pub local_asn: u32,
|
||||
/// Default hold time advertised to peers that have no per-peer override.
|
||||
pub hold_time_secs: u16,
|
||||
pub listen_endpoints: Vec<SocketAddr>,
|
||||
pub peers: Vec<PeerConfig>,
|
||||
}
|
||||
|
||||
pub struct PeerConfig {
|
||||
pub name: String,
|
||||
pub endpoint: SocketAddr,
|
||||
pub remote_asn: u32,
|
||||
pub hold_time_secs: u16,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_proto(proto: BgpConfig) -> Result<Self> {
|
||||
let router_id = Ipv4Addr::from_str(&proto.router_id)
|
||||
.map_err(|_| eyre!("Invalid router_id {:?}: must be a dotted-quad IPv4 address", proto.router_id))?;
|
||||
|
||||
if proto.local_asn == 0 {
|
||||
bail!("local_asn must be non-zero");
|
||||
}
|
||||
|
||||
let listen_endpoints = if proto.listen_endpoints.is_empty() {
|
||||
vec![
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), DEFAULT_BGP_PORT),
|
||||
SocketAddr::new(IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED), DEFAULT_BGP_PORT),
|
||||
]
|
||||
} else {
|
||||
proto.listen_endpoints
|
||||
.into_iter()
|
||||
.map(|ep| parse_endpoint(&ep.address, ep.port))
|
||||
.collect::<Result<_>>()?
|
||||
};
|
||||
|
||||
let peers = proto.peers
|
||||
.into_iter()
|
||||
.map(|p| {
|
||||
let ep = p.endpoint.as_ref()
|
||||
.ok_or_else(|| eyre!("Peer {:?} is missing endpoint", p.name))?;
|
||||
let endpoint = parse_endpoint(&ep.address, ep.port)?;
|
||||
|
||||
if p.remote_asn == 0 {
|
||||
bail!("Peer {:?} remote_asn must be non-zero", p.name);
|
||||
}
|
||||
|
||||
let hold_time_secs = if p.hold_time_seconds == 0 {
|
||||
DEFAULT_HOLD_TIME_SECS
|
||||
} else if p.hold_time_seconds < 3 {
|
||||
bail!("Peer {:?} hold_time_seconds must be 0 or >= 3", p.name);
|
||||
} else {
|
||||
p.hold_time_seconds as u16
|
||||
};
|
||||
|
||||
Ok(PeerConfig {
|
||||
name: p.name,
|
||||
endpoint,
|
||||
remote_asn: p.remote_asn,
|
||||
hold_time_secs,
|
||||
})
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
Ok(Self { router_id, local_asn: proto.local_asn, hold_time_secs: DEFAULT_HOLD_TIME_SECS, listen_endpoints, peers })
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_endpoint(address: &str, port: u32) -> Result<SocketAddr> {
|
||||
let port = if port == 0 {
|
||||
DEFAULT_BGP_PORT
|
||||
} else if port > u16::MAX as u32 {
|
||||
bail!("Port {port} is out of range");
|
||||
} else {
|
||||
port as u16
|
||||
};
|
||||
|
||||
let ip = IpAddr::from_str(address)
|
||||
.map_err(|_| eyre!("Invalid IP address {address:?}"))?;
|
||||
|
||||
Ok(SocketAddr::new(ip, port))
|
||||
}
|
||||
Reference in new issue
Block a user