Skip to content

transit edge with rpki

Thomas Mangin edited this page May 30, 2026 · 5 revisions

Pre-Alpha. This page describes behavior that may change. Not for production use.

A transit customer edge router. Two upstream providers, one customer downstream, RPKI origin validation to drop Invalid routes at the door, and BGP Role (RFC 9234) to prevent accidental route leaks.

Topology

     +-----------+         +-----------+
     | Transit A |         | Transit B |
     | AS64501   |         | AS64502   |
     | 10.0.0.1  |         | 10.0.0.2  |
     +-----+-----+         +-----+-----+
           |                     |
           +----------+----------+
                      |
              +-------+-------+
              |   Ze Edge     |
              |   AS65000     |
              +-------+-------+
                      |
              +-------+-------+
              | Customer X    |
              | AS65100       |
              | 10.0.1.1      |
              +---------------+

Two eBGP sessions upstream, one eBGP session downstream. Ze enforces:

  • RPKI validation on routes received from transit providers.
  • BGP Role customer on the transit sessions and provider on the customer session.
  • Prefix limits that match the expected table sizes.
  • Sensible GR and timers.

Configuration

plugin {
    internal rib         { use bgp-rib }
    internal adj-rib-in  { use bgp-adj-rib-in }
    internal rpki        { use bgp-rpki }
    internal role        { use bgp-role }
    internal gr          { use bgp-gr }
}

system {
    host edge-01;
}

bgp {
    router-id 10.0.0.100;
    session { asn { local 65000; } }

    rpki {
        cache-server 10.0.0.50 { port 323; }
        policy { invalid-action reject; }
    }

    group transit {
        session {
            capability {
                asn4;
                route-refresh;
                graceful-restart { restart-time 120; }
            }
            family {
                ipv4/unicast { prefix { maximum 1000000; } }
                ipv6/unicast { prefix { maximum 200000;  } }
            }
        }
        role {
            import customer;
            strict true;
        }
        process rib         { receive [ update state ]; send [ update ]; }
        process adj-rib-in  { receive [ update state ]; }
        process rpki        { receive [ update ]; }
        process gr          { receive [ state eor ]; }
    }

    group customers {
        session {
            capability {
                asn4;
                route-refresh;
                graceful-restart { restart-time 120; }
            }
            family {
                ipv4/unicast { prefix { maximum 10000; } }
                ipv6/unicast { prefix { maximum 10000; } }
            }
        }
        role {
            import provider;
            strict false;
        }
        process rib         { receive [ update state ]; send [ update ]; }
        process adj-rib-in  { receive [ update state ]; }
        process rpki        { receive [ update ]; }
        process gr          { receive [ state eor ]; }
    }

    peer transit-a {
        inherit transit;
        connection { remote { ip 10.0.0.1; } local { ip 10.0.0.100; } }
        session    { asn { remote 64501; } }
    }

    peer transit-b {
        inherit transit;
        connection { remote { ip 10.0.0.2; } local { ip 10.0.0.100; } }
        session    { asn { remote 64502; } }
    }

    peer customer-x {
        inherit customers;
        connection { remote { ip 10.0.1.1; } local { ip 10.0.1.100; } }
        session    { asn { remote 65100; } }
    }
}

Two groups: transit (where you are a customer of the remote side) and customers (where you are their provider). Each group sets the correct Role direction, the right prefix limits, and the plugin bindings.

Verification

ze cli -c "bgp summary"

Every session should be ESTABLISHED. Prefix counts should make sense: around a million v4 from each transit, a few hundred or low thousands from the customer.

ze cli -c "peer transit-a capabilities"

Look for graceful-restart: yes, bgp-role: yes with the role pair (provider, customer), and ADD-PATH not negotiated (you do not need it for a customer edge).

ze cli -c "rpki status"

Look for a non-zero VRP count and an established RTR session. If the count is zero, RPKI is not actually validating anything.

ze cli -c "rib routes received transit-a ipv4/unicast | count"

The count should roughly match the global v4 table size. If it is much smaller, something is filtering upstream.

ze show warnings

This is where you catch the interesting operational problems: prefix threshold crossings, RPKI stale warnings, session drops that did not exchange a NOTIFICATION.

What to watch

RPKI Invalid counts. ze show errors | jq '.errors[] | select(.code == "rpki-invalid")' surfaces the Invalid drops. A handful per day is normal. Thousands means something is wrong with your cache or with your transit provider.

Role mismatches. ze show errors | jq '.errors[] | select(.code == "notification-sent")' and look for subcode 11. If a transit session bounces with Role Mismatch, either the transit provider did not expect you to declare yourself as customer, or their role is misconfigured.

Prefix threshold. ze show warnings during full-table reconvergence. If you cross the warning threshold on a transit link, the report bus pushes a warning and the login banner shows it.

What could go wrong

RPKI cache unreachable on startup. The daemon starts normally but drops nothing, because the VRP count stays at zero. Monitor ze show warnings for the RPKI health state. Alternatively, set rpki/policy/not-found-action reject during RPKI outages as a belt-and-braces measure (dangerous: drops everything until the cache comes back).

Transit AS-path regex rejection. If your policy is "reject routes whose path goes through AS X", use the bgp-filter-aspath plugin: a named AS-path regex filter with ordered entries (first match wins, accept/reject). Reference it in the peer's import filter chain.

Customer announcing more than you expect. The customer's prefix maximum is 10,000 here. If they accidentally leak their full table, Ze tears down the session with Cease/MaxPrefixes. That is usually what you want, but it can be a surprise if the customer never hits that limit in normal operation.

Missing community scrubbing on the customer ingress. This config does not include a community-scrub filter. A real transit edge would scrub customer communities on ingress to prevent control leakage. The bgp-filter-community plugin does this; the specific filter set is deployment-specific.

See also

Home

About

First Steps

Configuration

Operation

Interfaces

Plugins

Plugin Development

Chaos Testing

Blueprints

Development

Reference

Clone this wiki locally