In April 2018, a BGP hijack redirected traffic destined for Amazon's Route 53 DNS service through a small ISP in Ohio. The attackers rerouted queries for MyEtherWallet.com to a server in Russia and stole approximately $150,000 in cryptocurrency in under two hours. The attack exploited exactly one thing: the absence of route filtering on upstream providers.
BGP — the Border Gateway Protocol — is the routing protocol that holds the internet together. Every autonomous system (AS) on the internet uses BGP to announce which IP prefixes it owns and to learn routes to reach every other network. The problem is that BGP was designed in 1989 with implicit trust. Any AS can announce any prefix, and without filtering, its neighbors will believe it.
This article covers practical BGP route filtering techniques you can implement today, from basic prefix lists to RPKI validation, with real configurations for both Cisco IOS-XE and BIRD (common on Linux-based routers).
Before configuring filters, you need to understand what you are defending against. BGP attacks fall into three categories:
Route hijacking occurs when an AS announces prefixes it does not own. Traffic destined for the legitimate owner gets pulled toward the hijacker instead. This is the most common and dangerous attack — it can intercept email, DNS, financial transactions, and API traffic without the victim even knowing.
Route leaks happen when an AS re-announces routes it learned from one peer to another peer in violation of the intended routing policy. A classic example: a small ISP accidentally re-announces its full upstream table to another upstream, briefly becoming a transit provider for half the internet. This causes congestion, packet loss, and sometimes full outages for major networks.
Prefix de-aggregation attacks exploit BGP's preference for more-specific routes. If you announce 192.0.2.0/24, an attacker can announce 192.0.2.0/25 and 192.0.2.128/25 — two more-specific routes that will be preferred by every router on the internet, effectively stealing your traffic even though your announcement is still visible.
The most basic and most important defense is explicit prefix filtering on every BGP session. You should never accept or announce routes without a filter. The principle is simple: define exactly which prefixes you expect from each peer, and deny everything else.
Cisco IOS-XE — filtering a customer that should only announce 203.0.113.0/24:
ip prefix-list CUSTOMER-A-IN seq 5 permit 203.0.113.0/24
ip prefix-list CUSTOMER-A-IN seq 100 deny 0.0.0.0/0 le 32
router bgp 64500
neighbor 198.51.100.1 remote-as 64501
address-family ipv4 unicast
neighbor 198.51.100.1 prefix-list CUSTOMER-A-IN in
BIRD 2 — equivalent configuration:
filter customer_a_import {
if net = 203.0.113.0/24 then accept;
reject;
}
protocol bgp customer_a {
local as 64500;
neighbor 198.51.100.1 as 64501;
ipv4 {
import filter customer_a_import;
export none;
};
}
For upstream and transit providers, you typically accept a full table but should still filter out bogons, your own prefixes (to prevent loops), and excessively specific routes:
ip prefix-list BOGONS seq 5 deny 0.0.0.0/8 le 32
ip prefix-list BOGONS seq 10 deny 10.0.0.0/8 le 32
ip prefix-list BOGONS seq 15 deny 100.64.0.0/10 le 32
ip prefix-list BOGONS seq 20 deny 127.0.0.0/8 le 32
ip prefix-list BOGONS seq 25 deny 169.254.0.0/16 le 32
ip prefix-list BOGONS seq 30 deny 172.16.0.0/12 le 32
ip prefix-list BOGONS seq 35 deny 192.0.2.0/24 le 32
ip prefix-list BOGONS seq 40 deny 192.168.0.0/16 le 32
ip prefix-list BOGONS seq 45 deny 198.18.0.0/15 le 32
ip prefix-list BOGONS seq 50 deny 198.51.100.0/24 le 32
ip prefix-list BOGONS seq 55 deny 203.0.113.0/24 le 32
ip prefix-list BOGONS seq 60 deny 224.0.0.0/4 le 32
ip prefix-list BOGONS seq 65 deny 240.0.0.0/4 le 32
ip prefix-list BOGONS seq 100 permit 0.0.0.0/0 le 24
That last line — le 24 — is critical. It rejects any prefix more specific than a /24 from your upstream sessions. In the global BGP table, /24 is the generally accepted minimum prefix length. Accepting anything longer invites de-aggregation attacks and table bloat.
Prefix lists tell you what networks a peer should announce. AS-path filters tell you who should be in the path. Combining both creates a much stronger defense.
For a customer AS that should only announce its own prefixes (no transit):
ip as-path access-list 10 permit ^64501$
router bgp 64500
address-family ipv4 unicast
neighbor 198.51.100.1 filter-list 10 in
The regex ^64501$ means: the AS path must contain exactly AS 64501 and nothing else. If the customer accidentally leaks routes from other networks they peer with, those routes will have additional ASNs in the path and get rejected.
For transit providers, limit the maximum AS-path length to reject absurdly long paths (which are sometimes used in hijacking to poison specific routes):
ip as-path access-list 20 deny _[0-9]+_[0-9]+_[0-9]+_[0-9]+_[0-9]+_[0-9]+_[0-9]+_
ip as-path access-list 20 permit .*
This rejects any route with more than 7 ASNs in the path — a reasonable ceiling for legitimate internet routes. Paths longer than that are almost always either route leaks or deliberate manipulation.
Resource Public Key Infrastructure (RPKI) is the most significant improvement to BGP security in the last decade. It works by creating cryptographic attestations called Route Origin Authorizations (ROAs) that bind an IP prefix to the AS number authorized to originate it.
Here is how it works in practice:
• You create a ROA in your RIR portal (RIPE, ARIN, APNIC, etc.) saying "AS 64500 is authorized to announce 203.0.113.0/24 with maximum length /24"
• Validator software (Routinator, FORT, rpki-client) downloads all ROAs from the five RIRs and builds a validated cache
• Your BGP routers query this cache via the RTR (RPKI-to-Router) protocol
• Each received BGP route is checked: if the origin AS and prefix match a ROA, it is marked Valid. If it contradicts a ROA, it is marked Invalid. If no ROA exists, it is NotFound
Setting up Routinator on a Linux server:
# Install Routinator (Rust-based RPKI validator)
apt install routinator
# Initialize the TAL (Trust Anchor Locator) files
routinator init --accept-arin-rpa
# Run as a daemon with RTR on port 3323
routinator server --rtr 0.0.0.0:3323 --http 127.0.0.1:8323
Connecting Cisco IOS-XE to the RPKI validator:
router bgp 64500
rpki server tcp 10.0.0.50 port 3323 refresh 300
address-family ipv4 unicast
neighbor 198.51.100.1 route-map RPKI-FILTER in
route-map RPKI-FILTER permit 10
match rpki valid
set local-preference 200
route-map RPKI-FILTER permit 20
match rpki not-found
set local-preference 100
route-map RPKI-FILTER deny 30
match rpki invalid
BIRD 2 RPKI configuration:
protocol rpki rpki_validator {
roa4 { table roa_v4; };
roa6 { table roa_v6; };
remote "10.0.0.50" port 3323;
retry keep 90;
refresh keep 300;
expire keep 600;
}
filter apply_rpki {
if (roa_check(roa_v4, net, bgp_path.last) = ROA_INVALID) then reject;
if (roa_check(roa_v4, net, bgp_path.last) = ROA_VALID) then {
bgp_local_pref = 200;
accept;
}
bgp_local_pref = 100;
accept;
}
The key design decision here is what to do with NotFound routes. Dropping them would break connectivity to a significant portion of the internet — as of mid-2026, roughly 60% of routes have ROAs, which is enormous progress from the 20% coverage of 2022, but still means 40% of legitimate routes would be rejected. The safe approach is to accept NotFound at a lower local preference while rejecting Invalid outright.
BGP communities are metadata tags attached to routes that signal policy intent between networks. Well-known communities like NO_EXPORT (65535:65281) and NO_ADVERTISE (65535:65282) prevent routes from propagating beyond their intended scope.
Most tier-1 and tier-2 transit providers support action communities that let you control how your routes propagate. For example, with many providers you can tag a route to be announced only to peers in a specific region, or prepend your AS toward a specific upstream.
# Announce to European peers only (example provider community)
route-map EXPORT-TRANSIT permit 10
match ip address prefix-list MY-PREFIXES
set community 64500:1000 64500:2100
# Blackhole a prefix under attack (RFC 7999)
route-map BLACKHOLE permit 10
match ip address prefix-list ATTACKED-PREFIX
set community 65535:666
set ip next-hop 192.0.2.1
The blackhole community (65535:666) defined in RFC 7999 is particularly important for DDoS mitigation. When you announce a /32 with this community, your upstream providers will null-route traffic destined for that IP at their edge, preventing attack traffic from ever reaching your network. This sacrifices the targeted IP but protects everything else.
Filtering prevents bad routes from entering your network, but you also need to know when someone is trying to hijack your prefixes externally. Several free and commercial tools exist for this:
• RIPE RIS / RIPEstat: provides real-time BGP data from hundreds of route collectors worldwide. You can set up alerts for your prefixes at stat.ripe.net
• BGPStream (CAIDA): open-source framework for consuming real-time BGP data, useful for building custom monitoring
• Cloudflare Radar: tracks BGP route announcements and can alert on anomalies
• bgp.tools: excellent free tool that shows who is announcing your prefixes and from where, with historical data
A basic monitoring script using BGPStream:
#!/usr/bin/env python3
import pybgpstream
stream = pybgpstream.BGPStream(
project="ris-live",
filter="prefix more 203.0.113.0/24"
)
for elem in stream:
if elem.type == "A": # Announcement
origin_asn = elem.fields["as-path"].split()[-1]
if origin_asn != "64500": # Not our AS
print(f"ALERT: {elem.fields['prefix']} announced by AS{origin_asn}")
print(f" Path: {elem.fields['as-path']}")
print(f" Collector: {elem.collector}")
No single layer of filtering is sufficient. A production BGP deployment should combine all four layers:
1. Prefix lists on every session. Define exactly what each peer should announce. Deny everything else. Update these when customers add new allocations — never use an open inbound policy.
2. AS-path filters to validate the originator. Ensure customer routes contain only their ASN. Limit maximum path length on upstream sessions to reject manipulated paths.
3. RPKI validation to cryptographically verify origin authority. Run a local validator, reject Invalid routes, prefer Valid routes with higher local preference. Create ROAs for all your own prefixes.
4. Community-based signaling for traffic engineering and incident response. Use blackhole communities for DDoS mitigation. Tag routes with appropriate scope communities to prevent unintended propagation.
On top of these, implement maximum-prefix limits on every BGP session. If a customer who should announce 2 prefixes suddenly sends 10,000, the session should shut down automatically:
router bgp 64500
address-family ipv4 unicast
neighbor 198.51.100.1 maximum-prefix 10 warning-only 80
This shuts down the session if the neighbor sends more than 10 prefixes, with a warning at 80% (8 prefixes). It is a simple but effective safeguard against route leaks and misconfigurations.
If you are running a hosting company — or choosing one — BGP security is not optional. A hosting provider that does not filter routes is exposing every customer on its network to hijacking risk. When a provider's upstream routes get hijacked, customer traffic can be silently intercepted, analyzed, or dropped. Email, API calls, database replication, backup transfers — all of it transits BGP.
At SwissLayer, our network operates on a Cisco backbone with strict prefix filtering on every BGP session, RPKI validation, and real-time route monitoring. Combined with our multi-layer DDoS protection and dedicated server infrastructure, we ensure that your traffic takes the path it is supposed to — and nowhere else.
Need infrastructure with network security built in, not bolted on? Explore our dedicated servers with 10Gbps–40Gbps connectivity on our filtered, RPKI-validated network, or check out our managed infrastructure for fully hands-off operation.