BGP on VyOS: Filters Are Not Optional

BGP is the protocol that runs the internet. It’s also the protocol that can take down the internet — one misconfiguration, one missing filter, and you’re either leaking routes or accepting someone else’s garbage. Every major BGP incident (and there are many) comes down to the same thing: missing or inadequate filtering.

VyOS uses FRR (Free Range Routing) as its BGP implementation. It’s battle-tested and feature-complete. But FRR, like any BGP implementation, will happily accept and advertise whatever you tell it to. It’s your job to tell it the right things.

BGP Fundamentals

eBGP (External BGP): Between different Autonomous Systems (AS). Your connection to ISPs, IXPs, or other organizations.

iBGP (Internal BGP): Within the same AS. Distributes external routes internally.

Key difference: eBGP changes the AS path on advertisements. iBGP doesn’t — routes learned via iBGP keep their original AS path.

Basic eBGP Configuration

Typical scenario: You have AS 65000, connecting to an upstream provider (AS 12345).

Terminal window
configure
# Define your AS and router ID
set protocols bgp system-as '65000'
set protocols bgp parameters router-id '203.0.113.1'
# Define neighbor (your upstream)
set protocols bgp neighbor 198.51.100.1 remote-as '12345'
set protocols bgp neighbor 198.51.100.1 description 'Upstream Provider'
set protocols bgp neighbor 198.51.100.1 update-source '203.0.113.1'
# Announce your prefix
set protocols bgp address-family ipv4-unicast network 203.0.113.0/24
commit

Never run this in production. This configuration has no filters — you’ll accept whatever your upstream sends (including their full table if they misconfigure) and potentially leak routes.

The Golden Rule: Always Filter

BGP without filters is an incident waiting to happen. Every BGP session needs:

  • Inbound filter: What routes you accept
  • Outbound filter: What routes you advertise
  • max-prefix limit: Protection against route leaks from peers

No exceptions.

Prefix Lists

Prefix lists define which IP prefixes to match:

Terminal window
configure
# What you own and want to advertise
set policy prefix-list MY-PREFIXES rule 10 action 'permit'
set policy prefix-list MY-PREFIXES rule 10 prefix '203.0.113.0/24'
# What you accept from upstream (example: allow all but filter specifics)
set policy prefix-list UPSTREAM-IN rule 10 action 'deny'
set policy prefix-list UPSTREAM-IN rule 10 prefix '0.0.0.0/0'
set policy prefix-list UPSTREAM-IN rule 10 description 'Deny default unless explicitly wanted'
set policy prefix-list UPSTREAM-IN rule 20 action 'deny'
set policy prefix-list UPSTREAM-IN rule 20 prefix '10.0.0.0/8'
set policy prefix-list UPSTREAM-IN rule 20 le '32'
set policy prefix-list UPSTREAM-IN rule 20 description 'Deny RFC1918'
set policy prefix-list UPSTREAM-IN rule 30 action 'deny'
set policy prefix-list UPSTREAM-IN rule 30 prefix '172.16.0.0/12'
set policy prefix-list UPSTREAM-IN rule 30 le '32'
set policy prefix-list UPSTREAM-IN rule 40 action 'deny'
set policy prefix-list UPSTREAM-IN rule 40 prefix '192.168.0.0/16'
set policy prefix-list UPSTREAM-IN rule 40 le '32'
set policy prefix-list UPSTREAM-IN rule 1000 action 'permit'
set policy prefix-list UPSTREAM-IN rule 1000 prefix '0.0.0.0/0'
set policy prefix-list UPSTREAM-IN rule 1000 le '24'
set policy prefix-list UPSTREAM-IN rule 1000 description 'Accept /24 and larger'
commit

Prefix List Syntax

  • prefix: The IP prefix to match
  • le (less than or equal): Match prefixes with length up to this value
  • ge (greater than or equal): Match prefixes with length at least this value

Example: prefix 10.0.0.0/8 le 24 ge 16 matches any prefix within 10.0.0.0/8 that is /16 to /24.

Route Maps

Route maps combine conditions with actions. They’re the Swiss Army knife of BGP policy:

Terminal window
configure
# Outbound: Only advertise my prefixes, set correct attributes
set policy route-map TO-UPSTREAM rule 10 action 'permit'
set policy route-map TO-UPSTREAM rule 10 match ip address prefix-list 'MY-PREFIXES'
set policy route-map TO-UPSTREAM rule 10 set local-preference '100'
set policy route-map TO-UPSTREAM rule 1000 action 'deny'
set policy route-map TO-UPSTREAM rule 1000 description 'Deny everything else'
# Inbound: Filter bad routes, set local-pref based on source
set policy route-map FROM-UPSTREAM rule 10 action 'deny'
set policy route-map FROM-UPSTREAM rule 10 match ip address prefix-list 'BOGONS'
set policy route-map FROM-UPSTREAM rule 10 description 'Drop bogons'
set policy route-map FROM-UPSTREAM rule 100 action 'permit'
set policy route-map FROM-UPSTREAM rule 100 match ip address prefix-list 'UPSTREAM-IN'
set policy route-map FROM-UPSTREAM rule 100 set local-preference '200'
set policy route-map FROM-UPSTREAM rule 1000 action 'deny'
commit

Apply Route Maps to Neighbors

Terminal window
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast route-map import 'FROM-UPSTREAM'
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast route-map export 'TO-UPSTREAM'

Max-Prefix Protection

This is your safety valve. If a peer sends more prefixes than expected, the session tears down:

Terminal window
# Expect ~10 prefixes, warn at 8, shut down at 10
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast maximum-prefix '10'
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast maximum-prefix threshold '80'
# For full table peers (~900k prefixes as of 2024)
set protocols bgp neighbor 198.51.100.2 address-family ipv4-unicast maximum-prefix '1000000'
set protocols bgp neighbor 198.51.100.2 address-family ipv4-unicast maximum-prefix threshold '90'

When max-prefix triggers:

  1. Session goes down
  2. Log entry created
  3. Manual intervention required (or restart-timer)
Terminal window
# Auto-restart after 30 minutes (use carefully)
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast maximum-prefix restart '30'

BGP Communities

Communities are tags attached to routes. Use them to:

  • Signal intent to upstreams (traffic engineering)
  • Control route propagation
  • Implement policy at scale

Standard Communities

Terminal window
configure
# Define community list
set policy community-list BLACKHOLE rule 10 action 'permit'
set policy community-list BLACKHOLE rule 10 regex '65000:666'
# Set community in route-map
set policy route-map SET-COMMUNITY rule 10 action 'permit'
set policy route-map SET-COMMUNITY rule 10 set community '65000:100'
# Match community in route-map
set policy route-map MATCH-COMMUNITY rule 10 action 'permit'
set policy route-map MATCH-COMMUNITY rule 10 match community 'BLACKHOLE'
commit

Well-Known Communities

CommunityMeaning
no-exportDon’t advertise outside the AS
no-advertiseDon’t advertise to any peer
local-asDon’t advertise outside the local confederation
no-peerDon’t advertise to eBGP peers (RFC 3765)
Terminal window
# Example: Mark routes as no-export
set policy route-map INTERNAL-ONLY rule 10 set community 'no-export'

Blackhole Communities

Most transit providers support blackhole communities. When you’re under DDoS attack:

Terminal window
# Advertise the attacked IP with blackhole community
# (community varies by provider — check their documentation)
set policy route-map BLACKHOLE-ANNOUNCE rule 10 action 'permit'
set policy route-map BLACKHOLE-ANNOUNCE rule 10 match ip address prefix-list 'ATTACKED-IP'
set policy route-map BLACKHOLE-ANNOUNCE rule 10 set community '12345:666'

The upstream drops traffic to that prefix at their edge. You stop receiving the DDoS.

iBGP Configuration

iBGP distributes routes within your AS. Different rules apply:

  • No AS path modification
  • Full mesh required (or use route reflectors)
  • Next-hop often needs adjustment

Basic iBGP

Terminal window
configure
set protocols bgp neighbor 10.0.0.2 remote-as '65000'
set protocols bgp neighbor 10.0.0.2 description 'iBGP peer - Router 2'
set protocols bgp neighbor 10.0.0.2 update-source 'lo'
# Next-hop-self for eBGP routes advertised via iBGP
set protocols bgp neighbor 10.0.0.2 address-family ipv4-unicast nexthop-self
commit

Why Next-Hop-Self?

When you learn a route via eBGP, the next-hop is the eBGP peer’s IP. If you advertise this to iBGP neighbors, they need a route to that external IP — which they might not have.

next-hop-self rewrites the next-hop to your own address, which iBGP peers definitely can reach.

Route Reflectors

Full mesh iBGP doesn’t scale. With N routers, you need N*(N-1)/2 sessions. Route reflectors solve this:

Terminal window
# On route reflector
set protocols bgp neighbor 10.0.0.2 address-family ipv4-unicast route-reflector-client
set protocols bgp neighbor 10.0.0.3 address-family ipv4-unicast route-reflector-client
set protocols bgp neighbor 10.0.0.4 address-family ipv4-unicast route-reflector-client
# Clients just peer with reflector, not each other

Route reflector modifies the normal iBGP advertisement rules to re-advertise routes to other iBGP peers.

Local Preference

Local preference determines which path to use when you have multiple options. Higher is preferred.

Terminal window
# Prefer primary upstream (local-pref 200) over backup (local-pref 100)
set policy route-map FROM-PRIMARY rule 10 set local-preference '200'
set policy route-map FROM-BACKUP rule 10 set local-preference '100'

Local preference is only meaningful within your AS — it’s not advertised to eBGP peers.

AS Path Prepending

Make your routes less attractive to specific upstreams (traffic engineering):

Terminal window
# Prepend your AS twice when advertising to backup upstream
set policy route-map TO-BACKUP rule 10 action 'permit'
set policy route-map TO-BACKUP rule 10 match ip address prefix-list 'MY-PREFIXES'
set policy route-map TO-BACKUP rule 10 set as-path prepend '65000 65000'

The longer AS path makes this route less preferred. Traffic should prefer the primary path.

Warning: Prepending more than 3x is usually pointless. Some networks filter very long AS paths.

BGP Timers

Terminal window
# Keepalive every 30 seconds, hold time 90 seconds
set protocols bgp neighbor 198.51.100.1 timers keepalive '30'
set protocols bgp neighbor 198.51.100.1 timers holdtime '90'
# For faster failover (aggressive)
set protocols bgp neighbor 198.51.100.1 timers keepalive '10'
set protocols bgp neighbor 198.51.100.1 timers holdtime '30'

For sub-second failover, use BFD instead of aggressive BGP timers:

Terminal window
set protocols bgp neighbor 198.51.100.1 bfd

Bogon Filtering

Always filter bogons — prefixes that should never appear in the global routing table:

Terminal window
configure
# IPv4 Bogons (not exhaustive — use a maintained list)
set policy prefix-list BOGONS-V4 rule 10 action 'permit'
set policy prefix-list BOGONS-V4 rule 10 prefix '0.0.0.0/8'
set policy prefix-list BOGONS-V4 rule 10 le '32'
set policy prefix-list BOGONS-V4 rule 20 action 'permit'
set policy prefix-list BOGONS-V4 rule 20 prefix '10.0.0.0/8'
set policy prefix-list BOGONS-V4 rule 20 le '32'
set policy prefix-list BOGONS-V4 rule 30 action 'permit'
set policy prefix-list BOGONS-V4 rule 30 prefix '100.64.0.0/10'
set policy prefix-list BOGONS-V4 rule 30 le '32'
set policy prefix-list BOGONS-V4 rule 40 action 'permit'
set policy prefix-list BOGONS-V4 rule 40 prefix '127.0.0.0/8'
set policy prefix-list BOGONS-V4 rule 40 le '32'
set policy prefix-list BOGONS-V4 rule 50 action 'permit'
set policy prefix-list BOGONS-V4 rule 50 prefix '169.254.0.0/16'
set policy prefix-list BOGONS-V4 rule 50 le '32'
set policy prefix-list BOGONS-V4 rule 60 action 'permit'
set policy prefix-list BOGONS-V4 rule 60 prefix '172.16.0.0/12'
set policy prefix-list BOGONS-V4 rule 60 le '32'
set policy prefix-list BOGONS-V4 rule 70 action 'permit'
set policy prefix-list BOGONS-V4 rule 70 prefix '192.0.0.0/24'
set policy prefix-list BOGONS-V4 rule 70 le '32'
set policy prefix-list BOGONS-V4 rule 80 action 'permit'
set policy prefix-list BOGONS-V4 rule 80 prefix '192.0.2.0/24'
set policy prefix-list BOGONS-V4 rule 80 le '32'
set policy prefix-list BOGONS-V4 rule 90 action 'permit'
set policy prefix-list BOGONS-V4 rule 90 prefix '192.168.0.0/16'
set policy prefix-list BOGONS-V4 rule 90 le '32'
set policy prefix-list BOGONS-V4 rule 100 action 'permit'
set policy prefix-list BOGONS-V4 rule 100 prefix '198.18.0.0/15'
set policy prefix-list BOGONS-V4 rule 100 le '32'
set policy prefix-list BOGONS-V4 rule 110 action 'permit'
set policy prefix-list BOGONS-V4 rule 110 prefix '198.51.100.0/24'
set policy prefix-list BOGONS-V4 rule 110 le '32'
set policy prefix-list BOGONS-V4 rule 120 action 'permit'
set policy prefix-list BOGONS-V4 rule 120 prefix '203.0.113.0/24'
set policy prefix-list BOGONS-V4 rule 120 le '32'
set policy prefix-list BOGONS-V4 rule 130 action 'permit'
set policy prefix-list BOGONS-V4 rule 130 prefix '224.0.0.0/3'
set policy prefix-list BOGONS-V4 rule 130 le '32'
# Apply in route-map
set policy route-map FROM-PEER rule 10 action 'deny'
set policy route-map FROM-PEER rule 10 match ip address prefix-list 'BOGONS-V4'
commit

For production, use Team Cymru’s bogon reference or automate prefix list updates from IRR.

Debugging BGP

Check BGP Status

Terminal window
# Summary of all BGP neighbors
show ip bgp summary
# Detailed neighbor info
show ip bgp neighbors 198.51.100.1
# Advertised routes
show ip bgp neighbors 198.51.100.1 advertised-routes
# Received routes
show ip bgp neighbors 198.51.100.1 received-routes
# Routes in BGP table
show ip bgp

Common Issues

SymptomCauseFix
State: IdleNo route to peer, firewallCheck connectivity, allow TCP 179
State: ActiveTCP connect failingFirewall, wrong IP, peer not configured
State: OpenSentWaiting for peer’s OPENPeer might be filtering, AS mismatch
No routes receivedInbound filter too strictCheck route-map, prefix-list
Routes not advertisedOutbound filter, route not in BGPCheck network statement, route-map

BGP Messages

Terminal window
# Enable BGP debugging (careful in production)
debug bgp neighbor-events
debug bgp updates
# View logs
show log | match bgp

Complete eBGP Configuration

Terminal window
# === BGP Core ===
set protocols bgp system-as '65000'
set protocols bgp parameters router-id '203.0.113.1'
set protocols bgp parameters log-neighbor-changes
# === Prefix Lists ===
set policy prefix-list MY-PREFIXES rule 10 action 'permit'
set policy prefix-list MY-PREFIXES rule 10 prefix '203.0.113.0/24'
set policy prefix-list BOGONS-V4 rule 10 action 'permit'
set policy prefix-list BOGONS-V4 rule 10 prefix '10.0.0.0/8'
set policy prefix-list BOGONS-V4 rule 10 le '32'
# ... (other bogon entries)
set policy prefix-list INBOUND-FILTER rule 10 action 'deny'
set policy prefix-list INBOUND-FILTER rule 10 prefix '0.0.0.0/0'
set policy prefix-list INBOUND-FILTER rule 10 ge '25'
set policy prefix-list INBOUND-FILTER rule 10 description 'Deny too-specific prefixes'
set policy prefix-list INBOUND-FILTER rule 1000 action 'permit'
set policy prefix-list INBOUND-FILTER rule 1000 prefix '0.0.0.0/0'
set policy prefix-list INBOUND-FILTER rule 1000 le '24'
# === Route Maps ===
set policy route-map TO-UPSTREAM rule 10 action 'permit'
set policy route-map TO-UPSTREAM rule 10 match ip address prefix-list 'MY-PREFIXES'
set policy route-map TO-UPSTREAM rule 1000 action 'deny'
set policy route-map FROM-UPSTREAM rule 10 action 'deny'
set policy route-map FROM-UPSTREAM rule 10 match ip address prefix-list 'BOGONS-V4'
set policy route-map FROM-UPSTREAM rule 100 action 'permit'
set policy route-map FROM-UPSTREAM rule 100 match ip address prefix-list 'INBOUND-FILTER'
set policy route-map FROM-UPSTREAM rule 100 set local-preference '200'
set policy route-map FROM-UPSTREAM rule 1000 action 'deny'
# === Neighbor Configuration ===
set protocols bgp neighbor 198.51.100.1 remote-as '12345'
set protocols bgp neighbor 198.51.100.1 description 'Primary Upstream'
set protocols bgp neighbor 198.51.100.1 update-source '203.0.113.1'
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast route-map import 'FROM-UPSTREAM'
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast route-map export 'TO-UPSTREAM'
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast maximum-prefix '1000000'
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast soft-reconfiguration inbound
# === Networks to Advertise ===
set protocols bgp address-family ipv4-unicast network 203.0.113.0/24

The Lesson

BGP without filters is an incident waiting to happen. Every route leak, every hijack, every accidental full-table advertisement — they all trace back to missing or inadequate filtering.

The essentials:

  1. Prefix lists: Define exactly what you advertise and accept
  2. Route maps: Apply policy consistently
  3. Max-prefix: Protect against route leaks from peers
  4. Bogon filtering: Never accept or announce garbage prefixes
  5. Community discipline: Use communities for consistent policy

The internet runs on trust and filtering. BGP peers trust that you advertise only what you should. Filters ensure that even when mistakes happen, damage is limited.

Before any BGP session goes live, ask: “What’s the worst that happens if I misconfigure this?” Then add filters to prevent that worst case.