Skip to content

route injection

Thomas Mangin edited this page Jul 25, 2026 · 3 revisions

Pre-Alpha. This page describes behavior that may change.

Route injection is one of the two things Ze was built for. You announce routes at runtime from a script, from the CLI, or from a plugin, and Ze sends them to the peers you selected. The mechanism is the same whether the source is a human typing at ze cli or a Python process reading events from stdin.

Three wire formats are supported for UPDATE commands: text (human readable), hex (wire-level), and base64 (compact). Text is what you use day to day. Hex and base64 are for debugging, replay, and scripts that already produce wire bytes.

Text format

ze cli -c "peer upstream update text \
    origin igp \
    nhop 192.168.1.1 \
    local-preference 200 \
    as-path [ 65001 65002 ] \
    community [ 65000:100 no-export ] \
    nlri ipv4/unicast add 10.0.0.0/24 10.0.1.0/24"

Attributes are flat: keyword followed by value. There is no set/add/del on attributes. add and del are NLRI-only operations (announce versus withdraw).

Attribute keywords

Keyword Example
origin origin igp, origin egp, origin incomplete
nhop nhop 192.168.1.1 or nhop self
med med 100
local-preference local-preference 200
as-path as-path [ 65000 65001 ]
community community [ 65000:100 no-export ]
large-community large-community [ 65000:1:1 ]
extended-community extended-community [ rt:65000:100 ]

AS_PATH and the local AS

An as-path you supply is not always what goes on the wire. For a plain eBGP peer, Ze prepends the local AS unless your path already leads with it, because RFC 4271 section 5.1.2 requires the announcing speaker's AS at the front. A path that omits it would be non-conformant and, worse, invisible to the receiver's own loop detection.

Two cases keep the path verbatim: an iBGP peer, where nothing is prepended, and a peer marked as a route-server client, where RFC 7947 section 2.2.2.1 grants exactly this transparency. That transparency is scoped to RS clients and to nothing else. Because the two shapes differ, a route-server client and an ordinary eBGP peer never share a built UPDATE, even when they are announced in the same batch.

Well-known communities

no-export, no-advertise, no-export-subconfed, and nopeer are recognised by name. Any other community uses the numeric high:low form.

Next-hop self

nhop self resolves to the local bind address of each destination peer at wire time. This is usually what you want on route servers and iBGP next-hop-self configurations.

NLRI operations

add and del are MP_REACH and MP_UNREACH respectively.

nlri ipv4/unicast add 10.0.0.0/24 10.0.1.0/24    # Announce prefixes
nlri ipv4/unicast del 10.0.2.0/24                # Withdraw prefix
nlri ipv4/unicast eor                            # End-of-RIB marker

Multiple families in one command:

nlri ipv4/unicast add 10.0.0.0/24 \
nlri ipv6/unicast add 2001:db8::/32

Hex format

Wire-encoded bytes. Use this for replay or for scripts that already have the bytes.

ze cli -c "peer upstream update hex \
    attr set 40010100400200400304c0a80101 \
    nhop set c0a80101 \
    nlri ipv4/unicast add 180a0000"

Base64 format

Compact alternative to hex.

ze cli -c "peer upstream update b64 \
    attr set QAEBAAQDAsCoBQE= \
    nlri ipv4/unicast add GAoAAA=="

Peer selectors

Routes are sent to peers matching a selector. Every selector that applies to a route command is the same shape as the one peer list takes.

Selector Example Effect
* peer * Every peer.
Name peer upstream One peer by its configured name.
IP peer 10.0.0.1 One peer by IP.
Glob peer 192.168.*.* Every peer matching the pattern.
Exclusion peer !10.0.0.1 Every peer except this one.
ASN peer as65001 Every peer with this remote ASN.

peer * update text ... announces the route to every established session. peer !upstream update text ... announces it to every session except the one named upstream.

Injecting into the local RIB

The commands above announce a route to peers. A separate command, request bgp rib inject, inserts a synthetic candidate directly into Ze's own BGP RIB without needing a live session. If the candidate wins best-path selection it continues through the system RIB and the configured FIB backends, so this is how you place a route into forwarding from a script or a test.

ze cli -c "request bgp rib inject 192.0.2.10 ipv4/unicast 198.51.100.0/24 origin igp nexthop 127.0.0.1 med 42"
ze cli -c "show bgp rib best prefix 198.51.100.0/24"
ze cli -c "show rib"

Withdraw the same candidate with the matching synthetic peer, family, and prefix:

ze cli -c "request bgp rib withdraw 192.0.2.10 ipv4/unicast 198.51.100.0/24"

An injected IPv4 route may carry an IPv6 next-hop (RFC 5549 extended next-hop). The bgp-rib and rib plugins provide best-path and system-RIB selection; on Linux, fib-kernel programs the selected route through netlink.

Commit workflow for atomic batches

When you need to announce several routes as one transaction, the commit workflow is what you want. Start a named window, add routes, end the window. Every route in the window is sent together.

ze cli -c "commit start my-batch"
ze cli -c "peer * update text nhop 10.0.0.1 nlri ipv4/unicast add 10.0.0.0/24"
ze cli -c "peer * update text nhop 10.0.0.1 nlri ipv4/unicast add 10.0.1.0/24"
ze cli -c "commit end my-batch"

commit end is the point at which the updates actually go out on the wire. commit rollback <name> discards the window without sending anything. commit eor <name> sends an End-of-RIB marker for the window.

The commit workflow is the right tool for announcing a group of routes together, for draining and re-announcing a customer's routes, and for any operation where the "many routes in flight, some sent, some not" state is unacceptable.

From plugins

External plugins send routes through the SDK UpdateRoute call. The command string is the same text command you would type at the CLI.

Python, through the ze_api SDK:

from ze_api import API

api = API()
# ... 5-stage startup ...
api.send("peer * update text nhop 10.0.0.1 nlri ipv4/unicast add 10.0.0.0/24")

Go, through the SDK:

p.UpdateRoute(ctx, "*", "update text nhop 10.0.0.1 nlri ipv4/unicast add 10.0.0.0/24")

The selector in the SDK is the second argument ("*" here). The command string starts with update because the plugin is already scoped to "a route injection command".

From the MCP server

The MCP server exposes a typed ze_announce tool that is the same mechanism with a structured API. An AI assistant calls ze_announce with origin, next-hop, communities, prefixes, and the peer selector, and the server translates the call into the same text command internally.

See MCP for the full tool surface.

See also

Adapted from main/docs/guide/route-injection.md.

Home

About

First Steps

Configuration

Operation

Interfaces

Plugins

Plugin Development

Chaos Testing

Blueprints

Development

Reference

Clone this wiki locally