From ce979d8f262bfa25551d6c42b735b22421b6771d Mon Sep 17 00:00:00 2001
From: centra
Date: Mon, 30 Mar 2026 20:06:08 +0900
Subject: [PATCH] fiberlb: add BGP interop, drain, and policy validation
---
.../crates/fiberlb-api/proto/fiberlb.proto | 1 +
.../crates/fiberlb-server/src/bgp_client.rs | 854 +++++++++++++++++-
fiberlb/crates/fiberlb-server/src/config.rs | 90 ++
fiberlb/crates/fiberlb-server/src/main.rs | 17 +-
.../src/services/loadbalancer.rs | 54 +-
.../crates/fiberlb-server/src/vip_manager.rs | 137 ++-
flake.nix | 18 +
nix/modules/fiberlb.nix | 69 ++
...fiberlb-native-bgp-ecmp-drain-vm-smoke.nix | 745 +++++++++++++++
.../fiberlb-native-bgp-interop-vm-smoke.nix | 737 +++++++++++++++
10 files changed, 2667 insertions(+), 55 deletions(-)
create mode 100644 nix/tests/fiberlb-native-bgp-ecmp-drain-vm-smoke.nix
create mode 100644 nix/tests/fiberlb-native-bgp-interop-vm-smoke.nix
diff --git a/fiberlb/crates/fiberlb-api/proto/fiberlb.proto b/fiberlb/crates/fiberlb-api/proto/fiberlb.proto
index 08f218f..0ada402 100644
--- a/fiberlb/crates/fiberlb-api/proto/fiberlb.proto
+++ b/fiberlb/crates/fiberlb-api/proto/fiberlb.proto
@@ -48,6 +48,7 @@ message CreateLoadBalancerRequest {
string org_id = 2;
string project_id = 3;
string description = 4;
+ string vip_address = 5;
}
message CreateLoadBalancerResponse {
diff --git a/fiberlb/crates/fiberlb-server/src/bgp_client.rs b/fiberlb/crates/fiberlb-server/src/bgp_client.rs
index 65dda4f..cc2ccfc 100644
--- a/fiberlb/crates/fiberlb-server/src/bgp_client.rs
+++ b/fiberlb/crates/fiberlb-server/src/bgp_client.rs
@@ -5,15 +5,15 @@
//! and `/32` VIP advertise/withdraw driven by the VIP manager.
use std::collections::HashMap;
-use std::net::{IpAddr, Ipv4Addr};
-use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
+use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+use std::sync::atomic::{AtomicU32, AtomicU64, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
-use tokio::net::TcpStream;
+use tokio::net::{TcpStream, UdpSocket};
use tokio::sync::{mpsc, watch, RwLock};
-use tokio::time::sleep;
+use tokio::time::{sleep, MissedTickBehavior};
use tracing::{debug, info, warn};
use crate::config::{BgpConfig, BgpPeerConfig};
@@ -26,18 +26,31 @@ const BGP_TYPE_NOTIFICATION: u8 = 3;
const BGP_TYPE_KEEPALIVE: u8 = 4;
const BGP_MAX_MESSAGE_SIZE: usize = 4096;
const BGP_ORIGIN_IGP: u8 = 0;
+const ATTR_FLAG_OPTIONAL: u8 = 0x80;
const ATTR_FLAG_TRANSITIVE: u8 = 0x40;
const ATTR_FLAG_EXTENDED_LEN: u8 = 0x10;
const ATTR_TYPE_ORIGIN: u8 = 1;
const ATTR_TYPE_AS_PATH: u8 = 2;
const ATTR_TYPE_NEXT_HOP: u8 = 3;
+const ATTR_TYPE_MULTI_EXIT_DISC: u8 = 4;
+const ATTR_TYPE_COMMUNITIES: u8 = 8;
const AS_PATH_SEGMENT_SEQUENCE: u8 = 2;
const METRIC_BGP_CONFIGURED_PEERS: &str = "fiberlb_bgp_configured_peers";
const METRIC_BGP_CONNECTED_PEERS: &str = "fiberlb_bgp_connected_peers";
const METRIC_BGP_DESIRED_ROUTES: &str = "fiberlb_bgp_desired_routes";
const METRIC_BGP_PEER_SESSION_UP: &str = "fiberlb_bgp_peer_session_up";
+const METRIC_BGP_PEER_BFD_UP: &str = "fiberlb_bgp_peer_bfd_up";
const METRIC_BGP_SESSION_ESTABLISHED_TOTAL: &str = "fiberlb_bgp_session_established_total";
const METRIC_BGP_SESSION_ENDS_TOTAL: &str = "fiberlb_bgp_session_ends_total";
+const BFD_CONTROL_PORT: u16 = 3784;
+const BFD_VERSION: u8 = 1;
+const BFD_DIAGNOSTIC_NONE: u8 = 0;
+const BFD_STATE_ADMIN_DOWN: u8 = 0;
+const BFD_STATE_DOWN: u8 = 1;
+const BFD_STATE_INIT: u8 = 2;
+const BFD_STATE_UP: u8 = 3;
+const BFD_PACKET_LEN: usize = 24;
+static NEXT_BFD_DISCRIMINATOR: AtomicU32 = AtomicU32::new(1);
/// Result type for BGP operations.
pub type Result = std::result::Result;
@@ -121,6 +134,9 @@ impl NativeBgpSpeaker {
record_desired_routes(0);
for peer in &config.peers {
set_peer_session_up(peer, false);
+ if peer.bfd.enabled {
+ set_peer_bfd_up(peer, false);
+ }
}
for peer in config.peers.clone() {
@@ -187,6 +203,266 @@ pub async fn create_bgp_client(config: BgpConfig) -> Result>
Ok(Arc::new(speaker))
}
+#[derive(Debug)]
+enum SessionEvent {
+ Bgp(Result),
+ Bfd(BfdPeerState),
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum BfdPeerState {
+ Up,
+ Down,
+}
+
+#[derive(Debug)]
+struct BfdRuntime {
+ shutdown: watch::Sender,
+ task: tokio::task::JoinHandle<()>,
+}
+
+fn maybe_start_bfd_runtime(
+ stream: &TcpStream,
+ peer: &BgpPeerConfig,
+ event_tx: mpsc::Sender,
+) -> Result