Guide

Graceful Restart

Graceful Restart (RFC 4724) preserves forwarding state across BGP session restarts. When a peer goes down and comes back, routes are held during the restart window instead of being immediately withdrawn, preventing traffic black-holes.

Configuration

plugin {
    internal gr {
        use bgp-gr
    }
    internal rib {
        use bgp-rib
    }
}

bgp {
    peer upstream1 {
        remote { ip 10.0.0.1; as 65001; }
        ...
        capability {
            graceful-restart {
                restart-time 120;
            }
        }

        attach process gr {
            receive [ open-received state eor ]
            send [ update ]
        }
        attach process rib {
            receive [ update state refresh ]
            send [ update ]
        }
    }
}

Config Reference

Path Type Default Description
graceful-restart / restart-time uint16 120 Seconds to hold stale routes during restart (0-4095)
graceful-restart / mode enum -- require: reject peers without GR capability
graceful-restart / disable presence -- Disable GR for this peer

How It Works

Normal Session

  1. Peer session establishes, GR capability negotiated in OPEN
  2. Routes received and installed in RIB normally

Peer Restarts

  1. Peer goes down -- GR plugin sends retain-routes to RIB
  2. RIB marks routes as stale -- routes kept in forwarding but flagged
  3. Restart timer starts -- countdown from restart-time seconds
  4. Peer reconnects -- new session established, fresh routes received
  5. Fresh routes replace stale -- each new route implicitly clears its stale flag
  6. End-of-RIB received -- GR plugin sends purge-stale to RIB
  7. Remaining stale routes removed -- any route not refreshed is withdrawn

Restart Timer Expiry

If the peer does not reconnect within restart-time seconds, all stale routes are purged. A safety margin of 5 seconds is added to account for processing delays.

Fail-Safe

If the GR plugin crashes or fails to issue purge-stale, the RIB automatically expires stale routes after restart-time + 5s.

Plugin Bindings

The GR plugin requires: - receive [ open-received state eor ] -- needs the peer's OPEN, its up/down events, and End-of-RIB markers - The RIB plugin must also be loaded with receive [ update state refresh ] and send [ update ]

The GR plugin depends on bgp-rib (declared in its registration). The engine ensures bgp-rib starts first.

Keep the bgp-gr engine in the daemon process

Load the plugin with internal <name> { use bgp-gr; }, as every example on this page does. run "ze plugin bgp-gr" starts the plugin engine in a child process, and that arrangement changes what the daemon sends to every peer.

The RFC 9494 egress filter runs in the daemon. The peer capability state it reads is written by the plugin engine, so with run that state stays empty for the life of the daemon process. The filter then treats every neighbor as a neighbor from which the LLGR Capability was never received: it withdraws stale routes from external peers (Section 4.3) and attaches NO_EXPORT with LOCAL_PREF 0 to the routes it sends to internal peers (Section 4.6). Peers that negotiated LLGR with the child engine get that same treatment.

ze doctor reports the arrangement as doctor-bgp-gr-out-of-process. Run ze explain doctor-bgp-gr-out-of-process for the full text.

CLI

 $ ze cli -c "show bgp rib received"
# Shows routes with stale flag when applicable

Long-Lived Graceful Restart (RFC 9494)

LLGR extends standard GR with a second, much longer stale period. When the GR restart-time expires without the peer reconnecting, instead of purging all stale routes, LLGR keeps them for up to ~194 days (per-family configurable) with reduced priority.

Configuration

Add long-lived-stale-time under the graceful-restart block:

capability {
    graceful-restart {
        restart-time 120;
        long-lived-stale-time 3600;    # LLGR period in seconds (0-16777215)
    }
}
Path Type Default Description
graceful-restart / long-lived-stale-time uint32 -- Seconds to hold LLGR-stale routes per family (0-16777215, 24-bit)

LLGR is only active when both peers negotiate it. Ze advertises LLGR capability (code 71) in OPEN when long-lived-stale-time is configured. LLGR requires GR capability (code 64) to also be present -- LLGR without GR is ignored per RFC 9494.

How It Works

  1. GR period expires -- peer has not reconnected within restart-time seconds
  2. LLGR begins -- for each family with long-lived-stale-time > 0: - Routes carrying the NO_LLGR community (0xFFFF0007) are deleted - LLGR_STALE community (0xFFFF0006) is attached to remaining stale routes - Routes are marked as stale level 2 (deprioritized in best-path selection) - Per-family LLST timer starts
  3. During LLGR -- LLGR-stale routes lose to any non-stale route in best-path selection. Between two LLGR-stale routes, normal tiebreaking applies.
  4. LLST timer expires -- stale routes for that family are purged
  5. Peer reconnects during LLGR -- standard RFC 4724 procedures apply; families with F-bit=0 or missing from the new OPEN are purged

Stale Levels

Ze uses a graduated stale level system for route prioritization:

Level Meaning Best-path behavior
0 Fresh Normal selection
1 GR-stale Normal selection (not deprioritized)
2+ LLGR-stale Loses to any route with level < 2

Readvertising a Stale Route

RFC 9494 Section 4.3 governs a stale route that ze forwards on to a neighbor from which the LLGR Capability was not received. The stale level on the ROUTE decides, not the restart state, so a route staled by request bgp rib mark-stale takes the same treatment as one staled by a peer restart.

Destination Action
LLGR was received from it Advertise unchanged
Internal, LLGR not received Advertise with NO_EXPORT and LOCAL_PREF 0 (Section 4.6)
External, LLGR not received Withdraw the route (Section 4.3)

The filter fails closed. When it cannot read the peer capability state, it answers "LLGR was not received" for every destination. It raises one warning per process and then withdraws or depreferences. An unread state used to accept, which put a long-lived stale route into a neighbor that never agreed to hold one.

The one arrangement that leaves the state permanently unread is run "ze plugin bgp-gr". See Keep the bgp-gr engine in the daemon process.

Special Case: Skip GR

If restart-time is 0 but long-lived-stale-time is nonzero, the GR period is skipped entirely. On session drop, LLGR begins immediately.

restart-time long-lived-stale-time Behavior
0 nonzero Skip GR, enter LLGR immediately
nonzero 0 GR only, no LLGR
0 0 Neither GR nor LLGR
nonzero nonzero GR then LLGR (serial)

Well-Known Communities

Community Value Purpose
LLGR_STALE 0xFFFF0006 Attached to stale routes during LLGR period
NO_LLGR 0xFFFF0007 Routes with this community are deleted on LLGR entry

CLI

Decode LLGR capability from hex:

$ ze plugin bgp-gr --capa 00010180000e10

Shows per-family LLST values and F-bit flags.

Without Graceful Restart

When GR is not configured or the peer does not advertise the GR capability, routes are withdrawn immediately on session down. No stale state, no restart timer.