Skip to content

policies

Thomas Mangin edited this page Apr 11, 2026 · 5 revisions

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

Ze does not have a built-in filter language in the style of BIRD's filter DSL or FRR's route-map. Policy is a composition of plugins, assembled by referencing named filters in a peer's filter { import/export } block and (for filters that need tuning) defining their instances under bgp { policy { } }. This is a deliberate design choice: plugins can be written in Go or in any external language, and the engine stays out of the policy business.

This page covers what the current policy surface looks like, how the chain is built, and what the shipping plugins provide. The filter API itself, including filter block syntax, default-filter auto-population, and the inactive: / deactivate mechanism, is documented under route filters; this page is for operators picking policy, not authors writing it.

Loop detection is on by default

Ze always runs loop detection on ingress. Without any config, you get:

  • Local ASN in AS_PATH (AS_SEQUENCE or AS_SET), per RFC 4271 Section 9.
  • Local Router ID as ORIGINATOR_ID on iBGP sessions, per RFC 4456 Section 8.
  • Local Router ID in CLUSTER_LIST on iBGP sessions, per RFC 4456 Section 8.

Routes that fail any check are treated as withdrawn. No NOTIFICATION, the session stays up, the route never enters the RIB.

To tune the check (allow-own-as count, custom cluster-id), define a loop-detection filter instance under bgp/policy:

bgp {
    policy {
        loop-detection no-self-as { allow-own-as 0; }
    }
}

The instance name is then auto-populated at the front of every peer's import chain. To suppress loop detection on a specific peer, use inactive:<name> or deactivate in the CLI editor. See route filters for the mechanics.

The Role plugin enforces RFC 9234

When the role plugin is loaded and a peer has a role { import <role> } declaration, RFC 9234 role filtering runs on both ingress and egress. On receipt, routes from an invalid role direction are marked as leaks based on the OTC attribute. On send, OTC is stamped with the local ASN for peers whose role is customer, route-server-client, or peer (but not provider or route-server).

Role enforcement lives in the role plugin, not as an entry in the filter chain. See BGP Role for the reference.

Community filtering with bgp-filter-community

bgp-filter-community is a user filter for tagging, stripping, and matching communities (standard, large, extended). A typical use:

bgp {
    peer customer {
        connection { remote { ip 10.0.0.1; } }
        session {
            asn { remote 65001; }
            family { ipv4/unicast { prefix { maximum 1000000; } } }
        }
        filter {
            import [ community:scrub ];
            export [ community:mark-customer ];
        }
    }
}

The exact named filters (community:scrub, community:mark-customer, and so on) come from the plugin's own config, which you author to match your community policy. The in-tree plugin ships with a small set of examples, and the plugin reference has the full list.

If you only need to stop Ze from sending standard, large, or extended communities to a specific peer, use the session/community/send leaf instead of a filter. That is a session-level knob, not a filter.

Prefix list and AS-path regex matching

Two things people reach for on other daemons are not in Ze: prefix list matching with ge/le modifiers, and AS-path regular expression matching. Neither is implemented.

If you need prefix list matching, the workaround is a custom plugin. The plugin receives UPDATE events, keeps its own prefix table, and calls UpdateRoute with a rewritten attribute set when the prefix matches. This is more code than a filter expression, but it works with what the engine provides.

If you need AS-path regex matching, the same workaround applies.

The shape of a policy chain

Every filter chain has the same shape:

  1. Default filters (auto-populated from loop-detection entries under bgp/policy).
  2. User filters from the bgp { filter { } } block.
  3. User filters from the group { filter { } } block.
  4. User filters from the peer { filter { } } block.

The chains are cumulative. A peer inherits the BGP-level chain plus its group's chain plus its own. Each filter in the chain sees the previous filter's output, and each one can accept, reject, or modify. A reject short-circuits the chain.

To insert a filter at a specific position in the chain rather than appending, use insert in the CLI editor:

insert filter import reject-bogons before rpki:validate

A filter that modifies an attribute writes into a ModAccumulator, which is lazy: if no modification happens, no copy of the wire bytes is allocated. This is the engine's copy-on-need story, described in architecture.

Ingress vs egress, when it matters

Ingress filters run on routes as they come in from a peer. They can mark metadata on the cached entry (which shows up in best-path selection and in later filters) and they can reject routes before they enter the RIB.

Egress filters run per destination peer, per route, as the route is being forwarded. They are the right place to modify per-peer attributes (prepending AS path for a specific peer, adding a customer community, rewriting next-hop).

Because the destination peer is different on every egress, the same underlying cached UPDATE can produce different wire bytes on the way out to different peers. Ze only materialises those different bytes when a filter actually changes something. A destination peer with no egress-modifying filters gets the cached bytes unchanged when source and destination share an encoding context.

What to use today

The honest picture of what the shipping policy surface covers.

What works well today. Loop detection (loop-detection filter type), RFC 9234 role enforcement (role plugin), RPKI origin validation (bgp-rpki), community tagging and stripping (bgp-filter-community), session-level next-hop control / AS override / community-send control / default-originate (under session), iBGP route reflection (route-reflector-client and cluster-id), and custom policy through external plugins.

What does not. Prefix list matching with ge/le, AS-path regex, a named-policy DSL. If any of these are load-bearing for your deployment, Ze is the wrong answer today.

See also

Adapted from main/docs/guide/plugins.md and main/docs/guide/redistribution.md.

Home

About

First Steps

Configuration

Operation

Interfaces

Plugins

Plugin Development

Chaos Testing

Blueprints

Development

Reference

Clone this wiki locally