Ze holds one Loc-RIB for every route source. An import chain judges each route as it enters that Loc-RIB. This page shows where you attach a chain, how the chain decides, and what happens to a route it rejects. BGP sessions keep their own import and export chains: BGP Policy covers them.
Every filter instance is defined once, under the top-level policy {} root.
Each filter type is a list there, and an entry of that list is a named
instance. A chain names instances. One name belongs to one filter type only.
Four filter types can run at a Loc-RIB attachment today:
| Filter type | What it does to a route |
|---|---|
match |
Tests the route against the conditions the instance names. With action accept, the default, a route that meets every condition passes on and any other is rejected. With action reject, a route that meets them is rejected and any other passes on |
modify |
A route that meets its match block gets the values its set block names. At a Loc-RIB attachment, set takes distance and tag. The route always passes on |
prefix-list |
The first entry that matches decides: accept passes the route on, reject stops the chain. A route that matches no entry is rejected |
family-filter |
action remove rejects a route of the instance's family and passes every other route on. action tear-down closes a BGP session, so it is refused at a Loc-RIB attachment |
match belongs to the policy component and is in every build. The other three
come from BGP plugins, so a build without the ze_bgp tag refuses a Loc-RIB
chain that names one of them. Their Loc-RIB evaluators are linked into the
daemon and run inside the policy plugin, so a Loc-RIB chain needs no BGP
plugin process and no bgp { } block: a config with only policy and an
IS-IS, OSPF, static or connected import chain starts without BGP. The BGP
filter plugins themselves load only when the config holds a bgp { } block.
A match instance names one or more conditions. A route must meet every
condition it names, and a condition with several values is met by any one of
them. A route that does not carry the field a condition reads never meets it:
an untagged route fails tag [ 0 ].
Each condition is registered by the package that owns what it reads, and
match itself names none: the policy component registers the conditions over
the fields every route carries, the prefix filter registers prefix-list, and
the community match filter registers community, and the reject-asn filter
registers reject-asn. A build without the ze_bgp
tag therefore has no prefix-list or community condition, as it has no list
for either to name. A condition is a pure function of the route
and the instance's config: it runs inside the chain, never in a plugin
process. The AS-path filters register as-path and as-path-length, which
only a BGP chain judges: a Loc-RIB route carries no AS path, so a match naming
either there is refused.
| Condition | Met when |
|---|---|
prefix-list <name> |
The named prefix-list instance accepts the prefix. The editor completes the configured prefix-lists, and a commit naming any other is refused |
reject-asn <name> |
The named reject-asn instance rejects the route on neither arm: no listed ASN at a listed position of its AS path, and no pattern matching the whole path. The sender is known on import only. A BGP chain only. The editor completes the configured lists, and a commit naming any other is refused |
community <name> |
The named community-match instance accepts the route: its first entry whose standard, large or extended community the route carries decides, and a route no entry matches fails. Only a BGP chain judges it. The editor completes the configured lists, and a commit naming any other is refused |
as-path <name> |
The named as-path-list instance accepts the UPDATE's AS path, read as its space-separated decimal ASNs. Only a BGP chain judges it. The editor completes the configured lists, and a commit naming any other is refused |
as-path-length <name> |
The UPDATE's AS path hop count, every ASN of the path, lies within the named as-path-length instance's min and max. Only a BGP chain judges it. The editor completes the configured instances, and a commit naming any other is refused |
family [ ... ] |
The route's address family is listed, such as ipv4/unicast |
protocol [ ... ] |
The route's source is listed: a registered source such as static, or ebgp or ibgp for a BGP path |
next-hop [ ... ] |
The next hop lies in a listed prefix. A connected route has no next hop |
interface [ ... ] |
The outgoing interface is listed |
metric { minimum; maximum; } |
The metric lies in the range. Either bound may be left out |
tag [ ... ] |
The route carries one of the tags |
level [ ... ] |
The IS-IS level is listed, 1 or 2 |
route-type [ ... ] |
The OSPF path type is listed: intra-area, inter-area, external-1, external-2, nssa-1, nssa-2 |
The role plugin inserts bgp-role:upstream-asn where the declared role needs
the upstream check. You do not need a reject-asn match for this check.
The check compares provider ASNs from the config and any role transit-asns
set. Unlike the required OTC members, this member can be disabled.
See BGP Role.
This hides every static route tagged 4242 and keeps the others:
policy {
match NO-4242 {
action reject;
tag [ 4242 ];
}
}
rib {
filter {
import {
filters [ NO-4242 ];
final accept;
}
}
}
modify takes the source conditions in its match block: protocol, tag,
level and route-type. A modify with match { tag [ 100 ]; } and
set { distance 90; } on ospf filter import ranks OSPF routes tagged 100 at
distance 90 and leaves the others at the OSPF distance.
Ze checks each instance against the fields it reads and sets. A route carries only what its source knows, so a filter reading a field the source never sets is refused rather than matching nothing.
| Attachment | Routes carry | A filter may set |
|---|---|---|
connected filter import |
prefix, family, protocol, interface | distance, tag |
static filter import |
prefix, family, protocol, next-hop, interface, metric, tag | distance, tag |
ospf filter import |
prefix, family, protocol, next-hop, interface, metric, tag, route-type | distance, tag |
isis filter import |
prefix, family, protocol, next-hop, interface, metric, level | distance, tag |
rib filter import |
prefix, family, protocol, next-hop, interface, metric, tag, level, route-type | distance, tag |
isis filter export |
prefix, family, protocol, next-hop, metric, tag | metric |
ospf filter export |
prefix, family, protocol, next-hop, metric, tag | tag, metric, metric-type |
redistribute destination D import S |
prefix, family, protocol, next-hop, metric, tag | tag, metric |
bgp filter import, bgp filter export (bgp, group and peer) |
prefix, withdrawn, family, protocol, next-hop, origin-as, community, large-community, extended-community, as-path, local-preference, origin, aigp, med, atomic-aggregate, aggregator, originator-id, cluster-list | as-path, local-preference, community, large-community, extended-community, med, origin, next-hop, aigp |
BGP names a route's metric med and carries no metric field, so each fact
has one name. A match instance with a metric condition is refused on a BGP
chain, and the refusal names med as the BGP field; the same instance still
loads at every other attachment that carries metric.
IS-IS routes carry no tag, because Ze does not implement the IS-IS
administrative tag (RFC 5130). A static route carries a tag only when its tag
leaf is set, and tag 0 is a real tag. An OSPF external route carries the tag
of its LSA. A redistributed route always carries a tag, 0 when its source set
none, so tag [ 0 ] on a redistribute import selects the untagged routes.
Five config roots carry filter { import { ... } }:
| Root | Chain | Routes it judges |
|---|---|---|
isis |
source | Routes IS-IS installs |
ospf |
source | Routes OSPF installs |
static |
source | Static routes |
connected |
source | Connected routes |
rib |
central | Routes from every source, BGP included |
A route meets its source's chain first, then the rib chain. A route is
selected only when both accept it.
Three more chains judge the routes one protocol hands another, never the routes the Loc-RIB selects:
| Config | Chain | Routes it judges |
|---|---|---|
redistribute { destination <protocol> { import <source> { ... } } } |
redistribute import | Each route a source offers that destination |
isis filter export |
IS-IS export | Each redistributed route before it enters the LSP |
ospf filter export |
OSPF export | Each redistributed route before its AS-external LSA is originated |
A redistributed route meets the redistribute import chain first, then the
destination's export chain, and a set metric or set tag in the export chain
replaces the one the import chain set. Route Filters and
Redistribution covers both.
Every one of these chains, and the five above, judges the same typed route, so
each attachment registers with MustRegisterRouteAttachment, naming the fields
its routes carry and the fields a filter there may set ("The fields each
attachment carries", above).
A BGP peer's import and export lists can hold members you did not write.
A plugin declares an insertion: a filter it serves, a direction, a priority and
one condition on the peer's config. When the peer's resolved config (bgp,
then its group, then the peer) meets the condition, config load places the
member ahead of your own members, in ascending priority. A peer whose config
does not meet it carries no member, and the plugin is never called for its
routes. An inserted member leaves the list's end disposition yours: a list
holding only inserted members is still unconfigured for RFC 8212, so an eBGP
peer with no final of your own still rejects at the end.
A plugin compiled into Ze conditions an insertion only on config its own YANG
module adds, so the member appears exactly where you configured that plugin.
Naming an inserted member yourself places it where you wrote it, or switches it
off with inactive:, unless the plugin marked it required: a required member
named in a chain is refused at load with the reason the plugin declared.
bgp-role:otc-ingress and bgp-role:otc-egress, which bgp-role inserts
where role import is set, are required, because RFC 9234 Section 5 forbids
the operator to modify those procedures (docs/guide/bgp-role.md).
bgp-filter-community:blackhole-agreement, which bgp-filter-community
inserts on export to every eBGP session that is not a route-server client, is
required too: RFC 7999 Section 3.1 says the BLACKHOLE community "MUST be agreed
upon by the two networks before advertising it" (docs/guide/bgp-policy.md).
The community settings themselves (filter ingress community, filter egress community, relation-tag) run only where you name their member, as any
other member: bgp-filter-community:ingress and bgp-filter-community:relation
in import, bgp-filter-community:egress in export. A peer whose settings
no member of its chain (bgp, group or peer level) runs is refused at load with
the line to add; a deactivated member counts as absent. Because inserted
members come first, a procedure such as bgp-gr:llgr-ingress judges the
route as the peer sent it, before your community strip or tag changes it.
A chain is an ordered filters list and a mandatory final. Each filter
either rejects the route, which stops the chain, or passes it on to the next
filter. A filter's accept means "continue", not "install". After the last
filter, final accept accepts the route and final reject rejects it. So
final decides what survives the chain.
This example installs 10.0.0.0/24 from IS-IS and hides 10.1.0.0/24:
policy {
prefix-list ISIS-IN {
entry 10.0.0.0/24 { action accept; }
}
match ISIS-IN-PREFIXES {
prefix-list ISIS-IN;
}
}
isis {
filter {
import {
filters [ ISIS-IN-PREFIXES ];
final accept;
}
}
}
10.0.0.0/24 matches the entry, so the prefix-list accepts it, the match
passes it on, and final accept accepts it. 10.1.0.0/24 matches no entry, so
the prefix-list does not accept it and the match rejects it. With
final reject instead, the chain would reject both prefixes, because every
route that passes the filters then meets the reject.
The protocol's own database is never filtered. The chain judges the route as it enters the Loc-RIB. IS-IS keeps the prefix in its LSDB and floods it to its neighbors, and OSPF keeps its LSDB the same way.
A rejected route stays in the Loc-RIB as a hidden path. A hidden path is never selected, so it is not the best path and it does not reach the FIB. When every path for a prefix is hidden, the prefix has no best path and Ze removes its FIB entry.
A reload re-judges every stored path. When you commit a changed chain, Ze judges each path again from the values its protocol offered, hidden paths included. Relax the filter and the hidden path is installed, with no IS-IS or OSPF event and no SPF run. Tighten it and the path is withdrawn.
Seeing the hidden paths
show rib lists only selected paths, so a hidden path never appears there.
show rib candidates lists every path each protocol offered, hidden ones
included. Add prefix <prefix> to see one prefix:
show rib candidates prefix 192.0.2.0/24
Each path carries its source, next hop, interface, distance, metric, and the
tag, IS-IS level or OSPF route type its protocol states. selected marks the
path the Loc-RIB chose. A hidden path has hidden true, hidden-by set to
source (its own protocol's chain) or central (the rib chain), and
chain naming the attachment, such as isis import or rib import. The
chain shown is the one whose verdict the Loc-RIB acted on, not a fresh
evaluation, so a reload in flight cannot make the answer disagree with what
was installed. The answer is structured, so | json, | table and | match
apply. Anything after candidates other than prefix <prefix> is refused with
the correct form.
Filtering an IS-IS or OSPF prefix locally does not stop the IGP from flooding
it, so the routers around you keep sending its traffic here. When the hidden
prefix is the route a selected next hop resolves through, this router resolves
that next hop through a shorter prefix, or not at all, and the traffic is
blackholed or loops. show doctor warns with
doctor-policy-igp-import-hides-nexthop when a hidden IS-IS or OSPF path is
longer than the cover a selected route's next hop resolves through now. The
warning names the chain, the hidden prefix, the next hop and the route, and
points at show rib candidates prefix <prefix>. Let the chain accept that
prefix, or stop originating it in the IGP. The check reads the running
daemon's Loc-RIB, so ze doctor before a start does not report it.
| Metric | Labels | Counts |
|---|---|---|
ze_rib_policy_hidden_paths |
source |
Paths of that source the Loc-RIB holds hidden now |
ze_rib_policy_rejected_total |
source, chain (source or central) |
Rejections. Every route a protocol offers that a chain rejects counts once. A reload counts a stored path again only when its verdict changes, so recommitting the same chain adds nothing |
A match on a BGP peer chain writes, for each UPDATE it judges, the line its
condition's filter writes, on the same logger and at the same level. Turn a
logger on with its ze.log.<subsystem> variable, for example
ze.log.bgp.filter.prefix=info. show policy test writes none of them,
because it refuses no route.
| Condition | Logger | Line |
|---|---|---|
prefix-list |
bgp.filter.prefix |
Info prefix-list accept, reject or modify, with filter=<list>, peer, nlri (the judged prefixes), and accepted and rejected counts for a modify |
as-path |
bgp.filter.aspath |
Info as-path-list accept, reject or modify, with filter=<list>, peer, as-path |
as-path-length |
bgp.filter.aspath-length |
Info as-path-length reject only, with filter=<instance>, peer, length, max, min (-1 for a bound not set) |
community |
bgp.filter.community.match |
Info community-match accept, reject or modify, with filter=<list>, peer |
reject-asn |
bgp.filter.path.asn |
Info reject-asn reject with filter=<list>, direction, peer, then asn and position (and index for nth), or pattern, then as-path; Debug reject-asn accept with as-path |
Ze checks every chain when it verifies the config. These are refused, and the message names the chain and the fix:
| Refused | Why |
|---|---|
An import with no final |
Every import chain needs final accept or final reject, even with no filters |
A <plugin>:<filter> reference |
The Loc-RIB gate runs in the engine and never calls a plugin process. Name an instance from the policy root instead |
| A name that is not a filter instance | The name must be an entry of a filter type list under policy |
| A filter that reads a field the routes do not carry | For example, a community match on IS-IS routes |
| A filter type with no Loc-RIB route evaluator | The message lists the filter types that have one |
A family-filter with action tear-down |
A Loc-RIB route has no session to close |
A match condition on a BGP peer chain that reads a field BGP routes do not carry |
A match runs at a BGP chain over the conditions BGP can judge, prefix-list, community, as-path, as-path-length and reject-asn today; one that reads tag, metric or level is refused naming the field. A modify that sets distance or tag on a BGP chain is refused the same way |
Tab on filters offers the filter instances this chain accepts, and only
those: every entry of a filter type list under policy whose fields fit the
attachment, so isis filter import filters [ offers a prefix-list and not a
community-match. A policy route entry is
never offered. Commit asks the same question of every name, member by member,
with the refusals in the table above, so a name the editor did not offer still
commits when the attachment accepts it: a qualified prefix-list:NAME, or an
external plugin's <plugin>:<filter> on a BGP chain. A redistribute import chain is
checked the same way, through the path its plugin registers
(policy.MustRegisterRouteChainPath).