Longest Prefix Match and the Mechanics of Route Leaks
fib_trie.c), while IPv6 uses a radix trie (ip6_fib.c). Both implementations guarantee that if a packet matches both a broad aggregate announcement (such as a /20) and a narrower sub-prefix (such as a /24), the router will forward the packet along the path defined by the /24, regardless of AS-Path length, Multi-Exit Discriminator (MED), or BGP local preference.
Because Border Gateway Protocol (BGP) version 4 is inherently trusting of Network Layer Reachability Information (NLRI) attributes by default, border routers without cryptographic origin enforcement accept advertisements containing unauthorized more-specific prefixes. In a classic BGP route leak, an intermediary Autonomous System (AS)—often a multi-homed non-transit customer or regional tier-2 network—receives internal or customer routes and inadvertently re-announces them to an upstream transit provider. When downstream transit providers do not enforce strict ingress prefix filters, these more-specific routes traverse Tier-1 backbones within seconds.
Because the injected routes feature narrower subnet masks (for example, breaking down an authorized 198.51.100.0/20 into sixteen /24 announcements), the global LPM behavior forces outbound edge traffic toward the leaking intermediary. If the intermediary lacks the transit pipe capacity to absorb the diverted aggregate volume, packets encounter severe bufferbloat followed by sustained tail-drop at the interface ingress rings, effectively creating a traffic blackhole across the global control plane.
Mitigating this failure mode on Linux-based software routers requires decoupling prefix reception from FIB insertion by introducing cryptographically validated trust boundaries. Resource Public Key Infrastructure (RPKI) paired with Route Origin Validation (ROV) provides this boundary by validating the right of an originating AS to announce a given prefix through signed Route Origin Authorizations (ROAs).
RPKI Architecture and the RPKI-to-Router (RTR) Protocol
- Prefix: The IP prefix authorized for origination (e.g.,
203.0.113.0/24). - Origin ASN: The autonomous system authorized to originate the prefix in eBGP (e.g.,
AS64496). - Maximum Length (MaxLength): The most specific prefix length the origin AS is allowed to advertise (e.g.,
/24). If absent, it defaults to the length of the Prefix itself.
rpki-client) perform repository synchronization and cryptographic verification. Once validated, the cache validator extracts the normalized tuple list: (Prefix, MaxLength, Origin ASN).
The validator then acts as a cache server, delivering this sanitized dataset to the routing daemon via the RPKI-to-Router protocol (RTR, RFC 6810 and RFC 8210). The routing daemon maintains an in-memory validation database populated by these RTR streams and cross-references all received BGP announcements against this table before allowing an advertisement to enter the routing table (RIB) or the kernel forwarding plane (FIB).
| RPKI Component | Operational Layer | Primary Responsibilities | Network / Transport Protocol |
|---|---|---|---|
| Trust Anchor / Repository | Authoritative Storage | Hosts signed manifests, CRLs, and ROA CMS payloads | HTTPS (RRDP) / Rsync |
| Cache Validator (e.g., Routinator) | Validation Proxy | Fetches signed objects, validates cryptographic chains, generates flat tables | RRDP/Rsync (Inbound), RTR (Outbound) |
| Routing Daemon (e.g., BIRD) | Control Plane (RIB) | Receives prefix-AS tuples, evaluates BGP updates, drops invalid routes | RTR over TCP (RFC 6810 / RFC 8210) |
| Linux Kernel | Data Plane (FIB) | Forwarding plane, packet lookup via LPM (fib_trie) | Netlink (RTM_NEWROUTE / RTM_DELROUTE) |
Route Origin Validation Decision States
AS_PATH attribute (the Origin AS, O). It searches its local ROA table for any matching entries. The evaluation engine produces one of three deterministic states as defined in RFC 6483:
- Valid (
ROA_VALID): At least one ROA matches the NLRI prefix where the candidate prefix length L is less than or equal to the ROAMaxLength, and the candidate Origin ASN O exactly matches the ROAAS. - Invalid (
ROA_INVALID): At least one ROA covers the prefix (i.e., the candidate prefix is identical to or falls within the subnet bounds of a known ROA prefix), but either the candidate Origin ASN O differs from the authorized ASN, or the advertised prefix length L exceeds the authorizedMaxLength. - Unknown / NotFound (
ROA_UNKNOWN): No ROA covers the announced prefix. The address space has not been registered in RPKI, or the corresponding RIR repository lacks a valid object.
TEXT
+---------------------------+
| Inbound BGP UPDATE (P, O) |
+-------------+-------------+
|
v
Does any ROA cover Prefix P?
|
+---------------+---------------+
| NO | YES
v v
+---------------+ Does ROA match ASN 'O' AND
| ROA_UNKNOWN | Length(P) <= MaxLength?
+---------------+ |
+-------+-------+
| YES | NO
v v
+-------------+ +-------------+
| ROA_VALID | | ROA_INVALID |
+-------------+ +-------------+ROA_INVALID routes. Leaking entities often emit de-aggregated subnets with a longer prefix length than the authorized MaxLength, or they re-advertise prefixes via local peering infrastructure where their own ASN or customer ASN ends up as the origin. Dropping ROA_INVALID routes at the ingress policy engine before path selection completely neutralizes these leaks, while leaving ROA_UNKNOWN routes unpenalized prevents collateral reachability loss for legacy networks that have not yet published ROAs.
Linux Kernel Data Plane and Netlink Bottlenecks
NETLINK_ROUTE).
Every route installation invokes an RTM_NEWROUTE Netlink message. The kernel's Netlink subsystem deserializes the payload, parses routing attributes (such as RTA_GATEWAY, RTA_OIF, and RTA_PRIORITY), and executes fib_table_insert() in net/ipv4/fib_trie.c. The IPv4 trie operates using node compression, which groups prefixes into dynamically resized trie nodes called tnodes.
When a router without RPKI ROV receives a leak comprising hundreds of thousands of disaggregated /24 prefixes, the control plane experiences three acute performance regressions:
- Trie Explosions and Cache Invalidation: The insertion of deeply nested, highly granular
/24leaves causes widespread reallocation and node resizing oftnodesin the kernel trie. This flushes the route cache and invalidates CPU L1/L2/L3 cache lines associated with high-speed FIB lookups. If this occurs concurrently with regular forwarding, packet forwarding latency increases dramatically. - Netlink Receive Buffer Overflows: During a massive route leak, BIRD generates an unprecedented burst of
RTM_NEWROUTEmessages. If the kernel Netlink receive buffer (net.core.rmem_default/net.core.rmem_max) cannot absorb the update volume, the socket emitsENOBUFS. BIRD loses synchronization with the kernel FIB and must trigger a full table resynchronization (kernelprotocol scan), consuming massive CPU cycles. - Lock Contention in the Routing Subsystem: In kernel versions prior to the integration of per-namespace decoupled routing tables, updating the FIB requires acquiring the
fib_trielock, competing directly with forwarding plane software interrupts (softirqcontext executingfib_lookup()). Under high egress firewall jitter or high interrupt rates, this lock contention leads to measurable packet drop at the NIC ring buffers.
reject policy on ROA_INVALID routes, the daemon drops the advertisements directly out of memory. The Netlink boundary is never crossed, no RTM_NEWROUTE allocations occur, and the kernel fib_trie remains untouched.
Deploying and Validating Routinator as an RTR Server
BASH
# Initialize the TAlarm directory and accept default APNIC, AFRINIC, ARIN, LACNIC, and RIPE TAs
routinator init --accept-arin-rsa
# Perform the initial cryptographical synchronization over RRDP/Rsync
routinator -v vrps3323 for RTR and exposes Prometheus metrics on port 9556:
INI
# /etc/routinator/routinator.conf
rtr-listen = ["10.0.100.10:3323", "[2001:db8:100::10]:3323"]
http-listen = ["10.0.100.10:9556"]
refresh = 600
retry = 120
expire = 7200
history-size = 10
strict = false
stale = "reject"
verbose = falseBASH
# Check RTR socket status
ss -tulpn | grep 3323
# Query local metrics to verify the quantity of Validated ROA Payloads (VRPs)
curl -s http://10.0.100.10:9556/metrics | grep routinator_vrps_totalTEXT
routinator_vrps_total{tal="afrinic",type="ipv4"} 18452
routinator_vrps_total{tal="apnic",type="ipv4"} 142104
routinator_vrps_total{tal="arin",type="ipv4"} 95412
routinator_vrps_total{tal="lacnic",type="ipv4"} 32185
routinator_vrps_total{tal="ripe",type="ipv4"} 210940Implementing ROV in BIRD 2
rpki protocol module. It integrates ROA records directly into internal lookup tables (roa4 and roa6), allowing validation inside standard filter functions.
The following configuration illustrates an enterprise or transit edge deployment with two redundant RTR validators, strict sanity validation, and drop rules for invalid NLRI on all external eBGP peering sessions.
INI
# /etc/bird/bird.conf
router id 198.51.100.1;
# Define dedicated ROA tables in the BIRD control plane
roa4 table rpkitable4;
roa6 table rpkitable6;
# Protocol instance for primary Routinator validator
protocol rpki rpki_primary {
roa4 { table rpkitable4; };
roa6 { table rpkitable6; };
# RTR server IPv4 address and port
remote 10.0.100.10 port 3323;
# Session parameters (RFC 8210)
retry 90;
refresh 600;
expire 7200;
transport tcp;
}
# Protocol instance for secondary fallback Routinator validator
protocol rpki rpki_secondary {
roa4 { table rpkitable4; };
roa6 { table rpkitable6; };
remote 10.0.100.11 port 3323;
retry 90;
refresh 600;
expire 7200;
transport tcp;
}
# Linux Kernel synchronization protocol
protocol kernel {
ipv4 {
table master4;
import none;
export filter {
# Never export private or link-local subnets into kernel FIB
if net ~ [ 10.0.0.0/8+, 172.16.0.0/12+, 192.168.0.0/16+ ] then reject;
accept;
};
};
kernel table 254; # Standard Linux main routing table
merge paths on; # Enable ECMP in kernel
}
protocol device {
scan time 10;
}
# Define the RPKI ingress validation filter
filter ebgp_in_validation {
# Step 1: Validate against internal IPv4 ROA table
if roa_check(rpkitable4, net, bgp_path.last) = ROA_INVALID then {
print "[RPKI REJECT] Invalid prefix detected: ", net, " ASN: ", bgp_path.last, " via Peer: ", from;
reject;
}
# Step 2: Handle ROA_VALID and ROA_UNKNOWN
# Standard routing logic accepts both, but can apply BGP communities for telemetry
if roa_check(rpkitable4, net, bgp_path.last) = ROA_VALID then {
# Attach informational community (e.g., 64496:100 = Valid)
bgp_community.add((64496, 100));
} else {
# Attach informational community for ROA_UNKNOWN (e.g., 64496:101 = Unknown)
bgp_community.add((64496, 101));
}
# Accept the route for BIRD path selection
accept;
}
# External BGP Session to Upstream Transit Provider
protocol bgp upstream_transit_a {
local as 64496;
neighbor 203.0.113.254 as 64500;
ipv4 {
table master4;
import filter ebgp_in_validation;
export filter {
# Only export local aggregate prefix
if net = 198.51.100.0/20 then accept;
reject;
};
};
}BASH
bird -p -c /etc/bird/bird.conf
birdc configureVerifying Route Origin Validation via birdc
BASH
birdc show protocolsTEXT
Name Proto Table State Since Info
rpki_primary RPKI --- up 10:14:02.124 Connected
rpki_secondary RPKI --- up 10:14:05.891 Connected
upstream_transit_a BGP master4 up 10:15:30.012 EstablishedBASH
birdc show roa table rpkitable4TEXT
Table rpkitable4:
198.51.100.0/24 max 24 as 64496 [rpki_primary 10:14:02.340]
203.0.113.0/24 max 24 as 64511 [rpki_primary 10:14:02.340]
1.0.0.0/24 max 24 as 13335 [rpki_primary 10:14:02.345]
1.1.1.0/24 max 24 as 13335 [rpki_primary 10:14:02.345]BASH
# Syntax: roa check <table-name> <prefix> <asn>
birdc "roa check rpkitable4 1.1.1.0/24 13335"TEXT
roa_check: 1.1.1.0/24 AS13335: ROA_VALID/25 sub-prefix or replaces the origin ASN with its own:
BASH
# Simulating origin AS mismatch
birdc "roa check rpkitable4 1.1.1.0/24 64500"TEXT
roa_check: 1.1.1.0/24 AS64500: ROA_INVALIDBASH
# Simulating prefix length exceeding MaxLength (/24)
birdc "roa check rpkitable4 1.1.1.128/25 13335"TEXT
roa_check: 1.1.1.128/25 AS13335: ROA_INVALIDmaster4:
BASH
birdc show route all 1.1.1.128/25TEXT
BIRD 2.0.10 ready.
# (No routes found - the announcement was rejected at ingress filter)Low-Level Kernel Verification and Diagnostics
iproute2:
BASH
# Query the kernel FIB explicitly
ip route show 1.1.1.128/25 table mainss:
BASH
ss -n -a --netlinkROUTE subsystem socket associated with the bird PID:
TEXT
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
rtnl UNCONN 0 0 kernel:0 bird:24810Recv-Q or Send-Q displays persistent non-zero values, the kernel or routing daemon is falling behind on socket deserialization. This typically requires tuning the Linux socket buffers in sysctl:
BASH
# Set maximum and default receive socket buffer to 16MB for route bursts
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.rmem_default=16777216
sysctl -w net.core.wmem_max=16777216
sysctl -w net.core.wmem_default=16777216/etc/sysctl.d/99-routing-buffers.conf.
Tracing Ingress Netlink Calls with bpftrace
bpftrace, administrators can trace the execution of fib_table_insert to verify that no ROA_INVALID routes trigger Netlink modifications.
The following bpftrace script tracks IPv4 routing table insertions in real time, logging the prefix and prefix length:
TEXT
#!/usr/bin/env bpftrace
#include <net/ip_fib.h>
kprobe:fib_table_insert
{
$cfg = (struct fib_config *)arg2;
dst =cfg->fc_dst;
len =cfg->fc_dst_len;
printf("FIB Insert: %d.%d.%d.%d/%d by PID %d (%s)
",
$dst & 0xFF,
($dst >> 8) & 0xFF,
($dst >> 16) & 0xFF,
($dst >> 24) & 0xFF,
$len,
pid,
comm
);
}BASH
bpftrace fib_trace.bt1.1.1.128/25) never hit fib_table_insert. This proves that the data plane is fully decoupled from the ingress BGP churn, preserving line-rate forwarding and shielding CPU caches from trie reorganization overhead.
Handling Fail-Open, Validator Outages, and Edge Trade-offs
RTR Transport Failures
expire timer (typically set between 3600 to 7200 seconds). If the routing daemon loses contact with all RTR caches, it retains the existing ROA database until the timer expires.
If the timer lapses without re-establishing an RTR session, BIRD purges the ROA tables. When the ROA table is empty, the result of every roa_check() query shifts from either ROA_VALID or ROA_INVALID to ROA_UNKNOWN. Under the standard RFC 6483 configuration, ROA_UNKNOWN routes are accepted. The router therefore gracefully "fails open": it loses protection against route leaks and hijacks, but does not execute a widespread self-inflicted routing outage.
Sub-Prefix Filtering Integration
ROA_UNKNOWN). To establish defense-in-depth on border routers:
- Enforce strict Maximum Prefix Limits (
mrtdump/import limit) per eBGP peer to drop sessions when a peer leaks its entire full table. - Apply explicit Prefix-Length Filtering to reject any incoming IPv4 announcement longer than
/24and IPv6 longer than/48, as transit backbones uniformly drop these lengths according to global filtering conventions. - Combine ROV with internal tracking systems. When managing multi-datacenter distributed systems, ensure that external route churn does not induce jitter across edge egress firewalls, a scenario explored in Hardening Raft Consensus Against Egress Firewall Jitter in Multi-Region Sharded Databases.
fib_trie from malicious or accidental routing disruptions.