Skip to content
Open
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
58813e7
bgp: set TCP_NODELAY on all TCP sessions
taspelund Feb 11, 2026
aace8b9
bgp: fix test isolation for cargo test compatibility
taspelund Feb 11, 2026
1f85bf8
Chunk originated BGP Updates exceeding max size
taspelund Feb 11, 2026
ff9383d
Ensure an AFI is enabled for unnumbered peers too
taspelund Feb 12, 2026
b50d9ce
static: add extended next-hop support
taspelund Feb 12, 2026
ccc3196
mgadm, ddmadm: handle broken pipe gracefully
taspelund Feb 12, 2026
f4b00d5
rdb: Fix Ord for static paths w/ same nexthop
taspelund Feb 12, 2026
3850edd
Plumb nexthop_interface through static routing
taspelund Feb 12, 2026
eee9be5
Add graceful handler for enforce-first-as change
taspelund Feb 13, 2026
3eaf111
rdb: deduplicate v4/v6 dispatch patterns
taspelund Feb 13, 2026
9e6d632
Convert origin{4,6} methods to be AF-agnostic
taspelund Feb 14, 2026
65adb33
bgp: multiple treat-as-withdraw bug fixes
taspelund Feb 14, 2026
23b66f6
Stop walking RIBs for AFIs that weren't negotiated
taspelund Feb 14, 2026
4ed7c95
bgp: handle failed AFI/SAFI negotiation better
taspelund Feb 20, 2026
8daf8ab
bgp: validate OPEN received in Established
taspelund Feb 21, 2026
972f846
bgp: move most fsm events into "major" history
taspelund Feb 21, 2026
634dc33
Split BGP Message History into major/all buffers
taspelund Feb 21, 2026
c1058d9
Track/expose better diagnostics for BGP peers
taspelund Feb 21, 2026
a146209
Add dev docs
taspelund Feb 24, 2026
2a0cf4d
bgp: disallow wildcard match arms in BGP FSM
taspelund Feb 24, 2026
f1497e3
Enable IP DSCP / IPv6 Traffic Class for BGP peers
taspelund Feb 24, 2026
ebf438a
bgp: update min_tll and dscp gracefully
taspelund Feb 26, 2026
e6a1ebf
bgp: fix TTL sockopt calls for IPv6
taspelund Feb 26, 2026
effd280
bgp: Use a default TTL for outgoing packets
taspelund Feb 26, 2026
053af1a
cargo fmt gooder
taspelund Feb 27, 2026
542ef51
un-flatten BgpParameters
taspelund Feb 27, 2026
ed2247d
rdb: implement full RFC 4271 bestpath tie-breaking
taspelund Feb 27, 2026
412462e
Add detail option for mgadm rib status
taspelund Feb 27, 2026
5a13cb6
Add prefix query for rib status
taspelund Feb 27, 2026
c9bde3b
bfd: add ipv6 peers to bfd tests
taspelund Feb 27, 2026
6bf4b69
bgp: remove update filtering of originated routes
taspelund Mar 2, 2026
c16a85b
Add justfile
taspelund Mar 2, 2026
f164536
bgp: use socket2 for TTL and DSCP socket options
taspelund Mar 9, 2026
ee5806a
Merge branch 'main' into trey/random-fix-fun-time
taspelund Mar 10, 2026
b97d1d8
bgp: revert socket2 wrappers for DSCP/TCLASS
taspelund Mar 10, 2026
3cfc17c
Merge branch 'main' into trey/random-fix-fun-time
taspelund Mar 10, 2026
1f38210
bgp: implement BGP Extended Message Size
taspelund Mar 10, 2026
e0b941c
Refactor BGP Capability storage
taspelund Mar 12, 2026
0ff08fb
bgp: store extended-nexthop capability in caps_tx
taspelund Mar 12, 2026
dad89ec
bgp: use ipv4-mapped ipv6 addresses for v6-over-v4
taspelund Mar 12, 2026
b287527
Finalize extended nexthops for numbered peers
taspelund Mar 12, 2026
268d6c3
bgp: add negotiate_capabilities and select_nexthop tests
taspelund Mar 12, 2026
c3b20e5
bgp: reset session when ENHE is toggled
taspelund Mar 12, 2026
14d1a6b
bgp: increase TEST_ROUTE_COUNT to 32k
taspelund Mar 12, 2026
039ea8e
Merge branch 'main' into trey/random-fix-fun-time
taspelund Mar 12, 2026
547e311
Use ManagedThread in bfd
taspelund Mar 4, 2026
ae04a43
Use thread builder to name BFD threads
taspelund Mar 4, 2026
db99e96
Plumb Result up from thread spawn
taspelund Mar 4, 2026
9f7d316
Handful of small improvements
taspelund Mar 4, 2026
bb21536
Stop bgp SessionClock thread on session shutdown
taspelund Mar 17, 2026
3c89bc4
rename API version to SPRING_CLEANING
taspelund Mar 12, 2026
7017354
Merge branch 'main' into trey/random-fix-fun-time
taspelund Mar 13, 2026
61ba7aa
rdb: no means no
taspelund Mar 17, 2026
8f25eda
openapi: convert v6 to git stub
taspelund Mar 17, 2026
5c2e586
LoopbackIpManager: Set temp flag on IPs on illumos
taspelund Mar 18, 2026
7c3b3d8
LoopbackIpManager: fix addrobj for ipadm delete
taspelund Mar 18, 2026
d9399db
LoopbackIpManager: Remember to skip ::1 too
taspelund Mar 18, 2026
2c6bc7d
LoopbackIpManager: use int for installed count
taspelund Mar 18, 2026
81d751f
LoopbackIpManager: Rollback IPs on install failure
taspelund Mar 18, 2026
9b5e8ae
LoopbackIpManager: cargo fmt
taspelund Mar 19, 2026
959fd5b
Merge branch 'main' into trey/random-fix-fun-time
taspelund Mar 30, 2026
79bc7d2
Merge branch 'main' into trey/random-fix-fun-time
taspelund Apr 8, 2026
52fb075
Review feedback
taspelund Apr 8, 2026
3d44036
bgp: disallow OPEN/KEEPALIVE from extended msg
taspelund Apr 8, 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 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 bfd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ anyhow.workspace = true
schemars.workspace = true
serde.workspace = true
rand.workspace = true
thiserror.workspace = true

[dev-dependencies]
pretty_assertions.workspace = true
Expand Down
214 changes: 146 additions & 68 deletions bfd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use anyhow::Result;
use mg_common::thread::ManagedThread;
use num_enum::TryFromPrimitive;
use rdb::SessionMode;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use slog::{Logger, warn};
use sm::StateMachine;
use std::collections::HashMap;
use std::net::IpAddr;
use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use std::time::Duration;
use std::{
collections::HashMap,
net::IpAddr,
sync::{Arc, atomic::AtomicU64},
time::Duration,
};

pub mod bidi;
pub mod log;
Expand Down Expand Up @@ -49,38 +52,25 @@ impl Daemon {
}
}

/// Add a peer session to the deamon. Peer sessions are started immediately
/// when added.
/// Add a peer session to the daemon.
/// Peer sessions are started immediately when added.
pub fn add_peer(
&mut self,
peer: IpAddr,
required_rx: Duration,
detection_multiplier: u8,
mode: SessionMode,
endpoint: BfdEndpoint,
db: rdb::Db,
) {
rq: AddPeerRequest,
) -> Result<(), AddPeerError> {
if self.sessions.contains_key(&peer) {
warn!(self.log, "attempt to add peer that already exists";
"component" => COMPONENT_BFD,
"module" => MOD_DAEMON,
"unit" => UNIT_PEER,
"peer" => format!("{peer}")
);
return;
return Err(AddPeerError::PeerExists(peer));
}
self.sessions.insert(
peer,
Session::new(
peer,
endpoint,
required_rx,
detection_multiplier,
mode,
db,
self.log.clone(),
),
);
self.sessions
.insert(peer, Session::new(peer, rq, self.log.clone())?);
Ok(())
}

/// Remove a peer from the daemon. The peer will be immediately shut down.
Expand Down Expand Up @@ -112,35 +102,47 @@ pub struct SessionCounters {
pub unexpected_message: AtomicU64,
}

/// Parameters for adding a new BFD peer session.
pub struct AddPeerRequest {
pub required_rx: Duration,
pub detection_multiplier: u8,
pub mode: SessionMode,
pub endpoint: BfdEndpoint,
pub egress_thread: Option<Arc<ManagedThread>>,
pub db: rdb::Db,
}

/// A session holds a BFD state machine for a particular peer.
pub struct Session {
pub sm: StateMachine,
pub mode: SessionMode,
pub counters: Arc<SessionCounters>,
/// Managed egress thread for UDP packet transmission. Stored here to
/// ensure automatic cleanup on drop — the ManagedThread's Drop impl
/// will signal shutdown and join the thread. None in test contexts
/// where egress is handled differently.
_egress_thread: Option<Arc<ManagedThread>>,
}

impl Session {
/// Create a new session and start running the underlying BFD state machine
/// immediately.
fn new(
addr: IpAddr,
ep: BfdEndpoint,
required_rx: Duration,
detection_multiplier: u8,
mode: SessionMode,
db: rdb::Db,
log: Logger,
) -> Self {
fn new(addr: IpAddr, rq: AddPeerRequest, log: Logger) -> Result<Self> {
let counters = Arc::new(SessionCounters::default());
let mut sm = StateMachine::new(
addr,
required_rx,
detection_multiplier,
rq.required_rx,
rq.detection_multiplier,
counters.clone(),
log,
);
sm.run(ep, db);
Session { sm, mode, counters }
sm.run(rq.endpoint, rq.db)?;
Ok(Session {
sm,
mode: rq.mode,
counters,
_egress_thread: rq.egress_thread,
})
}
}

Expand Down Expand Up @@ -237,6 +239,15 @@ pub enum BfdPeerState {

pub struct Admin {}

#[derive(Debug, thiserror::Error)]
pub enum AddPeerError {
#[error("BFD peer {0} already exists")]
PeerExists(IpAddr),

#[error(transparent)]
Other(#[from] anyhow::Error),
}

#[cfg(test)]
mod test {
use super::*;
Expand Down Expand Up @@ -324,17 +335,40 @@ mod test {
let mut daemon = Daemon::new(log);
assert_eq!(daemon.sessions.len(), 0);

// Add an IPv4 peer.
let (a, _b) = bidi::channel();
let p1_addr = ip("203.0.113.10");
let v4_addr = ip("203.0.113.10");
daemon.add_peer(
p1_addr,
Duration::from_secs(5),
3,
SessionMode::MultiHop,
a,
db.db().clone(),
);
assert_eq!(daemon.peer_state(p1_addr), Some(BfdPeerState::Down));
v4_addr,
AddPeerRequest {
required_rx: Duration::from_secs(5),
detection_multiplier: 3,
mode: SessionMode::MultiHop,
endpoint: a,
egress_thread: None,
db: db.db().clone(),
},
)?;
assert_eq!(daemon.peer_state(v4_addr), Some(BfdPeerState::Down));

// Add an IPv6 peer to the same daemon.
let (a, _b) = bidi::channel();
let v6_addr = ip("2001:db8::10");
daemon.add_peer(
v6_addr,
AddPeerRequest {
required_rx: Duration::from_secs(5),
detection_multiplier: 3,
mode: SessionMode::MultiHop,
endpoint: a,
egress_thread: None,
db: db.db().clone(),
},
)?;
assert_eq!(daemon.peer_state(v6_addr), Some(BfdPeerState::Down));

// Both peers coexist.
assert_eq!(daemon.sessions.len(), 2);

Ok(())
}
Expand All @@ -347,39 +381,83 @@ mod test {

let mut net = Network::default();

let addr1 = ip("203.0.113.10");
let addr2 = ip("203.0.113.20");
// IPv4 peer pair.
let v4_addr1 = ip("203.0.113.10");
let v4_addr2 = ip("203.0.113.20");

// IPv6 peer pair.
let v6_addr1 = ip("2001:db8::10");
let v6_addr2 = ip("2001:db8::20");

// Daemon 1 peers with both v4 and v6 counterparts.
let mut d1 = Daemon::new(test_logger());
let (a, b) = bidi::channel();
d1.add_peer(
addr1,
Duration::from_secs(5),
3,
SessionMode::MultiHop,
a,
db.db().clone(),
);
net.register(addr2, b);
v4_addr1,
AddPeerRequest {
required_rx: Duration::from_secs(5),
detection_multiplier: 3,
mode: SessionMode::MultiHop,
endpoint: a,
egress_thread: None,
db: db.db().clone(),
},
)?;
net.register(v4_addr2, b);

let (a, b) = bidi::channel();
d1.add_peer(
v6_addr1,
AddPeerRequest {
required_rx: Duration::from_secs(5),
detection_multiplier: 3,
mode: SessionMode::MultiHop,
endpoint: a,
egress_thread: None,
db: db.db().clone(),
},
)?;
net.register(v6_addr2, b);

// Daemon 2 peers with both v4 and v6 counterparts.
let mut d2 = Daemon::new(test_logger());
let (a, b) = bidi::channel();
d2.add_peer(
addr2,
Duration::from_secs(5),
3,
SessionMode::MultiHop,
a,
db.db().clone(),
);
net.register(addr1, b);
v4_addr2,
AddPeerRequest {
required_rx: Duration::from_secs(5),
detection_multiplier: 3,
mode: SessionMode::MultiHop,
endpoint: a,
egress_thread: None,
db: db.db().clone(),
},
)?;
net.register(v4_addr1, b);

let (a, b) = bidi::channel();
d2.add_peer(
v6_addr2,
AddPeerRequest {
required_rx: Duration::from_secs(5),
detection_multiplier: 3,
mode: SessionMode::MultiHop,
endpoint: a,
egress_thread: None,
db: db.db().clone(),
},
)?;
net.register(v6_addr1, b);

net.run();

sleep(Duration::from_secs(10));

assert_eq!(d1.peer_state(addr1), Some(BfdPeerState::Up));
assert_eq!(d2.peer_state(addr2), Some(BfdPeerState::Up));
// All four sessions should reach Up.
assert_eq!(d1.peer_state(v4_addr1), Some(BfdPeerState::Up),);
assert_eq!(d1.peer_state(v6_addr1), Some(BfdPeerState::Up),);
assert_eq!(d2.peer_state(v4_addr2), Some(BfdPeerState::Up),);
assert_eq!(d2.peer_state(v6_addr2), Some(BfdPeerState::Up),);

Ok(())
}
Expand Down
Loading