Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ out
download
tags
!.github
claude
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