Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
719a096
bgp: initial MP-BGP implementation
taspelund Nov 18, 2025
7d1715d
bgp: more MP-BGP fill-in and parse error framework
taspelund Dec 5, 2025
534b01b
bgp: split RouteUpdate into specific variants
taspelund Dec 11, 2025
01ed779
bgp: add per-address-family import/export policies
taspelund Dec 11, 2025
3fe984d
bgp: validate message and attribute lengths
taspelund Dec 11, 2025
810aa34
bgp: remove update filtering of originated routes
taspelund Dec 13, 2025
e00e6a2
Bump API for MP-BGP
taspelund Dec 14, 2025
490a0d0
Revert "bgp: remove update filtering of originated routes"
taspelund Dec 31, 2025
890affb
Introduce next-hop override configuration
taspelund Jan 1, 2026
dc3e760
bgp: add comprehensive peer info to neighbor status
taspelund Jan 2, 2026
7e318e8
bgp: add nexthop override update handler
taspelund Jan 3, 2026
5ea560d
rdb: use HashMap keying for path identity management
taspelund Jan 3, 2026
890b2d5
Fix RIB deadlock and optimize bestpath fanout reads
taspelund Jan 3, 2026
cd55c15
Improve loopback-manager logging
taspelund Jan 5, 2026
2671585
mg-common: fix addrobj format for loopbackmanager
taspelund Jan 5, 2026
e77a190
remove unused import
taspelund Jan 5, 2026
f32f3f8
Fixup bgp neighbor status
taspelund Jan 7, 2026
45a75bd
Add per AFI/SAFI support to BGP neighbor reset
taspelund Jan 8, 2026
f13f187
rebase api version fixup
rcgoodfellow Jan 10, 2026
ac62477
ry's feedback
taspelund Jan 15, 2026
e033f58
More minor updates
taspelund Jan 20, 2026
fb88e80
rdb: fix bestpath test comments and add MED coverage
taspelund Jan 20, 2026
74baaa4
another mg-lower conditional import
taspelund Jan 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ out
download
tags
!.github
claude
CLAUDE.local.md
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ rand = "0.8.5"
backoff = "0.4"
mg-api = { path = "mg-api" }
mg-common = { path = "mg-common" }
bgp = { path = "bgp" }
rdb-types = { path = "rdb-types" }
chrono = { version = "0.4.42", features = ["serde"] }
oxide-tokio-rt = "0.1.2"
Expand Down
1 change: 1 addition & 0 deletions bgp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ itertools.workspace = true
oxnet.workspace = true
uuid.workspace = true
rand.workspace = true
clap = { workspace = true, optional = true }

[target.'cfg(target_os = "illumos")'.dependencies]
libnet = { workspace = true, optional = true }
Expand Down
77 changes: 65 additions & 12 deletions bgp/src/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::connection::{BgpConnection, ConnectionId};
use crate::params::{DynamicTimerInfo, JitterRange};
use crate::session::{ConnectionEvent, FsmEvent, SessionEvent};
use mg_common::lock;
use mg_common::thread::ManagedThread;
Expand Down Expand Up @@ -68,9 +69,8 @@ pub struct Timer {
pub interval: Duration,

/// Optional jitter range applied on restart. None = no jitter.
/// Some((min, max)) applies a random factor in [min, max] to the interval.
/// RFC 4271 recommends (0.75, 1.0) for ConnectRetryTimer and related timers.
jitter_range: Option<(f64, f64)>,
/// RFC 4271 recommends min: 0.75, max: 1.0 for ConnectRetryTimer and related timers.
jitter_range: Option<JitterRange>,

/// Timer state. The first value indicates if the timer is enabled. The
/// second value indicates how much time is left.
Expand All @@ -91,12 +91,11 @@ impl Timer {
}

/// Create a new timer with the specified interval and jitter range.
/// The jitter_range parameter expects (min, max) where both values are
/// factors to multiply the interval by. RFC 4271 recommends (0.75, 1.0) for
/// ConnectRetryTimer and related timers.
/// The jitter_range parameter specifies factors to multiply the interval by.
/// RFC 4271 recommends min: 0.75, max: 1.0 for ConnectRetryTimer and related timers.
pub fn new_with_jitter(
interval: Duration,
jitter_range: (f64, f64),
jitter_range: JitterRange,
) -> Self {
Self {
interval,
Expand Down Expand Up @@ -151,10 +150,10 @@ impl Timer {
/// The jitter is recalculated on every reset.
pub fn reset(&self) {
let interval = match self.jitter_range {
Some((min, max)) => {
Some(jitter) => {
use rand::Rng;
let mut rng = rand::thread_rng();
let factor = rng.gen_range(min..=max);
let factor = rng.gen_range(jitter.min..=jitter.max);
self.interval.mul_f64(factor)
}
None => self.interval,
Expand All @@ -176,9 +175,14 @@ impl Timer {

/// Update the jitter range for this timer. The new jitter will be applied
/// on the next restart() call.
pub fn set_jitter_range(&mut self, jitter_range: Option<(f64, f64)>) {
pub fn set_jitter_range(&mut self, jitter_range: Option<JitterRange>) {
self.jitter_range = jitter_range;
}

/// Get the jitter range for this timer.
pub fn jitter_range(&self) -> Option<JitterRange> {
self.jitter_range
}
}

impl Display for Timer {
Expand Down Expand Up @@ -210,8 +214,8 @@ impl SessionClock {
resolution: Duration,
connect_retry_interval: Duration,
idle_hold_interval: Duration,
connect_retry_jitter: Option<(f64, f64)>,
idle_hold_jitter: Option<(f64, f64)>,
connect_retry_jitter: Option<JitterRange>,
idle_hold_jitter: Option<JitterRange>,
event_tx: Sender<FsmEvent<Cnx>>,
log: Logger,
) -> Self {
Expand Down Expand Up @@ -304,6 +308,27 @@ impl SessionClock {
lock!(timers.connect_retry).stop();
lock!(timers.idle_hold).stop();
}

/// Get a snapshot of session-level timer state
pub fn get_timer_snapshot(&self) -> SessionTimerSnapshot {
let connect_retry = lock!(self.timers.connect_retry);
let idle_hold = lock!(self.timers.idle_hold);
SessionTimerSnapshot {
connect_retry_remaining: connect_retry.remaining(),
connect_retry_jitter: connect_retry.jitter_range(),
idle_hold_remaining: idle_hold.remaining(),
idle_hold_jitter: idle_hold.jitter_range(),
}
}
}

/// Snapshot of session-level timer state
#[derive(Debug, Clone)]
pub struct SessionTimerSnapshot {
pub connect_retry_remaining: Duration,
pub connect_retry_jitter: Option<JitterRange>,
pub idle_hold_remaining: Duration,
pub idle_hold_jitter: Option<JitterRange>,
}

impl Display for SessionClock {
Expand Down Expand Up @@ -446,6 +471,34 @@ impl ConnectionClock {
lock!(timers.hold).disable();
lock!(timers.delay_open).disable();
}

/// Get a snapshot of connection-level timer state
pub fn get_timer_snapshot(&self) -> ConnectionTimerSnapshot {
let hold = lock!(self.timers.hold);
let keepalive = lock!(self.timers.keepalive);
let delay_open = lock!(self.timers.delay_open);
ConnectionTimerSnapshot {
hold: DynamicTimerInfo {
configured: self.timers.config_hold_time,
negotiated: hold.interval,
remaining: hold.remaining(),
},
keepalive: DynamicTimerInfo {
configured: self.timers.config_keepalive_time,
negotiated: keepalive.interval,
remaining: keepalive.remaining(),
},
delay_open_remaining: delay_open.remaining(),
}
}
}

/// Snapshot of connection-level timer state
#[derive(Debug, Clone)]
pub struct ConnectionTimerSnapshot {
pub hold: DynamicTimerInfo,
pub keepalive: DynamicTimerInfo,
pub delay_open_remaining: Duration,
}

impl Display for ConnectionClock {
Expand Down
1 change: 1 addition & 0 deletions bgp/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::net::SocketAddr;
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct PeerConfig {
pub name: String,
pub group: String,
pub host: SocketAddr,
pub hold_time: u64,
pub idle_hold_time: u64,
Expand Down
25 changes: 25 additions & 0 deletions bgp/src/connection_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,15 @@ impl BgpConnectionChannel {
break;
}

// Note: Unlike BgpConnectionTcp, this has no ParseErrors.
// BgpConnectionChannel is a wrapper around Message,
// which is the type representation of a fully parsed
// and valid message. This means it's not possible to
// exchange invalid messages as-is. To support this,
// the channel would need to wrap a different type
// (feasible, but of limited utility) or update the
// Message type to include possibly-invalid states
// (also feasible, but undesirable).
match rx.recv_timeout(timeout) {
Ok(msg) => {
connection_log_lite!(log,
Expand Down Expand Up @@ -414,6 +423,22 @@ impl BgpConnectionChannel {
"connection_id" => conn_id.short(),
"channel_id" => channel_id
);
// Notify session runner that the connection failed,
// unless this is a graceful shutdown
if !dropped.load(Ordering::Relaxed)
&& let Err(e) = event_tx.send(FsmEvent::Connection(
ConnectionEvent::TcpConnectionFails(conn_id),
))
{
connection_log_lite!(log, warn,
"error sending TcpConnectionFails event to {peer}: {e}";
"direction" => direction.as_str(),
"peer" => format!("{peer}"),
"connection_id" => conn_id.short(),
"channel_id" => channel_id,
"error" => format!("{e}")
);
}
break;
}
}
Expand Down
Loading