Lab: dropping Invalids in FRR
This lab was run exactly as shown: three containers on one Docker network. R1 (AS64500) validates and R2 (AS64501) announces four prefixes. StayRTR serves two VRPs from a JSON file. All the output on this page is copied from the running lab.
FRR's RPKI support is a loadable module: bgpd must start with -M rpki (set in /etc/frr/daemons), or the rpki commands won't exist.
To run it yourself: put the four files below in a folder, copy /etc/frr/daemons out of the FRR image, enable bgpd with the -M rpki option, and run docker compose up -d.
- R1 loads the VRPs. 203.0.113.0/24-24 may be originated by AS64501; 198.51.100.0/24-24 by AS64502. Nothing covers 192.0.2.0/24.
- R2 announces four prefixes. All four are originated by AS64501: 203.0.113.0/24, 203.0.113.128/25, 198.51.100.0/24 and 192.0.2.0/24.
- R1 classifies and filters. Valid: 203.0.113.0/24. Invalid (too specific): 203.0.113.128/25. Invalid (wrong origin): 198.51.100.0/24. NotFound: 192.0.2.0/24. The route-map drops the two invalids.
frr defaults traditional
hostname R1
!
rpki
rpki polling_period 300
rpki cache tcp 10.0.12.10 3323 preference 1
exit
!
route-map R2-IN deny 10
match rpki invalid
exit
!
route-map R2-IN permit 20
exit
!
router bgp 64500
bgp router-id 10.255.0.1
neighbor 10.0.12.2 remote-as 64501
!
address-family ipv4 unicast
neighbor 10.0.12.2 soft-reconfiguration inbound
neighbor 10.0.12.2 route-map R2-IN in
exit-address-family
exit
!- Lines 4–7: The RPKI block. It exists only when bgpd runs with
-M rpki. - Line 5: How often to poll the cache for updates (seconds). RTR also pushes notifications when the data changes.
- Line 6: The RTR server. FRR 10 syntax includes the transport (
tcp;sshis also supported). Add a second line withpreference 2for a backup validator. - Lines 9–11: Drop Invalids.
match rpkialso acceptsvalidandnotfound. - Lines 13–14: Accept everything else (Valid and NotFound). In production this route-map would also contain your prefix filters.
- Line 21: Keeps a pre-policy copy of received routes so
received-routescan show what was filtered. Costs memory; enable it while deploying. - Line 22: Attach the policy inbound.
frr defaults traditional
hostname R2
!
ip route 192.0.2.0/24 Null0
ip route 198.51.100.0/24 Null0
ip route 203.0.113.0/24 Null0
ip route 203.0.113.128/25 Null0
!
router bgp 64501
bgp router-id 10.255.0.2
no bgp ebgp-requires-policy
neighbor 10.0.12.1 remote-as 64500
!
address-family ipv4 unicast
network 192.0.2.0/24
network 198.51.100.0/24
network 203.0.113.0/24
network 203.0.113.128/25
exit-address-family
exit
!- Lines 4–7: Discard routes so the
networkstatements have something to originate. - Line 11: Lab shortcut so R2 sends without an outbound policy. Never do this in production.
- Lines 15–18: One NotFound, two Invalids and one Valid, from R1's point of view.
# docker compose up -d — then: docker compose exec r1 vtysh
# Needs ./daemons from the FRR image with bgpd=yes and
# bgpd_options=" -A 127.0.0.1 -M rpki"
name: rpki-lab
services:
rtr:
image: rpki/stayrtr:latest
command: ["-cache", "/vrps.json", "-checktime=false", "-bind", ":3323", "-refresh", "30"]
volumes: ["./lab-vrps.json:/vrps.json:ro"]
networks: { lab: { ipv4_address: 10.0.12.10 } }
r1:
image: quay.io/frrouting/frr:10.2.1
privileged: true
volumes: ["./daemons:/etc/frr/daemons:ro", "./r1-rov-frr.conf:/etc/frr/frr.conf"]
networks: { lab: { ipv4_address: 10.0.12.1 } }
r2:
image: quay.io/frrouting/frr:10.2.1
privileged: true
volumes: ["./daemons:/etc/frr/daemons:ro", "./r2-frr.conf:/etc/frr/frr.conf"]
networks: { lab: { ipv4_address: 10.0.12.2 } }
networks:
lab:
ipam: { config: [{ subnet: 10.0.12.0/24, gateway: 10.0.12.254 }] }- Lines 2–3: FRR's RPKI module must be loaded in
/etc/frr/daemons. - Line 8: StayRTR serves a local JSON file instead of a validator's output.
-checktime=falsestops it rejecting the file as stale. - Line 23: Docker reserves the first address as the gateway by default, so it's moved out of the way of R1's 10.0.12.1.
R1# show bgp ipv4 unicast
BGP table version is 4, local router ID is 10.255.0.1, vrf id 0
Default local pref 100, local AS 64500
Status codes: s suppressed, d damped, h history, u unsorted, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found
Network Next Hop Metric LocPrf Weight Path
N*> 192.0.2.0/24 10.0.12.2 0 0 64501 i
I*> 198.51.100.0/24 10.0.12.2 0 0 64501 i
V*> 203.0.113.0/24 10.0.12.2 0 0 64501 i
I*> 203.0.113.128/25 10.0.12.2 0 0 64501 i
Displayed 4 routes and 4 total paths
R1# show bgp ipv4 unicast 198.51.100.0/24
BGP routing table entry for 198.51.100.0/24, version 3
Paths: (1 available, best #1, table default)
Not advertised to any peer
64501
10.0.12.2 from 10.0.12.2 (10.255.0.2)
Origin IGP, metric 0, valid, external, best (First path received), rpki validation-state: invalid
Last update: Fri Oct 16 17:25:51 2026- Line 8: With RPKI enabled, FRR adds a state code in front of each route.
- Line 11: N: no VRP covers 192.0.2.0/24.
- Line 12: I: covered by 198.51.100.0/24-24, but only AS64502 is authorised and the origin here is 64501.
- Line 13: V: origin, prefix and length all match the VRP.
- Line 14: I: right origin, but /25 exceeds maxLength 24.
- Line 23: The per-route view spells the state out.
R1# show rpki cache-connection
Connected to group 1
rpki tcp cache 10.0.12.10 3323 pref 1 (connected)
R1# show rpki prefix-table
RPKI/RTR prefix table
Prefix Prefix Length Origin-AS
198.51.100.0 24 - 24 64502
203.0.113.0 24 - 24 64501
Number of IPv4 Prefixes: 2
Number of IPv6 Prefixes: 0
R1# show bgp ipv4 unicast neighbors 10.0.12.2 received-routes
Network Next Hop Metric LocPrf Weight Path
*> 192.0.2.0/24 10.0.12.2 0 0 64501 i
*> 198.51.100.0/24 10.0.12.2 0 0 64501 i
*> 203.0.113.0/24 10.0.12.2 0 0 64501 i
*> 203.0.113.128/25 10.0.12.2 0 0 64501 i
Total number of prefixes 4 (2 filtered)
R1# show bgp ipv4 unicast
Network Next Hop Metric LocPrf Weight Path
N*> 192.0.2.0/24 10.0.12.2 0 0 64501 i
V*> 203.0.113.0/24 10.0.12.2 0 0 64501 i
Displayed 2 routes and 2 total paths- Lines 1–3: The RTR session is up.
- Lines 4–10: The two VRPs the router is enforcing.
- Lines 11–18: Pre-policy view (thanks to soft-reconfiguration): all four routes arrived, and 2 were filtered.
- Lines 19–24: Post-policy: only the Valid and NotFound routes remain.
Check your understanding
In the lab, the VRP for 203.0.113.0/24 is changed to maxLength 25. R1 receives the new VRP over RTR within seconds. What happens to 203.0.113.128/25, which R1 had already filtered as Invalid?
- It is accepted automatically as soon as the VRP arrives
- It stays filtered until inbound policy is re-run, e.g.
clear bgp ipv4 unicast 10.0.12.2 soft in - The BGP session resets so it can be re-validated
- 203.0.113.0/24 becomes Invalid