Logo

Securing a BGP session: RPKI, MD5, max-prefix, GTSM, prefix-list

Securing a BGP session: RPKI, MD5, max-prefix, GTSM, prefix-list

The 5 essential mechanisms to harden a transit IP BGP session: RPKI ROAs, TCP-MD5 authentication, prefix limit, GTSM (TTL security), and bogon prefix-list. With concrete examples for BIRD, FRR, Cisco, Arista.

Introduction

An unsecured BGP session means:

  • An attacker hijacking TCP/179 and injecting routes
  • A peer accidentally sending you 800k prefixes and OOM-ing your router
  • You accepting prefixes that should never exist on the Internet (RFC 1918, bogons)
  • A neighbor announcing a prefix it doesn't own, and you sending traffic there

This tutorial gives you the 5 layers every transit operator (and every transit customer, like you with VeryCloud) should put in place. From minimum-viable to serious-pro.

Prerequisites

  • A working BGP session to VeryCloud (see BIRD/FRR/Cisco/Arista tutorials)
  • Your IPv4/IPv6 prefixes and an account at your RIR
  • Access to your BGP daemon config

Layer 1: RPKI ROAs (Route Origin Authorization)

RPKI (RFC 6480) cryptographically links a prefix to an ASN authorized to announce it. The #1 defense against BGP hijacks.

You publish a ROA at your RIR saying:

"Prefix 192.0.2.0/24 may be announced by AS198825, max-length 24"

VeryCloud (and all serious operators) filter RPKI-invalid announcements.

Create a ROA at RIPE NCC

  1. Sign in at https://my.ripe.net
  2. Go to Resources → ROAs
  3. Click Create ROA
  4. Fill in:
    • Prefix: 192.0.2.0/24
    • Max length: 24 (only /24 allowed) or 32 (all sub-divisions allowed)
    • Authorized ASN: 198825 (the AS announcing, here VeryCloud)
  5. Validate

⚠️ If you use your own public ASN instead of AS198825, put your ASN in the ROA. With a private AS (64512-65534), no ROA possible — rely solely on IRR.

Verification (propagation ~30 min):

curl -s "https://stat.ripe.net/data/rpki-validation/data.json?resource=AS198825&prefix=192.0.2.0/24" | jq

Your router side: RPKI validation

If you want to filter RPKI-invalid routes yourself (in addition to VeryCloud's filtering), you need a local validator. Simplest: routinator (NLnet Labs).

sudo apt install -y routinator
sudo routinator init --accept-arin-rpa
sudo systemctl enable --now routinator

Routinator exposes an RTR endpoint on TCP/3323.

BIRD 2:

roa4 table r4;
roa6 table r6;

protocol rpki rpki1 {
    roa4 { table r4; };
    roa6 { table r6; };
    remote "127.0.0.1" port 3323;
    retry keep 90;
}

filter bgp_in_v4 {
    if (roa_check(r4, net, bgp_path.last) = ROA_INVALID) then reject;
    accept;
}

FRR:

rpki
 rpki cache 127.0.0.1 3323 preference 1
exit

router bgp 65000
 address-family ipv4 unicast
  neighbor 169.254.X.1 route-map RPKI-CHECK in
 exit-address-family
exit

route-map RPKI-CHECK deny 10
 match rpki invalid
route-map RPKI-CHECK permit 20

Layer 2: TCP-MD5 authentication

TCP-MD5 (RFC 2385) adds a MAC in every TCP segment of the BGP session. Without the shared password, impossible to inject traffic.

It's old (1998, MD5 collision-broken) but enough to block attackers trying to spoof the BGP session — they'd need to break MD5 in seconds while spoofing an exact TCP port, operationally unfeasible.

Modern successor is TCP-AO (RFC 5925), much less deployed. MD5 remains de-facto standard.

protocol bgp verycloud_v4 {
    neighbor 169.254.X.1 as 198825;
    password "YOUR_MD5_SECRET";
}
router bgp 65000
 neighbor 169.254.X.1 password YOUR_MD5_SECRET

💡 Password must be identical byte-for-byte both sides. VeryCloud provides MD5 in the technical email.

Layer 3: Max-prefix / maximum-prefix / import limit

Hard cap on accepted prefixes. If VeryCloud accidentally sends 600k routes, your router drops the session instead of exploding.

Tuning the value:

  • Default route only: 5 is enough
  • Full BGP table v4 (~950k routes May 2026): set 1100000
  • Full BGP table v6 (~200k routes May 2026): set 250000
ipv4 {
    import limit 5000 action restart;
};
neighbor 169.254.X.1 maximum-prefix 5000 80 restart 30
neighbor VC-V4 maximum-routes 5000 warning-limit 4000

Layer 4: GTSM (Generalized TTL Security Mechanism)

GTSM (RFC 5082) checks the TTL of BGP packets. A BGP session between two directly-connected peers should have TTL=255. A distant attacker spoofing TCP/179 will have a lower TTL → drop.

Inapplicable simply if peer is several hops away, but usable for tunnels: it's always 1 hop in the tunnel, so TTL=255 expected.

protocol bgp verycloud_v4 {
    ttl security on;
}
neighbor 169.254.X.1 ttl-security hops 1
neighbor 169.254.X.1 ttl maximum-hops 1

⚠️ Activating ttl security requires disabling ebgp-multihop (incompatible in some implementations). Check with VeryCloud first.

Layer 5: Prefix-list / anti-bogon filter

Explicitly reject any prefix that should never transit on the Internet: RFC 1918, link-local, multicast, doc (RFC 5737, RFC 3849).

Minimum IPv4 list:

0.0.0.0/8        # this network
10.0.0.0/8       # RFC 1918
100.64.0.0/10    # CGNAT (RFC 6598)
127.0.0.0/8      # loopback
169.254.0.0/16   # link-local
172.16.0.0/12    # RFC 1918
192.0.0.0/24     # IETF protocol assignments
192.0.2.0/24     # TEST-NET-1
192.168.0.0/16   # RFC 1918
198.18.0.0/15    # benchmark
198.51.100.0/24  # TEST-NET-2
203.0.113.0/24   # TEST-NET-3
224.0.0.0/4      # multicast
240.0.0.0/4      # reserved

Minimum IPv6 list:

::/8             # incl. ::1 loopback
64:ff9b::/96     # NAT64
100::/64         # discard prefix
2001::/23        # IETF protocol
2001:db8::/32    # documentation
fc00::/7         # ULA
fe80::/10        # link-local
ff00::/8         # multicast

💡 You can automate the list with Team Cymru Bogon Reference: a free BGP service flushing bogons in real-time.

Bonus: IRR (Internet Routing Registry)

Beyond RPKI, many operators filter on the IRR: route(6)-object in RIPE/RADB database.

To announce 192.0.2.0/24 via AS65000, create in RIPE:

route:          192.0.2.0/24
origin:         AS65000
mnt-by:         YOUR-MNT
source:         RIPE

VeryCloud (and Hurricane Electric, Cogent, etc.) check IRR. Without route-object, many operators drop your announcements.

Minimum security checklist

#LayerMandatory?
1ROA RPKI on your prefixes✅ YES
2TCP-MD5 on the session✅ YES
3maximum-prefix / import limit✅ YES
4GTSM (if both sides support)Recommended
5Anti-bogon prefix-list inbound✅ YES
6Route-object IRR✅ YES
7Strict outbound filter (only your prefixes)✅ YES

Without 1, 2, 3, 5, 6, 7 → don't put the session in prod. Everything else is bonus.

Troubleshooting

ROA published but routes still rejected

  • Slow propagation: wait 30-60 min
  • Local validator stale cache: routinator vrps
  • Incorrect max-length in ROA (often forgotten)

MD5 password failing

  • Invisible whitespace in password (very common when copy-pasted)
  • Encoding: avoid non-ASCII chars

maximum-prefix exceeded

  • Receiving more than expected: tune value
  • VeryCloud side sending full table when you wanted default

Useful commands

sudo birdc show route protocol verycloud_v4
sudo vtysh -c "show rpki cache-server"
sudo vtysh -c "show rpki prefix-table"
sudo routinator vrps

Conclusion

5 layers: RPKI, MD5, max-prefix, GTSM, prefix-list. Plus IRR bonus. Sounds like a lot, but it's 30 minutes of clean config for years of peace. Every serious operator does this. Skip a layer, you take proportional risk.

Going further: ASPA (RFC 9286) to validate AS paths, BGPsec (RFC 8205) to sign announcements, MANRS routing security framework.

Resources

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members