RPKI/IRR Filtering Strategy: Practical, Not Academic

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:

Terminal window
# 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 enable
systemctl enable --now routinator

Step 2: Configure VyOS as RPKI Client

Connect VyOS to your validator:

Terminal window
configure
# Define RPKI cache server
set 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'
commit

Verify the connection:

Terminal window
show rpki cache-connection
show rpki prefix-table

You 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:

Terminal window
configure
# Route-map for upstream: reject invalid, accept valid and unknown
set 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'
commit

Why 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

Terminal window
configure
set protocols bgp neighbor 198.51.100.1 address-family ipv4-unicast route-map import 'UPSTREAM-RPKI'
commit

IRR-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:

Terminal window
# Install bgpq4
apt 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-EXAMPLE

Example output:

set policy prefix-list CUSTOMER-PREFIXES rule 10 action permit
set policy prefix-list CUSTOMER-PREFIXES rule 10 prefix 203.0.113.0/24
set policy prefix-list CUSTOMER-PREFIXES rule 20 action permit
set policy prefix-list CUSTOMER-PREFIXES rule 20 prefix 198.51.100.0/24

Automation 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:

/opt/scripts/update-irr-filters.sh
#!/bin/bash
CUSTOMER_AS="AS-EXAMPLE"
OUTPUT="/tmp/customer-prefixes.vyos"
# Generate VyOS commands
bgpq4 -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 end
echo "set policy prefix-list CUSTOMER-IN rule 9999 action deny" >> $OUTPUT
echo "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 begin
while read line; do
/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper $line
done < $OUTPUT
/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper commit
/opt/vyatta/sbin/vyatta-cfg-cmd-wrapper end

Run 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.

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.sh

Benefits:

  • 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 logs

2. 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 validators

3. Incident Response

When you see RPKI invalid or unexpected routes:

Terminal window
# Check specific prefix
show rpki prefix 203.0.113.0/24
# See what the validator says
show rpki cache-server
# Check where route is coming from
show ip bgp 203.0.113.0/24

Combined Policy Example

Real-world inbound filter combining RPKI and prefix-list:

Terminal window
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-map
set 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'
commit

Monitoring and Alerting

Filtering without monitoring is incomplete. Track:

Terminal window
# Number of RPKI-invalid routes (should alert if > 0 accepted)
show rpki prefix-table | grep -c Invalid
# Prefix counts by validation state
show ip bgp summary
show rpki cache-server

Set 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:

  1. Automated generation (IRR → filters)
  2. Cryptographic validation (RPKI)
  3. Version control (Git)
  4. Regular review (is this still correct?)
  5. 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.