Newansage v0.4.0 is out
Guides / Transit and filters

Transit and filters

Roles (RFC 9234), filters, passing routes on to customers, and a default route for them.

By default ansage sends every neighbor its own prefixes and nothing else (export = "own"). To pass learned routes on, for example a transit's table to your customers, give each neighbor a role and an export filter. The roles decide what may go where at all; filters decide what goes within that.

Roles

role says what the neighbor is to you:

role =The neighborGets (at most)
"provider"sells you transityour own prefixes and your customers' routes
"customer"buys transit from youeverything its export filter passes
"peer"settlement-free peeryour own prefixes and your customers' routes
"rs"an IXP route serveryour own prefixes and your customers' routes

The rule behind it (RFC 9234): routes from providers, peers and route servers only ever go to customers, so you never carry traffic between two of your upstreams. No filter can change that. Also:

  • Routes from a customer may go to every neighbor with a role.
  • Routes learned over iBGP (no role there) may go to customers only.
  • Routes from an eBGP neighbor without a role are never passed on: set its role first.
  • Routes with NO_EXPORT or NO_ADVERTISE (RFC 1997) are never passed on.
  • Roles are for eBGP sessions only. ansage is no route server, so there is no "rs-client".

On the wire

ansage sends its own role in the OPEN (BGP Role capability): "customer" on your side means ansage says "provider". If the neighbor sends a role that does not fit, ansage closes the session with an OPEN Message Error / Role Mismatch (2/11). A neighbor that sends no role is fine, unless strict_role = true. ansage show peer shows both sides:

  Role: customer (RFC 9234), it says: customer

The neighbor configures its own role, which is the same word as yours for it:

Your role =BIRD on the neighborFRR on the neighbor
"provider"local role provider;neighbor ADDR local-role provider
"customer"local role customer;neighbor ADDR local-role customer
"peer"local role peer;neighbor ADDR local-role peer
"rs"local role rs_server;neighbor ADDR local-role rs-server

Routes also carry the Only-To-Customer attribute (OTC): ansage adds it to routes from providers, peers and route servers, and to what it sends to customers and peers. A route with OTC never goes to a provider, peer or route server, also when it comes back over another router. A customer that sends a route with OTC leaks it; ansage drops such routes (and from a peer, OTC with another AS than the peer's).

Filters

A filter is a list of rules. The first rule that matches a route decides; a route no rule matches is rejected. import and export name a filter instead of "all" / "own":

/etc/ansage/ansage.toml
asn = 64500
router_id = "192.0.2.1"
announce = ["192.0.2.0/24", "2001:db8:1::/48"]

[[neighbor]]
address = "10.0.0.1"
asn = 64501
description = "transit"
role = "provider"
import = "from-transit"
export = "to-upstream"

[[neighbor]]
address = "10.0.2.3"
asn = 64502
description = "IXP route server"
role = "rs"
import = "all"
export = "to-upstream"

[[neighbor]]
address = "10.0.1.2"
asn = 64510
description = "customer"
role = "customer"
import = "from-customer"
export = "to-customer"
default_route = true

# Too short and too long prefixes are not taken from the transit.
[filter.from-transit]
[[filter.from-transit.rule]]
prefixes = ["0.0.0.0/0 le 7", "0.0.0.0/0 ge 25", "::/0 le 15", "::/0 ge 49"]
action = "reject"
[[filter.from-transit.rule]]
action = "accept"

# Only the customer's own prefixes; preferred, and tagged.
[filter.from-customer]
[[filter.from-customer.rule]]
prefixes = ["198.51.100.0/24", "2001:db8:a::/48"]
origin_as = [64510]
action = "accept"
local_pref = 200
add_communities = ["64500:100"]

# Upstream: our prefixes and our customers' (the tag).
[filter.to-upstream]
[[filter.to-upstream.rule]]
source = "own"
action = "accept"
[[filter.to-upstream.rule]]
communities = ["64500:100"]
action = "accept"

# The customer gets everything the roles allow.
[filter.to-customer]
[[filter.to-customer.rule]]
action = "accept"

Rules

Conditions; a rule matches when all its conditions hold, a list when any of its values does:

ConditionMatches
source = "own" / "learned"our announce prefixes / routes from neighbors
from_role = ["customer"]learned from a neighbor with one of these roles
from_neighbor = ["10.0.1.2"]learned from one of these neighbor addresses
prefixes = ["10.0.0.0/8 le 24"]prefix ranges, see below
origin_as = [64510]the last AS of the path (ours for our own prefixes)
as_path_contains = [64496]one of these AS numbers anywhere in the path
as_path_max_len = 50at most this many AS numbers in the path
communities = ["64500:100"]carries one of these communities
large_communities = ["64500:1:2"]carries one of these large communities

Then action = "accept" or "reject", and what an accepting rule changes:

Change
prepend = 2export only: our AS this many extra times
add_communities, remove_communities["asn:value", ...]
add_large_communities, remove_large_communities["global:data1:data2", ...]
med = 10set the MED (also on eBGP, where ansage otherwise drops it)
local_pref = 200import only: preferred in best path selection (default 100)

Prefix ranges are written like FRR and Cisco prefix lists:

RangeMatches
"203.0.113.0/24"exactly 203.0.113.0/24
"10.0.0.0/8 le 24"inside 10.0.0.0/8, /8 to /24
"10.0.0.0/8 ge 16"inside 10.0.0.0/8, /16 to /32
"::/0 ge 49"any IPv6 prefix longer than /48

ansage check refuses filters that cannot work: an unknown name, prepend in an import filter, local_pref in an export filter, a rule that rejects and changes the route, and an export filter that can pass learned routes on to a neighbor without role (limit its accepting rules to source = "own" for such a neighbor). none, own and all are not filter names. Every field is in the config reference.

A default route for customers

default_route = true sends a neighbor 0.0.0.0/0 and ::/0 from ansage itself while at least one "provider" session is up, and withdraws them when the last one goes down. It replaces any default route the neighbor would otherwise get, and no filter applies to it. Customers that only need a way out don't need your full table: give them export = "none" and default_route = true, or a filter that passes just your own prefixes.

What a neighbor gets

ansage show rib --out lists what ansage sent to one neighbor, with the attributes as the neighbor received them:

ansage show rib --out 10.0.0.1
Sent to 10.0.0.1: 3 prefixes
  PREFIX           SOURCE  NEXT HOP     AS PATH      ORIGIN  LOCAL PREF  MED  COMMUNITIES
  192.0.2.0/24     -       10.0.0.2     64500        igp     -           -    -
  198.51.100.0/24  -       10.0.0.2     64500 64510  igp     -           -    64500:100
  2001:db8:1::/48  -       2001:db8::2  64500        igp     -           -    -

To see why a route goes somewhere or not, before or after a change, use ansage test.

Changing filters

ansage reload applies changed filters, import, export and default_route without closing the session. A new export policy re-checks every route for that neighbor and sends the difference. A new import policy asks the neighbor to send its routes again (ROUTE-REFRESH, RFC 2918), so the new filter sees all of them; a neighbor without route refresh is reconnected instead (Cease / Other Configuration Change). role and strict_role are part of the OPEN: a change reconnects the session.

Esc to close