Skip to main content
RunBook Academy

VyOSXXVIII · BGP Prefix FilteringPrefix filters

Filter troubleshooting — prefix not received, prefix not advertised, regex errors, AS-path-list syntax

Advanced⏱ ~22 minshow ip bgpshow ip bgp summaryshow ip bgp neighbors <ip> received-routesshow ip bgp neighbors <ip> routesshow ip bgp neighbors <ip> advertised-routesshow ip bgp regexpshow policy prefix-listshow policy as-path-listshow log frrvtysh -c show ip bgpvtysh -c show ip prefix-listvtysh -c show ip as-path-access-list

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-15

Not yet marked complete on this device.

A filter ticket arrives in one of three forms: “the prefix is not received”, “the prefix is not advertised”, or “the regex is wrong”. Each form has a canonical triage path; each leaves different evidence in the BGP table and the FRR log. This lesson walks the operator through the discipline: define the failure, gather evidence, hypothesise the cause, test the hypothesis, document.

The BGP filter is deterministic — the filter produces the same output for the same inputs. If the operator sees an unexpected output, the inputs are different from what the operator expected, or the filter is configured differently from what the operator expected. The troubleshooting is the operator’s job of finding the input or the configuration that differs.

The troubleshooting discipline

The discipline is the same as for any routing problem, with the filter dimension layered on top:

  1. Define the failure. “The prefix 198.51.100.0/24 is not in the local BGP table” — not “the network is broken”. The first is testable; the second is a guess.
  2. Gather evidence. Three layers — pre-filter view, post-filter view, FRR log. Each tells a different story.
  3. Hypothesise the cause. State a specific candidate cause (“the prefix-list denies the prefix because the prefix-length is not in the ge/le range”) before changing configuration.
  4. Test the hypothesis. Make one change; observe the result; commit or rollback.
  5. Document the evidence. The next operator on call at 03:00 will want to know what you found.
flowchart TD
  S1["1. Define: what fails"] --> S2["2. Evidence: pre-filter, post-filter, FRR log"]
  S2 --> S3["3. Hypothesise: prefix-list / filter-list / distribute-list"]
  S3 --> S4["4. Test: one change at a time"]
  S4 --> S5["5. Document: ticket, runbook, post-mortem"]
  S5 -.-> S1

The lesson assumes the BGP session is up. If the session is down, the troubleshooting starts with the session (covered in vyos-xxxi-01-session-states).

Failure 1 — Prefix not received from peer

The operator sees a prefix in the upstream’s show ip bgp but not in the local BGP table. The BGP session is up. The first question is “where is the route — in the received-routes, in the BGP table, or in neither?”.

vyos@vyos:~$ show ip bgp summary
Neighbor        V    AS   MsgRcvd  MsgSent  Up/Down   State/PfxRcd
192.0.2.2       4  65001      1247      892   03:45:12       42

The peer is up and advertising 42 prefixes.

vyos@vyos:~$ show ip bgp 198.51.100.0/24
% Network not in table

The route is not in the local BGP table. The first question: did the peer send it?

vyos@vyos:~$ show ip bgp neighbors 192.0.2.2 received-routes | grep 198.51.100.0/24

If the route is in received-routes, the peer sent it and the local router accepted it before the filter. The issue is later (best-path selection, routing table). If the route is not in received-routes, the peer did not send it (the upstream’s filter rejected it, or the upstream is not originating the route).

vyos@vyos:~$ show ip bgp neighbors 192.0.2.2 received-routes | head
BGP table version is 8, local router ID is 10.255.0.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal

   Network          Next Hop            Metric LocPrf Weight Path
*> 198.51.100.0/24  192.0.2.2                 0    100      0 65001 i
*> 203.0.113.0/24   192.0.2.2                 0    100      0 65001 i
*> 198.51.100.0/26  192.0.2.2                 0    100      0 65001 i

The peer sent 198.51.100.0/24, 203.0.113.0/24, and 198.51.100.0/26. The peer did NOT send 198.51.100.0/24 (the prefix the operator is looking for). Wait, the prefix IS in received-routes. The issue is later.

vyos@vyos:~$ show ip bgp neighbors 192.0.2.2 routes
BGP table version is 6, local router ID is 10.255.0.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal

   Network          Next Hop            Metric LocPrf Weight Path
*> 198.51.100.0/24  192.0.2.2                 0    100      0 65001 i
*> 203.0.113.0/24   192.0.2.2                 0    100      0 65001 i

The post-filter view shows only the /24 and /28. The /26 was filtered out. The fix is to check the inbound filter on the local router.

vyos@vyos:~$ show ip bgp neighbors 192.0.2.2 | grep -A2 prefix-list
  Inbound prefix-list: FROM-UPSTREAM

The inbound filter is FROM-UPSTREAM. The operator checks the prefix-list:

vyos@vyos:~$ show policy prefix-list FROM-UPSTREAM
Rules:
  Rule 10: action permit, prefix 198.51.100.0/24
  Rule 20: action permit, prefix 203.0.113.0/24

The prefix-list only permits 198.51.100.0/24 and 203.0.113.0/24. The route 198.51.100.0/26 is denied (the implicit deny at the end). The operator found the bug.

The fix is to add the /26 to the prefix-list:

configure
set policy prefix-list FROM-UPSTREAM rule 30 action 'permit'
set policy prefix-list FROM-UPSTREAM rule 30 prefix '198.51.100.0/26'
commit
save

The operator who is debugging a prefix-not-received ticket must compare the pre-filter (received-routes) and post-filter (routes) views to find the filter that is rejecting the prefix.

flowchart TD
  A["Prefix not in BGP table"] --> B["In received-routes?"]
  B -->|No| BX["Peer did not send it.<br/>Check peer's filter or<br/>upstream's advertisement"]
  B -->|Yes| C["In routes (post-filter)?"]
  C -->|No| CX["Local filter rejected it.<br/>Check inbound filter<br/>(prefix-list, distribute-list)"]
  C -->|Yes| D["In show ip bgp?"]
  D -->|No| DX["Best-path selection rejected.<br/>Check MED, Local Preference,<br/>AS Path, Origin"]
  D -->|Yes| DY["In routing table?"]
  DY -->|No| DZ["Next-hop unreachable in IGP.<br/>Check OSPF / IS-IS"]
  DY -->|Yes| DZ

The decision tree walks the four layers in order: peer’s advertisement, local inbound filter, best-path selection, IGP next-hop reachability. The first layer that fails is the one that needs to be fixed.

Failure 2 — Prefix not advertised to peer

The operator has a route in the local BGP table but the peer does not see it. The first question is “where is the route — in the advertised-routes, in the BGP table, or in neither?”.

vyos@vyos:~$ show ip bgp 198.51.100.0/24
Paths: (1 available, best #1)
  Path 1 (best):
    Local, (aggregated by 64512 10.255.0.1)
      Origin IGP, localpref 100, weight 0, valid, aggregated, local

The route is in the local BGP table. The peer session is up. The question is whether the peer is receiving the route.

vyos@vyos:~$ show ip bgp neighbors 192.0.2.2 advertised-routes

The command shows the routes the local router advertises to the peer. If the route is in the list, the local router is sending it. If the route is not in the list, the local router is not sending it — likely because of an outbound filter.

vyos@vyos:~$ show ip bgp neighbors 192.0.2.2 | grep -A2 prefix-list
  Outbound prefix-list: TO-UPSTREAM

The outbound filter is TO-UPSTREAM. The operator checks the prefix-list:

vyos@vyos:~$ show policy prefix-list TO-UPSTREAM
Rules:
  Rule 10: action permit, prefix 203.0.113.0/24

The prefix-list only permits 203.0.113.0/24. The route 198.51.100.0/24 is denied (the implicit deny at the end). The operator found the bug.

The fix is to add the prefix to the outbound prefix-list:

configure
set policy prefix-list TO-UPSTREAM rule 20 action 'permit'
set policy prefix-list TO-UPSTREAM rule 20 prefix '198.51.100.0/24'
commit
save

Failure 3 — Regex error in filter-list

The operator configures a filter-list with a regex; the filter is not matching the expected set. The canonical regex bugs:

Bug 1: Unanchored regex matches too much. The regex 65001 matches the literal 65001 anywhere. The route 198.51.100.65/24 with AS_PATH 650010 64512 65010 matches because the first AS 650010 contains 65001. The fix is to use anchored regex _65001_ or ^65001_ or 65001$.

Bug 2: Wrong anchors. The regex ^65001$ matches only the literal 65001 with no other characters. The AS_PATH is usually longer than one AS. The fix is to use the right anchor for the matching intent.

Bug 3: Wrong character class. The regex [65001] matches any single character in the set {6, 5, 0, 0, 1} — i.e., a single digit 6, 5, 0, or 1. The regex matches the literal 6, 5, 0, or 1 anywhere. The fix is to use [65001]+ (one or more digits in the set) — but the AS-path-list regex uses POSIX ERE, which interprets character classes differently. The operator who wants to match an AS number must use the literal digits.

Bug 4: Wrong escape. The regex 65001\. matches the literal 65001. (with a dot). The AS_PATH uses spaces, not dots. The fix is to use 65001_ (space) or 65001\s (in ERE).

The operator who is debugging a regex error uses the show ip bgp regexp <regex> command to test the regex against the current BGP table:

vyos@vyos:~$ show ip bgp regexp '_65001_'
BGP table version is 8, local router ID is 10.255.0.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal

   Network          Next Hop            Metric LocPrf Weight Path
*> 198.51.100.0/24  192.0.2.2                 0    100      0 65001 65010 i

The regex matches the expected set. The operator who sees the matched set is the expected set has a regex that is correct.

vyos@vyos:~$ show ip bgp regexp '65001'
BGP table version is 8, local router ID is 10.255.0.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal

   Network          Next Hop            Metric LocPrf Weight Path
*> 198.51.100.0/24  192.0.2.2                 0    100      0 65001 65010 i
*> 198.51.101.0/24  192.0.2.2                 0    100      0 650010 65010 i

The unanchored regex matches both 65001 and 650010 (the second AS contains 65001 as a substring). The operator found the bug.

Failure 4 — AS-path-list syntax error

The operator configures an AS-path-list, but the FRR fails to load it. The FRR log shows the syntax error.

vyos@vyos:~$ show log frr | grep -i "as-path"
2026-08-15T14:23:01+00:00 vyos bgpd[1234]: %BGP-4-ASPATH: Failed to parse as-path access-list FROM-UPSTREAM-A: bad regex

The FRR log shows the syntax error. The cause is a malformed regex.

The canonical syntax errors:

Error 1: Unbalanced parentheses. The regex ^(65001 has an unbalanced parenthesis. The FRR rejects the regex. The fix is to balance the parentheses.

Error 2: Unbalanced brackets. The regex [65001 has an unbalanced bracket. The FRR rejects the regex. The fix is to balance the brackets.

Error 3: Invalid escape sequence. The regex 65001\ has an invalid escape. The FRR rejects the regex. The fix is to remove the backslash or use a valid escape.

Error 4: POSIX ERE limitations. The AS-path-list uses POSIX ERE. The regex \d (digit in PCRE) is not supported in POSIX ERE. The fix is to use [0-9] instead of \d.

The operator who sees the FRR log error fixes the regex syntax.

Failure 5 — Maximum-prefix limit triggered

The operator sees a peer session in the Idle (shutdown) state. The first question is “why was the session shut down?”.

vyos@vyos:~$ show ip bgp summary
Neighbor        V    AS   MsgRcvd  MsgSent  Up/Down   State/PfxRcd
192.0.2.2       4  65001          0        0   00:00:00 Idle (shutdown)

The session is in the Idle (shutdown) state. The fix is to check the FRR log for the reason.

vyos@vyos:~$ show log frr | grep -i "192.0.2.2"
2026-08-15T14:23:01+00:00 vyos bgpd[1234]: %BGP-3-MAXPFX: 192.0.2.2 (65001): 600001 prefixes, exceeding maximum 600000
2026-08-15T14:23:01+00:00 vyos bgpd[1234]: %BGP-5-ADJCHANGE: neighbor 192.0.2.2 (65001) Down BGP Notification sent: maximum-prefix exceeded

The FRR log shows the maximum-prefix limit was exceeded. The session was shut down because of the limit.

The fix is to either:

  • Increase the limit (the operator decides the limit was too low).
  • Remove the limit (the operator decides the limit is not needed).
  • Restart the session manually (the operator decides the upstream is fixed and the session can come back).
  • Wait for the restart-time knob to fire (if configured).

The lesson vyos-xxviii-05-maximum-prefix covers the maximum-prefix configuration in detail.

The FRR log as evidence

The FRR log is the canonical evidence for BGP filter failures. The operator who sees a filter failure should first read the FRR log:

vyos@vyos:~$ show log frr | grep -E "bgpd"

The log shows:

  • BGP state changes.
  • BGP notifications received (with the error code).
  • Filter list errors.
  • Maximum-prefix warnings.
  • Best-path changes.

The operator who correlates the FRR log with the symptom finds the cause in seconds.

For deep debugging:

vyos@vyos:~$ show log frr | grep -E "maximum-prefix|as-path|prefix-list|MAXPFX|ASPATH"

The grep filters for the specific keywords. The operator who greps for the relevant keywords finds the cause faster.

The prefix-list test command

The operator who has a prefix-list and wants to test a specific prefix uses the show ip prefix-list <list> <prefix> command:

vyos@vyos:~$ show ip prefix-list MY-FILTER 198.51.100.0/26

The command shows whether the prefix matches the prefix-list and which rule it matches. The operator who is debugging a prefix-list filter tests the prefix against the list before committing.

For deep debugging:

vyos@vyos:~$ vtysh -c 'show ip prefix-list MY-FILTER 198.51.100.0/26'

The vtysh command shows the same information with the FRR-rendered output.

The AS-path-list test command

The operator who has an AS-path-list and wants to test a specific regex uses the show ip bgp regexp <regex> command:

vyos@vyos:~$ show ip bgp regexp '_65001_'

The command shows the routes that match the regex. The operator who is debugging a filter-list regex tests the regex against the current BGP table before committing.

For deep debugging:

vyos@vyos:~$ vtysh -c 'show bgp regexp <regex>'

The vtysh command shows the same information with the FRR-rendered output.

How the result is validated

show ip bgp summary
show ip bgp neighbors <ip> received-routes
show ip bgp neighbors <ip> routes
show ip bgp neighbors <ip> advertised-routes
show ip bgp regexp <regex>
show ip prefix-list <list> <prefix>
show policy prefix-list
show policy as-path-list
show log frr

The first command shows the BGP session state. The second, third, and fourth show the pre-filter, post-filter, and outbound views. The fifth shows the regex match. The sixth shows the prefix-list match. The seventh and eighth show the filter configurations. The ninth shows the recent log messages.

For deep debugging:

vtysh -c 'show ip bgp <prefix>'
vtysh -c 'show ip prefix-list <list>'
vtysh -c 'show ip as-path-access-list'
vtysh -c 'show running-config' | grep -A20 'prefix-list\|as-path'

How it fails

The production failure modes the engineer must recognise:

  • The wrong filter is applied. The operator applies the inbound filter to the outbound direction (or vice versa). The fix is to correct the direction.
  • The filter is applied to the wrong neighbour. The operator applies the filter to peer A when the intent was peer B. The fix is to apply to the correct neighbour.
  • The filter is too permissive. The operator’s prefix-list has a catch-all permit rule. The local router accepts every route. The fix is to remove the catch-all or add explicit deny rules.
  • The filter is too aggressive. The operator’s prefix-list denies a route the operator expected to accept. The fix is to add the route to the prefix-list.
  • The regex is wrong. The operator’s regex matches too much or too little. The fix is to test the regex with show ip bgp regexp and refine the regex.
  • The maximum-prefix limit is too low. The operator’s limit is lower than the expected prefix count. The fix is to increase the limit.
  • The maximum-prefix limit is too high. The operator’s limit is higher than the expected prefix count. The fix is to tighten the limit.
  • The filter-list references a non-existent AS-path-list. The validator rejects the configuration. The fix is to define the AS-path-list before referencing it.

Rollback

Filter troubleshooting changes are configuration changes. The standard rollback path applies:

  • compare before commit to see the filter change.
  • commit-confirm <timeout> for any remote change.
  • rollback N; commit; save to revert to the previous configuration.
  • load /config/archive/<known-good-file>; commit; save to revert to a specific snapshot.

The operator who is troubleshooting a filter failure must also know that the rollback restores the previous filter behaviour. The standard rollback is rollback 1; commit; save.

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 filter troubleshooting on the firewall side. The BGP lessons vyos-xxviii-01-prefix-list-concept through vyos-xxviii-05-maximum-prefix cover the individual filters and their configuration. The lesson vyos-xxxi-05-bgp-blackhole covers the broader prefix-filtering troubleshooting. The Observability course’s XII-Observability-HostAgents covers scraping the FRR log for monitoring.

Quiz

Knowledge check · 4 questions

  1. Q1. An operator sees a prefix in the upstream's `show ip bgp` but not in the local BGP table. The BGP session is up. The first command to run is:

  2. Q2. The regex `65001` (without anchors) matches the AS_PATH `650010 64512 65010` because `65001` is a substring of `650010`.

  3. Q3. An operator configures a filter-list with regex `^65001$` and applies it inbound on a peer. The operator expected the filter to accept routes from AS 65001. The filter rejects every route. Why?

    The regex `^65001$` matches only the literal `65001` with no other characters. The AS_PATH is usually longer (e.g., `65001 65010`), so the filter rejects every route.

  4. Q4. An operator configures a maximum-prefix limit of 100,000 on a peer that sends 500,000 prefixes. The session is shut down. The operator did not intend to shut down the session. What is the fix?

    The limit is set too low. The peer sends 500,000 prefixes (a full table). The limit is 100,000. The session is shut down when the count exceeds 100,000.

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