Skip to main content
RunBook Academy

VyOSXXVIII · BGP Prefix FilteringPrefix filters

Prefix-list configuration — sequence numbers, ge/le operators, descriptions, hit counters

Intermediate⏱ ~20 minset policy prefix-listshow configuration commandsshow ip bgp neighbors received-routesshow ip bgp neighbors routesvtysh -c show ip prefix-listvtysh -c show ip prefix-list detail

What you'll learn

  • Configure a prefix-list with sequence numbers and the ge/le operators
  • Add a description to each rule for self-documentation
  • Read the configured list back from FRR, with its hit counters, and confirm FRR accepted it
  • Attach a list to a BGP neighbour with the VyOS import/export nodes rather than the FRR in/out spelling
  • Recognise the production failure modes around prefix-list configuration

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) · 2026-08-19

Not yet marked complete on this device.

The prefix-list configuration on VyOS 1.5 LTS follows the set policy prefix-list syntax. Each rule has a sequence number, an action (permit or deny), a prefix, optional ge and le operators, and an optional description. The configuration is committed to the running config and rendered into FRR’s ip prefix-list commands on the backend.

The configuration discipline is the same as for any list-based filter: define the rules in order, use sparse sequence numbers for future insertion, document the intent with descriptions, and test the filter with commit-confirm before applying to production.

The anatomy of a prefix-list rule

A single rule has the following structure:

set policy prefix-list LIST-NAME rule 10 action 'permit'
set policy prefix-list LIST-NAME rule 10 prefix '198.51.100.0/24'
set policy prefix-list LIST-NAME rule 10 ge '26'
set policy prefix-list LIST-NAME rule 10 le '28'
set policy prefix-list LIST-NAME rule 10 description 'why this rule exists'

The fields:

  • The list name — the operator chooses it; the convention is uppercase and hyphen-separated. IPv6 lists live in a separate tree, set policy prefix-list6, and a v4 name and a v6 name never collide because they are different nodes.
  • The rule number — rules are evaluated in ascending order and this number becomes the FRR sequence number. Use sparse numbers (10, 20, 30) so a later insertion does not need a renumber.
  • actionpermit or deny. It is not optional; a rule with a prefix and no action is incomplete.
  • prefix — what to match, as network and prefix length.
  • ge — the minimum prefix length the rule will match. Must be strictly greater than the length in prefix.
  • le — the maximum prefix length the rule will match. Must be at least the ge value.
  • description — the intent, in words.

The ge/le invariant is FRR’s, and FRR states it in the message it rejects a bad range with: len < ge-value <= le-value. Read that as three separate requirements:

  • ge must be strictly greater than the prefix’s own length. 198.51.100.0/24 ge 24 is invalid, because a ge equal to the length adds nothing and FRR refuses to guess what you meant.
  • le must be at least ge. ge 28 le 26 is invalid.
  • With le alone and no ge, the same “must be longer than the prefix” logic applies: le has to exceed the prefix’s own length to describe anything the plain prefix does not already cover.

The canonical configuration patterns

The four canonical patterns:

Pattern 1: Exact-match filter — accept a single prefix:

configure
set policy prefix-list MY-PREFIXES rule 10 action 'permit'
set policy prefix-list MY-PREFIXES rule 10 prefix '198.51.100.0/24'
set policy prefix-list MY-PREFIXES rule 10 description 'customer edged routes'
commit
save

The list accepts only 198.51.100.0/24. Every other route is rejected by the implicit deny.

Pattern 2: Prefix + subnets filter — accept the prefix and any more-specific subnets:

configure
set policy prefix-list MY-PREFIXES rule 10 action 'permit'
set policy prefix-list MY-PREFIXES rule 10 prefix '198.51.100.0/24'
set policy prefix-list MY-PREFIXES rule 10 le '32'
set policy prefix-list MY-PREFIXES rule 10 description 'prefix and subnets up to /32'
commit
save

The le 32 operator says “prefix-length ≤ 32”. The rule accepts 198.51.100.0/24, /25, /26, …, /32. The implicit deny catches everything else.

Pattern 3: More-specific only filter — accept subnets but not the prefix itself:

configure
set policy prefix-list MY-PREFIXES rule 10 action 'permit'
set policy prefix-list MY-PREFIXES rule 10 prefix '198.51.100.0/24'
set policy prefix-list MY-PREFIXES rule 10 ge '26'
set policy prefix-list MY-PREFIXES rule 10 description 'subnets /26 or more-specific'
commit
save

The ge 26 operator says “prefix-length ≥ 26”. The rule accepts 198.51.100.0/26, /27, …, /32. It does not accept /24 or /25. The implicit deny catches everything else.

Pattern 4: Specific range filter — accept subnets of a specific length range:

configure
set policy prefix-list MY-PREFIXES rule 10 action 'permit'
set policy prefix-list MY-PREFIXES rule 10 prefix '198.51.100.0/24'
set policy prefix-list MY-PREFIXES rule 10 ge '26'
set policy prefix-list MY-PREFIXES rule 10 le '28'
set policy prefix-list MY-PREFIXES rule 10 description 'subnets /26 to /28'
commit
save

The ge 26 le 28 operator says “prefix-length in [26, 28]”. The rule accepts 198.51.100.0/26, /27, /28. It does not accept /24, /25, /29, or more-specific.

The operator who designs the prefix-list chooses the pattern that matches the routing policy intent.

Adding, editing and removing rules

Every set happens inside a configure session and lands in the candidate configuration, not the running one, until commit. Rule numbers are independent of each other: there is no requirement that they be contiguous, and no requirement that a lower number exist before you create a higher one. Rule 15 can be created on a list that only has rules 10 and 20, and it will be evaluated between them.

configure
set policy prefix-list MY-FILTER rule 10 action 'permit'
set policy prefix-list MY-FILTER rule 10 prefix '198.51.100.0/24'
set policy prefix-list MY-FILTER rule 20 action 'deny'
set policy prefix-list MY-FILTER rule 20 prefix '198.51.100.0/16'
set policy prefix-list MY-FILTER rule 20 le '20'

The two rules are evaluated in order: 10, then 20. The first rule that matches wins.

The operator who edits a rule (e.g., changes the prefix) uses set again with the same sequence number:

set policy prefix-list MY-FILTER rule 10 prefix '203.0.113.0/24'

The set command overwrites the existing prefix. The compare output shows the change.

The delete syntax

The operator deletes a single rule with delete:

delete policy prefix-list MY-FILTER rule 20

The rule is removed. Nothing is renumbered — rule 10 stays rule 10 and rule 30 stays rule 30, leaving a gap at 20. That is the intended behaviour and it is why sparse numbering works: the numbers are addresses, not positions.

There is one consequence worth stating plainly, because it is the usual way a prefix-list change causes an outage. Deleting a permit rule does not make the list neutral about those prefixes — it hands them to the implicit deny at the end. Every prefix-list ends in an implicit deny any, so removing the only rule that matched a customer’s aggregate silently stops accepting it. compare before commit and read the removal as “these prefixes are now denied”, not as “this rule is now absent”.

The operator deletes the entire prefix-list with:

delete policy prefix-list MY-FILTER

All rules are removed. The prefix-list is gone.

The description field

The description is where the intent lives. A prefix-list rule is four numbers and a verb; six months later nobody remembers which customer 203.0.113.0/24 was, and the person who has to decide whether it can be removed is not the person who added it.

set policy prefix-list MY-FILTER rule 10 description 'CUST-A aggregate, contract NET-1187'

The discipline is one line per rule, naming the why rather than restating the what. 'permit the /24' is worthless — the rule already says that. 'CUST-A aggregate, contract NET-1187' tells the next operator who to ask.

Counting matches, without a count field

There is no count node on a VyOS prefix-list rule and no count NAME clause on an FRR ip prefix-list line. Anything that tells you to configure one is describing a router that does not exist.

What does exist is a hit counter that FRR maintains automatically for every sequence, with no configuration at all. Read it with the detail form:

vtysh -c 'show ip prefix-list detail MY-FILTER'

Each sequence is printed with a hit count alongside it. That number is the answer to the question the fabricated count field was reaching for:

  • A sequence with a rising hit count is matching, which confirms the rule is doing something.
  • A sequence stuck at zero is either never reached — an earlier rule matched first — or is matching nothing that the peer actually sends. Those are different problems and the next command distinguishes them.

To ask which sequence a specific prefix lands on, give FRR the prefix:

vtysh -c 'show ip prefix-list MY-FILTER 198.51.100.0/26 first-match'

That is the single most useful prefix-list debugging command there is, because it replaces reasoning about rule order with an answer from the thing doing the matching.

The inbound and outbound BGP filter patterns

The prefix-list is applied to a BGP neighbour in either direction. VyOS names the directions import and export, and the words are worth taking literally: import is what the router takes in from the peer, export is what the router sends out to it.

Inbound filter — applied to routes the peer advertises to the local router. It decides which routes the local router accepts.

configure
set policy prefix-list PEER-FROM-ISP rule 10 action 'permit'
set policy prefix-list PEER-FROM-ISP rule 10 prefix '198.51.100.0/24'
set policy prefix-list PEER-FROM-ISP rule 10 description 'customer routes from ISP'

set protocols bgp system-as '64512'
set protocols bgp neighbor 192.0.2.2 remote-as '65001'
set protocols bgp neighbor 192.0.2.2 address-family ipv4-unicast prefix-list import 'PEER-FROM-ISP'
commit
save

The peer advertises routes to the local router; the prefix-list filters them; only the matching routes are accepted. Note the AS number: on 1.4 and 1.5 it is set protocols bgp system-as '64512', and the neighbour hangs off set protocols bgp neighbor ... rather than off the AS number. The 1.3 form set protocols bgp 64512 neighbor ... is rejected.

Outbound filter — applied to routes the local router advertises to the peer. It decides which routes the local router sends.

configure
set policy prefix-list PEER-TO-ISP rule 10 action 'permit'
set policy prefix-list PEER-TO-ISP rule 10 prefix '198.51.100.0/24'
set policy prefix-list PEER-TO-ISP rule 10 description 'local customer routes'

set protocols bgp neighbor 192.0.2.2 address-family ipv4-unicast prefix-list export 'PEER-TO-ISP'
commit
save

The local router advertises routes to the peer; the prefix-list filters them; only the matching routes are sent. The peer receives only the routes the local router permits.

The canonical pattern:

  • Inbound filter for “what I will accept from the peer” — protects the local router from accepting bad routes.
  • Outbound filter for “what I will send to the peer” — protects the peer from receiving bad routes from the local router.

Both filters are required for production BGP. The operator who has an inbound filter but no outbound filter is exposed to accepting routes from the peer but also exposing the local network to the peer. The operator who has an outbound filter but no inbound filter is exposed to the other direction.

The set policy prefix-list to set protocols bgp neighbor connection

The connection between the prefix-list and the BGP neighbour is made inside address-family ipv4-unicast, which matters because a filter is per-address-family. A router with both ipv4-unicast and ipv6-unicast up on the same neighbour needs a filter under each; configuring one and assuming it covers both leaves the other direction of the internet entirely unfiltered.

The VyOS BGP node renders into FRR’s spelling:

router bgp 64512
 neighbor 192.0.2.2 remote-as 65001
 address-family ipv4-unicast
  neighbor 192.0.2.2 prefix-list PEER-FROM-ISP in
  neighbor 192.0.2.2 prefix-list PEER-TO-ISP out
 exit-address-family

This is the render that makes the import/export versus in/out confusion so durable: the operator configures import, reads in back from vtysh, and then types in the next time. Confirm the render with vtysh -c 'show running-config', and remember which half of the pair each tool speaks.

The sparse sequence number convention

The convention is to use sparse sequence numbers (10, 20, 30, …) for room to insert new rules. The operator who uses sparse numbers can insert a new rule between two existing rules without renumbering:

# Rules 10, 20, 30 exist
# Insert rule 15 between 10 and 20
set policy prefix-list MY-FILTER rule 15 action 'deny'
set policy prefix-list MY-FILTER rule 15 prefix '198.51.100.0/24'

Rule 15 is evaluated between rule 10 and rule 20. The existing rules are not renumbered.

The operator who uses dense sequence numbers (1, 2, 3) ends up renumbering every rule to insert a new one. The cost of dense numbers is high.

How the result is validated

Four questions, in order, each with the command that answers it.

1. What did I ask for?

show configuration commands | match 'policy prefix-list MY-FILTER'

The VyOS tree, descriptions included. This is intent, not state.

2. What did FRR accept?

vtysh -c 'show ip prefix-list MY-FILTER'
vtysh -c 'show ip prefix-list detail MY-FILTER'

The compiled list, with hit counts in the detail form. A rule present in step 1 and absent here was rejected downstream, which is the failure a clean commit hides.

3. Which rule does a given prefix hit?

vtysh -c 'show ip prefix-list MY-FILTER 198.51.100.0/26 first-match'

This settles rule-ordering arguments without anyone having to reason about them.

4. What is the filter actually doing to the session?

PEER=192.0.2.2
vtysh -c "show ip bgp neighbors $PEER received-routes"
vtysh -c "show ip bgp neighbors $PEER routes"

The first is the pre-filter view — everything the peer sent — and it requires soft-reconfiguration inbound on that neighbour to have been kept. The second is the post-filter view: what survived. The difference between the two lists is your filter, stated as data rather than as an argument about what the rules ought to do.

For the rendered FRR configuration as a whole:

vtysh -c 'show running-config' | grep -A20 prefix-list

How it fails

The production failure modes the engineer must recognise:

  • The ge value is not greater than the prefix length. 198.51.100.0/24 ge 24 violates len < ge-value. Set ge to 25 or higher, or drop it if the plain prefix is what you meant.
  • The le value is less than ge. ge 28 le 26 describes an empty range. Swap them or correct the intent.
  • The list is referenced by name but never defined. A neighbour pointing at a prefix-list that does not exist is a filter that filters nothing. Define the list before you reference it, and re-read the neighbour with vtysh afterwards.
  • The direction is wrong. prefix-list import where export was meant. This one is quiet in the worst way: an inbound filter applied outbound usually still permits a plausible-looking set of prefixes, so the session stays up and the leak is discovered by someone else.
  • in/out typed instead of import/export. The path is rejected at commit, which is the good outcome — but a runbook or automation template carrying the FRR spelling will fail at exactly the moment it is being relied on.
  • Applied to the wrong neighbour. Peer A gets peer B’s policy. show configuration commands | match 'neighbor' and read the pairs rather than trusting the one you just typed.
  • Only one address family filtered. The v4 filter is present and the v6 session is wide open. Every filter is per-address-family.
  • The implicit deny catches something you meant to accept. Every list ends in deny any. If routes are missing, compare received-routes against routes and look at what fell out.
  • The commit succeeded and FRR refused the rule. The most dangerous of the set, because there is no error to notice. vtysh -c 'show ip prefix-list MY-FILTER' after every prefix-list change is the habit that catches it.

Rollback

Prefix-list changes are ordinary configuration changes, but they act on a BGP session, which means the blast radius is other people’s traffic rather than this router’s uptime. That shapes which mechanism is right.

configure

# See exactly what is about to change, as prefixes rather than as lines.
compare

# For a change to a filter protecting the session you are reached over:
commit-confirm 5
# ... verify received-routes against routes ...
confirm

save

Undo the specific rule where you can — delete policy prefix-list MY-FILTER rule 20 and commit is precise, and it leaves the rest of the list untouched. For a broader revert, load a known-good file from /config/archive/, compare it, and commit that.

Do not reach for rollback N. The VyOS documentation states that it applies the stored revision by rebooting the router, and rebooting an edge router to undo a filter typo takes every session on it down — including the ones that were fine.

One property of filter rollbacks catches people out: undoing the configuration does not immediately undo the effect. The routes the filter dropped are gone from the table and will not come back until the peer resends them. Finish the rollback with a soft refresh:

clear ip bgp 192.0.2.2 soft in

Production discipline

Cross-course references

The Linux course’s XIX-Linux-NetFoundations covers the kernel FIB. The OPNsense course’s XXX-OPNsense-DynamicRouting covers the same FRR prefix-list pattern on the firewall side. The BGP lessons vyos-xxvi-02-as-path (the AS_PATH attribute in detail), vyos-xxviii-01-prefix-list-concept (the semantics), and vyos-xxviii-03-distribute-list (the BGP application) cover the broader context. The lesson vyos-xxviii-06-filter-troubleshoot walks the debugging of filter failures.

Quiz

Knowledge check · 4 questions

  1. Q1. What is the operator constraint violation in this rule: `permit 198.51.100.0/24 ge 24`?

  2. Q2. A BGP neighbour can have an inbound prefix-list applied and no outbound prefix-list.

  3. Q3. An operator configures a prefix-list with rule 10 `permit 198.51.100.0/24 ge 26` and applies it inbound on a BGP peer. The operator expects the rule to accept `198.51.100.0/24` and any more-specific subnets. The rule only accepts the more-specific subnets. What is the fix?

    The rule `198.51.100.0/24 ge 26` matches prefixes with prefix-length ≥ 26. The /24 has prefix-length 24, which is less than 26. The rule does not match the /24.

  4. Q4. An operator adds a new deny rule to a prefix-list between rule 10 and rule 20, using rule 15. The operator did not add a `description` field. The commit succeeds but the next operator on call struggles to understand the rule's intent. What is the discipline lesson?

    The rule is added without a description. The next operator on call reads the prefix-list and does not know why the deny rule is there. The discipline is to document every rule with a description.

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