Skip to content

Latest commit

 

History

History
402 lines (348 loc) · 26 KB

route-policy.md

File metadata and controls

402 lines (348 loc) · 26 KB

Route Policy at the Loc-RIB

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.

The shared policy root

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.

The match conditions

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.

The fields each attachment carries

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.

Where you attach a chain

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).

Members a plugin inserts for you

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.

How a chain decides

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.

What a rejected route becomes

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.

When an IGP filter blackholes transit traffic

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.

Metrics

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

Decision lines on a BGP chain

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

What Ze refuses

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).