Skip to content

Latest commit

 

History

History
109 lines (76 loc) · 3.65 KB

File metadata and controls

109 lines (76 loc) · 3.65 KB

ADR-0042: Long-Lived Graceful Restart (RFC 9494)

Status: Accepted Date: 2026-03-05

Context

Standard Graceful Restart (RFC 4724, ADR-0024) preserves a restarting peer's routes for a bounded window (typically 120s). If the peer takes longer to recover — common in VM migrations, container rescheduling, or software upgrades with long init times — its routes are purged and traffic is disrupted.

RFC 9494 (Long-Lived Graceful Restart) extends GR with a second stale-timer phase. Instead of purging routes when the GR timer expires, LLGR-negotiated families are promoted to "LLGR-stale" and preserved for a much longer window (up to ~194 days). This is the standard mechanism used by large-scale deployments (Google, major IXPs) to handle extended peer outages gracefully.

GoBGP supports LLGR. Adding it closes a meaningful parity gap.

Decision

Capability

  • Code 71 (LLGR): Vec<LlgrFamily> where each family carries afi, safi, forwarding_preserved (1 bit), and stale_time (24-bit seconds, max 16,777,215 ≈ 194 days).
  • Advertised when llgr_stale_time > 0 in neighbor config.

Two-phase timer

GR timer expires → check LLGR negotiation:

  1. LLGR negotiated for family: promote routes to LLGR-stale, start LLGR timer with effective_stale_time = min(local llgr_stale_time, peer's per-family minimum).
  2. LLGR not negotiated for family: purge stale routes (existing GR behavior).

LLGR timer expires → purge all remaining LLGR-stale routes.

Best-path integration

Three-tier stale ranking at step 0 (before LOCAL_PREF), via stale_rank():

  • Fresh routes: rank 0 (preferred)
  • GR-stale routes: rank 1
  • LLGR-stale routes: rank 2 (least preferred)

This ensures LLGR-stale routes are used only as a last resort when no fresh or GR-stale alternative exists.

Community semantics

  • LLGR_STALE (0xFFFF0006): Added to routes when promoted from GR-stale to LLGR-stale. Locally-injected instances are tracked and removed when stale flags are cleared (EoR or peer recovery). Peer-originated LLGR_STALE communities are preserved.
  • NO_LLGR (0xFFFF0007): Routes carrying this community are purged at the GR-to-LLGR transition (they explicitly opt out of long-lived preservation).

RibManager state

  • llgr_peers: HashMap<IpAddr, HashSet<(Afi, Safi)>> — peers in LLGR phase
  • llgr_stale_deadlines: HashMap<IpAddr, tokio::time::Instant> — LLGR timer deadlines
  • llgr_peer_config: HashMap<IpAddr, LlgrPeerConfig> — stored at PeerUp, consumed at GR timer expiry

Third timer arm in run() select! loop for LLGR deadline polling.

PeerUp during LLGR

If a peer reconnects while in the LLGR phase, its families move back to gr_peers (awaiting EoR), and the LLGR timer is cancelled. EoR clears is_llgr_stale and removes locally-injected LLGR_STALE communities.

Config

[[neighbors]]
address = "10.0.0.2"
remote_asn = 65002
llgr_stale_time = 3600   # 0 = disabled (default), max 16777215

No new global config. LLGR is strictly per-neighbor.

Consequences

Positive

  • Routes survive extended peer outages (VM migration, slow restarts)
  • Matches GoBGP LLGR support
  • Three-tier ranking ensures LLGR-stale routes are least preferred
  • NO_LLGR community provides explicit opt-out per route

Negative

  • Additional timer complexity in RibManager (third deadline set)
  • LLGR_STALE community provenance tracking adds per-route state
  • Very long stale times could mask genuine peer failures

Neutral

  • LLGR capability is only advertised when configured (no impact on peers that don't negotiate it)
  • Builds on existing GR infrastructure (ADR-0024) with minimal new code paths