Skip to content
Thomas Mangin edited this page May 24, 2026 · 8 revisions

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

Ze validates received BGP routes against RPKI ROA data. Invalid routes are rejected before they enter the RIB. The feature connects to RTR cache servers (RFC 8210), downloads Validated ROA Payloads, and applies the RFC 6811 origin validation algorithm to each received prefix. The implementation is a plugin (bgp-rpki) that depends on bgp-adj-rib-in for the validation gate.

A minimal setup

bgp-rpki and bgp-adj-rib-in are internal plugins loaded automatically.

bgp {
    rpki {
        cache-server 192.0.2.1 {
            port 323;
        }
    }

    peer upstream {
        connection {
            remote { ip 10.0.0.1; }
            local  { ip 10.0.0.2; }
        }
        session {
            asn { local 65000; remote 65001; }
            family { ipv4/unicast { prefix { maximum 1000000; } } }
        }

        process bgp-rpki        { receive [ update ]; }
        process bgp-adj-rib-in  { receive [ update state ]; }
    }
}

bgp-rpki must be bound to the peer with process bgp-rpki { receive [ update ] }. bgp-adj-rib-in must be bound with process bgp-adj-rib-in { receive [ update state ] }. bgp-adj-rib-in is what holds routes pending validation.

Configuration reference

Path Type Default Description
rpki/cache-server <addr> list required RTR cache server, keyed by IP or hostname.
rpki/cache-server/port uint16 323 RTR TCP port.
rpki/cache-server/preference uint8 100 Server preference. Lower is preferred.
rpki/validation-timeout uint16 30 Seconds before fail-open on pending routes.
rpki/aspa-validation boolean false Enable ASPA path verification using RTR v2 ASPA records.
rpki/policy/invalid-action enum reject Action for Invalid routes: reject, log-only, accept.
rpki/policy/not-found-action enum accept Action for NotFound routes: accept, reject, log-only.

Multiple cache servers are supported for redundancy. The VRP tables from all servers are merged as a union.

The validation states

Each received route gets one of three states, per RFC 6811.

State Meaning Default action
Valid Origin AS and prefix length match a VRP. Accept.
Invalid A VRP covers the prefix, but the origin AS or the prefix length does not match. Reject.
NotFound No VRP covers the prefix. Accept.

Override the actions with rpki/policy/invalid-action and rpki/policy/not-found-action. Setting invalid-action reject is what you want in production: it implements RPKI-based route origin validation. Setting it to log-only is useful during deployment while you build confidence in your cache.

How the validation flows

The order is deterministic and the same on every UPDATE.

  1. Ze connects to the configured RTR cache servers and downloads the VRPs.
  2. A BGP UPDATE arrives from a peer.
  3. bgp-adj-rib-in stores the route as "pending".
  4. rpki extracts the origin AS (the rightmost AS in the final AS_SEQUENCE segment).
  5. For each NLRI prefix in the UPDATE, rpki looks up covering VRPs and computes the validation state.
  6. Valid and NotFound routes are promoted to "installed". Invalid routes are discarded (or logged, depending on policy).

Fail-open safety

If the bgp-rpki plugin does not respond within validation-timeout seconds (default 30), pending routes are automatically promoted. This prevents route black-holing when the RPKI infrastructure is down.

If every RTR cache server disconnects, the existing VRP cache is retained until a connection is re-established. Routes continue to be validated against the last known good cache. The failure mode is graceful: a cache outage does not immediately invalidate everything.

AS_PATH edge cases

A few edge cases worth knowing about.

AS_PATH Origin AS Result
Normal sequence [65000 65001] 65001 (rightmost) Normal validation.
Ends with AS_SET {65001 65002} None Always Invalid when a covering VRP exists.
Empty (iBGP, no prepend) None NotFound.

An AS_SET at the end of the path has no single origin, so RPKI cannot validate it. The safest default is Invalid when a VRP covers the prefix.

CLI commands

ze cli -c "rpki status"      # RTR session count, VRP counts
ze cli -c "rpki cache"       # Cache server connection details
ze cli -c "rpki roa"         # ROA table summary
ze cli -c "rpki summary"     # Validation statistics
ze cli -c "rpki validate <prefix> <origin-as>"  # Manual validation check
ze cli -c "rpki aspa"        # ASPA records and validation state

Example output:

{"running":true,"vrp-count-ipv4":3,"vrp-count-ipv6":0,"sessions":1}

RPKI events

The bgp-rpki plugin emits validation events that other plugins can subscribe to. A plugin that subscribes to bgp-rpki direction received receives a JSON event for each validated UPDATE.

{
  "type": "bgp",
  "bgp": {
    "peer":    {"address": "10.0.0.1", "remote": {"as": 65001}},
    "message": {"id": 42, "type": "rpki"},
    "rpki": {
      "ipv4/unicast": {
        "10.0.1.0/24": "valid",
        "10.0.2.0/24": "invalid"
      }
    }
  }
}

When the ROA cache is empty, the event is "rpki": {"status": "unavailable"}.

Merged events with bgp-rpki-decorator

Instead of receiving separate UPDATE and RPKI events, subscribe to merged update-rpki events from the bgp-rpki-decorator plugin. Each event carries the full UPDATE plus the validation state in one envelope.

plugin {
    external rpki-decorator {
        use bgp-rpki-decorator;
        encoder json;
    }
}

bgp {
    peer upstream {
        process my-consumer   { receive [ update-rpki ] }
        process bgp-rpki          { receive [ update ] }
        process rpki-decorator    { receive [ update rpki ] }
        process bgp-adj-rib-in    { receive [ update state ] }
    }
}

The merged event is the full UPDATE JSON with an rpki section injected. If the RPKI validation does not arrive within the decorator's 2-second timeout, the event is emitted without the rpki section (graceful degradation).

ASPA path verification

ASPA (Autonomous System Provider Authorization) verifies that AS_PATH hops are authorized by provider-customer relationships. ASPA records are distributed via RTR v2 (RFC 9582) alongside VRPs. Ze implements the upstream path verification algorithm from draft-ietf-sidrops-aspa-verification Section 6.

ASPA is opt-in. Enable it with aspa-validation true under the rpki block. It requires a cache server that supports RTR v2 (the session negotiates v2 automatically when the server supports it, falling back to v1 otherwise).

ASPA policy enforcement

By default, ASPA operates in informational mode: the state is included in the RPKI event JSON but does not trigger accept/reject decisions. To enforce ASPA results, configure the ASPA policy:

bgp {
    rpki {
        cache-server 192.0.2.1 { port 323; }
        aspa-validation true;
        aspa-policy {
            invalid-action reject;
        }
    }
}
Path Type Default Description
rpki/aspa-policy/invalid-action enum accept Action for ASPA Invalid paths: reject, log-only, accept.

Configuration

bgp {
    rpki {
        cache-server 192.0.2.1 {
            port 323;
        }
        aspa-validation true;
    }
}

ASPA validation states

Each route receives one of three ASPA states, independent of the origin validation state:

State Meaning
Valid Every hop pair in the AS_PATH is authorized by an ASPA record.
Invalid At least one hop pair has an ASPA record that does not list the provider candidate.
Unknown No ASPA records exist for one or more customer ASNs in the path.

How it works

  1. The RTR session negotiates v2 with the cache server (falls back to v1 on error code 4, which disables ASPA since v1 has no ASPA PDU)
  2. The cache server sends ASPA PDUs (type 11, RFC 9582 Section 5.12) alongside VRPs
  3. For each received UPDATE, the plugin normalizes the AS_PATH: removes consecutive duplicate ASNs (prepend artifacts), strips AS_CONFED_SEQUENCE segments, and flags AS_SET or AS_CONFED_SET as unverifiable
  4. Each adjacent pair (provider candidate, customer) is checked against the ASPA cache
  5. The result appears as "aspa-state" in the RPKI event JSON

ASPA in events

The "aspa-state" field is included alongside per-prefix origin validation results:

{
  "type": "bgp",
  "bgp": {
    "peer": {"address": "10.0.0.1", "name": "upstream", "remote": {"as": 65001}},
    "message": {"id": 42, "type": "rpki"},
    "rpki": {
      "ipv4/unicast": {
        "10.0.1.0/24": "valid"
      },
      "aspa-state": "valid"
    }
  }
}

When ASPA validation is disabled or the cache has no ASPA records, the "aspa-state" field is omitted from the event.

Re-validation on cache change

Routes are tracked with their normalized AS_PATH. When ASPA cache data changes (new or removed records from the RTR server), affected routes are automatically re-validated and updated events are emitted for any route whose ASPA state changed.

Testing ASPA

The ze-test rtr-mock command supports ASPA records with the --aspa flag:

ze-test rtr-mock --port 3323 \
    --vrp 10.0.0.0/8,24,65001 \
    --aspa 64502:64501 \
    --aspa 64501:64500

The format is customer:provider1,provider2,... (repeatable). When ASPA records are present, the mock server responds with RTR v2. Four functional tests validate all ASPA states: rpki-aspa-valid, rpki-aspa-invalid, rpki-aspa-unknown, and rpki-aspa-disabled.

Testing locally

ze-test rpki --port 3323

ze-test rpki starts a deterministic mock RTR server that auto-generates VRPs based on the first octet of each /8 prefix. Validation states are predictable for routes from AS 65001.

First octet State
0, 3, 6, 9, 12, ... Valid
1, 4, 7, 10, 13, ... Invalid
2, 5, 8, 11, 14, ... NotFound

Without RPKI

When the bgp-rpki plugin is not loaded, routes flow directly into bgp-adj-rib-in with zero overhead. No pending state, no validation delay. The validation gate only activates when the bgp-rpki plugin sends bgp-adj-rib-in enable-validation during startup.

Troubleshooting

Symptom Cause Fix
Routes delayed 30 seconds then accepted RTR cache server unreachable. Check connectivity, verify the port.
All routes Invalid Wrong cache server data, or origin AS mismatch. Check rpki roa, verify VRP coverage.
No VRPs loaded RTR session not established. Check rpki status, verify the cache server is running.
Routes accepted without validation The bgp-rpki plugin is not bound to the peer. Add process bgp-rpki { receive [ update ] } to the peer config.

See also

  • Overview for the surrounding config model.
  • BGP peers for the peer-level settings.
  • Plugins for bgp-rpki and bgp-rpki-decorator.

Adapted from main/docs/guide/rpki.md.

Home

About

First Steps

Configuration

Operation

Interfaces

Plugins

Plugin Development

Chaos Testing

Blueprints

Development

Reference

Clone this wiki locally