RPKI and IRR filtering aren’t academic exercises. They’re the difference between a stable network and being part of someone else’s route hijack. Every year we see incidents where networks accept hijacked prefixes because they didn’t validate. The tools exist. The question is whether you use them.
This isn’t a theoretical overview. This is how to actually implement route validation on VyOS and maintain it over time. Because filtering isn’t something you configure once — it’s an ongoing process.
The Problem We’re Solving
BGP trusts what peers tell it. If your upstream sends you a route for 8.8.8.0/24, BGP will accept it (unless you filter). If that route is a hijack, you’re now directing your users to an attacker. RPKI and IRR give us ways to validate that routes are legitimate.
RPKI (Resource Public Key Infrastructure): Cryptographic validation. Route Origin Authorizations (ROAs) are signed by the prefix owner and published in repositories. You query a validator, get the validation state.
IRR (Internet Routing Registry): Database-driven. Networks register their routing policy (what they originate, what they accept). You build filters from these registrations.
Neither is perfect. Use both.
RPKI Validation on VyOS
Step 1: Run an RPKI Validator
VyOS connects to an RPKI validator via the RTR (RPKI-to-Router) protocol. You need a validator running somewhere — this can be on the VyOS router itself (limited resources) or preferably on a separate server.
Popular validators:
- Routinator (NLnet Labs) — Rust, lightweight, recommended
- FORT Validator — C, LACNIC
- rpki-client — OpenBSD team, very lightweight
Example: Running Routinator on a separate Linux server:
# On your validator server (Debian/Ubuntu)apt install routinator
# Configure /etc/routinator/routinator.conf[server]rtr-listen = ["0.0.0.0:3323"]http-listen = ["0.0.0.0:8323"]
# Start and enablesystemctl enable --now routinatorStep 2: Configure VyOS as RPKI Client
Connect VyOS to your validator:
configure
# Define RPKI cache serverset protocols rpki cache VALIDATOR address '10.0.0.50'set protocols rpki cache VALIDATOR port '3323'set protocols rpki cache VALIDATOR preference '1'
# Optional: Set polling interval (default 300 seconds)set protocols rpki polling-period '300'
commitVerify the connection:
show rpki cache-connectionshow rpki prefix-tableYou should see prefixes with their validation states: valid, invalid, or not-found.
Step 3: Create Validation Policy
Here’s where RPKI becomes useful. Create route-maps that act on validation state:
configure
# Route-map for upstream: reject invalid, accept valid and unknownset policy route-map UPSTREAM-RPKI rule 10 action 'deny'set policy route-map UPSTREAM-RPKI rule 10 match rpki 'invalid'
set policy route-map UPSTREAM-RPKI rule 20 action 'permit'set policy route-map UPSTREAM-RPKI rule 20 match rpki 'valid'
set policy route-map UPSTREAM-RPKI rule 30 action 'permit'set policy route-map UPSTREAM-RPKI rule 30 match rpki 'notfound'set policy route-map UPSTREAM-RPKI rule 30 set local-preference '90'
commitWhy accept notfound? RPKI adoption isn’t 100%. Rejecting unknown would break connectivity to many legitimate destinations. Instead, we lower preference — valid routes are preferred when available.
Step 4: Apply to BGP Sessions
configure
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast route-map import 'UPSTREAM-RPKI'
commitIRR-Based Filtering
RPKI tells you if the origin AS is authorized. IRR tells you what prefixes a network claims to originate and what their routing policy is. Use IRR to build prefix-lists.
Generating Filters from IRR
You don’t manually maintain these filters. Tools like bgpq4 generate them:
# Install bgpq4apt install bgpq4
# Generate prefix-list for AS-EXAMPLE (an AS-SET)bgpq4 -4 -l CUSTOMER-PREFIXES AS-EXAMPLE
# Output (VyOS-compatible format)bgpq4 -4 -l CUSTOMER-PREFIXES -F 'set policy prefix-list %n rule %N action permit\nset policy prefix-list %n rule %N prefix %i/%l\n' AS-EXAMPLEExample output:
set policy prefix-list CUSTOMER-PREFIXES rule 10 action permitset policy prefix-list CUSTOMER-PREFIXES rule 10 prefix 203.0.113.0/24set policy prefix-list CUSTOMER-PREFIXES rule 20 action permitset policy prefix-list CUSTOMER-PREFIXES rule 20 prefix 198.51.100.0/24Automation Is Required
IRR data changes. New prefixes get registered, old ones removed. If you generate filters once and forget, they become stale. This must be automated:
#!/bin/bashCUSTOMER_AS="AS-EXAMPLE"OUTPUT="/tmp/customer-prefixes.vyos"
# Generate VyOS commandsbgpq4 -4 -l CUSTOMER-IN -F 'set policy prefix-list %n rule %N action permit\nset policy prefix-list %n rule %N prefix %i/%l\n' $CUSTOMER_AS > $OUTPUT
# Add deny rule at endecho "set policy prefix-list CUSTOMER-IN rule 9999 action deny" >> $OUTPUTecho "set policy prefix-list CUSTOMER-IN rule 9999 prefix 0.0.0.0/0 le 32" >> $OUTPUT
# Apply to VyOS (use vbash for configure mode)/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper beginwhile read line; do /opt/vyatta/sbin/vyatta-cfg-cmd-wrapper $linedone < $OUTPUT/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper commit/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper endRun this on a schedule (cron job) — daily or when you receive notification of policy changes.
Where to Store Policy
Filtering configuration gets complex. Where should it live?
Option 1: On the Router (Simple but Limited)
For small deployments, keep everything in VyOS config. Works, but doesn’t scale and no version control.
Option 2: Git Repository (Recommended)
Store all policies in Git:
network-policy/├── prefix-lists/│ ├── customers/│ │ ├── customer-a.txt│ │ └── customer-b.txt│ └── bogons.txt├── as-sets/│ └── customer-as-sets.txt├── route-maps/│ └── templates/└── scripts/ ├── generate-filters.sh └── deploy.shBenefits:
- Version history (who changed what, when)
- Review process (PRs before deployment)
- Rollback capability
- Documentation alongside config
Option 3: Automation Platform (Ansible/Nornir)
For larger networks, use configuration management:
# Ansible playbook- name: Update BGP filters hosts: routers tasks: - name: Generate prefix-lists from IRR local_action: module: shell cmd: bgpq4 -4 -l {{ customer.name }}-IN {{ customer.as_set }} register: prefix_list
- name: Apply prefix-lists vyos.vyos.vyos_config: lines: "{{ prefix_list.stdout_lines }}"Validation Workflow
Here’s a practical workflow that actually gets maintained:
1. Onboarding New Peers/Customers
Customer request → Verify AS/prefix ownership →Add to IRR monitoring → Generate initial filters →Apply with soft-reconfiguration → Monitor logs2. Ongoing Maintenance
Weekly: - Regenerate IRR-based filters (automated) - Review RPKI invalid count (should be zero or near-zero) - Check validator health
Monthly: - Review filter hit counts (unused rules?) - Verify customer IRR registrations current - Test failover to backup validators3. Incident Response
When you see RPKI invalid or unexpected routes:
# Check specific prefixshow rpki prefix 203.0.113.0/24
# See what the validator saysshow rpki cache-server
# Check where route is coming fromshow ip bgp 203.0.113.0/24Combined Policy Example
Real-world inbound filter combining RPKI and prefix-list:
configure
# Bogon prefix-list (never accept these)set policy prefix-list BOGONS rule 10 action 'permit'set policy prefix-list BOGONS rule 10 prefix '0.0.0.0/8'set policy prefix-list BOGONS rule 10 le '32'
set policy prefix-list BOGONS rule 20 action 'permit'set policy prefix-list BOGONS rule 20 prefix '10.0.0.0/8'set policy prefix-list BOGONS rule 20 le '32'
set policy prefix-list BOGONS rule 30 action 'permit'set policy prefix-list BOGONS rule 30 prefix '127.0.0.0/8'set policy prefix-list BOGONS rule 30 le '32'
# ... add remaining bogons
# Combined route-mapset policy route-map UPSTREAM-IN rule 10 action 'deny'set policy route-map UPSTREAM-IN rule 10 match ip address prefix-list 'BOGONS'set policy route-map UPSTREAM-IN rule 10 description 'Reject bogons'
set policy route-map UPSTREAM-IN rule 20 action 'deny'set policy route-map UPSTREAM-IN rule 20 match rpki 'invalid'set policy route-map UPSTREAM-IN rule 20 description 'Reject RPKI invalid'
set policy route-map UPSTREAM-IN rule 30 action 'permit'set policy route-map UPSTREAM-IN rule 30 match rpki 'valid'set policy route-map UPSTREAM-IN rule 30 set local-preference '110'set policy route-map UPSTREAM-IN rule 30 description 'Prefer RPKI valid'
set policy route-map UPSTREAM-IN rule 100 action 'permit'set policy route-map UPSTREAM-IN rule 100 description 'Accept remaining'
commitMonitoring and Alerting
Filtering without monitoring is incomplete. Track:
# Number of RPKI-invalid routes (should alert if > 0 accepted)show rpki prefix-table | grep -c Invalid
# Prefix counts by validation stateshow ip bgp summaryshow rpki cache-serverSet up alerts when:
- RPKI validator connection drops
- Number of invalid routes increases suddenly
- Prefix count changes dramatically
The Lesson
Filtering is a process, not a config.
You can’t configure BGP filters once and forget them. Policies change, customers add prefixes, hijacks happen. Effective filtering requires:
- Automated generation (IRR → filters)
- Cryptographic validation (RPKI)
- Version control (Git)
- Regular review (is this still correct?)
- Monitoring (are filters working?)
The networks that avoid hijack incidents aren’t lucky — they have processes that keep their filters current. The ones that become headlines treated filtering as a one-time task.
Start with RPKI (it’s the lowest effort for significant protection), add IRR-based filters for customers and peers, automate the maintenance, and review regularly. That’s the practical path to route validation that actually works.