Skip to main content
RunBook Academy

VyOSLIII · Security HardeningSecurity

Prefix filtering — prefix-list, AS-path, RPKI invalid, and the leak firewall

Advanced⏱ ~26 minvyosvtyshshow ip prefix-listshow ip bgp regexpshow bgp ipv4 unicast neighbors <ip> received-routesshow bgp ipv4 unicast neighbors <ip> advertised-routes

What you'll learn

  • Build a prefix-list that admits expected prefixes and rejects everything else
  • Attach a filter to a BGP neighbour as a prefix-list, a filter-list, or a route-map
  • Configure an AS-path filter that rejects unwanted AS paths
  • Combine prefix-list with RPKI invalid drop for a layered defence

Prerequisites

Verified against VyOS 1.5.x LTS (circinus) · VyOS 1.4.x (sagitta) — legacy · FRRouting 10.x (VyOS 1.5) · Linux kernel 6.6 LTS (VyOS 1.5 base) · strongSwan 5.9.x (IPsec) · WireGuard 1.0.x (kernel module + userspace tooling)

Not yet marked complete on this device.

Prefix filtering is the operator’s tool for deciding which prefixes the local router will accept from a peer and which prefixes the local router will advertise to a peer. The default behaviour — accept every prefix a peer sends, advertise every prefix the local router has learned — is the root cause of every BGP route leak on the public Internet.

On VyOS 1.5 LTS / FRR 10.x, prefix filtering is configured through three mechanisms that compose: the prefix-list (matches IP prefixes), the AS-path filter (matches BGP AS-path attributes), and the RPKI validation (cryptographic origin validation). Each mechanism protects against a different class of unwanted prefix; the production operator combines them into a layered defence.

This lesson covers the prefix-list, the AS-path filter, the RPKI integration, and the leak-firewall pattern that combines all three.

The three classes of unwanted prefix

The unwanted prefixes that a BGP speaker must filter are:

  • Prefixes outside the peer’s authorised cone. A customer-cone peer is supposed to advertise only the prefixes the customer is allocated. A peer that advertises the full Internet table (or a peer’s cone) is leaking.
  • Prefixes with an unallocated or invalid origin. A prefix that no RPKI ROA authorises the peer to advertise is a hijack or a misconfiguration. The operator wants to drop these before they enter the Loc-RIB.
  • Prefixes with an AS-path that does not match the expected upstream. A peer that is supposed to advertise only prefixes originated in AS 65001 but advertises prefixes with AS 65002 in the path is forwarding routes it should not be.
flowchart LR
  P[Peer] -->|UPDATE| R1[Local router]
  R1 --> P1{Prefix-list match?}
  P1 -->|no| DROP1[Drop]
  P1 -->|yes| A1{AS-path match?}
  A1 -->|no| DROP2[Drop]
  A1 -->|yes| R1V{RPKI valid?}
  R1V -->|no| DROP3[Drop invalid]
  R1V -->|yes| ACC[Accept\ninstall in Loc-RIB]

The diagram shows the layered defence: each filter eliminates a class of unwanted prefix before the next filter is applied. The combination is the production leak firewall.

Prefix-list — matching IP prefixes

A prefix-list is an ordered list of rules; each rule matches (or denies) a prefix with optional ge (greater than or equal) and le (less than or equal) length qualifiers. The list is processed top to bottom; the first match wins.

# Customer cone: 192.168.0.0/16 with sub-prefixes
set policy prefix-list CUST-CONE rule 10 action permit
set policy prefix-list CUST-CONE rule 10 prefix 192.168.0.0/16
set policy prefix-list CUST-CONE rule 10 ge 17
set policy prefix-list CUST-CONE rule 10 le 24

The configuration says: permit 192.168.0.0/16 with sub-prefixes between /17 and /24 inclusive; deny everything else. The ge 17 le 24 qualifier restricts the prefix length; without it, the rule matches only the exact /16.

(192.168.0.0/16 stands in for a public allocation here so the ge/le arithmetic is readable. A real customer cone is globally routable space.)

AS-path filter — matching AS-path attributes

The AS-path filter matches the AS-path attribute of a BGP route. The configuration uses a regular expression:

# AS-path filter for AS 65001 originated routes
set policy as-path-list FROM-65001 rule 10 action permit
set policy as-path-list FROM-65001 rule 10 regex '^65001$'

# The same, tolerating any number of prepends by 65001 itself
set policy as-path-list FROM-65001-PREP rule 10 action permit
set policy as-path-list FROM-65001-PREP rule 10 regex '^65001(_65001)*$'

The regex ^65001$ matches routes originated in AS 65001 (no other AS in the path). The regex ^65001(_65001)*$ matches routes originated in AS 65001 with any number of prepends.

As with the prefix-list, there is no closing deny rule to add: an AS-path that matches no rule is denied, and a rule with an action but no regex is not a valid rule.

Attaching a filter to a neighbour

A prefix-list or an AS-path list that is not attached to anything filters nothing. VyOS gives three attachment points under a neighbour’s address-family, and they differ in what they can express rather than in how hard they are to type:

set protocols bgp system-as 64512

# The prefix-list directly, inbound
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast prefix-list import CUST-CONE

# The AS-path list directly, inbound
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast filter-list import FROM-65001

# A route-map, which can match on both and on anything else
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-map import CUST-IN

Each of these has an export counterpart that filters what the local router advertises to that peer. import protects you from the peer; export protects the rest of the Internet from you, and it is the one operators forget.

Use prefix-list import when the whole policy is “these prefixes and no others”. Reach for route-map import as soon as the policy needs more than one kind of match, or needs to set anything (local-preference, community) on the way in. The leak firewall below needs a route-map, because it layers three different tests.

RPKI invalid drop

RPKI (covered in detail in the next lesson, liii-04-rpki) lets the router check a route’s origin AS against the ROAs the address holder published. Validation produces one of three states, and the distinction between the last two is the whole operational story:

  • valid — a ROA covers the prefix and authorises this origin AS.
  • invalid — a ROA covers the prefix and does not authorise this origin AS, or the prefix is longer than the ROA’s maxLength.
  • notfound — no ROA covers the prefix at all.

Only invalid is evidence that something is wrong. notfound means nobody has published a ROA, which is still true of a large share of the routing table.

Where the router gets its ROAs from — the cache session, its transport and its failure behaviour — is liii-04’s subject. What matters here is that once a validation state exists, policy consumes it through a single route-map match:

# Drop RPKI invalid, pass everything else
set policy route-map DROP-RPKI-INVALID rule 10 action deny
set policy route-map DROP-RPKI-INVALID rule 10 match rpki invalid

set policy route-map DROP-RPKI-INVALID rule 20 action permit

# Attach it as the neighbour's inbound policy
set protocols bgp system-as 64512
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-map import DROP-RPKI-INVALID

Rule 20 is a bare permit and it is not decoration. A route-map ends in an implicit deny, so a route-map containing only rule 10 would drop every route the peer sends, not just the invalid ones. This is the exact opposite of the prefix-list, where the implicit deny at the end is the behaviour you wanted. Same word, opposite consequence, and it is the most expensive difference in this lesson.

The CLI keyword is notfound, one word. match rpki notfound and match rpki valid exist alongside match rpki invalid, but reach for them deliberately — denying notfound drops every prefix whose holder has not published a ROA yet.

The leak firewall — combining the three mechanisms

The leak firewall is the production pattern for combining prefix-list, AS-path filter, and RPKI invalid drop into a single defence:

# Customer cone prefix-list (only /17-/24 sub-prefixes)
set policy prefix-list CUST-CONE rule 10 action permit
set policy prefix-list CUST-CONE rule 10 prefix 192.168.0.0/16
set policy prefix-list CUST-CONE rule 10 ge 17
set policy prefix-list CUST-CONE rule 10 le 24

# AS-path filter for the customer cone
set policy as-path-list CUST-AS rule 10 action permit
set policy as-path-list CUST-AS rule 10 regex '^65001$'

# One inbound route-map that layers all three checks
set policy route-map CUST-IN rule 10 action deny
set policy route-map CUST-IN rule 10 match rpki invalid

set policy route-map CUST-IN rule 20 action permit
set policy route-map CUST-IN rule 20 match ip address prefix-list CUST-CONE
set policy route-map CUST-IN rule 20 match as-path CUST-AS

# Attach it inbound on the neighbour
set protocols bgp system-as 64512
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-map import CUST-IN

Three tests, two rules, and the order is the whole design:

  1. Rule 10 drops RPKI invalid. It has to come first, because a deny placed after a permit that already matched never runs.
  2. Rule 20 permits a route only if it satisfies both matches. Several match statements inside one route-map rule are ANDed, so a prefix inside the customer cone whose AS-path is not exactly 65001 fails rule 20 and falls through.
  3. The end of the route-map denies whatever fell through. There is no rule to write for this.

The leak firewall is the production default for every BGP neighbour. The operator who runs a peer without a leak firewall is one configuration error away from a public route leak.

Failure modes

The peer’s routes vanished after the filter went on

The operator attaches a prefix-list with one permit rule and the peer’s other prefixes stop arriving. This is the filter working: a prefix matching no rule is denied.

The diagnostic is to compare what the peer sent with what survived policy:

vtysh -c 'show bgp ipv4 unicast neighbors 10.0.0.1 received-routes'
vtysh -c 'show bgp ipv4 unicast neighbors 10.0.0.1 routes'

received-routes is pre-policy and routes is post-policy, so the difference between them is exactly what your filter dropped. received-routes needs soft-reconfiguration inbound enabled on the neighbour, or FRR refuses the command rather than showing an empty list — read the error rather than concluding the peer sent nothing:

set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast soft-reconfiguration inbound

Fix: usually none. If some of the denied prefixes were meant to arrive, widen the permit — add another rule with its own prefix and ge/le. Do not try to express “allow the rest” as a trailing rule; the list is deny-by-default and the only way to admit something is to permit it explicitly.

Keeping soft-reconfiguration inbound on permanently costs memory proportional to the peer’s table, which is why it is not the default. On a full-table peer, turn it on to investigate and off again afterwards.

AS-path regex without anchors

The operator writes the regex 65001 with no anchors, meaning “routes from AS 65001”. It matches every AS-path with 65001 anywhere in it, in any role:

  • 65002 65001 — originated by 65001 and transited by 65002. Probably what was meant.
  • 65001 65002 — originated by 65002; 65001 is only the neighbour that handed it over. Not what was meant, and this is the one that admits a whole cone the filter was supposed to exclude.
  • 65003 65001 65002 — 65001 is pure transit in the middle.

Fix: decide which question you are asking, then anchor for it. ^65001$ for “originated by 65001 and received directly from them”. 65001$ for “originated by 65001, however it got here”. The defensive idiom is not “always anchor both ends” — it is that an unanchored AS number in a filter is almost never the question anyone meant to ask.

RPKI invalid drop too aggressive

The operator enables RPKI invalid drop before the ROAs are deployed. A prefix that is legitimately advertised but has no ROA yet validates as notfound, not invalid. If the route-map denies notfound as well, those legitimate prefixes disappear.

Fix: deny invalid and nothing else.

set policy route-map DROP-RPKI-INVALID rule 10 action deny
set policy route-map DROP-RPKI-INVALID rule 10 match rpki invalid

set policy route-map DROP-RPKI-INVALID rule 20 action permit

Rule 20 matters as much as rule 10. Drop it and the implicit deny at the end of the route-map takes every route the peer sends, which looks identical from the peer’s side to “RPKI dropped everything” and is a much easier mistake to make than adding a notfound rule on purpose.

Do not add a rule for notfound. Those are routes without a ROA, and deciding to drop them is a policy choice with a real cost, not a hardening default.

Prefix-list ge / le mismatch

The operator configures a prefix-list with prefix 192.168.0.0/16 and no ge/le qualifiers. The rule matches only the exact /16, not any sub-prefixes. The peer advertises 192.168.1.0/24, 192.168.2.0/24, … and all are denied.

Fix: add ge 17 le 24 to match sub-prefixes between /17 and /24 inclusive.

Rollback

A prefix-filtering change is reversible, and the order you reverse it in matters more than any individual command.

Detach first. The neighbour goes back to accepting everything and the policy objects stay on the box for the next attempt:

delete protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-map import CUST-IN
commit

Delete second, and only once nothing references them:

delete policy route-map CUST-IN
delete policy prefix-list CUST-CONE
delete policy as-path-list CUST-AS
commit

Deleting in the other order leaves a neighbour pointing at a route-map that no longer exists. Do not find out during an incident what your release does with that reference — whether it passes everything or drops everything, both answers are bad on a live session. Check what still points at the name before you remove it:

show configuration commands | match CUST-IN

Two notes on the wider rollback tools. rollback restores a whole previous commit revision, so it also undoes every other change committed since — the wrong granularity for backing out one filter, and worth reading your release’s notes on before you rely on it in an incident. commit-confirm 5 is the right tool at the moment of application: it applies the filter and reverts it on its own unless you confirm inside the window, which is what you want when the filter you are attaching could cut off the path you are managing the router over.

Production discipline

Cross-course references

  • XXVIII-VyOS-BGPPrefixFilter (vyos-xxviii-01-prefix-list-concept, vyos-xxviii-02-prefix-list-config, vyos-xxviii-04-filter-list) covers the BGP prefix-filtering tools in detail.
  • LIII-VyOS-Security (vyos-liii-04-rpki, the next lesson) covers the RPKI mechanism for cryptographic origin validation.
  • XXXIII-VyOS-RoutePolicy covers the route-map composition that is the basis for the leak firewall.
  • The BGP Operations and Security BCP (RFC 7454) is the IETF’s operational guidance on the same patterns.

Quiz

Knowledge check · 4 questions

  1. Q1. Which combination of mechanisms forms the production leak firewall for a BGP neighbour?

  2. Q2. An AS-path filter regex of `^65001$` matches every AS-path containing AS 65001.

  3. Q3. R1 has a BGP peer 10.0.0.1 (AS 65001). The operator configured a prefix-list permitting only 192.168.0.0/16 with sub-prefixes /17-/24. The peer starts advertising 192.168.1.0/24, 192.168.2.0/24, and 10.0.0.0/8. R1's Loc-RIB only contains 192.168.1.0/24 and 192.168.2.0/24. The 10.0.0.0/8 prefix is not installed. The operator is surprised — why?

    R1 has a BGP peer with a prefix-list permitting 192.168.0.0/16 with sub-prefixes /17-/24. The peer advertises 192.168.1.0/24, 192.168.2.0/24, and 10.0.0.0/8. Only 192.168.1.0/24 and 192.168.2.0/24 are in the Loc-RIB.

  4. Q4. R1 peers with 10.0.0.1 (AS 65001) and the operator has just attached an RPKI-aware inbound route-map. A legitimate customer prefix, 192.168.1.0/24, is no longer in the Loc-RIB, and the customer has not published a ROA for it. What is happening and what is the fix?

    R1 has a BGP peer with an RPKI-aware inbound route-map. A legitimate customer prefix (192.168.1.0/24) validates as `notfound` — no ROA covers it — and is not being installed in the Loc-RIB.

Passing score: 75%. Answers are checked in this browser.